If what you are looking for is more of a general rule, then you probobly have to create your own UrlProvider and ContentFinder. There is a great tutorial about this here:
Let me know if umbracoUrlName does´nt work for you and i can spare a few minutes to create a code example on how the ContentFinder works. Althogh i think umbracoUrlName is kind of what you are looking for.
using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper.Internal;
using Umbraco.Core;
using Umbraco.Web;
using Umbraco.Web.Routing;
namespace MyApplication.App_Code
{
public class CustomUrlProvider : IUrlProvider
{
public IEnumerable<string> GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
{
return Enumerable.Empty<string>();
}
public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode)
{
var content = umbracoContext.ContentCache.GetById(id);
if (content != null && content.HasProperty("customUrl") && content.HasValue("customUrl"))
return content.GetPropertyValue("customUrl").ToNullSafeString();
return null;
}
}
public class StartUpApplication : ApplicationEventHandler
{
protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
UrlProviderResolver.Current.InsertTypeBefore<DefaultUrlProvider, CustomUrlProvider>();
}
}
}
Add a property for your child items called CustomUrl:
Which will give you an url like this, even though this item is on level 3:
Maybe this could work for you, or help you come up with your own UrlProvider. You might want to create a general rule for all nodes of a specific doctype to get a automated root url. But this will probobly get you started.
Yes the page has a template
The alias name too is "customUrl"
The Link to document is too removing Parent Url from it.
But on running it shows page not found.
So, i had made use of umbracourlalias and provided the same name to it, Now its working perfectly
I can confirm that this is the case: You must provide the custom URL in the umbracoUrlAlias property as well (without the leading slash). Otherwise you'll get the 404
using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper.Internal;
using Umbraco.Core;
using Umbraco.Web;
using Umbraco.Web.Routing;
using System.Web;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Core.Events;
CustomUrlProvider:
public class CustomUrlProvider : IUrlProvider
{
public IEnumerable<string> GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
{
return Enumerable.Empty<string>();
}
public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode)
{
var content = umbracoContext.ContentCache.GetById(id);
if (content != null && content.HasProperty("customUrl") && content.HasValue("customUrl"))
return content.GetPropertyValue("customUrl").ToNullSafeString();
return null;
}
}
CustomUrlPContentFinder:
public class CustomUrlPContentFinder : IContentFinder
{
public bool TryFindContent(PublishedContentRequest contentRequest)
{
try
{
if (contentRequest != null)
{
//Get the current url.
var url = contentRequest.Uri.AbsolutePath;
//Get the page nodes that are already cached.
var cachedPageNodes = (Dictionary<string, ContentFinderItem>)HttpContext.Current.Cache["CachedPageNodes"];
if (cachedPageNodes != null)
{
//Check if the current url already has a page item.
if (cachedPageNodes.ContainsKey(url))
{
//If the current url already has a node use that so the rest of the code doesn't need to run again.
var contentFinderItem = cachedPageNodes[url];
contentRequest.PublishedContent = contentFinderItem.Content;
contentRequest.TrySetTemplate(contentFinderItem.Template);
return true;
}
}
//Split the url segments.
var path = contentRequest.Uri.AbsolutePath;
//Get all the root nodes.
var rootNodes = contentRequest.RoutingContext.UmbracoContext.ContentCache.GetAtRoot();
//Find the item that matches the last segment in the url.
var pageItem = rootNodes.DescendantsOrSelf("page").Where(x => x.HasProperty("customUrl") && x.GetPropertyValue("customUrl").ToString() == path).FirstOrDefault();
if (pageItem != null)
{
//Get the page item template.
var template = ApplicationContext.Current.Services.FileService.GetTemplate(pageItem.TemplateId);
if (template != null)
{
//Store the fields in the ContentFinderItem-object.
var contentFinderItem = new ContentFinderItem()
{
Template = template.Alias,
Content = pageItem
};
//If the correct node is found display that node.
contentRequest.PublishedContent = contentFinderItem.Content;
contentRequest.TrySetTemplate(contentFinderItem.Template);
if (cachedPageNodes != null)
{
//Add the new ContentFinderItem-object to the cache.
cachedPageNodes.Add(url, contentFinderItem);
}
else
{
//Create a new dictionary and store it in the cache.
cachedPageNodes = new Dictionary<string, ContentFinderItem>()
{
{
url, contentFinderItem
}
};
HttpContext.Current.Cache.Add("CachedPageNodes",
cachedPageNodes,
null,
DateTime.Now.AddDays(1),
System.Web.Caching.Cache.NoSlidingExpiration,
System.Web.Caching.CacheItemPriority.High,
null);
}
}
}
}
}
catch (Exception)
{
}
return contentRequest.PublishedContent != null;
}
}
ContentFinderItem:
public class ContentFinderItem
{
public ContentFinderItem()
{
}
public IPublishedContent Content { get; set; }
public string Template { get; set; }
}
StartUpApplication:
public class StartUpApplication : ApplicationEventHandler
{
protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
// After the node is found it's also added in a custom cache layer.
// Because of that the ContentFinder doesn't need to look for the node each time the page is requested.
// Make sure to clear the cache after save and publish because otherwise you'll get a cached node which is not up to date.
//Listen for when content is being saved
ContentService.Saving += ContentService_Saving;
//With the url providers we can change node urls.
UrlProviderResolver.Current.InsertTypeBefore<DefaultUrlProvider, CustomUrlProvider>();
//With the content finder we can match nodes to urls.
ContentFinderResolver.Current.InsertTypeBefore<ContentFinderByNotFoundHandlers, CustomUrlPContentFinder>();
}
private void ContentService_Saving(IContentService sender, SaveEventArgs<IContent> e)
{
//Clear the content finder cache.
HttpContext.Current.Cache.Remove("CachedPageNodes");
}
}
Thank you Micha! It should be mentioned that i have´nt tried this in production on an actual website, only locally.
I just wipped this together quickly to help Sagar with his problem, based on his criteria. It might need some extra tweaking or testing before relying on completly.
If anyone do try it in production, please post here if/how it works.
However, in the backoffice, GetById() is generating errors.
Do I need to implement an IContentFinder even if I am just using the Page Id for the Url?
Any help you can give me would be really useful.
Thanks
Muiris
EDIT: I've just been looking at the link below and I think I've found my problem. I was outputting the Page Id without a leading slash. I've added the slash now and it seems to have fixed my problem.
URL Routing
Hi all...
I want to chnage my URL name from domain.com/Parent/Child to domain/child
can any1 suggest how can i do that?
Hi Sagar.
Maybe you could use a umbracourlalias property?
Just create a textfield property for your doctype with the alias umbracourlalias.
https://our.umbraco.org/wiki/reference/umbraco-best-practices/umbracourlalias/.
http://www.mayflymedia.co.uk/blog/umbraco/alternate-page-name-in-umbraco-using-umbracourlalias/
If what you are looking for is more of a general rule, then you probobly have to create your own UrlProvider and ContentFinder. There is a great tutorial about this here:
http://24days.in/umbraco/2014/urlprovider-and-contentfinder/
Best of luck to you Sagar!
Hi Dennis
I don't want the alternate URl but want to change it.
Oh sorry, use umbracoUrlName instead. It replaces the url instead of making a alternative.
Otherwise i´d advise u to look in to http://24days.in/umbraco/2014/urlprovider-and-contentfinder/.
Let me know if umbracoUrlName does´nt work for you and i can spare a few minutes to create a code example on how the ContentFinder works. Althogh i think umbracoUrlName is kind of what you are looking for.
umbracoUrlName doesn't remove parent url from child Url
for e.g overview is child of about its url looks like domain/about/overview
i want it to be looked like domain/overview
Ok. Then you might need a UrlProvider.
A quick example:
CustomUrlProvider:
Add a property for your child items called CustomUrl:
Which will give you an url like this, even though this item is on level 3:
Maybe this could work for you, or help you come up with your own UrlProvider. You might want to create a general rule for all nodes of a specific doctype to get a automated root url. But this will probobly get you started.
best of luck!!
Did you follow the steps in my post? Does the page you are trying to navigate to have a template?
Make sure that the alias of the CustomUrl is exacly: customUrl. You might need to restart the app (change something in the web.config).
What value does the child element get on the generic property "Link to document"?
Yes the page has a template The alias name too is "customUrl" The Link to document is too removing Parent Url from it. But on running it shows page not found.
So, i had made use of umbracourlalias and provided the same name to it, Now its working perfectly
customUrl : /overview
umbracourlalias : overview
Cheers Sagar... Thnxx Dennis
I can confirm that this is the case: You must provide the custom URL in the umbracoUrlAlias property as well (without the leading slash). Otherwise you'll get the 404
Hi Sagar! You shoud´nt need to use both customUrl and umbracourlalias, buy hey, if it works for you thats great.
Glad it worked out for you! Best of luck with the rest of the site.
Have a great day!!
/ Dennis
About that 404 page ...
... implement a custom ContentFinder for this situation.
Dennis Adolfi already mentioned the ContentFinder; without it, a 404 page will appear.
@Dennis Adolfi: do you perhaps have a simple way to implement a contentfinder for this situation?
Off course, here goes (based on http://24days.in/umbraco/2014/urlprovider-and-contentfinder/):
Usings:
CustomUrlProvider:
CustomUrlPContentFinder:
ContentFinderItem:
StartUpApplication:
Best of luck to you!!
Hi Dennis,
When url routing needs to go beyond what's possible with umbracoUrlAlias, this is a great and flexible solution for a custom url!
Thanks!
Thank you Micha! It should be mentioned that i have´nt tried this in production on an actual website, only locally. I just wipped this together quickly to help Sagar with his problem, based on his criteria. It might need some extra tweaking or testing before relying on completly.
If anyone do try it in production, please post here if/how it works.
But thanks again Micha for the kind word.
All the best! See you at Codegarden?
/ Dennis
Hi Dennis,
your solution has been a fantastic help.
I've been working on an IUrlProvider that just outputs the Page.Id (Stéphane Gay briefly mentioned such a scenario in his video http://stream.umbraco.org/video/9921022/core-internals-for-website-development)
However, in the backoffice, GetById() is generating errors.
Do I need to implement an IContentFinder even if I am just using the Page Id for the Url?
Any help you can give me would be really useful.
Thanks
Muiris
EDIT: I've just been looking at the link below and I think I've found my problem. I was outputting the Page Id without a leading slash. I've added the slash now and it seems to have fixed my problem.
http://www.ucodebase.be/blog/implementing-a-custom-urlprovider-in-umbraco-and-its-pitfalls/
is working on a reply...