Sitecore links in multisite solutions – SiteResolving

One of the key features in Sitecore is that you can have multiple sites in the same installation. This is not just about saving money on licenses. The feature allows you to, for example, operate one main website and a multitude of subsites each having their own URL. Because they share the same Sitecore, you can share resources like the media library or templates, layouts, users etc.

Another key benefit is that it allows you to create internal links between sites. These links are maintained by the LinkManager so links are updated automatically. And when they are rendered they will still point to the correct host.

Imagine this simplified scenario where you have 2 websites called SITE1 and SITE2. In Sitecore you create the 2 websites:

/sitecore/

/content/

/site1/

/home/

/subitems…

/site2/

/home/

/subitems…

To give each website its own hostname you need to set up 2 new sites in the web.config:

<site name="website_1" hostName="www.site1.com" language="en" cacheHtml="false" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content/site1" startItem="/home" database="web" domain="extranet" allowDebug="true" htmlCacheSize="10MB" registryCacheSize="0" viewStateCacheSize="0" xslCacheSize="5MB" filteredItemsCacheSize="2MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" />
<site name="website_2" hostName="www.site2.com" language="en" cacheHtml="false" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content/site2" startItem="/home" database="web" domain="extranet" allowDebug="true" htmlCacheSize="10MB" registryCacheSize="0" viewStateCacheSize="0" xslCacheSize="5MB" filteredItemsCacheSize="2MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" />

Now when accessing www.site1.com you are given the home page at /sitecore/content/site1/home, and www.site2.com returns the home page at /sitecore/content/site2/home.

Imagine you create a link on the page at /site1/home/subitem to /site2/home/subitem. Internally in Sitecore this is stored as a GUID. But when rendered, the LinkManager ensures that the link is rendered as www.site2.com/subitem.aspx.

It does so because in the web.config, SiteResolving is enabled by default:

<setting name="Rendering.SiteResolving" value="true" />

When SiteResolving is true, the LinkManager will resolve the correct site from the <sites> section in web.config. It makes a best match and finds the site that the page belongs to, and resolves the hostname and URL based on the context of the site it found.

If you disable the SiteResoving the LinkManager stays in the current context. The link would then have been www.site1.com/sitecore/content/site2/home/subitem.aspx.

LIMITATIONS IN THE SITERESOLVER

UPDATE: Please note that these limitations have been adressed from Sitecore 6.4.1 Update 6. Read more about it here.

There are limitations to siteresolving. Imagine you have a setup where you not only have multiple sites, but also multiple languages per site. And that each of the languages have their own host.

So if your SITE1 has 2 languages (english and danish) you have www.site1.com and www.site1.dk. And SITE2 also has 2 languages, www.site2.com and www.site2.dk.

If you enable SiteResolving, all links linking to the same website (all links from SITE1 to SITE2) will be resolved as links to the first site content found in the web.config.

What does this mean? It means that if you in the web.config have the following order of sites:

All links from www.site1.dk will point to www.site1.com/dk/subitem.aspx. The SiteResolver does this because the algorithm resolves URLS as the first path that is the shortest is the one used. And since www.site1.com/dk/subitem.aspx has just as short a path as www.site1.dk/dk/subitem.aspx it’s a match, and voila, problem solved.

MORE READING

Sitecore.Links.LinkManager and the context

A new feature in Sitecore 6 is the LinkManager. Previously, in Sitecore 5, in order to get a friendly url (an URL with the .aspx extension) you would write the following:

Item item;
string path = item.Paths.GetFriendlyUrl(false);

In Sitecore 6, the GetFriendlyUrl() is deprecated. Instead we are encouraged to use the Sitecore.Links.LinkManager. The same piece of code would look like this:

Item item;
string path = Sitecore.Links.LinkManager.GetItemUrl(item);

The LinkManager provides us with a lot of features that the GetFriendlyUrl() did not. But it also introduces some pitfalls. The LinkManager runs in a context that is not necessarily the same as the Item you are getting the link from, nor the context you wish to run in. Let me explain. Imagine you write a Sitecore shell extension that returns the path to an item. The Item is grabbed from the web database:

Sitecore.Data.Database database = Sitecore.Data.Database.GetDatabase("web");
Sitecore.Data.Items.Item item = database.GetItem("/sitecore/content/home");
string path = item.Paths.FullPath;

The path to the item is “/sitecore/content/home”.

Now, lets get the item’s URL:

string path = Sitecore.Links.LinkManager.GetItemUrl(item)

This returns the item URL for the current Site as defined in the LinkManager.GetDefaultUrlOptions().Site. If my code runs in the modules_shell site, the url is /en/sitecore modules/shell.aspx. Is this really the url of my item? Yes, seen from the modules_shell site it is, but not as seen from the website site.

So my Item used the website site, but my LinkManager uses the modules_shell site. The URL i wanted is the one from the website site, but the URL i requested is from the modules_shell site.

What can I do about this? The URL is constructed by prefixing the current site’s virtual path (which for the modules_shell site is /sitecore modules/shell) to the actual path. The easy solution would be to remove the virtual path from the URL:

string virtualFolder = LinkManager.GetDefaultUrlOptions().Site.VirtualFolder.TrimEnd('/');
Response.Write(LinkManager.GetItemUrl(item).Replace(virtualFolder, ""));

This produces the URL /en.aspx, which is correct for my website site, since this site has no virtual path.

Another, and probably better, solution would be to change the context before getting the link:

string oldSiteName = Sitecore.Context.GetSiteName();
Sitecore.Context.SetActiveSite("website");
Response.Write(LinkManager.GetItemUrl(item));
Sitecore.Context.SetActiveSite(oldSiteName);

This produces the URL “/”, which is correct for the home page of my website.

Sitecore 5.3: Those darn links

The new link field in Sitecore 5.3 i pretty cool. You can inset links/media library links/external links and so on by choosing the appropriate link style from the context menu.
Choosing the correct link type from the context menu allows you to use the control in your XSLT’s.

But what if the user does’nt choose the correct link type? What if he just inserts an internal link that is pointing to a media library item? Then the will make a link to the media library item and not the actucal document contained in the media library.

Well, we have to code here. This function takes the GUID of a Sitecore node and a field name. The field name is the name of the Link field on the node. The function will return the absolute path of the item linked to, even if the item is a media library item:

public string LinkToAbsolutePath(string itemID, string field)
{
  Item item = Sitecore.Context.Database.GetItem( new Sitecore.Data.ID(itemID) );
  if (item == null)
    throw new NullReferenceException( itemID + " not found" );
  Sitecore.Data.Fields.LinkField lf = item.Fields[field];
  if (lf.IsMediaLink)
    return lf.TargetItem["Path"];
  if (lf.IsInternal)
  {
    if ( lf.TargetItem.TemplateName.ToLower() == "file" )
      return lf.TargetItem["Path"];
    return lf.TargetItem.Paths.GetFriendlyUrl();
  }
  return lf.Url;
}

The first few lines of code gets the correct Sitecore item, and the link field.
The first if statement checks to see if the links is a media library link. If so I return the value of the target item’s “Path” field. You would have assumed that the media library item path is the lf.MediaPath, but ohh no, this is just the Sitecore node name.
The next if statement checks to see if the link is an internal link. Then I check to see if the target item points to a file template, or inheriting from a file template. Media library items are actually not based on the file template, they inherit from it. If it is a file template, again return the “path” field contents, not the MediaPath. I inheritance is not of file, I return the friendly URL.
The default return value is the Url field, which is hit if the link is an external type.

Follow

Get every new post delivered to your Inbox.

Join 92 other followers