Content Sharing and Links in Sitecore

Sitecore is great for content sharing. The item linking functionality is best of breed and the ability to share and reuse data is an example of that. One of the challenges of item sharing however can be links. Item sharing within 1 site is easy. Item sharing between multiple sites gets tricky. Here’s an example.

Below is an example of 3 site nodes in the content tree. Above the 2 sites is a folder called “Shared Content”. Below the “Shared Content” is the US site and the UK site. In the shared content folder, there is an item called “Example Link Translation”

Each site has a similar content structure. Each site has a “Shared Item Test” page. On each page, there is a rendering. Both page’s renderings point to the same datasource item. The item from the shared folder.

The datasource item has 2 links on it. Since a link has to be filled in, we are using the US site as the “standard” link. The link that gets filled in.

In the below screenshot, we are on the “http://samplesite/en_gb/shared-item-test”. The rendering below is using the same datasource item as shown above and the same datasource image that the US site is using. 2 things to note here.

  1. The about us link – the UK site doesn’t have an our-company page, so the link does not get translated.
  2. The media center link – the UK site does have a media center page, so the link does get translated.

This rendering on the US site, simply shows the US site.

The example code below is pretty straightforward. The GetItem method is the main method, that essentially determines whether the site that is resolving has a relative path for the link that’s being rendering. If the item exists, we get that item and return it, then call the CreateLinkBuilder method. You may want to add more methods to hone in on when this should run vs letting the original item just pass through, perhaps if the datasource for the rendering is located in the same site as the passed in item. There are a couple of other helper methods that get the item’s site and get the items relative path from the start node. Finally, we simply test to see of the “translated path” exists for the link. If it does, return that item. If it doesn’t, return the original.

Content authors would have to be made aware of how this works. It could lead to some confusion. This is just one example of how you might handle this requirement.

using Sitecore;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using System;
using System.Collections.Generic;
using System.Linq;
using Sitecore.Links;
using Sitecore.Sites;
using Sitecore.Web;

namespace SampleSite.Providers
{
    public class CustomLinkProvider : LinkProvider
    {
        public override string GetItemUrl(Item item, UrlOptions options)
        {
            Assert.ArgumentNotNull((object)item, "item");
            Assert.ArgumentNotNull((object)options, "options");

            options.SiteResolving = true;

            if (Sitecore.Context.Site != null && !string.IsNullOrEmpty(Sitecore.Context.Site.Name))
            {
                //Move to a configuration file.  Here for example only.
                List<string> ignoreList = new List<string>();
                ignoreList.Add("shell");
                ignoreList.Add("scheduler");
                ignoreList.Add("publisher");

                bool ignore = ignoreList.Any(s => Sitecore.Context.Site.Name.Contains(s));

                if (!ignore)
                {
                    item = GetItem(item);
                }
            }

            var itemUrl = this.CreateLinkBuilder(options).GetItemUrl(item).ToLowerInvariant();
            return itemUrl;
        }

        private Item GetItem(Item item)
        {
            var homePath = Sitecore.Context.Site.StartPath;

            try
            {
                string itemPath = item.Paths.FullPath;

                if (!itemPath.StartsWith(homePath, StringComparison.CurrentCultureIgnoreCase))
                {
                    string newPath = string.Concat(homePath, GetItemRelativePath(item));

                    if (newPath != homePath)
                    {
                        //Does the translated path exist, if so, link to that item
                        //if not, return the original item
                        var newItem = Context.Database.GetItem(newPath);
                        return newItem != null ? newItem : item;
                    }
                }
            }
            catch (Exception exception)
            {
                Log.Error(exception.Message, this);
            }

            return item;
        }

        private static string GetItemRelativePath(Item item)
        {
            if (item != null)
            {
                var site = GetSiteFromItem(item);

                if (site != null)
                {
                    var homePath = site.RootPath + "\\" + site.StartItem;
                    string itemPath = item.Paths.Path.ToString().ToLower();
                    itemPath = itemPath.Replace(site.RootPath.ToLower(), "");
                    itemPath = itemPath.Replace(site.StartItem.ToLower(), "");
                    return itemPath;
                }
            }

            return string.Empty;
        }

        private static SiteInfo GetSiteFromItem(Item item)
        {
            if (item != null)
            {
                string itemPath = item.Paths.FullPath;

                SiteInfo site = SiteContextFactory.Sites
                    .Where(s => s.RootPath != "" && itemPath.StartsWith(s.RootPath, StringComparison.OrdinalIgnoreCase))
                    .OrderByDescending(s => s.RootPath.Length)
                    .FirstOrDefault();

                return site;
            }

            return null;
        }
    }
}

About Phil Paris

Hi, my name is Phil Paris and I’m a Sitecore Architect and general Sitecore enthusiast. I’ve been working with Sitecore since 2013. Through this blog I will be sharing Sitecore insights, tips and tricks, best practices and general knowledge with the hopes to further the community at large. Please feel free to reach out to me at any time!

View all posts by Phil Paris →