Tuesday, May 26, 2015

Using the package designer to sync Sitecore content between environments


The Sitecore 8 package designer allows you to create a package (zip file) with selected Sitecore items which can then be imported into another Sitecore installation. An example of where this might be useful is moving content, templates and layouts from a development environment to a test or production environment.

Creating the XML project

An XML project is the settings/variables for a given package. This project can then be run to create the actual package (which contains the Sitecore content) and even exported/imported itself so that you can generate the same packages on multiple environments.

To create an XML project, load up the Sitecore administration desktop. Click the start menu, then Development Tools and then Package Designer. You will be greeted with a form for creating your XML project, the fields (apart from Package Name) are all optional, but generally it's a good idea to specify the author, a version and perhaps some documentation notes.
You now have the option to specify which items/files to include both statically or dynamically. As all of my layouts physical files are inside a visual studio solution (which itself can be deployed), I won't need to include files in this package. Because this package needs to be re-usable I will be taking items dynamically (if you have a set of files which will not have additions/deletions you could create a dynamic package just for them). Click on the Items Dynamically menu item and the following screen will appear:
Select the root template item, or if you have created a folder under templates with all of your custom templates select that. Click Next and you will be prompted to add a filter to which items to include, in this example I want to take everything so enter no filters and click Next again. Now give your source a name, in this case I used "MyPackage_Templates_Source". A package can have multiple sources, so you will want to create one to cover all of the Sitecore items you have configured, in this case I have taken Content, Templates and Layouts:
It is also important which order these sources run in, templates should be first because the content items will have content assigned to fields on the templates. If you don't import templates first all of the custom fields (from the templates) will be blank.

Once you have chosen all of the sources, click the Save As button to save the XML project. Your projects can now be opened, downloaded and then imported into other Sitecore instances.
In this example (for demonstration purposes) I have selected entire content trees from Sitecore which can make for some very large packages. Generally you would want to become very granular to ensure you only package up the content which is custom from the base Sitecore. Using filters under Items Dynamically or by selecting the precise Items Statically, Sitecore allows you to create minimal packages which allow the right Sitecore content items to be synced between environments.

Creating the package

Once you have defined an XML project you can then run that project to generate a zip file. Click Open off the main menu of the Sitecore Package Designer and select the XML project you wish to run. Once open, click Generate Zip on the main menu.
Give the zip file a meaningful name (perhaps indicating the project being run and a timestamp), then click next. The zip file will begin to generate (which may take several minutes) and once complete a confirmation box will popup with a link to download the Sitecore package (in zip file format).

Importing the package

Once you have downloaded a package you can then import it into another Sitecore instance. Load up the Sitecore administration desktop and click the start menu, then Development Tools and then Installation Wizard. Click the Choose package button, here you are able to upload your package (and select it) or select a package you have uploaded previously. Once you have selected a package, click the Open button, and then Next where some of the package details (entered during the XML project creation stage) will show. 
Click the install button and the package installation will begin. You may be promoted with messages when an item in the package is already installed on the Sitecore instance. Generally I will select Overwrite and click Apply to all because this ensure the environments are the same when it comes to GUIDs of Sitecore items.
The installation may take several minutes to complete, but once finished you will see a message with the option to restart the client or server, I generally restart the client. 
Now you should deploy any code to the server as required and perform a full publish in Sitecore. Now both of your Sitecore environments should be synced and running the exact same content and code.

Monday, May 25, 2015

Sitecore remove en from URLs

By default Sitecore may automatically add the language options to the sites URL. For example the URL may appear as "http://domain.com/en/page" when you would rather it appear as "http://domain.com/page".

This is controlled by the "languageEmbedding" setting in the web.config file (in the website's root). Simply find the following line under linkManager > providers and change the value to never:
<add name="sitecore" languageEmbedding="never" />
The valid options for this setting are:
  • asNeeded
  • never
  • always

Sitecore package designer gets stuck when generating a zip

When trying to generate a zip from the Sitecore package designer (using the items statically method), I found that sometimes the process would work and other times it would get stuck at generation of the zip.
The error appeared to occur when the process got stuck once, and therefore any other jobs kicked off after that would never complete. An IISReset appeared to clear up the issue and the zips began to generate as expected.

Thursday, May 21, 2015

Using regex inside LINQ

When using .Contains with LINQ, there can sometimes be some false positives with the results. For example the word "concatenate" would return for .contains of the string "cat". This may not be iddeal, so regex can be used in LINQ to provide more relevant results.
var testList = new [] {"You may concatenate a string.", "They have a cat.", "Cat is a feline.", "Her name is Catherine."};
var regex = new Regex(@"(^|\W)cat", RegexOptions.IgnoreCase | RegexOptions.Multiline);

var matches = testList.Where (l => rexex.IsMatch(l));
In the example above the regex is going to match for inputs that begin with the word "cat" or contain a word beginning with cat. So "You may concatenate a string." would not pass, because cat is not at the start of the word. However the other 3 examples would because the word cat is there or a word that begins with it.

Sitecore content search Invalid Method Call Argument Type

I came across the following error when trying to use regex to match search results in the Sitecore search context.
Invalid Method Call Argument Type: Field - FieldNode - Field: content - System.String. Only constant arguments is supported.
This is due to the fact that when using LINQ with Sitecore content search will only accept constant expressions rather than lamda expressions.

Sitecore 8 Lucene search index and implementation

Creating the index

In the "Website\App_Config\Include" folder in your Sitecore web site you will find some example files containing search index XML. 
  • Sitecore.ContentSearch.Lucene.Indexes.Sharded.Core.config.example
  • Sitecore.ContentSearch.Lucene.Indexes.Sharded.Master.config.example
  • Sitecore.ContentSearch.Lucene.Indexes.Sharded.Web.config.example
  • Sitecore.ContentSearch.Solr.DefaultIndexConfiguration.config.example
  • Sitecore.ContentSearch.Solr.Index.Analytics.config.example
  • Sitecore.ContentSearch.Solr.Index.Core.config.example
  • Sitecore.ContentSearch.Solr.Index.Master.config.example
  • Sitecore.ContentSearch.Solr.Index.Web.config.example
There are examples present for both Lucene and Solr and a seperate file for each Sitecore database (web, core master and analytics). In this example, I will be using Lucene for search and wish to index the web database so will begin with "Sitecore.ContentSearch.Lucene.Indexes.Sharded.Web.config.example". 
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
      <configuration type="Sitecore.ContentSearch.ContentSearchConfiguration, Sitecore.ContentSearch">
        <indexes hint="list:AddIndex">
          <!-- Change this to Sitecore.ContentSearch.LuceneProvider.SwitchOnRebuildLuceneIndex, Sitecore.ContentSearch.LuceneProvider if you would like indexes to be
               built in a temporary directory i.e. while rebuilding is happening, your old indexes work like normal until the rebuild is finished. -->
          <index id="CustomSearch" type="Sitecore.ContentSearch.LuceneProvider.LuceneIndex, Sitecore.ContentSearch.LuceneProvider">
            <param desc="name">$(id)</param>
            <param desc="folder">$(id)</param>
            <!-- This initializes index property store. Id has to be set to the index id -->
            <param desc="propertyStore" ref="contentSearch/indexConfigurations/databasePropertyStore" param1="$(id)" />
            <configuration ref="contentSearch/indexConfigurations/defaultLuceneIndexConfiguration">
    <include hint="list:IncludeTemplate">
            <strategies hint="list:AddStrategy">
              <!-- NOTE: order of these is controls the execution order -->
              <strategy ref="contentSearch/indexConfigurations/indexUpdateStrategies/onPublishEndAsync" />
            <locations hint="list:AddCrawler">
              <crawler type="Sitecore.ContentSearch.SitecoreItemCrawler, Sitecore.ContentSearch">
The first step is to give the index a name, this is set on the id attribute of index. This name is what you use to refer to the index in code and in Sitecore, so make it descriptive. Under the configuration section I will also add XML to specify which template(s) should be indexed and which fields.
<include hint="list:IncludeTemplate">
In  this case I am indexing two templates (referred to by GUID) and the fact that I want all fields indexed. The index is also left to the default of "onPublishEndAsync" which means it will execute automatically at the end of a publish.

An XML file can contain multiple indexes, and can be given any name. It should be saved in the same location as the example files ("Website\App_Config\Include"), and Sitecore will automatically pick it up (if the XML is formed correctly).

Running the index

To give the index an initial run, navigate to the Sitecore desktop. Click on the main menu icon and select "Control Panel", here you will find a section on indexing.

Click on the "Indexing manager" option and you should see the index created earlier and when it was last run. You can also select the index, click "Rebuild", to manually run the index.

Accessing the index in code

Now that the index has been defined, and run it can be queried in code to return relevant items (aka a custom search on a Sitecore 8 web site). In this example I have a custom template for content pages, which contains a number of custom fields related to navigation, metadata and content. For the purposes of search the two fields that will be queried are: Title (which is the title of the page) and Content (which is the main block of HTML content for the page).
public partial class SearchResults : System.Web.UI.UserControl
    protected void Page_Load(object sender, EventArgs e)
        var searchQuery = Request.QueryString["s"]; // search query
        var searchResults = new List<SearchResult>();

        if (!string.IsNullOrEmpty(searchQuery))
            // sitecore search index
            var index = ContentSearchManager.GetIndex("CustomSearch");

            using (var context = index.CreateSearchContext())
                var results =
                            resultItem =>
                                resultItem.PageTitle.Like(searchQuery) ||

                foreach (var result in results)
                    var searchResult = new SearchResult();
                    var item = result.Document.GetItem();

                    searchResult.Title = item.Fields["Title"].ToString();
                    string content = Common.StripTags(item.Fields["Content"].ToString()); // get page content and strip HTML tags
                    searchResult.Summary = Common.TruncateString(content, 300); // truncate content to first 300 characters

                    searchResult.Link = Sitecore.Links.LinkManager.GetItemUrl(item);

            // bind search results to frontend
            rptSearchResults.DataSource = searchResults;
            // handle no search query

    /// <summary>
    /// Search results object for binding to frontend
    /// </summary>
    public class SearchResult
        public string Title { get; set; }

        public string Summary { get; set; }

        public string Link { get; set; }

    /// <summary>
    /// Custom sitecore search results item with required fields
    /// </summary>
    public class MyResultItem : SearchResultItem
        public string PageTitle { get; set; }

        public string PageContent { get; set; }
In the above code sample I am accessing the CustomSearch index and getting the items where the page title is like the search query and where the page content contains the search query. These results are then cast to the list of MyResultItem object which can then be enumerated to build a list of custom object SearchResult which is then bound to the front end.

Further reading

Monday, May 18, 2015

Breadcrumbs for pages in Sitecore

Breadcrumbs will generally start with the home page followed, by each level of the site architecture and ending in the current item. Therefore in this example, the breadcrumbs will always have the home item (linked to the home page) and the current item (with no link). The logic required is filling in the missing pieces (the structure in between home and current item).
Item currentItem = Sitecore.Context.Item; // Current sitecore item
BreadCrumbs.Current = currentItem.Fields["Title"].ToString(); // set current items title on breadcrumb object
currentItem = currentItem.Parent; // go to parent of current item

while (currentItem.ID != new ID(Items.ContentRootItem))
    // add parent item to breadcrumb object (a list of parent items)
    BreadCrumbs.Parents.Add(new Common.MenuItem
        Link = Sitecore.Links.LinkManager.GetItemUrl(currentItem),
        Text = currentItem.Fields["Title"].ToString()

    currentItem = currentItem.Parent; // get parent of current item

BreadCrumbs.Parents.Reverse(); // reverse order of parents to get in correct order
/// <summary>
/// Menu Item
/// </summary>
public class MenuItem
    /// <summary>
    /// Gets or sets the link value.
    /// </summary>
    public string Link { get; set; }

    /// <summary>
    /// Gets or sets the text value.
    /// </summary>
    public string Text { get; set; }

    /// <summary>
    /// Gets or sets the css class value.
    /// </summary>
    public string CssClass { get; set; }
In the code above there is a simple breadcrumb object which contains a list of parent items and the current item's title. On the front end you simply output the home item in the sublayout, set the current item's titleand using a repeater, loop through the parent items to display their title as a hyperlink. Items.ContentRootItem is a resources file string containing the GUID for the root item, we are looping through the parents until we hit this item.

C# nested repeater for a menu with child items

For a two layered menu (parent menu items that can have child items) in C# the following solution can be used.
protected void rptMenuParent_OnItemDataBound(object sender, RepeaterItemEventArgs e)
    var row = (TopMenu)e.Item.DataItem;

    Repeater nestedRepeater = e.Item.FindControl("rptMenuChild") as Repeater;
    nestedRepeater.DataSource = row.Children;

protected void Page_Load(object sender, EventArgs e)
    var menuItems = new List<TopMenuItem>();
    // add menu items
    rptMenuParent.DataSource = menuItems;
public class TopMenu
    public MenuItem Parent { get; set; }

    public List<MenuItem> Children { get; set; }

public class MenuItem
    public string Link { get; set; }

    public string Text { get; set; }
<ul id="menu-header" class="menu">
    <asp:Repeater ID="rptMenuParent" runat="server" OnItemDataBound="rptMenuParent_OnItemDataBound">
            <li class="menu-item">
                <a href="<%# Eval("Parent.Link") %>"><%# Eval("Parent.Text") %></a>
                <asp:Repeater ID="rptMenuChild" runat="server">
                    <HeaderTemplate><ul class="sub-menu"></HeaderTemplate> 
                        <li class="menu-item">
                            <a href="<%# Eval("Link") %>"><%# Eval("Text") %></a>
A list of TopMenu can be assigned to the parent repeater. This will output the first level (parent items) and the on data bound event will then pass the children (if any) to the child repeater. This could be extended to a third level by adding another list of menu items to the child and adding a third repeater.

Friday, May 8, 2015

The positives and negatives of SharePoint for an external facing web site

SharePoint gets a lot of negativity when it comes to utilising it for an external facing web site, and many developers and business users alike will dismiss it without a second thought. In this post I will look at both the good and bad when it comes to implementing SharePoint as an external web site.

The Positives

  • SharePoint has a large amount of on-line support, documentation and code samples across a variety of web sites (including MSDN forums and Stack Overflow). The problem with many other CMS platforms is the lack of help available on-line, which can make it difficult when developing or even attempting to fix a tricky error. With SharePoint there is plenty of help available and you won't be stuck with no direction on how to implement a given feature of the CMS. There are also many community events available for SharePoint users (both official and unofficial), which makes networking with other SharePoint users possible in person.
  • The licensing is cost effective! When it comes down to the brass tax, a lot of organisations can't afford to pay huge licensing/maintenance fees for the CMS and this can grow exponentially with load balancing and hot disaster recovery servers. SharePoint has arguably the most cost effective licensing costs, and it can even be reduced further with Microsoft enterprise agreements (education and government possibly). 
  • The possibility for a modular code solution architecture, makes it an attractive option for larger development teams. With the average CMS, generally the code would be in one large project/solution which can make team development difficult (we have all done a monster merge or two). With SharePoint you can separate each feature (containing web parts etc.) into a separate Visual Studio project, which can then be deployed and retracted as required. This allows for logical separation of the code (by task, business unit or project) and means less of a need for branching - and ultimately merging.
  • SharePoint as a CMS is very simple to use for the end user and training is not as in depth as other with other CMSes. There is also less material to cover and users/administrators generally do not require multiple courses for additional features like marketing automation etc. As a lot of companies already use SharePoint for their internal intranets, staff members are more likely to have already used SharePoint compared to other web CMSes. 
  • SharePoint is very quick to get started. The basic structure is there and defined, as are the template for sub sites (such as a publishing site). All you need to do is plugin a master page, and you can get going. Any custom code can be coded as a web part and imported to pages as required and data can be imported using powershell.
  • SharePoint has the Client Object Model, which is a JavaScript service that exposes all of the features of SharePoint to the front-end. With the emergence of front end libraries for code (such as AngularJS) the client object model can be used for data access, data writing, creation of the lists, workflow and so much more. With other CMSes you would need to write a custom service to do all this, and then implement Web API to give access to the front end. 

The Negatives

  • SharePoint is not just a CMS, because it does so much more like document management, workflows etc. Because it was not designed with external web sites in mind, and more for internal intranets, a lot of developers feel it makes a poor fit. Arguably the vision of SharePoint is not clear: is it going to focus on intranet, extranet or both and this can be a worry for business stakeholders and developers alike.
  • It is missing a lot of key components that might be needed on an external web site. eCommerce functionality is an example that is not handled out of the box and that would require significant development time to implement. However there are third party solutions available that can bring these components into SharePoint, although there can be costs cassocked with them.
  • The lack of marketing/automation functionality can be seen as a major negative of SharePoint for an external web site. Sitecore for example comes with a lot of support for this via their AIDA platform, and with the way the web space is moving, this can rule out SharePoint for some implementations. There are some third party components available that address some marketing/automation components, yet it's not the same as an out of the box feature.
  • Some people believe that development takes longer in SharePoint when compared to other leading CMSes and that like for like functionality would mean more developer, BA and testing hours when done in SharePoint. Personally I have found this not to be true, and that because SharePoint is very ready out of the box, in the beginning you can actually get going faster. In the end, it comes down to the experience of the developer and how long they have used/developed for the CMS.

The Verdict

For the positive reasons above I wouldn't rule out SharePoint as a CMS for external facing web sites and I don't believe the negatives are enough to rule it out. Yes there are cases where it will just not perform (e-commerce or marketing heavy), but the average web site would do perfectly well in SharePoint. Some business users have had bad experiences with poorly delivered SharePoint implementations and this is what puts them off. However at the end of the day SharePoint is a tool, and a tool is only as good as the person using it.

Monday, May 4, 2015

SharePoint quickly check which site template a page is using

A quick way to identify which site template a site is using in SharePoint is to view the HTML source of the page and search for the following tag:
 An example would be:
var g_wsaSiteTemplateId = 'CMSPUBLISHING#0';
 The template id "CMSPUBLISHING#0" corresponds to a publishing site template.

Saturday, May 2, 2015

Synology DiskStation Error Repairing Volume

When attempting to replace a hard drive in the Synology DiskStation, the following error occurred during the repair of the volume:
Connection Error. Please check your network settings.
The DiskStation Manager software was upgraded to the latest version, and the network was having no issues. By simply setting the new hard drive as a hot spare, Synology DSM automatically picked it up and started repairing the volume automatically.