I'm trying to create a blog post in which the URL would be blog/{year}/{month}/{name}. I want the blog items to be the direct children of the blog area node. The year & month value would come from the UpdateDate property of each blog item node.
Any help in directing my curiousty would be greatly appreciated. I'm guessing it's going to involve refereting back to standard MVC or possiblely adding an event which changes adds new string to the umbracoURLAlias when a blog item is saved, then referencing the umbracoURLAlias for menus
Not sure exactly I'm afraid, but most things in this area of customising the pipeline require you to create a class that inherits ApplicationEventHandler, and then set up/override the resolution to find your class in the ApplicationStarting event. So I'd expect it''s there you need to do it.
'JohnChurchley.Web.Extensions.UrlSegmentProvider' does not implement interface member 'Umbraco.Core.Strings.IUrlSegmentProvider.GetUrlSegment(Umbraco.Core.Models.IContentBase, System.Globalization.CultureInfo)'D:\Websites\JohnChurchley\JohnChurchley.Web\Extensions\BlogHelper.cs
My code is below (thanks for all the help)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Umbraco.Core;
using Umbraco.Core.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Optimization;
using Umbraco.Core;
using Umbraco.Core.Models;
namespace JohnChurchley.Web.App_Start
{
public class ApplicationEventHandler : IApplicationEventHandler
{
public interface IUrlSegmentProvider
{
string GetUrlSegment(IContentBase content);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Umbraco.Core;
using Umbraco.Core.Models;
namespace JohnChurchley.Web.App_Start
{
public class UrlSegmentProvider : Umbraco.Core.Strings.IUrlSegmentProvider
{
public string GetUrlSegment(IContentBase content)
{
var services = ApplicationContext.Current.Services;
var contentType = services.ContentTypeService.GetContentType(content.ContentTypeId);
if (contentType.Alias != "BlogItem") return null;
var date = content.UpdateDate.ToString("yyyy-MM-");
Not sure I'll be able to help much more I'm afraid John as not actually done this myself, but it looks like the problem is that the compiler is looking for a method with a signature like this:
Thanks Andy gave it ago but unfortunatly created a new compiler error.
Error1'JohnChurchley.Web.App_Start.UrlSegmentProvider' does not implement interface member 'Umbraco.Core.Strings.IUrlSegmentProvider.GetUrlSegment(Umbraco.Core.Models.IContentBase, System.Globalization.CultureInfo)'D:\Websites\JohnChurchley\JohnChurchley.Web\App_Start\BlogConfig.cs1118JohnChurchley.Web
New Code:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Optimization;
using Umbraco.Core;
using Umbraco.Core.Models;
namespace JohnChurchley.Web.App_Start
{
public class ApplicationEventHandler : IApplicationEventHandler
{
public void OnApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { }
public void OnApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { }
public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Strings;
namespace JohnChurchley.Web.App_Start
{
public class Something : ApplicationEventHandler
{
protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
UrlSegmentProviderResolver.Current.InsertTypeBefore<DefaultUrlSegmentProvider, UrlSegmentProvider>();
}
}
public class UrlSegmentProvider : Umbraco.Core.Strings.IUrlSegmentProvider
{
public string GetUrlSegment(IContentBase content)
{
// Implement me
}
public string GetUrlSegment(IContentBase content, System.Globalization.CultureInfo cultureInfo)
{
var services = ApplicationContext.Current.Services;
var contentType = services.ContentTypeService.GetContentType(content.ContentTypeId);
if (contentType.Alias != "BlogItem")
return null;
var date = content.UpdateDate.ToString("yyyy-MM-");
return date + content.Name.ToUrlSegment();
}
}
}
The error you're getting is because you need to implement both public string GetUrlSegment(IContentBase content) AND public string GetUrlSegment(IContentBase content, System.Globalization.CultureInfo cultureInfo) methods.
Please do not define your own IUrlSegmentProvider in IApplicationEventHandler, you need to use Umbraco's provider. Also note that IApplicationEventHandler should be avoided, use ApplicationEventHandler instead. As that was the name of your class, I renamed it to "Something".
I don't know if this code will do anything but it should build (once you implement the other method). Be aware that the URL only changes when you publish the page that matches this scheme.
If you're not doing a multilingual site you might be able to get away with ignoring the culture info like so (not sure how well this would work though):
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Strings;
namespace JohnChurchley.Web.App_Start
{
public class Something : ApplicationEventHandler
{
protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
UrlSegmentProviderResolver.Current.InsertTypeBefore<DefaultUrlSegmentProvider, UrlSegmentProvider>();
}
}
public class UrlSegmentProvider : Umbraco.Core.Strings.IUrlSegmentProvider
{
public string GetUrlSegment(IContentBase content)
{
var services = ApplicationContext.Current.Services;
var contentType = services.ContentTypeService.GetContentType(content.ContentTypeId);
if (contentType.Alias != "BlogItem")
return null;
var date = content.UpdateDate.ToString("yyyy-MM-");
return date + content.Name.ToUrlSegment();
}
public string GetUrlSegment(IContentBase content, System.Globalization.CultureInfo cultureInfo)
{
return GetUrlSegment(content);
}
}
}
Hi Sebastiaan, it appears to work fine for multiple segements contains only numbers. However I'm was getting an error in the media section when I try to create a new folder media section. The work around I used was to check if the object exists before checking if the contentType.Alias = "BlogItem" not sure on whether this conforms to any good practice but might be useful to anyone trying to replicate this in the future.
if (Object.ReferenceEquals(contentType, null)){return null;}
is routing possible in this way?
I'm trying to create a blog post in which the URL would be blog/{year}/{month}/{name}. I want the blog items to be the direct children of the blog area node. The year & month value would come from the UpdateDate property of each blog item node.
Any help in directing my curiousty would be greatly appreciated. I'm guessing it's going to involve refereting back to standard MVC or possiblely adding an event which changes adds new string to the umbracoURLAlias when a blog item is saved, then referencing the umbracoURLAlias for menus
Can't help you much further than this as haven't yet tried to do something similar, but my understanding is you should look into implementing an IContentFinder and override the default URL provider. Documentation is rather patchy unfortunately, but it was all explained very well in a talk by Stephane at CodeGarden this year, so I'd suggest starting with that.
Andy
Brillaint thanks Andy.
Hi Andy,
Do you have anyideas where you would include the UrlSegmentProvider class?
Not sure exactly I'm afraid, but most things in this area of customising the pipeline require you to create a class that inherits ApplicationEventHandler, and then set up/override the resolution to find your class in the ApplicationStarting event. So I'd expect it''s there you need to do it.
Not sure I'll be able to help much more I'm afraid John as not actually done this myself, but it looks like the problem is that the compiler is looking for a method with a signature like this:
And yours is like this:
So if you add that extra argument that should resolve this build error.
Andy
Thanks Andy gave it ago but unfortunatly created a new compiler error.
Error1'JohnChurchley.Web.App_Start.UrlSegmentProvider' does not implement interface member 'Umbraco.Core.Strings.IUrlSegmentProvider.GetUrlSegment(Umbraco.Core.Models.IContentBase, System.Globalization.CultureInfo)'D:\Websites\JohnChurchley\JohnChurchley.Web\App_Start\BlogConfig.cs1118JohnChurchley.Web
New Code:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Optimization;
using Umbraco.Core;
using Umbraco.Core.Models;
namespace JohnChurchley.Web.App_Start
{
public class ApplicationEventHandler : IApplicationEventHandler
{
public void OnApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { }
public void OnApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { }
public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
public interface IUrlSegmentProvider
{
string GetUrlSegment(IContentBase content, CultureInfo cultureInfo);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Umbraco.Core;
using Umbraco.Core.Models;
namespace JohnChurchley.Web.App_Start
{
public class UrlSegmentProvider : Umbraco.Core.Strings.IUrlSegmentProvider
{
public string GetUrlSegment(IContentBase content, System.Globalization.CultureInfo cultureInfo)
{
var services = ApplicationContext.Current.Services;
var contentType = services.ContentTypeService.GetContentType(content.ContentTypeId);
if (contentType.Alias != "BlogItem") return null;
var date = content.UpdateDate.ToString("yyyy-MM-");
return date + content.Name.ToUrlSegment();
}
}
}
I think you need to do something like this:
The error you're getting is because you need to implement both
public string GetUrlSegment(IContentBase content)
ANDpublic string GetUrlSegment(IContentBase content, System.Globalization.CultureInfo cultureInfo)
methods.Please do not define your own
IUrlSegmentProvider
inIApplicationEventHandler
, you need to use Umbraco's provider. Also note thatIApplicationEventHandler
should be avoided, useApplicationEventHandler
instead. As that was the name of your class, I renamed it to "Something".I don't know if this code will do anything but it should build (once you implement the other method). Be aware that the URL only changes when you publish the page that matches this scheme.
Also see: http://our.umbraco.org/forum/developers/api-questions/49059-Custom-IUrlSegmentProvider-not-hit
If you're not doing a multilingual site you might be able to get away with ignoring the culture info like so (not sure how well this would work though):
Thanks Sebastiaan it worked great! I next attempted to add multiple segments i.e.
var date = content.UpdateDate.ToString("yyyy-MM-");
Became
var date = content.UpdateDate.ToString("yyyy/MMMM/");
however gave me a 404 error but when I changed it to
var date = content.UpdateDate.ToString("yyyy/");
or
var date = content.UpdateDate.ToString("yyyy/MM/");
It worked! Very close to getting my desired result, thanks for the suggestions and patience.
See the link I posted earlier, "/" is not allowed: http://our.umbraco.org/forum/developers/api-questions/49059-Custom-IUrlSegmentProvider-not-hit
Hi Sebastiaan, it appears to work fine for multiple segements contains only numbers. However I'm was getting an error in the media section when I try to create a new folder media section. The work around I used was to check if the object exists before checking if the contentType.Alias = "BlogItem" not sure on whether this conforms to any good practice but might be useful to anyone trying to replicate this in the future.
is working on a reply...