Custom 404 page ignored by IIS 7

If your IIS does not display your own custom error page, this might be the problem – and the solution:

I recently had to implement a custom 404 error page. The code for that is pretty simple. In the web.config, the custom 404 error page is added:

<customErrors mode="Off">
  <error statusCode="404" redirect="/components/Error/404.aspx" />
</customErrors>

And the page is requested to return a 404 not found:

protected void Page_Load(object sender, EventArgs e)
{
  Response.StatusCode = 404;
  Response.StatusDescription = "Page not found";
  Response.Flush();
}

Here is the problem: The code worked fine on my Windows 7 developer machine’s localhost, but when I copied the code to my Windows 2008 server, the IIS refused to call my custom page, displaying the default 404 error page instead.

Here is the catch: The Windows 2008 server is running the latest IIS 7.5, and this version will by default “override” the HTTP response with its default error pages if the page returns a HTTP error status code (like 404, or 500).

Here is the solution: .NET 3.5 has a new property called Response.TrySkipIisCustomErrors that will, if set to true, instruct IIS 7.5 to ignore it’s own default error pages, allowing me to use my own. The code change is very small, but highly effective:

protected void Page_Load(object sender, EventArgs e)
{
  // Allow me to use my own error page:
  Response.TrySkipIisCustomErrors = true;
  Response.StatusCode = 404;
  Response.StatusDescription = "Page not found";
  Response.Flush();
}

Violá. A true WTF moment solved with one line of code.

Posted in .net, c#, General .NET | Tagged , , | 3 Comments

Open Sitecore Content Editor from the Page Editor

The Sitecore Page Editor is the WYSIWYG (not the Drupal “WYSIWYG”, but true, realtime WYSIWYG for real) version of the Sitecore Content Editor. So why would you open the Content Editor from the Page Editor?

Image you have a set of data in a tree structure that is not visual, but still required. This could be a metadata archive, a link archive, or it could be settings or search and index options. All of these data needs to be editable. And if you have made a “Page Edit Only” website you need to give access to these data from the page editor as well.

This is a case from a Google Maps project. In this project I have a set of overlays (the markers, polygons and other stuff that is on top of the map). And I need to give access to these data from the Page Editor:

Open Content Editor from the Web Editor

Open Content Editor from the Web Editor

Notice that the root of the tree is not the root of the web site, but a root that I have chosen myself.

The code to do this trick is even easier than opening the Media Library from the Page Editor.

To open the Content Editor you first need to add a button to the Page Editor. Go to the CORE database and find the Web Edit application at /sitecore/content/Applications/WebEdit.

The Preview and the Web Edit has their own individual toolbars, at /sitecore/content/Applications/WebEdit/Ribbons/Preview and /sitecore/content/Applications/WebEdit/Ribbons/WebEdit, so if you like the button to be visible in either mode you add the button twice.

  • Add a new Strip template below the toolbars to create a new tab.
  • Add a new Chunk template below the Strip template to create a new section on the strip.
  • Add a new Large Button template below the Chunk template to create a new button on the chunk.
  • Find a suitable icon and in the “click” field, write the name of the command to execute. In this case it’s “pt_googlemaps:OpenOverlays“ because It’s an overlays folder I open.
Web Edit Add Button

Web Edit Add Button

Now on to the code. First you need to couple the pt_googlemaps:OpenOverlays to the underlying code. This is done by adding a new .config file below /app_config/include, and add the following contents:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:x="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <commands>
      <command name="pt_googlemaps:OpenOverlays" type="MyCode.OpenOverlays,MyDll"/>
    </commands>
  </sitecore>
</configuration>

The code is simple. All you need is to provide a path to the Root Item of the content editor tree. In this case I’m using the function “OverlaysRoot” to find the item which is my root item.

using Sitecore;
using Sitecore.Text;
using Sitecore.Web.UI.Sheer;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Shell.Framework.Commands;

namespace MyCode
{
  public class OpenOverlays : Command
  {
    public override void Execute([NotNull] CommandContext context)
    {
      var url = new UrlString("/sitecore/shell/Applications/Content Manager/default.aspx");
      url.Add("ro", OverlaysRoot.ToString());
      SheerResponse.Eval("window.open('" + url + "', 'ContentEditor', 'location=0,resizable=1')");
    }

    private ID OverlaysRoot
    {
      get
      {
        Item overlaysRoot = Client.ContentDatabase.GetItem("/sitecore/system/Modules/PT_GoogleMaps/Overlays");
        Assert.IsNotNull(overlaysRoot, "Could not find overlays at /sitecore/system/Modules/PT_GoogleMaps/Overlays");
        return overlaysRoot.ID;
      }
    }
  }
}

You should change the “OverlaysRoot” function to return the root of your choice.

The cool part is that yoiu can provide context-sensible access to all of your non-visual data by adding more than one editor by simply create an array of buttons, say “Edit Metadata”, “Setup Website” and so on. This could really boost the usability of your solution.

MORE INFORMATION:

Posted in c#, Sitecore 6 | Tagged , , , , | Leave a comment

Open Media Library from Sitecore Page Editor

This article describes how you can add your own button to the Sitecore Page Editor.

In a previous post I explained how to edit hidden fields in the Page Editor. This post is about managing media contents, and to do so you need to open the Sitecore Media Library which is the best tool for managing ALT texts, image descriptions and so on.

Open Media Library from the Sitecore Page Editor

Open Media Library from the Sitecore Page Editor

To open the Media Library you first need to add a button to the Page Editor. Go to the CORE database and find the Web Edit application at /sitecore/content/Applications/WebEdit.

The Preview and the Web Edit has their own individual toolbars, at /sitecore/content/Applications/WebEdit/Ribbons/Preview and /sitecore/content/Applications/WebEdit/Ribbons/WebEdit, so if you like the button to be visible in either mode you add the button twice.

  • Add a new Strip template below the toolbars to create a new tab.
  • Add a new Chunk template below the Strip template to create a new section on the strip.
  • Add a new Large Button template below the Chunk template to create a new button on the chunk.
  • Find a suitable icon and in the “click” field, write “webedit:OpenMediaLibrary
New button to open media library

New button to open media library

Now on to the code. First you need to couple the webedit:OpenMediaLibrary to the underlying code. This is done by adding a new .config file below /app_config/include, and add the following contents:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:x="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <commands>
      <command name="webedit:OpenMediaLibrary" type="MyCode.OpenMediaLibrary,MyDll"/>
    </commands>
  </sitecore>
</configuration>

The code to open the media library looks like this:

using Sitecore;
using Sitecore.Text;
using Sitecore.Web.UI.Sheer;
using Sitecore.Shell.Framework.Commands;

namespace MyCode
{

  public class OpenMediaLibrary : Command
  {
    public override void Execute([NotNull] CommandContext context)
    {
      var url = new UrlString("/sitecore/shell/Applications/Content Manager/default.aspx");
      var header = string.Empty;
      var applicationItem = Client.CoreDatabase.GetItem("{7B2EA99D-BA9D-45B8-83B3-B38ADAD50BB8}");
      if (applicationItem != null)
      {
        header = applicationItem["Display name"];
      }
      if (header.Length == 0)
      {
        header = "Media Library";
      }
      url.Add("he", header);
      url.Add("pa", "1");
      url.Add("ic", "Applications/16x16/photo_scenery.png");
      url.Add("mo", "media");
      url.Add("ro", ItemIDs.MediaLibraryRoot.ToString());
      SheerResponse.Eval("window.open('" + url + "', 'MediaLibrary', 'location=0,resizable=1')");
    }
  }
}

Thats it. You will now have access to the Media Library from the Page Editor.

Posted in c#, General .NET, Sitecore 6 | Tagged , , , , , | 4 Comments

Edit hidden fields in Sitecore page editor

As the Sitecore Page Editor (also known as the Unified Page Editor) is getting better and better, the demand for “page edit only” solutions grow. Customers are seeking solutions where the page editor is the only tool used to edit the web site.

Sitecore Unified Page Editor

Sitecore Unified Page Editor

In order to create a solution where the user never opens the Sitecore shell you need to address 2 important issues.

ISSUE #1: Edit multilist fields

The first issue is how to give access to multilist fields, treelist fields, links or other custom fields? This is easily solved using an Edit Frame. Read about Using Sitecore EditFrame in PageEdit here.

ISSUE #2: Edit hidden fields

The next issue is how to give access to non-visual fields like metdata fields, spot selectors, search engine options and the likes?

Here my colleague Thomas Stern (iStern) have come up with a simple yet ingenious solution. He simply opens the Sitecore Field Editor (the editor that the Edit Frame pops) from a button in the toolbar of the Page Editor.

Read iSterns blog post here: Running Sitecore Field Editor from a command.

Image from iStern’s blog

Thanks to Thomas Stern for the solution.

 

Posted in c#, Sitecore 6 | Tagged , , , | 2 Comments

Javascript string encoding in C#

In a previous post I explained how you can use the C# System.Uri.EscapeDataString() to encode strings to be later decoded by the equvivalent JavaScript decodeURIComponent().

The method above is still valid when encoding HTML tags, but fails short when encoding quotations and ampersands.

The following string in C# will fail when rendered as JavaScript:

string s = "hello's are better than goodbye's";
Response.Write("<script language='javascript'>alert('" + s + "');</script>");

We need to encode the ampersands (the ‘). This will produce a correct output:

string s = @"hello\'s are better than goodbye\'s";
Response.Write("<script language='javascript'>alert('" + s + "');</script>");

The \ will escape the ‘ signs.

Now, .NET 4.0 have introduced a function, HttpUtility.JavaScriptStringEncode() method. This function escapes quotes and double quotes as well as ?’s and &’s.

For us who does not have the possibility to use .NET 4.0, the function is pretty easy to develop. And why not pair it with the System.Uri.EscapeDataString() so you get a complete clean string encoding.

This Extension Method will escape the data and all quotes in one go:

public static class StringExtensions
{
  public static string ToJavaScriptString(this String instr)
  {
    return Uri.EscapeDataString(instr).Replace("'", @"\'").Replace(@"""", @"\""");
  }
}

To use the function you can do the following:

string s = "<h1>hello's</h1>";
Response.Write("<script language='javascript'>alert(decodeURIComponent('" + s.ToJavaScriptString() "'));</script>");

Now you will never have any problems with HTML tags, quotes, double quotes, ampersands or other special signs.

Posted in .net, c#, General .NET | Tagged , , , | 4 Comments

IE9 makes Sitecore crash when uploading using flash upload

First I thought this was a Sitecore issue. Then I beileved it to be a Internet Explorer 9 issue. But it’s really a Macromedia Flash issue.

Flash has an issue with modal browser windows in Internet Explorer 9. When a window is modal, flash refuses to open the file upload “browse” window. It either crashes IE, or waits to open the browse window untill the modal window is closed.

Try the following: Open the Media Browser from an Image field and press the “upload” button. Then press the “Browse…” button and see your Internet Explorer 9 fail to respond:

Upload using Flash Mode

Upload Flash Mode

Sitecore uses a small flash file to mimmick the upload in the “Upload a file” dialog box. Because the Media Browser is a modal window, it stops IE9 from working.

The workaround is to go back to the “Classic” upload mode, by setting Upload.Classic to “true” in the web.config:

<setting name="Upload.Classic" value="true" />

Using classic mode replaces the flash with a normal asp:FileUpload control, and IE9 will survive:

Upload Classic Mode

Upload Classic Mode

A Quick note: Sitecore does not officially support IE9 unless you are running Sitecore 6.4.1 update 5 or 6.5 update 2. I expect Sitecore to support any newer version from 6.5 very soon.

Posted in Sitecore, Sitecore 6 | Tagged , , , , , | 2 Comments

Reading mails using IMAP and MailSystem.NET

In this article I will describe how you can use C# and MailSystem.NET to read mails from any IMAP source, including GMail.

MailSystem.NET is a Open Source, GNU Library General Public Licensed library for managing emails using SMTP, POP3, IMAP and many more protocols.

Download MailSystem.NET here.

I needed a component that will read mails from an IMAP source and import them into Sitecore. So I built this simple repository to get me all mails from a certain mailbox. Here is how I did it.

First you need to copy ActiveUp.Net.Common.dll and ActiveUp.Net.Imap4.dll from the MailSystem.NET package to your /bin/ folder.

Reading from MailSystem.NET is pretty straight forward:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using ActiveUp.Net.Mail;

namespace PT.MailIntegration.IMAP
{
  public class MailRepository
  {
    private Imap4Client _client = null;

    public MailRepository(string mailServer, int port, bool ssl, string login, string password)
    {
      if (ssl)
        Client.ConnectSsl(mailServer, port);
      else
        Client.Connect(mailServer, port);
      Client.Login(login, password);
    }

    public IEnumerable<Message> GetAllMails(string mailBox)
    {
      return GetMails(mailBox, "ALL").Cast<Message>();
    }

    public IEnumerable<Message> GetUnreadMails(string mailBox)
    {
      return GetMails(mailBox, "UNSEEN").Cast<Message>();
    }

    protected Imap4Client Client
    {
      get
      {
        if (_client == null)
          _client = new Imap4Client();
        return _client;
      }
    }

    private MessageCollection GetMails(string mailBox, string searchPhrase)
    {
      Mailbox mails = Client.SelectMailbox(mailBox);
      MessageCollection messages = mails.SearchParse(searchPhrase);
      return messages;
    }
  }
}

This repository can now be used to read all mails or all unread mails from a mailbox. The constructor takes the mail server name, port and SSL information, as well as the email address and password of the account to read from. GetAllMails() read all mails from the specified mailbox, while GetUnreadMails() reads all unread mails. Both functions returns a list of Message objects. A Message is the complete email in one object.

To read from a GMail mailbox you need to connect using SSL on port 993:

using ActiveUp.Net.Mail;
using PT.MailIntegration.IMAP;

protected void TestMail()
{
  MailRepository rep = new MailRepository("imap.gmail.com", 993, true, @"mailaddress@gmail.com", "password");
  foreach (Message email in rep.GetUnreadMails("Inbox"))
  {
    Response.Write(string.Format("<p>{0}: {1}</p><p>{2}</p>", email.From, email.Subject, email.BodyHtml.Text));
    if (email.Attachments.Count > 0)
    {
      foreach (MimePart attachment in email.Attachments)
      {
        Response.Write(string.Format("<p>Attachment: {0} {1}</p>", attachment.ContentName, attachment.ContentType.MimeType));
      }
    }
  }
}

Other IMAP mail servers might not need SSL, or requires another port number.

When calling GetUnreadMails(), mails will be marked as read afterwards.

Please note that reading using IMAP (at lest from GMail accounts) is very slow. Don’t be surprised if it takes up to a minute to get 30 mails from a mailbox.

Posted in .net, c# | Tagged , , , , , | 47 Comments

Identifying mobile devices in Sitecore

Let’s face it. We don’t do websites anymore. Websites are what we did last year. Websites are something you develop with Drupal, Joomla or Umbraco.

Sitecore is no longer a Content Managent System. It’s a digital communication platform, allowing your customers to target their customers on every platform.

If we need to read or write to Facebook or Twitter, we build it. If we need to target desktop, tablet and mobile devices, we build it. In the same system. Not by creating parallel, almost-the-same-system systems. No, we make one system that will work on every digital platform we need it to work on.

One of the many Sitecore features that helps us achive this one-system-many-targets goal is the Device concept. It allows you to target different platforms with the same content. With this seperation of content from design, you can create device specific pages for mobile phones, printable pages or whatever you need.

ENOUGH TALK, LETS CODE

Devices are set up in /sitecore/layout/Devices:

Sitecore Devices

Sitecore Devices

When devices are set up, you can add layouts, sublayouts and renderings to each device on your template, or you can create one default fallback device:

Layout Fallback

Layout Fallback

But how are the devices changed? You can add the device to the <sites> section in the web.config. You can define an URL parameter that will change the device. Or you can develop a DeviceResolver processor and add it to the httpRequestBegin pipeline.

John West have an article on Using the Sitecore Rules Engine in a Custom Context: Setting the Context Device. This is a cool way to use the Sitecore Rules Engine to determine which device is the current.

But if you think that’s too complicated, or you just would like to know how to do it yourself, here is an example.

CREATING A httpRequestBegin PIPELINE PROCESSOR THAT CHANGES THE DEVICE BASED ON THE CURRENT BROWSER

This is an example on how to determine if the website is running on a Desktop PC, a Tablet (iPad) or a mobile phone (iPhone, Android, …) and change the Sitecore device accordingly.

First of all I will define my 3 devices, Default (Desktop PC), Tablet and Mobile:

public enum DeviceType
{
  Mobile,
  Tablet,
  Default
}

These 3 devices needs to be defined in my Sitecore installation.

Next step is to create a Device type retrieval service. This service uses the HTTP_USER_AGENT server variable to determine the current browser, and from the browser type I guess the device type.

using System.Linq;
using System.Web;

namespace PT.SiteRedirect
{
  public class DeviceTypeRetrievalService
  {
    public static DeviceType RetrieveContext()
    {
      //GETS THE CURRENT USER CONTEXT
      var context = HttpContext.Current;

      //FIRST TRY BUILT IN ASP.NET CHECK
      if (context.Request.Browser.IsMobileDevice)
      {
        return DeviceType.Mobile;
      }

      //THEN TRY CHECKING FOR THE HTTP_X_WAP_PROFILE HEADER
      if (context.Request.ServerVariables["HTTP_X_WAP_PROFILE"] != null)
      {
        return DeviceType.Mobile;
      }

      //THEN TRY CHECKING THAT HTTP_ACCEPT EXISTS AND CONTAINS WAP
      if (context.Request.ServerVariables["HTTP_ACCEPT"] != null &&
          context.Request.ServerVariables["HTTP_ACCEPT"].ToLower().Contains("wap"))
      {
        return DeviceType.Mobile;
      }

      //AND FINALLY CHECK THE HTTP_USER_AGENT
      //HEADER VARIABLE FOR ANY ONE OF THE FOLLOWING
      if (context.Request.ServerVariables["HTTP_USER_AGENT"] != null)
      {
        var userAgent = context.Request.ServerVariables["HTTP_USER_AGENT"].ToLower();

        var tablets =
          new[]
            {
              "ipad", "android 3", "xoom", "sch-i800", "tablet", "kindle", "playbook"
            };

        //Loop through each item in the list created above
        //and check if the header contains that text
        if (tablets.Any(userAgent.Contains) || (userAgent.Contains("android") && !userAgent.Contains("mobile")))
        {
          return DeviceType.Tablet;
        }

        //Create a list of all mobile types
        var mobiles =
          new[]
            {
              "midp", "j2me", "avant", "docomo",
              "novarra", "palmos", "palmsource",
              "240x320", "opwv", "chtml",
              "pda", "windows ce", "mmp/",
              "blackberry", "mib/", "symbian",
              "wireless", "nokia", "hand", "mobi",
              "phone", "cdm", "up.b", "audio",
              "SIE-", "SEC-", "samsung", "HTC",
              "mot-", "mitsu", "sagem", "sony"
              , "alcatel", "lg", "eric", "vx",
              "NEC", "philips", "mmm", "xx",
              "panasonic", "sharp", "wap", "sch",
              "rover", "pocket", "benq", "java",
              "pt", "pg", "vox", "amoi",
              "bird", "compal", "kg", "voda",
              "sany", "kdd", "dbt", "sendo",
              "sgh", "gradi", "jb", "dddi",
              "moto", "iphone", "Opera Mini"
            };

        //Loop through each item in the list created above
        //and check if the header contains that text
        if (mobiles.Any(userAgent.Contains))
        {
          return DeviceType.Mobile;
        }
      }

      return DeviceType.Default;
    }
  }
}

The final piece of code is the device resolver processor itself, the piece of code that is exposed in the httpRequestBegin pipeline.

using System.Web;
using Sitecore;
using Sitecore.Data.Items;
using Sitecore.Pipelines.HttpRequest;

namespace PT.SiteRedirect
{
  public class DeviceResolver : HttpRequestProcessor
  {
    public override void Process(HttpRequestArgs args)
    {
      HttpContext currentHttpContext = HttpContext.Current;

      if (currentHttpContext == null || Context.Database == null)
        return;

      if (Context.Site.Name.ToLower() != "website")
        return;

      DeviceType deviceType = DeviceTypeRetrievalService.RetrieveContext();
      switch (deviceType)
      {
        case DeviceType.Default:
          break;
        case DeviceType.Mobile:
          SetDevice("Mobile");
          break;
        case DeviceType.Tablet:
          SetDevice("Tablet");
          break;
      }
    }

    private void SetDevice(string deviceName)
    {
      DeviceItem device = Context.Database.Resources.Devices["Default"];
      device = Context.Database.Resources.Devices[deviceName];
      Context.Device = device;
    }
  }
}

The processor is added to the httpRequestBegin pipeline:

  ...
  <processor type="Sitecore.Pipelines.HttpRequest.BeginDiagnostics, Sitecore.Kernel" />
  <!-- My porocessor -->
  <processor type="PT.SiteRedirect.DeviceResolver, PT.SiteRedirect" />
  <!-- My porocessor -->
  <processor type="Sitecore.Pipelines.HttpRequest.DeviceResolver, Sitecore.Kernel" />
  ...

CONCLUSION

When the processor is added it will change the device to Default, Tablet or Mobile depending on the user agent string.

When using a fallback device to render the layout, it’s still the original device that you can read:

public string CurrentDevice
{
  get
  {
    return Sitecore.Context.Device.Name;
  }
}

With this information in hand you can start making server-side based decisions on how content should be rendered. One example could be that you render images in different sizes depending on the device:

</pre>
<div class="Logo"><a href="/">

 </a></div>
<pre>
protected void Page_Load(object sender, EventArgs e)
{
  switch (Sitecore.Context.Device.Name.ToLower())
  {
    case "default":
      break;
    case "tablet":
      break;
    case "mobile":
      logoImage.MaxWidth = logoImage.MaxWidth / 2;
      break;
  }
  logoImage.DataBind();
}

MORE INFORMATION

Thanks to Kim Schiøtt for the initial device resolver.

Posted in c#, Sitecore 6 | Tagged , , , , , | 2 Comments

Sitecore cross-site and cross-language links resolved in 6.4.1 release 120113

In my previous post, Sitecore links in multisite solutions – SiteResolving, I described a limitation to the LinkManager when having multi-site and multi-language solutions.

The limitations occur in solutions where you have seperate domain names for languages AND having multiple sites at the same time. In this case it’s very hard to get the optimal links when linking between sites.

But from Sitecore 6.4.1 rev. 120113 (6.4.1 Update-6) Sitecore has introduced 2 new settings, allowing you to have more control on how links between sites and languages are created:

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

If Rendering.SiteResolvingMatchCurrentSite is true, the link provider will check if the target item is located under the start item for the current site before it tries to find a match in the full list of site definitions. This ensures that when the target item can be resolved using the current site, the target link will not change to a different site/hostname.

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

If Rendering.SiteResolvingMatchCurrentLanguage is true, the link provider will take the language attribute of the site definitions into consideration when resolving which site/hostname to use when rendering a cross-site link. This setting is enabled by default.

Thanks to Sitecore support for clarifying this.

 

Posted in Sitecore 6 | Tagged , , , , | 2 Comments

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

Posted in Sitecore 6 | Tagged , , , | 6 Comments