+1-888-365-2779
Try Now
More in this section

Forums / Suggestions / URL Page Navigation

URL Page Navigation

15 posts, 1 answered
  1. Bert
    Bert avatar
    22 posts
    Registered:
    06 Dec 2004
    10 Apr 2005
    Link to this post
    Hi,

    I would like to see the capability of using Page Names and/or Page Ids as well as Page GUIDs for page navigation. It would make things more user friendly from an end user perspective and also if you wish to email links and such to other people (r.a.d designer used Page Names).

    I would really appreciate some directions so I could implement it myself as according to telerik it should be easy. It seems like the SItefinity site (this one) is using Page Id and also to quote the Developer information page:



    "At present, the URL addresses are based on the page global ID, but this module can be easily replaced with one based on Page names for example"


    Thanks
    Bert
    Answered
  2. Tom Holder
    Tom Holder avatar
    7 posts
    Registered:
    09 Feb 2004
    11 Apr 2005
    Link to this post
    For search engines, I would say this is a essential requirement.

    It would best if you could actually make the request look like separate files... e.g. www.mysite.com/mypagename.aspx

    I will attempt to implement this myself on the project I'm working on if I get the chance.
  3. Vlad
    Vlad avatar
    498 posts
    Registered:
    15 Jul 2016
    12 Apr 2005
    Link to this post
    Hello,

    There were several other enquiries for this feature in SItefinity and we decided to implement it in one of the next releases.

    Meanwhile, you can use the following approach to implement it yourself:

  4. Create an HttpModule (see attached CustomTelerikHttpModule.cs)
  5. Replace TelerikHttpModule with CustomTelerikHttpModule in Web.config
  6. Update all navigation controls (RadMenuControl, RadTreeviewControl,..) to use friendly links for navigation

    We hope this helps.
  7. Regards,
    Vlado
    the telerik team

  • Vassil
    Vassil avatar
    5 posts
    Registered:
    18 May 2015
    12 Apr 2005
    Link to this post
    Hopefully the stuff we've provided in the previous post will be a good starting point. We'll help if you struggle with something that can you can't tackle yourself.

    Regards,
    Vlado
    the telerik team
  • Bert
    Bert avatar
    22 posts
    Registered:
    06 Dec 2004
    12 Apr 2005
    Link to this post
    This all sounds good and I look forward to the next release.

    I will alos have a go at implementing this in my current project.

    It's just one thing I can't get to the attachement. I get an error when I cilck on the link in my e-mail copy and the attachment does not appear on this web site. Please help.

    Bert
  • Vlad
    Vlad avatar
    498 posts
    Registered:
    15 Jul 2016
    13 Apr 2005
    Link to this post

    Indeed, the Forums do not support attachments, sorry for this. Here is one of the implementation of HttpModule.

    using System;
    using System.Data;
    using System.Web;
    using System.Web.Security;
    using System.Web.SessionState;
    using System.Security.Principal;
    using System.Data.OleDb;

    using Telerik.ContentManagement;
    using Telerik.ContentManagement.UserManagement;
    using Telerik.ServerUtils;

    namespace TelerikGenericApplication
    {
     /// <summary>
     ///  Represents an HttpModule responsible for URL rewriting and user authorization.
     /// </summary>
     public class CustomTelerikHttpModule : IHttpModule
     {
      public void Init(HttpApplication application)
      { 
       application.AuthenticateRequest += new EventHandler(this.Application_AuthenticateRequest);
       application.Error += (new EventHandler(this.Application_Error));
      }

      // Rewrites both global page ID and friendly page names URLs
      protected virtual void RewriteUrl(bool isPublic)
      {
       HttpContext context = HttpContext.Current;
       string path = context.Request.Path;

       if (path.EndsWith("Default.aspx"))
       {
        return;
       }
       if (path.EndsWith("Login.aspx"))
       {
        return;
       }
       if (!path.EndsWith(".aspx"))
       {
        return;
       }

       path = context.Request.RawUrl.Replace(applicationPath, "");
       string pageGuid = string.Empty;
       string pageName = path.Substring(0, path.LastIndexOf("."));
       
       try
       {
        Guid guid = new Guid(pageName);
        pageGuid = guid.ToString();
       }
       catch
       {
        // the names are separated by '_'
        string[] names = pageName.Split('_');
        DataTable pagesTable;
        if (isPublic)
        {
         pagesTable = TelerikCmsContext.Current.CmsManager.GetPublishedPages();
        }
        else
        {
         pagesTable = TelerikCmsContext.Current.CmsManager.GetPages();
        }
        DataRow[] rows = pagesTable.Select("ParentPageId IS NULL");
        DataRow resultRow = rows[0];
        for ( int i = 0; i < names.Length; i++ )
        {
         resultRow = GetResultRow(resultRow, names[i]);
         if (resultRow == null)
         {
          return;
         }
        }
        pageGuid = (string)resultRow["PageGuid"];
       }
       if (pageGuid.Length == 36)
       {
        string queryString = string.Empty;
        path = context.Request.RawUrl;
        int index = path.LastIndexOf("?");
        if (index > 0)
        {
         queryString = "&" + path.Substring(index + 1);
        }
        HttpContext.Current.Items["TelerikCmsVirtualUrl"] = path;
        context.RewritePath("~/Default.aspx?Page=" + pageGuid + queryString);
       }
      }

      private void Application_Error(object source, EventArgs args)
      {
       HttpApplication application = (HttpApplication) source;
       Exception last = application.Server.GetLastError();
       if (last is SecurityException)
       {
        FormsAuthentication.SignOut();
        application.Context.Response.Redirect(application.Context.Request.RawUrl, true);
       }
       else
       {
        if (last is HttpUnhandledException)
        {
         last = ((HttpUnhandledException) last).InnerException;
        }

        if ((last is OleDbException) || (last is InvalidLicenseFileException))
        {
         string applicationPath = ServerInfo.ApplicationPath;
         if (applicationPath != "/")
         {
          applicationPath += "/";
         }
         applicationPath += ServerInfo.AppSettings["appName"];
         if (!applicationPath.EndsWith("/"))
         {
          applicationPath += "/";
         }

         if ((last is OleDbException) &&
          (CannotWriteToDbFile(last as OleDbException)))
         {
          application.Context.Response.Redirect(applicationPath + "PermissionsError.aspx", true);
         }
         else if (last is InvalidLicenseFileException)
         {
          application.Context.Response.Redirect(applicationPath + "LicenseError.aspx", true);
         }
        }
       }
      }

      private bool CannotWriteToDbFile(OleDbException dbError)
      {
       if (dbError == null)
        return false;

       const string COULD_NOT_DELETE_FROM_SPECIFIED_TABLES = "3086";
       const string OPERATION_MUST_USE_AN_UPDATEABLE_QUERY = "3073";

       foreach (OleDbError realError in dbError.Errors)
       {
        if (realError.SQLState == COULD_NOT_DELETE_FROM_SPECIFIED_TABLES
         ||
         realError.SQLState == OPERATION_MUST_USE_AN_UPDATEABLE_QUERY)
         return true;
       }

       return false;
      }

      private void Application_AuthenticateRequest(object source, EventArgs e)
      {
       bool isPublic = true;
       HttpApplication application = (HttpApplication) source;
       HttpContext context = application.Context;
       bool authenticated = context.Request.IsAuthenticated;
       IPrincipal requestPrincipal = null;

       if (authenticated && (context.User.Identity is FormsIdentity))
       {
        isPublic = false;
        IPrincipal formsPrincipal = context.User;
        string[] userData = ((FormsIdentity) formsPrincipal.Identity).Ticket.UserData.Split(';');
        string[] roles = userData[0].Split(',');

        int userId = int.Parse(formsPrincipal.Identity.Name);
        UserPrincipal newPrincipal = CreatePrincipal(userId, roles, userData, context);

        try
        {
         string connectionString = newPrincipal.ConnectionString;
         object dbMode = newPrincipal.DbMode;
         if (connectionString == "" || dbMode == null)
          throw new ArgumentException("BadConfiguration");
         
         requestPrincipal = newPrincipal;
        }
        catch (ArgumentException)
        {
        }

       }
       
       if (requestPrincipal == null)
       {
        requestPrincipal = new AnonymousPrincipal();
       }

       context.User = requestPrincipal;
       // Calling rewrite method
       RewriteUrl(isPublic);
      }

      protected virtual UserPrincipal CreatePrincipal(int userId, string[] roles, string[] userData, HttpContext context)
      {
       UserPrincipal newPrincipal = new CachingUserPrincipal(userId);
       newPrincipal.RoleNames = roles;
       if (userData.Length == 2)
       {
        string realName = ServerInfo.AppSettings["appName"];
        if (!realName.EndsWith("/"))
        {
         realName += "/";
        }

        newPrincipal.AppName = userData[1];

        if ((newPrincipal.AppName != realName) && (realName != "/"))
        {
         FormsAuthentication.SignOut();
         context.Response.Redirect(context.Request.RawUrl, true);
        }
       }
       else
       {
        newPrincipal.AppName = null;
       }

       return newPrincipal;
      }

      public void Dispose()
      {
      }

      private DataRow GetResultRow(DataRow parentRow, string pageName)
      {
       foreach ( DataRow row in parentRow.GetChildRows(TelerikCmsContext.Current.CmsManager.ParentRelationName) )
       {
        if (pageName.Equals((string)row["PageName"]))
        {
         return row;    
        }
       }
       return null;
      }

      private string applicationPath
      {
       get
       {
        string appPath = HttpContext.Current.Request.ApplicationPath;
        if (appPath != "/")
         appPath += "/";
        appPath += System.Configuration.ConfigurationSettings.AppSettings["appName"];
        if (!appPath.EndsWith("/"))
         appPath += "/";
        return appPath;
       }
      }

     }
    }


    Regards,
    the telerik team

  • Bert
    Bert avatar
    22 posts
    Registered:
    06 Dec 2004
    14 Apr 2005
    Link to this post
    Thanks very much for this it certainly made things easy and I achieved what I wanted (except a few thing which I'll get back about in a the next post). I thought I would share how I implemented this for the benefit of anyone who would like to have a go. In the end it was quite easy.

    First I developed a class with a static method to get a fully qualified page name. You need that as the GUID page name is unique but when you use the new url rewrite module you need a heirarchical name, e. g. Home_Detail_MoreDetail. So I needed a class that fixed that given the pageRow which is available. Code C#:

    using System;
    using System.Data;

    namespace Telerik.ContentManagement.Controls
    {
    ///
    /// Summary description for DyosoftHelper.
    ///

    public class DyosoftHelper
    {
    // Private, not instantiated
    private DyosoftHelper()
    {
    }

    public static string GetFullyQualifiedPageName(DataRow PageRow)
    {
    string curPageName = PageRow["PageName"].ToString();
    return GetParentPageName(curPageName, PageRow);
    }

    private static string GetParentPageName(string pageNameQaul, DataRow ChildRow)
    {
    string parRelName = Telerik.ContentManagement.TelerikCmsContext.Current.CmsManager.ParentRelationName;
    DataRow parRow = ChildRow.GetParentRow(parRelName);
    if (parRow != null && parRow["ParentPageId"] != null && parRow["ParentPageId"] != DBNull.Value)
    pageNameQaul = GetParentPageName(parRow["PageName"].ToString(), parRow) + "_" + pageNameQaul;

    return pageNameQaul;
    }
    }
    }


    Then I needed to change the navigation Controls to call the new method (GetFullyQualifiedPageName). I changed the following controls:
    • RadMenuControl.ascx
    • RadPanelbarControl.ascx
    • RadTabstripControl.ascx
    • RadTreeviewControl.ascx

    Code snippets from each one (you have to look for the actual code bit within the method):

    RadMenuControl Method LoadMenuItems
    ...................
    SetMenuSeparator(group, isRoot, (group.Items.Count == 0) ? "menuLeft.gif" : "mainSeparator.gif");
    MenuItem item = group.AddItem();
    if (childRow.IsNull("PageAlias") || (childRow["PageAlias"].ToString() == string.Empty))
    {
    item.Label = (string) childRow["PageName"];
    }
    else
    {
    item.Label = (string) childRow["PageAlias"];
    }

    // Dyosoft Change
    item.Href = DyosoftHelper.GetFullyQualifiedPageName(childRow) + ".aspx";

    // Teletik Original
    // item.Href = ((string) childRow["PageGuid"]) + ".aspx";
    ...................................

    RadPanelbarControl Method GetPageChildPages
    ...................
    if (itemGroup.Length == 0)
    {
    string itemProperties = string.Empty;
    string groupProperties = string.Empty;
    if (Theme == CmsThemes.MonoGraphic)
    {
    itemProperties = string.Format(" Image=\"{0}Header.gif\" ImagePosition=\"Background\"", headerTheme);
    groupProperties = string.Format(" Image=\"{0}Body.gif\" ImagePosition=\"Background\"", headerTheme);
    footerImage = headerTheme + "Footer.gif";
    }
    itemGroup.AppendFormat("", GetPageLabel(row), itemProperties, groupProperties);
    }
    itemGroup.AppendFormat("",
    // Dyosoft Change
    GetPageLabel(childRow), DyosoftHelper.GetFullyQualifiedPageName(childRow) + ".aspx",
    (PageImage == string.Empty) ? string.Empty : string.Format(" Image=\"{0}\"", PageImage));
    // Telerik Original
    // GetPageLabel(childRow), ((string) childRow["PageGuid"]) + ".aspx",
    // (PageImage == string.Empty) ? string.Empty : string.Format(" Image=\"{0}\"", PageImage));
    }
    ........................
    RadTabstripControl Method LoadTabs
    ........................
    if (childRows[i].IsNull("PageAlias") || (childRows[i]["PageAlias"].ToString() == string.Empty))
    {
    tab.Text = (string) childRows[i]["PageName"];
    }
    else
    {
    tab.Text = (string) childRows[i]["PageAlias"];
    }
    // Dyosoft Change
    tab.NavigateUrl = DyosoftHelper.GetFullyQualifiedPageName(childRows[i]) + ".aspx";
    // Telerik Original
    // tab.NavigateUrl = ((string) childRows[i]["PageGuid"]) + ".aspx";
    tabstrip1.TabCollection.Add(tab);
    if ((int) childRows[i]["PageId"] == PageId)
    {
    tabstrip1.SelectedIndex = tabstrip1.TabCollection.Count - 1;
    }

    ...........................
    RadTreeviewControl Method LoadTreeView
    .............................................
    if (childRow.IsNull("PageAlias") || (childRow["PageAlias"].ToString() == string.Empty))
    {
    node.Text = (string) childRow["PageName"];
    }
    else
    {
    node.Text = (string) childRow["PageAlias"];
    }
    // Dyosoft Change
    node.Value = DyosoftHelper.GetFullyQualifiedPageName(childRow);
    node.Href = DyosoftHelper.GetFullyQualifiedPageName(childRow) + ".aspx";
    // Telerik Original
    // node.Value = (string) childRow["PageGuid"];
    // node.Href = ((string) childRow["PageGuid"]) + ".aspx";
    node.DropEnabled = false;
    if (PageId == (int) childRow["PageId"])
    {
    node.ExpandParentNodes();
    node.Selected = true;
    }
    LoadTreeView(childRow, node.Nodes);

    ................................

    I'm sure you can find the code snippet within the method. We really need an attachment thing here so you can post some sensible pieces of code.

    I hope this will help someone.

    Bert

  • Bert
    Bert avatar
    22 posts
    Registered:
    06 Dec 2004
    14 Apr 2005
    Link to this post

    I had to split this into two posts, sorry about that. You could say you always want something more and I do...

    I was happy that the telerik module achieved what I wanted but there is obviously more I would like. I hope that telerik will add a bit here.

    The page tree that is used when you navigate pages using the Page Manager wasn't easily changed and neither was the Custom Link bit using the r.a.d editor. It would be great if there was an easy way to make these two areas use page names as well. The most important one being Custom Links. The admin part would be nice to have but not as necessary.

    I hope for some more help.

    Thanks for now.

    Bert

  • Georgi Tunev
    Georgi Tunev avatar
    14 posts
    Registered:
    01 Jul 2016
    15 Apr 2005
    Link to this post
    Hello Bert.

    Thank you for your valuable feedback. We have sent this to our dev team and they will discuss it for future implementation. We have also updated your points.

    Regards,
    the telerik team
  • Jim Aderhold
    Jim Aderhold avatar
    48 posts
    Registered:
    29 Feb 2004
    11 May 2005
    Link to this post
    I attempted to use the httpmodule inclued in this thread. When I compile and run the application I get the following error:
    "Could not load type TelerikGenericApplication.CustomTelerikHttpModule from assembly TelerikGenericApplication". I added a class to the project, named "CustomTelerikHttpModule", and added the code supplied in the thread. I recompiled the project and made the change to the web.config file. When trying to view the site I get the error.
     
    Any suggestions? Did I miss something?
  • Jim Aderhold
    Jim Aderhold avatar
    48 posts
    Registered:
    29 Feb 2004
    11 May 2005
    Link to this post
    Nevermind, I figured it out. You have to change the namespace to refer to the current projects namespace. Since the default namespace for the projects is TelerikCmsProject, you have to change the web.config file to read:
     
    <
    addtype="TelerikGenericApplication.CustomTelerikHttpModule,TelerikCmsProject"name="TelerikGenericApplication"/>

     

  • Jim Aderhold
    Jim Aderhold avatar
    48 posts
    Registered:
    29 Feb 2004
    11 May 2005
    Link to this post
    Possible Bug in the CustomTelerikHttpModule. I made the changes for the HttpModule per this thread. When I posted it live I found that every so often it will throw an error stating that it couldn't find the page(404 error). I would rebuild the project and push it live and all of a sudden it would work.
     
    I then tried to use this same site on our laptop (for demo purposes). I pulled the files across and setup the site. When you browse to the site it renders fine, but when you click on a link, it gives me the 404 error.
     
    Any help?
  • Vlad
    Vlad avatar
    498 posts
    Registered:
    15 Jul 2016
    12 May 2005
    Link to this post

    The error "HTTP 404: The resource cannot be found " is most probably due to a nonworking HttpContext.RewritePath() method after about 20 requests when called in the AuthenticateRequest event. We found a very uncommon solution for this problem - calling the RewritePath() method twice.

    The updated CustomTelerikHttpModule.cs is attached here.

    Also we plan to implement 'URLs based on page names' built-in feature in the next version of Sitefinity.


    the telerik team

  • Vassil Petev
    Vassil Petev avatar
    48 posts
    Registered:
    28 Sep 2016
    13 May 2005
    Link to this post

    Guys,
    we slightly improved our CustomTelerikHttpModule, compared to the latest one we posted. Please, download it from this post.

    the telerik support team

  • bisht
    bisht avatar
    1 posts
    Registered:
    19 Feb 2011
    19 Feb 2011
    Link to this post
    WoW" Its realy a great thread.So I want information regarding URL page Navigation.Please someone sharing.
    15 posts, 1 answered