Using the property umbracoUrlName allows editors to alter the Url of a node ... and that works as expected.
However, Umbraco does not seem to check for duplicates, as it does when creating nodes with the same name (doing that results in numbered versions - e.g. "test page (1)").
So, we now have the situation where 2 pages have been created with different names but with the same umbracoUrlName value, giving them the same Url.
E.g. this is possible
Name = Test Page 1, umbracoUrlName = testpage, Url = /testpage
Name = Test Page 2, umbracoUrlName = testpage, Url = /testpage
I assume this is a bug? Is there an easy work around or code fix?
Yes, it has always been possible to create Urls which clash with umbracoUrlName and also with umbracoUrlAlias. Umbraco picks the first node it finds in umbraco.config cache file which matches the url.
umbracoUrlName and umbracoUrlAlias give the editor the power to override the default umbraco Url, but with this power comes the responsibility to not muck it up!
Which I've found catches people out sometimes.
In code you could work around this, you can could capture the Publishing Event, and check if any siblings of the saved node had a clashing url Name.
The gist of which is like so:
public class RegisterDuplicateUrlNameDetector : ApplicationEventHandler
{
protected override void ApplicationStarting
(UmbracoApplicationBase umbracoApplication,
ApplicationContext applicationContext)
{
ContentService.Publishing += ContentServicePublishing;
}
static void ContentServicePublishing(IPublishingStrategy sender,
PublishEventArgs<IContent> e)
{
foreach (var content in e.PublishedEntities){
// get content items UrlName - (more than one content item could be being published, hence the foreach)
var contentUrlName = content.HasProperty("umbracoUrlName") &&
!String.IsNullOrEmpty(content.GetValue<string>("umbracoUrlName"))
? content.GetValue<string>("umbracoUrlName")
: content.Name;
//get sibling content, eg published nodes at the same level in the tree that could contain a clashing url
if (content.Parent().Children().Any(f=>f.Id != content.Id && f.Published))
{
foreach (var sibling in content.Parent().Children().Where(f => f.Id != content.Id && f.Published))
{
// get each sibling UrlName
var siblingUrlName = sibling.HasProperty("umbracoUrlName") &&
!String.IsNullOrEmpty(sibling.GetValue<string>("umbracoUrlName"))
? sibling.GetValue<string>("umbracoUrlName")
: sibling.Name;
// check for clash
if (siblingUrlName.ToUrlSegment() == contentUrlName.ToUrlSegment())
{
// cancel publishing
e.Cancel = true;
// bubble up a message to the UI (umbraco 7.3 only)
e.Messages.Add(new EventMessage("Save Cancelled", "Clash of UrlNames: " + sibling.Id + "-" + sibling.Name + " and " + content.Id + "-" + content.Name, EventMessageType.Error));
}
}
}
}
}
}
But you'd have to think this would run on every publish, so there would be an overhead - you'd have to trade that off against how often the problem occurs, and how easy it is to resolve this kind of problem manually.
with umbracoUrlAlias - which enables clashes to happen anywhere across the site, I have in the past taken a different strategy of building a dashboard report that searched the site for clashing urls. So the editor would only need to run this report if a problem was perceived, rather than actually preventing the problem from occurring.
Hi, Marc, thank you for your post, it helped me a lot.
I think checking the cache is faster (note that im suscribing the published event because at the moment of publishing Umbraco has not calculated the final name:
private static void ContentServicePublished(IPublishingStrategy sender, PublishEventArgs<IContent> e)
{
var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
foreach (var content in e.PublishedEntities)
{
// get content items UrlName - (more than one content item could be being published, hence the foreach)
var contentUrlName = content.HasProperty("umbracoUrlName") &&
!String.IsNullOrEmpty(content.GetValue<string>("umbracoUrlName"))
? content.GetValue<string>("umbracoUrlName")
: content.Name;
var urlName = contentUrlName.ToUrlSegment();
var padre = umbracoHelper.TypedContent(content.ParentId);
var hermano = padre.Children.FirstOrDefault(c => c.Id != content.Id && c.UrlName == urlName);
if (hermano!=null)
{
// cancel publishing
//e.Cancel = true;
// bubble up a message to the UI (umbraco 7.3 only)
e.Messages.Add(new EventMessage("WARNING", "Clash of UrlNames: " + hermano.Id + "-" + hermano.Name + " and " + content.Id + "-" + content.Name, EventMessageType.Error));
}
}
}
At published event i cant cancel the event so i have change the message to be a warning. Hope this helps.
umbracoUrlName not checking for duplicates?
Using the property umbracoUrlName allows editors to alter the Url of a node ... and that works as expected.
However, Umbraco does not seem to check for duplicates, as it does when creating nodes with the same name (doing that results in numbered versions - e.g. "test page (1)").
So, we now have the situation where 2 pages have been created with different names but with the same umbracoUrlName value, giving them the same Url.
E.g. this is possible
Name = Test Page 1, umbracoUrlName = testpage, Url = /testpage Name = Test Page 2, umbracoUrlName = testpage, Url = /testpage
I assume this is a bug? Is there an easy work around or code fix?
We are using v7.2.8
Hi Gordon
Yes, it has always been possible to create Urls which clash with umbracoUrlName and also with umbracoUrlAlias. Umbraco picks the first node it finds in umbraco.config cache file which matches the url.
umbracoUrlName and umbracoUrlAlias give the editor the power to override the default umbraco Url, but with this power comes the responsibility to not muck it up!
Which I've found catches people out sometimes.
In code you could work around this, you can could capture the Publishing Event, and check if any siblings of the saved node had a clashing url Name.
The gist of which is like so:
But you'd have to think this would run on every publish, so there would be an overhead - you'd have to trade that off against how often the problem occurs, and how easy it is to resolve this kind of problem manually.
with umbracoUrlAlias - which enables clashes to happen anywhere across the site, I have in the past taken a different strategy of building a dashboard report that searched the site for clashing urls. So the editor would only need to run this report if a problem was perceived, rather than actually preventing the problem from occurring.
Hi, Marc, thank you for your post, it helped me a lot. I think checking the cache is faster (note that im suscribing the published event because at the moment of publishing Umbraco has not calculated the final name:
At published event i cant cancel the event so i have change the message to be a warning. Hope this helps.
is working on a reply...