Johan Björnfot
Apr 14, 2011
  4993
(2 votes)

Ensure unique url segments for read-only page providers

During save of a page EPiServer CMS will ensure that the url segment for the page is unique (with unique means that there is no other page on the same language with same parent that has the same url segment). This means that pages that are created through the EPiServer API will get unique url segments. However for read-only page providers it is up to the page provider itself to ensure that the url segments are unique (we might add helper functions for this in the product in the future). Currently if the provider uses method InitializePageData to initialize PageData instances the url segment will be set to the same as the page name. So if there are many pages at the same level with same page name in same language they will get the same url segment.

Below is a helper class that can be used in a read only page provider to ensure that the url segments will be unique. Note that this implementation does not store the generated url segment in a persistent storage (for example database). This means that there is no guarantee that a specific page gets the same url segment after an application restart. Instead the url segment is dependent on the request order of the pages that has the same page name.

First in your read only page provider add a field like: 

UrlSegmentHandler _urlSegmentHanlder = new UrlSegmentHandler();

Then in method GetLocalPage after the call to InitializePageData you add a call like:

_urlSegmentHanlder.EnsureUniqueUrlSegment(page);

The class UrlSegmentHanlder looks like:

public class UrlSegmentHandler
{
    private Dictionary<string, LanguageUrlSegments> _languageSegmentHandlers = 
        new Dictionary<string, LanguageUrlSegments>();

    public void EnsureUniqueUrlSegment(PageData page)
    {
        LanguageUrlSegments languageSegments;
        if (!_languageSegmentHandlers.TryGetValue(page.LanguageBranch, 
            out languageSegments))
        {
            languageSegments = new LanguageUrlSegments();
            _languageSegmentHandlers.Add(page.LanguageBranch, languageSegments);
        }

        languageSegments.EnsureUniqueUrlSegment(page);
    }
}

public class LanguageUrlSegments
{
    private Dictionary<PageReference, string> _pageUrlSegments =
        new Dictionary<PageReference, string>();

    //Dictionary <Parent, Dictionary<urlSegment, PageLink>>
    private Dictionary<PageReference, Dictionary<string, PageReference>> _urlSegments = 
        new Dictionary<PageReference, Dictionary<string, PageReference>>();

    public void EnsureUniqueUrlSegment(PageData page)
    {
        //NOTE: Here we expect that PageLink, ParentLink and UrlSegment is set
        //Which they are if InitializePageData has been called.

        //If we already generated a urlsegment for page, return it
        string urlSegment; 
        if (_pageUrlSegments.TryGetValue(page.PageLink, out urlSegment))
        {
            page.URLSegment = urlSegment;
            return;
        }

        urlSegment = page.URLSegment;
        //First get all urlsegments under parent
        Dictionary<string, PageReference> existingUrlSegments;
        if (!_urlSegments.TryGetValue(page.ParentLink, out existingUrlSegments))
        {
            existingUrlSegments = new Dictionary<string, PageReference>();
            _urlSegments.Add(page.ParentLink, existingUrlSegments);
        }

        PageReference existingPage;
        if (!existingUrlSegments.TryGetValue(urlSegment, out existingPage))
        {
            existingPage = page.PageLink;
            _pageUrlSegments[page.PageLink] = urlSegment;
            existingUrlSegments.Add(page.URLSegment, page.PageLink);
        }

        //In case there is another page with same segment "postfix"
        //urlsegment with counter until it is unique.
        int counter = 0;
        while (!existingPage.CompareToIgnoreWorkID(page.PageLink))
        {
            urlSegment = page.URLSegment + (++counter).ToString();
            if (!existingUrlSegments.TryGetValue(urlSegment, out existingPage))
            {
                existingPage = page.PageLink;
                _pageUrlSegments[page.PageLink] = urlSegment;
                existingUrlSegments.Add(urlSegment, page.PageLink);
            }
        }

        //Set the unique segment on page
        page.URLSegment = urlSegment;
    }
}
Apr 14, 2011

Comments

Apr 14, 2011 10:59 AM

Awesome! :)

Please login to comment.
Latest blogs
Opti ID overview

Opti ID allows you to log in once and switch between Optimizely products using Okta, Entra ID, or a local account. You can also manage all your use...

K Khan | Jul 26, 2024

Getting Started with Optimizely SaaS using Next.js Starter App - Extend a component - Part 3

This is the final part of our Optimizely SaaS CMS proof-of-concept (POC) blog series. In this post, we'll dive into extending a component within th...

Raghavendra Murthy | Jul 23, 2024 | Syndicated blog

Optimizely Graph – Faceting with Geta Categories

Overview As Optimizely Graph (and Content Cloud SaaS) makes its global debut, it is known that there are going to be some bugs and quirks. One of t...

Eric Markson | Jul 22, 2024 | Syndicated blog

Integration Bynder (DAM) with Optimizely

Bynder is a comprehensive digital asset management (DAM) platform that enables businesses to efficiently manage, store, organize, and share their...

Sanjay Kumar | Jul 22, 2024

Frontend Hosting for SaaS CMS Solutions

Introduction Now that CMS SaaS Core has gone into general availability, it is a good time to start discussing where to host the head. SaaS Core is...

Minesh Shah (Netcel) | Jul 20, 2024

Optimizely London Dev Meetup 11th July 2024

On 11th July 2024 in London Niteco and Netcel along with Optimizely ran the London Developer meetup. There was an great agenda of talks that we put...

Scott Reed | Jul 19, 2024