Constructing correct urls for Dynamic Pages with nested dynamic pages (.net c#)

I have a dynamic page item setup under another dynamic page item:

  • Page-1

This creates a page path like /page-1/category/article.

The problem I am having is when loading a dynamic page item, it is unaware it is associated with its parent, and no way to get the actual full url to that article item.

Is there a way in Agility's .NET API to figure out the full page path for the "Article"? Perhaps looking it up within the applications Site Map Node Tree?



  • Official comment

    When you have multiple routes for a single post, it can get complicated no doubt!

    Check out this github repo for an example of how to this.

    public ActionResult BlogDetails(AgilityContentItem module)
                #region --- Get Blog Config and Repos ---
                var blogConfigRef = module["BlogConfiguration"] as string;
                AgilityContentItem blogConfig = null;
                IAgilityContentRepository<AgilityContentItem> blogPosts = null;
                IAgilityContentRepository<AgilityContentItem> blogCategories = null;
                IAgilityContentRepository<AgilityContentItem> blogTags = null;
                IAgilityContentRepository<AgilityContentItem> blogAuthors = null;
                BlogUtils.GetBlogConfig(blogConfigRef, out blogConfig, out blogPosts, out blogCategories, out blogTags, out blogAuthors);
                #region --- Resolve Dynamic Post and Category (if any) ---
                var post = ResolveBlogPostDetails();
                if (post == null || !post.IsBlogPost()) {
                    Agility.Web.Tracing.WebTrace.WriteErrorLine("Cannot resolve current dynamic item to a Blog Posts.");
                    return null;
                var currentNode = SiteMap.CurrentNode;
                AgilityContentItem currentCategory = null;
                if (currentNode != null && currentNode.ParentNode != null)
                    currentCategory = AgilityContext.GetDynamicPageItem<AgilityContentItem>(currentNode.ParentNode);
                //only set category if the parent dynamic item is a category
                if (currentCategory != null && !currentCategory.IsBlogCategory())
                    currentCategory = null;
                #region --- Get Related Posts ---
                int relatedPostsLimit;
                int.TryParse(string.Format("{0}", module["RelatedPostsLimit"]), out relatedPostsLimit);
                if (relatedPostsLimit < 0) {
                    relatedPostsLimit = 0;
                var relatedPosts = BlogUtils.GetRelatedPosts(post, blogPosts, true, relatedPostsLimit);
                #region --- Set SEO Properties ---
                string canonicalUrl = post.BlogDynamicUrl(blogConfig, currentCategory);
                AgilityContext.CanonicalLink = BlogUtils.GetAbsoluteUrl(canonicalUrl);
                //Enables image for twitter cards - but twitter cards need to be enabled ...
                //to enable twitter cards include setting in app settings in web.config
                AgilityContext.FeaturedImageUrl = BlogUtils.GetPostImageUrl(blogConfig, post, PostImageType.Details);
                if (string.IsNullOrEmpty(AgilityContext.Page.MetaTags))
                    AgilityContext.Page.MetaTags = Server.HtmlEncode(BlogUtils.GetPostDescription(post, "Excerpt", 255, true));
                string websiteName = blogConfig["WebsiteName"] as string;
                if (string.IsNullOrEmpty(websiteName)) websiteName = AgilityContext.WebsiteName;
                AgilityContext.Page.MetaTagsRaw += string.Format(
                        "{6}" + 
                        "<meta property='og:title' content='{0}' />" +
                        "<meta property='og:type' content='{1}' />" +
                        "<meta property='og:url' content='{2}' />" +
                        "<meta property='og:image' content='{3}' />" +
                        "<meta property='og:site_name' content='{4}' />" +
                        "<meta property='og:description' content='{5}' />",
                        BlogUtils.GetPostImageUrl(blogConfig, post, PostImageType.Details),
                #region --- Set ViewModel ---
                var model = new PostViewModel();
                model.Configuration = blogConfig;
                model.Post = post;
                model.RelatedPosts = relatedPosts;
                model.CurrentCategory = currentCategory;
                model.Module = module;
                model.Categories = BlogUtils.GetItemsByIDs(blogCategories.ContentReferenceName, post["CategoriesIDs"]);;
                model.Tags = BlogUtils.GetItemsByIDs(blogTags.ContentReferenceName, post["BlogTagsIDs"]);;
                return PartialView(AgilityComponents.TemplatePath("Blog-PostDetailsModule"), model);

    The key line here is:

    currentCategory = AgilityContext.GetDynamicPageItem<AgilityContentItem>(currentNode.ParentNode);

    Does that help?

    Comment actions Permalink
  • Are you rendering the category page and trying to build URLs for all the articles on that page, or are you on the article page, trying to render URLs for its parent category?

    Comment actions Permalink
  • I am trying to render URLs for the parent page paths of a set of dynamic pages. 

    In the past I've done this through convention, as well as creating a settings panel where the "root page" is set, and I get the path of a particular category to construct a url like /{root-page-in-configuration}/{category}/{article} while on an "article" item.

    But what if you have a singular Content Item that generates two routes for instance?

    • /{root}/category-1/article-1
    • /{root}/category-2/article-1

    Now you have to pick apart the current page's url to figure out where the user is.

    I'm curious on how you have handled this in other MVC projects?
    Considering you define a dropdown of "build pages from" in Agility, it is too bad that this isn't captured in the API and returned with the Dynamic Page item:

    With these dynamic pages, I could always try and get the "parent" of the current page to figure out the correct path, not sure what the most efficient way to do that is in Agility, and with Dynamic Pages specifically.

    I'm looking for a reusable solution when it comes to these pags.

    Any thoughts?

    Comment actions Permalink
  • Awesome James that definitely helps with getting the correct category item by site map node.

    Thank you very much!

    Do you think my idea about the Agility API returning a "Build Pages From" property on dynamic pages makes sense?

    Like in an instance where you have to list out all paths to each blog post, and you want to be able to create it in a way where you don't need to know the structure of the dynamic pages (which could be easily changed by a content editor).

    In any regard what you have provided is very helpful thank you :) 

    Comment actions Permalink
  • Giovanni Mattucci - there is a way actually to build the dynamic URL path for a content item. But, you do need to know the path to the dynamic page node.

    For example, here is a custom property added in C# (to a partial class that extends the content item properties):

            /// <summary>
    /// Returns the dynamic page url for an item
    /// </summary>
    /// <returns>Url for the current item</returns>
    public Uri Url(string url)
    DynamicPageItem d = Data.GetDynamicPageItem(url, ReferenceName, Row);
    return new Uri(
    String.Format("{0}/{1}", url.Substring(0, url.LastIndexOf('/')), d.Name).ToLowerInvariant()
    ), UriKind.Relative

    And you would call it like this:

    var contentItem = AgilityContentRepository<SomeItem>("someref").Item("")
    var contentItemDynamicURL = contentItem.Url("~/posts/dynamic-post-node")
    //contentItemDynamicURL will return the URL (based on the page path formula in Agility CMS)
    Comment actions Permalink
  • I wanted to share a way I have been getting the actual URL for dynamic page items.

    If you can get the contentId of the dynamic page in question, in your .net core sdk I have been doing the following where I find the item referencing the root sitemap item of the site:

    var contentId = 1313; // Whatever the content id of the dynamic page is
    var rootNode = AgilityContext.AgilitySiteMap.RootNode;
    AgilityContext.AgilitySiteMap.FindDynamicNodeByContentID(rootNode, contentId).Url.Replace("~", string.Empty);

    Hopefully this could be useful to others.

    Let me know also if this way of doing it has any problems from a performance perspective.

    Comment actions Permalink

Please sign in to leave a comment.

Didn't find what you were looking for?

New post