onsdag den 27. august 2014

Upgraded to Visual Studio 2013, and now your Sitecore project can no longer find namespaces?

It seems like Visual Studio 2013 does things a bit differently.

This is not directly related to Sitecore - but it is related to having a web.config inside your project folder.

The problem seems to be, that Visual Studio 2013 starts to parse web.config, and if it finds any errors, or any mismatches (like a web.config that does not match the version of Sitecore, or some ASP.NET references doesn't match) - Visual Studio will start marking every namespace as invalid, even though intellisense works fine.

If this happens to you - try and renaming web.config to confirm that this is what is causing the problem for you.

If it is, try and use a clean web.config from the version of Sitecore you are using, to see if that works.

If it works - compare the two versions of web.config and see what is different, and work your way from here, untill you find what is causing this.

A hint might be to look at assemblybindings ;-)

onsdag den 20. august 2014

Disable indexing of an entire site using HTTP headers

Sometimes a customer wants to prevent an entire website from being indexed (like when they are creating content before the site goes live).

If the site is hosted on the same Sitecore instance as other websites, using robots.txt is not an option.

So here is another way, that is better, and that isn't an all-or-nothing approach.

First, take the template defining your frontpage item, and add a new checkbox field to this template - call the field "Not Indexable".

Next, create a new class in Visual Studio, and add the following code:

public class NotIndexableProcessor : HttpRequestProcessor
{
    public override void Process(HttpRequestArgs args)
    {
        if (args == null || args.Context == null)
        {
            return;
        }

        Item homeItem = Sitecore.Context.Database.GetItem(Sitecore.Context.Site.StartPath);

        if (homeItem == null)
        {
            return;
        }

        CheckboxField notIndexableField = homeItem.Fields["Not Indexable"];
        if (notIndexableField == null || !notIndexableField.Checked)
        {
            return;
        }

        args.Context.Response.Headers["X-Robots-Tag"] = "noindex, nofollow";
    }
}

Now you just need to create an include file, for Sitecore to run this.
So do this in your favorite XML editor, and add the following text to it:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:x="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <httpRequestBegin>
        <processor type="Namespace.Classname, Assembly" patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']"/>
      </httpRequestBegin>
    </pipelines>
  </sitecore>
</configuration>

Replace the Namespace, Classname and Assembly, so they match your setup.

Now, if this checkbox is checked, and an item below it is accessed, the X-Robots-Tag header will be added to the response, and search engines will not index anything on it.

Creating a new item with a specific ID instead of an auto-generated one.

Normally, when you create an item from code, you find the parent item, and use the Add method on this item to create the child.

This is the recommended way to create items, however - it misses one crucial thing, which is defining what ID the newly created item has.

The logical question to ask is - why would you want to deside which ID the item has?
The answer is - alot of times when you import data from something where the unique ID already is a GUID.

The reason for this is, that is makes it very fast to lookup if the item already exists, and easy to handle cross-references between items.

Also, if you are migrating items from one Sitecore instance to another by code (as in - creating new items, not copying the old items over) - keeping the ID's might be a good idea, to again make cross-references work.

Enough walk you say? Okay, here is how to do it:

// The name of the item to be created.
string newItemName = "My New Item";

// Some way to get the parent item.
Item parentItem = GetItemParent();

// The ID of the template to create the item from - can be a branch ID.
ID templateID = new ID("{1618FA40-8E57-4018-968A-5996E4952D93}");

// The ID of the item to be created.
ID newItemID = new ID("{9AAFC1F7-F276-4651-ADD2-6C9ED6F1B9E0}");

// Should security for the operation be checked?
SecurityCheck checkSecurity = SecurityCheck.Enable;

// Now we create the item.
Item newItem = ItemManager.CreateItem(newItemName, parentItem, templateID, newItemID, checkSecurity);

Now we have created an item, with the ID defined in newItemID.

Pretty simple, don't you think?

Require HTTP / HTTPS for certain pages with only a single processor, a base template and a few simple items.

From time to time, customers would like to define, that a certain page should be viewed using HTTPS, while the rest of the website remains as HTTP.

There are many ways to do this - however, I find this solution to be the most simple, since it doesn't require alot of custom code, and once implemented, it can be reused without having to think about it.

Some items

The first step is to create a few dummy items, that is used to define the states that can be selected in the dropdown box added to the base template.

To do this, go into the /sitecore/System/Modules folder, and create a new folder here called "Require SSL Modes"

Right click this folder, and select "Insert from Template" - now find the "Standard Template" template, and insert an item from this template - call the item "Inherit".

Duplicate this item three times, and call the three copies "Leave Default", "Force HTTP" and "Force HTTPS".

Now you should have 4 items in the folder.
The reason for using the "Standard Template" instead of our own template is, that we don't need any fields on these, so just creating an extra template just for this seems redundant.

A template

The next thing to do, is to create a base template, that has the field used on pages to define if a page should be using this or not.

So go into the /sitecore/Templates folder, and create a template where it makes sense in your structure.

On this template, add a single field, call it "HTTP Mode" - set it's type to Droplist, and define it's source to "/sitecore/System/Modules/Require SSL Modes" - then save the template.

Now make your page templates inherit from this template, so they have the field.

A simple processor

Now we are getting to the fun part - fire up Visual Studio, and create the following C# class:

public class RequireSslProcessor : HttpRequestProcessor
{
    public override void Process(HttpRequestArgs args)
    {
        if (Sitecore.Context.Item == null)
        {
            return;
        }

        if (args == null || args.Context == null)
        {
            return;
        }

        // First we set the current item as our starting point.
        Item currentItem = Sitecore.Context.Item;

        // We keep going until we haven't found anything.
        while (currentItem != null)
        {
            // Also, the current page need to inherit our field.
            Field httpModeField = currentItem.Fields["HTTP Mode"];
            if (httpModeField == null)
            {
                return;
            }

            Uri url = args.Context.Request.Url;
            Uri newUrl;

            // If one of the force modes, and the other one is present, we rewrite the url scheme.
            switch (httpModeField.Value)
            {
                case "Force HTTP":
                    if (url.Scheme.Equals("http", StringComparison.InvariantCultureIgnoreCase))
                    {
                        return;
                    }

                    newUrl = RewriteUrlScheme(url, "http");

                    break;
                case "Force HTTPS":
                    if (url.Scheme.Equals("https", StringComparison.InvariantCultureIgnoreCase))
                    {
                        return;
                    }

                    newUrl = RewriteUrlScheme(url, "https");

                    break;
                case "Inherit":
                    currentItem = currentItem.Parent;
                    continue;
                default:
                    return;
            }

            // And then we redirect, and stops the pipeline.
            args.Context.Response.Clear();
            args.Context.Response.Headers.Add("Location", newUrl.ToString());
            args.Context.Response.Status = "301 - Moved Permanently";
            args.Context.Response.StatusCode = 301;
            args.Context.Response.End();

            args.AbortPipeline();
            return;
        }
    }

    private static Uri RewriteUrlScheme(Uri url, string newScheme)
    {
        // First we change the scheme.
        UriBuilder newUriBuilder = new UriBuilder(url)
        {
            Scheme = newScheme
        };

        // Then we change the port, since changing the scheme doesn't change the port for some reason.
        switch (newScheme)
        {
            case "http":
                newUriBuilder.Port = 80;
                break;
            case "https":
                newUriBuilder.Port = 443;
                break;
        }

        return newUriBuilder.Uri;
    }
}

Save the class, compile the project and make sure the DLL file is in the bin folder of the Sitecore installation.

An include file to wrap it up

The last thing to do, is to create an include file, so Sitecore knows that it should use the processor.

Open your favorite XML editor and create a new XML file - call it Modules.RequireSSLModes.config .

Enter this into the file, replacing the Namespace, Classname and Assembly parts so they match your project:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:x="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <httpRequestBegin>
        <processor type="Namespace.Classname, Assembly" patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']"/>
      </httpRequestBegin>
    </pipelines>
  </sitecore>
</configuration>

Then save the config file.

To test this, make sure the website is configured to use both HTTP and HTTPS inside IIS.
Then create a few pages from items that inherits this field, and change their setting to different HTTP Mode values, and navigate around between them.

It is really that simple :)