Monday, December 19, 2016

Sitecore WFFM targeting field inputs with jQuery

With current and older versions of Sitecore's Web Forms for Marketers module, it can often be necessary to make use of jQuery or JavaScript to extend functionality of the form. A common example would be conditional logic, such as if field A has value/selection show field b.

The problem is that the fields output with long ids which contain information such as the form id along with containing placeholder and ordering of the element. Instead of targeting by such a long id, for any custom WFFM form logic I prefer to target via the field id alone.

This can be done by using the jQuery attribute ends with selector. An example for an input field would be:
$("input[id$='9047F37B17F245AE9F56ABBDB1934510']")
This will find the input element for a given Sitecore form field.

Conditional comments HTML for internet explorer 10

One method for displaying a message to users running outdated versions of internet explorer is via the use of conditional comments. For example the following block would only show for version 9 and below of internet explorer:
<!--[if lte IE 9]>
<p> Download Chrome today!</p>
<![endif]-->
 The problem with wanting to target version 10 of internet explorer is that it does not follow conditional comments and instead treats them as a standard comment. Therefore a CSS media query needs to be used to target this browser version.
@media all and (-ms-high-contrast: none) {
    .myClass{ display:block; } /* IE10 */
    *::-ms-backdrop, .myClass{ display: none } /* IE11 */
}
The example above will display a block when the browser is internet explorer 10 and hide it when internet explorer 11. Of course this block would be hidden by default and shown if necessary.

Monday, December 12, 2016

C# SOAP service call and an empty SOAPAction header

Working with a third party service, one of the methods tested okay when using SoapUI, however whenever called via .NET code it would return a 500 error.

Looking at the WSDL, the method required a soap action of "", literally an empty string. So using fiddler I was able to see the request made in SoapUI and code, and discovered that the .NET code was sending the soap action header as null and not an empty string.


To fix this I used the following line to correctly set the soap action in .NET:
webRequest.Headers.Add("SOAPAction", "\"\"");
It started to work as expected.

Tuesday, November 29, 2016

Sitecore package installation never ends

The Sitecore Package Designer is a great way to share content between environments and developers. One annoying error is that on installation of a package it will never end. Upon checking the content structure you will also notice that no content has been installed.


In Sitecore 8+ you will encounter this error if the Sitecore instance is unable to connect to MongoDB. The following will usually be present throughout the logs:
Exception: System.NullReferenceException
Message: Object reference not set to an instance of an object.
Source: Sitecore.Analytics.MongoDB

Simply installing MongoDB and having it running (and configured correctly in the connection strings) will allow the package installer to complete installation as expected.

Wednesday, November 9, 2016

Sitecore error - Could not get pipeline: speak.client.getStyle

After a deployment on Sitecore version 8.1 the following error was appearing when attempting to load the administration launchpad.
Could not get pipeline: speak.client.getStyle (domain: )
I did a search through a clean Sitecore instance's configuration file and found the reference speak.client.getStyle section in the App_Config/Include/001.Sitecore.Speak.Important.config file.

Loading up the show config administration page on the suspect server, there were no patches from 001.Sitecore.Speak.Important.config and browsing the App_Config folder confirmed this file was disabled. Removing the .disabled file extension solved the issue.

Wednesday, October 26, 2016

Sitecore publishing service is not running error

After spinning up the new Publishing Service for Sitecore 8.2 I noticed that publishing was not working and the following error was appearing on the publishing dashboard.
The publishing service is not running. Please contact your system administrator.


This error is generally caused when Sitecore is unable to connect to the publishing service itself. You should ensure that you can access it in the browser and are able to see "{“status”:0}" - which means it's working okay. Also check the PublishingServiceUrlRoot inside the show config admin page to ensure it points correctly to the service URL.

Monday, October 24, 2016

The Sitecore Publishing Service

The Sitecore Publishing Service is a new module released for Sitecore 8.2 which provides a stand alone publishing service for larger implementations. Where it excels is with large volume publishing jobs (think older Sitecore web sites with thousands of items) and it's built with the .NET core!

The end result is replacement of the out-of-box publishing feature in Sitecore, which delivers the following key benefits:
  • High performing - faster publishing.
  • More reliable publishing with better data consistency.
  • A better user experience with more feedback via the Publishing Dashboard.
  • Is run as a separate instance to the content management server - could even be on it's own server.
  • Makes use of it's own architecture rather than existing publishing pipelines, setting and features.
  • Installation is done via the publishing service instance and the integrated module on the Sitecore instance.
So what are the key concepts of the Sitecore Publishing Service?
  • Publishing job: A request of item(s) to be published - can be viewed on the publishing dashboard.
  • Manifests: The tasks to be run for a given publishing job. The service uses logic to work out item deletions, related items to be published and so on. A single publishing job can have multiple manifests.
  • Promotion: The act of moving items (data) from the source (generally the master database) to the target (generally the web database) by following the manifest(s).
  • Manifest results: A list of all changes that were made for a given publishing job. This can be accessed via pipelines for customization.
The steps that the publishing service takes are as follows:
  1. Publishing quests are queued - and then executed.
  2. Connect directly to the SQL Server source/target databases and process the job.
  3. Issue events - such as a cache clear on content delivery instances.
  4. Report back to the user interface - publishing dashboard.
The flow of a publishing job

Detailed look at the various steps

This is a great new release that shows Sitecore is looking to push forward into the future by building on top of the .NET core. Publishing has been a pain point for a number of years around consistency and my personal pet peeve of accidentally closing the publish window during a job. This module may not interest the smaller Sitecore implementations, but those of you who have to wait an hour for a publish using the classic method (imagine kicking off a full publish by mistake) will really appreciate this one.

Wednesday, September 28, 2016

Sitecore Authentication on CES Discovery service failed

Any good Sitecore developer periodically checks their production logs for any error messages or worrying warnings. Whilst doing this on an 8.2 implementation, I noticed the following warning:
ManagedPoolThread #3 09:15:45 WARN Authentication on CES Discovery service failed.
Exception: System.Net.WebException
Message: The remote server returned an error: (403) Forbidden.
Source: System
at System.Net.HttpWebRequest.GetResponse()
at Sitecore.CES.Client.WebClient.ExecuteRequest(String requestUri)
at Sitecore.CES.Client.ResourceConnector`1.Request(String endpoint, Object[] parameters)
at Sitecore.CES.Discovery.EndpointSource.GetEndpoint(String serviceName)
The line before this in the logs states:
INFO  Job started: Update device detection DB
Which  traces back to device detection in Sitecore which is a paid service (since Sitecore 8.1). This can be disabled by using the following patch file:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <settings>
      <setting name="DeviceDetection.Enabled">
        <patch:attribute name="value">false</patch:attribute>
      </setting>
    </settings>
  </sitecore>
</configuration>
It will then begin to insert the following (more graceful) warning :)
WARN  Device detection component is disabled
For those users who want to use this service, remember to enable the connections to discovery-ces.cloud.sitecore.net and devicedetection-ces.cloud.sitecore.net on the firewall.

Introducing the Data Exchange Framework for Sitecore

Some Sitecore implementations can have a requirement to integrate with other third party systems. A common example of this might be a CRM which would sync data to/from Sitecore experience database (contacts). Other examples might be the syncing of product data into Sitecore commerce or simply creating/updating Sitecore items based on a legacy system.

For those of us who have implemented or inherited such integrations in the past, the common problem was often around no real structure and possibility of error. Thankfully Sitecore have released the Data Exchange Framework.

What is the Data Exchange Framework?

To put it simply, it's a framework which handles the transfer of data between two systems. Commonly Sitecore would be one of the two systems (either the source or target). However it is possible to use the framework to handle data exchange between two independent systems - a case for this might be syncing data between a finance system and training management application.

Full documentation for the Data Exchange Framework is available on the Sitecore website - and this is a great example of the new documentation format Sitecore will be using moving forward.

What are it's uses?

Well, the great thing about it being a framework is that the potential uses are really open to your business requirements. Some potential examples I have done in the past whilst working on Sitecore projects, that would be cases for using the Data Exchange Framework are:

  1. Sync course/enrollment data from a training management system. The Sitecore website could then handle new enrollments, existing enrollments and even personalisation based on your course history.
  2. Syncing sales data from Sitecore to a finance system. 
  3. Syncing contact history (products current subscribed to and claims history) from a legacy internal system.
An actual example currently available from Sitecore that uses the Data Exchange Framework is the Dynamics CRM Connect module. The following image is an example of the pipelines (units of work) which this module can perform.


Being that CRM focuses around customers (aka contacts in Sitecore), you will notice that a lot of these pipelines revolve around contacts in the xDb. Other implementations of the Data Exchange Framework might revolve around item creation or update for example.

How does it work?

Without going into too much detail here, the basic gist of the Data Exchange Framework is as follows.

Pipelines are units of work that are performed and are usually related to data transfer. These pipelines can contain multiple Pipeline Steps, which are a series of tasks run in a given order. A Pipeline Processor runs the given pipeline by; deciding which steps to run, running these steps and handling errors. Each pipeline step can also have a Pipeline Step Processor which is the logic used to run that step. The pipeline step might be though of as the configuration (such as the connection string for a CRM), where the pipeline step processor then uses that configuration (step) to actually read/write data in the CRM. There is also a Pipeline Context which allows for data to be shared across multiple pipeline steps in a given pipeline.

Pipelines are run via a Pipeline Batch - which can contain one or multiple pipelines to be run. This wrapper maintains logs of the pipelines running (and when) and can be called via the content editor or as a scheduled job. Of course this then means that there is a Pipeline Batch Processor which handles the logic of which pipelines to run, actually running them and handling any errors.

Going back to my example of Syncing sales data from Sitecore to a finance system, there would likely be the following structure:
  • Update financial system (pipeline)
    • Read data items from Sitecore (pipeline step)
    • Loop through items (pipeline step)
    • Update target system (pipeline step)
This would all then be kicked off via a pipeline batch called sync financial data, which would be run on a schedule (every 30 minutes for example).

Getting started

It all sounds rather complex and may come off as gibberish, however the best advice I can give is to take a read through the Data Exchange Framework documentation. Then actually install the module into a clean Sitecore development environment, along with Sitecore's Dynamics CRM Connect module. The CRM connect module provides a good working example which can help one to grasp the concepts of the framework and provide background for implementing it yourself.

Improving and optimising the performance of your Sitecore website

Statistics around user behavior and page load times is a good bit of background reading on how users perceive the performance of a web site. The standout pieces of information for me was that:
"A 1 second delay in page response can result in a 7% reduction in conversions."
 Along with:
"40% of people abandon a website that takes more than 3 seconds to load."
This then sets the scene for why optimisation of a Sitecore website might be important to the end-users. For Sitecore websites which have a sales/lead generation component, there will be less conversions if the performance is not optimised.

Optimise the front-end

The first thing I always do is take a look at the website using the YSlow tool. This is a browser extension that essentially gives a rating on how well a given page will perform. They key areas it looks at is around the number of HTTP requests, CDN, compression, location of JavaScript/CSS (page blocking), minification and so on. It's a great starting point to get Sitecore optimised and often makes one of the biggest differences to end users.


The main outputs after using this tool should be:

  • CSS/JS bundled and minimised where possible
  • Any third party fonts or JavaScript libraries (jQuery) using CDN where possible
  • Images optimised in terms of size and kept minimal (use sprites in your CSS for example)
  • Static and dynamic compression enabled (this makes a big difference with those JS libraries)
  • Keep the HTTP requests to a minimum, once under load these exponentially affect performance

Optimising the caches


Database caches: Inside Sitecore.config (or web.config) will be a database section for Master, Core and Web. These caches are used to store data coming from the SQL databases and will lead to less database calls and ultimately better performance. The default values are generally not enough for smaller Sitecore implementations and should be optimised in a testing environment for every Sitecore website you work on. The http://server/sitecore/admin/cache.aspx page can be used to monitor these caches and in turn work out the ideal values they should be set to. There are plenty of blog posts/guides on correctly configuring these cache values.


Prefetch caches: When Sitecore is starting up, one of the tasks is to prefetch some data from the various databases, ready to use. These caches are configured in the files available in Website\App_Config\Prefetch and by default will hit key templates, items and item children. Recently I discovered that for the web prefetch cache, Sitecore attempts to cache the default home item and it's children. As many implementations use a custom home item, this is an example of a cache configuration which could be changed. This cache will have a minor impact on startup time, which should be okay on production environments, but will affect developers on their environments.

HTML Output cache: When you use caching for sublayouts/renderings, this data is stored in the HTML output cache. On the Site element for your web site (found under <configuration><sitecore><sites>) there is a variable called htmlCacheSize which controls the size of this cache. By using the cache administration page mentioned above, you are able to see if this cache is adequately sized. Remember to check this page on content delivery servers in a scaled environment, as these are the ones mainly serving data from this cache.

Optimising the database

SQL Server Index Fragmentation: For existing Sitecore implementations which have been active for some time, one area to investigate is the fragmentation percentage of SQL Server. As Sitecore puts it:
As indexes age, insertion and deletion of noncontiguous data can take its toll and cause fragmentation to occur. This can happen in just a few days on a busy CMS database. Minor amounts of fragmentation won't generally hurt performance. But as the percentage of fragmentation increases, performance suffers dramatically.
Your SQL Server database administrator should be able to help run the Index Physical Statistics report, which will then show which indexes have a high percentage of fragmentation  (over 10% is bad). The defragment indexes maintenance plan being run will solve these issues but ensure to make backups beforehand.

Further reading

These are some of the key areas in which I focus on and have found to provide the most benefit to optimising the performance of a Sitecore website. Of course this assumes that the custom code used on the website itself is optimised and running well. 

Monday, September 26, 2016

Sitecore Lucene appending pipe character to field

While implementing blog functionality in Sitecore, using Lucene as a datasource, I noticed that when I output the title field onto the page, it was appending a piped character ("|") to the end.


This wasn't a front-end issue where a | was being output in the repeater, so I used Luke to open the physical search index to see what was actually being stored.


As it turns out there were two fields on the blog post template which had the name title. The second title field was empty which made the pipe character appear to be appended. The truth was that Lucene uses the pipe character to concatenate all data for fields of the same name.

Monday, September 19, 2016

The new Sitecore standard - Helix principals

As a consultant working with Sitecore for a number of years I have had the pleasure to be involved with a large number of Sitecore implementations. These ranged from complete end-to-end implementation to reviews of existing sites. This has allowed me to see Sitecore development principals from a large amount of developers across multiple countries. One surprising aspect of this is the true extent that developers have implemented Sitecore, and whilst it's often pretty good, there are plenty of live examples of John West's Sitecore worst practices.

Luckily Sitecore has recently released the Helix principals which provide: "A set of overall design principles and conventions for Sitecore development". Whilst this has also been released in tandem with the habitat Sitecore project example (which I was lucky enough to be on a team which released one if the first production examples), the topic of this blog post will be primarily on Helix.

So why follow Helix principals?

Well I think Sitecore puts it best on the Helix site itself:
Helix provide a set of guidelines for your Sitecore projects. The Habitat example provides you with a pre-built and tested set of common modules that you can use as an inspiration to your project. Both improve the efficiency of your projects, reduce costs and time to market. As more and more people and organisations adopt the Helix conventions and principles, it will become a Sitecore standard. This means that people who are familiar with the conventions or the Habitat example will be able to work more easily on other convention-based projects with minimal training. It will be easier for Sitecore Product Support to understand projects built using the conventions, enabling them to resolve issues more quickly.
Some of the key take-aways for me are around the project efficiency, setting a global Sitecore standard and of course allowing official support to better understand your project.

What does it cover?

Rather than me rewriting the documentation in a blog format, lets cover off the main areas covered in Helix.

Patterns, Principles and Conventions
  • Architecture Principles: Looks at a modular solution architecturally.
  • Visual Studio: How to setup and structure and Visual Studio solution with projects.
  • File and Disk Structure: The physical structure of solutions and modules.
  • Managing Sitecore Items: Covers managing items in development and deploying them.
  • Templates: The setup of templates and how to reference them in code.
  • Page Layout: A look at page layouts, renderings, data sources and of course placeholders.
  • Configuration and Settings: Strategies for configuration and how to work with Sitecore configuration files.
  • Multi-site and multi-tenant: How to create multi site or tenant (logical grouping such as business unit).
  • Language and culture support: Multi-language implementations of Sitecore and use of Dictionaries.
  • Security and workflows: How to handle permissions in Sitecore and workflows for content editors.
  • Working with code: Code formatting for Sitecore.
  • Visual Design and Theming: A look at the front-end stack for your Sitecore site.
DevOps and development lifecycle management
  • Development: Covers general development and version control.
  • Build and integration: Covers build options for Sitecore projects along with integration points.
  • Testing: Covers managing your tests, along with unit testing and other testing activities (acceptance testing for example).
  • Deployment: Deployment strategy for Sitecore and what should be deployed where.

Get started!

For those users with large existing implementations, this can seem like a daunting list of changes to make your Sitecore solution standardised. But we all have to start somewhere, so I recommend taking one piece at a time. Fixing up how you deal with configuration can make upgrades easier so is a good start. As is the testing and deployment to ensure you have control and quality for what is released into each environment.

For the lucky ones who are starting a clean Sitecore solution, you have the option of taking the Habitat solution and using it outright (or as a best practice example). Or as intended you can take the Helix principals and bring them into your development team as general rules/guiding principals.

Wherever you are at with your particular Sitecore solution, lets all get on board with Habitat and ensure that moving forward we are all producing quality Sitecore sites that are efficient, easy to maintain and best practice!

Thursday, September 8, 2016

Setting a Sitecore password policy and protecting from brute force attacks

You might notice that out of the box Sitecore has no real password policy - take the default password for admin, a single character "b". This is not ideal and a security audit will often bring up this fact. On top of a secure password policy is the amount of times that a user can attempt to log in (with an incorrect password) before they are locked out.

By default, the web.config of your Sitecore website will have the following section:
<add name="sql" type="System.Web.Security.SqlMembershipProvider" connectionStringName="core" applicationName="sitecore" minRequiredPasswordLength="1" minRequiredNonalphanumericCharacters="0" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="256" />
The key attributes are:
  1. minRequiredPasswordLength - the minimum length that a password can be. By default this is 1 and is recommended to be changed to 8 or greater.
  2. minRequiredNonalphanumericCharacters - is the minimum required non alphabetic or numeric characters (such as @ or $). By default this is 0, and to enhance password security it should be set to at least 1.
  3. maxInvalidPasswordAttempts - is the number of failed logins a given user account can have before it is locked. By default it is set to 256, which is rather high and could allow for brute force access to an account. This should be changed to a much lower number, such as 10.
Remember as with any password policy it's important to communicate with users and have policies in place for unlocking accounts that breach the maximum invalid login attempts. 

Thursday, September 1, 2016

Sitecore custom button on the experience editor ribbon

Many content editors have a preference for using the experience editor (or page editor) to edit the Sitecore content. Much like the content editor it is possible to add your own buttons to the ribbon at the top, it's just done in a different way. In this example I am working with Sitecore 8.

Creating the button

  1. Open up the Sitecore desktop and load the content editor with the core database selected
  2. Navigate to "/sitecore/content/Applications/WebEdit/Ribbons/WebEdit/Page Editor" - it may have a display name of experience editor
  3. In my case I want the button to appear in the edit section, so under Edit I insert a large button (/sitecore/templates/System/Ribbon/Large Button)
    1. The Header field is required and contains the text that will display for the button
    2. The Icon field is required and will be the actual button
    3. The ID field is required and should be a unique id for the button

Creating the code

Firstly we need to create the code that will be run on the button click, this is a c# class.

namespace MyProject.Commands
{
    public class MyCommand : PipelineProcessorRequest<ItemContext>
    {
        public override PipelineProcessorResponseValue ProcessRequest()
        {
            Item item = base.RequestContext.Item; // The item being edited

            // Do code

            return new PipelineProcessorResponseValue
            {
                Value = "Return Value" // Return an object here on value
            };
        }
    }
}

Then we need to register this inside App_Config/Include/Sitecore.ExperienceEditor.Speak.Requests.config.
<request name="ExperienceEditor.MyCommand" type="MyProject.Commands.MyCommand, MyProject"/>

Now inside Website\sitecore\shell\client\Sitecore\ExperienceEditor\Commands we create a JavaScript file MyCommand.js. This is the code which the button click will call.
define(["sitecore"], function(Sitecore) {
    Sitecore.Commands.MyCommand = {
        canExecute: function(context) {
            // Determines whether command is disabled or enabled.
            return true;
        },
        execute: function(context) {
            Sitecore.ExperienceEditor.PipelinesUtil.generateRequestProcessor("ExperienceEditor.MyCommand", function(response) {
                // response.responseValue.value is the value passed back from the code
            }).execute(context);
        }
    };
});
The above JavaScript method uses canExecute to decide if the button is available for the given context/item and execute to run once the button is clicked.

Connecting the button

  1. Open up Sitecore rocks and locate the button just created (remembering it's in the core database)
  2. Right click the item and select Tasks > Design Layout


  3. Add a rendering and select Large Button


  4. Double click the newly added rendering to edit it's properties
    1. Id is required and should be a unique value (and different from the previous step)
    2. Click is required and should be set to "trigger:button:click"
    3. Command is required and should be the name of the command created earlier (MyCommand)
    4. PageCodeScriptFile is required and should be the location of the JavaScript file created earlier (/sitecore/shell/client/Sitecore/ExperienceEditor/Commands/MyCommand.js)

The button should now appear on the experience editor and run as expected! Remember you can secure the button via the item created in the core database if required.

Wednesday, August 31, 2016

Improving the speed of Sitecore's experience editor

One thing I noticed when speaking with Sitecore content users who have upgraded from earlier versions (such as 6.5) to version 8 and beyond is that the experience editor (or page editor) runs slow in the newer versions.

After opening up developer tools in Google Chrome and running analysis of the network tab, I was able to identify the three components of the experience editor which tool the most time to run.


Of these three items, the following two could be fixed to reduce loading times. In Sitecore implementations with a large amount of items and therefore large master database, these two components can be very slow to load.

  1. Optimization.SuggestedTests.Count: Gives suggestions on A/B multivariate tests which can be run on the page.
  2. ExperienceEditor.MyItems.Count: Shows the number of items currently locked by you, this functionality does not optimally return the count so may take some time to load.

The following Sitecore knowledge base articles provide details on these two items:
  1. How to disable Content Testing functionality
  2. Performance issue with the My items functionality
After applying both fixes, you will notice that Optimization.SuggestedTests.Count will no longer run at all and the performance of ExperienceEditor.MyItems.Count should be improved (this one will be noticed more by content editors who keep items locked).

Tuesday, August 30, 2016

Installing Sitecore performance counters

The performance monitor tool on windows is a great friend when it comes to troubleshooting a slow performing Sitecore website. In the past I have used performance monitor to troubleshoot IIS, however Sitecore expands upon the concept and provides a number of custom counters to monitor.

The counters can be installed via the following executable from Sitecore: Sitecore Counters. This package appears to be for Sitecore 8+. However if you made the mistake of installing the counters for Sitecore 6, the installation likely fails and corrupts performance monitor on your windows instance. Running the command "LODCTR /R" will fix up performance monitor by rebuilding it's settings.

Once the counters have been installed, the various counters can now be added/monitored in the performance monitor tool.


The important configuration settings in Sitecore related to this are available in Sitecore.config:

  • Counters.Enabled: Indicates if performance counters are enabled.
  • Counters.InstanceName: Instance name for performance counters.
  • Counters.ResetOnStartup: Indicates if performance counters will be reset on Sitecore startup.
Once installing and running these counters will enable you to better troubleshoot your Sitecore instances.

Thursday, August 25, 2016

Sitecore remove renderings or sublayouts with missing datasources

As a Sitecore implementation starts to get larger and older, items can start to have renderings or sublayouts which refer to datasources which no longer exist. The main cause for this is usually a content editor deleting the datasource item and leaving the links intact.

The following code can be setup as a scheduled task in Sitecore or added to a custom admin page to be run as needed. It will recursively move down the content tree and find items which have a layout, it then checks the renderings on those items with datasources and ensures the datasource exists. If a rendering with a broken datasource is found, it is removed from the item.
using Sitecore.Layouts;
using Sitecore.Data.Fields;
using Sitecore.Diagnostics;

public partial class DatasourceFix : System.Web.UI.Page
{
 private static Database database = Sitecore.Configuration.Factory.GetDatabase("master");
 private static string StartPath = "/sitecore/content/"; // Start path to enumerate
 private static string DefaultDevice = "{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}"; // Default device ID

 protected void btnRun_Click(object sender, EventArgs e)
 {
  var startItem = database.Items.GetItem(StartPath);
  CheckItem(startItem);
 }

 private Item CheckItem(Item mainItem)
 {
  RenderingRemover(mainItem);

  if (mainItem.HasChildren)
  {
   foreach (var myInnerItem in mainItem.Children.ToList())
   {
    CheckItem(myInnerItem);
   }
  }

  return null;
 }
 
 private void RenderingRemover(Item item)
 {
  if (item.Fields[Sitecore.FieldIDs.LayoutField] != null && !String.IsNullOrEmpty(item.Fields[Sitecore.FieldIDs.FinalLayoutField].Value))
  {
   // If item has a layout
   LayoutField layoutField = new LayoutField(item.Fields[Sitecore.FieldIDs.LayoutField]);
   LayoutDefinition layout = LayoutDefinition.Parse(layoutField.Value);
   DeviceDefinition device = device = layout.GetDevice(DefaultDevice); // Get the default device

   // Get renderings for the device
   var d = layout.GetDevice(device.ID.ToString());
   var ren = d.Renderings;

   // Query the renderings for those with a datasource
   var withDatasource = from RenderingDefinition renDef in ren
      where !string.IsNullOrEmpty(renDef.Datasource)
      select renDef;

   bool itemEdited = false; // item edited flag
   
   // Find missing datasources
   foreach (RenderingDefinition rendering in withDatasource.ToList() ?? Enumerable.Empty())
   {
    var datasource = database.GetItem(new Sitecore.Data.ID(rendering.Datasource)); // Get the datasource

    if (datasource == null)
    {
     // if datasource is broken, flag an item edit and remove the rendering
     itemEdited = true;
     d.Renderings.Remove(rendering);
     Log.Info(string.Format("Removing rendering {0} from item {1} due to datasource not being found.", rendering.ItemID.ToString(), item.Paths.Path), "");
    }
   }

   // edit the item if rendering(s) were removed
   if(itemEdited)
   {
    item.Editing.BeginEdit();
    layoutField.Value = layout.ToXml();
    item.Editing.EndEdit();
   }
  }
 }
}
It can be quite an expensive operation as it will go through an entire content tree, so be careful when/how it is run.

Sitecore removing a rendering or sublayout programatically

Sometimes a requirement comes up to remove renderings or sublayouts from Sitecore items programatically. This could be used in the case of a pipeline to detect and remove renderings/sublayouts with broken data sources or to bulk remove/change renderings/sublayouts which have been removed/replaced.

The code below will get a Sitecore item, then all renderings on that item which fall under the default device. It then queries to find a specific rendering, and edits the item with that rendering removed.
var database = Sitecore.Data.Database.GetDatabase("master");
var item = database.Items.GetItem("/sitecore/content/Home/Test/Test-Page/");

// If item has a layout
if (item.Fields[Sitecore.FieldIDs.LayoutField] != null && !String.IsNullOrEmpty(item.Fields[Sitecore.FieldIDs.LayoutField].Value))
{
 LayoutField layoutField = new LayoutField(item.Fields[Sitecore.FieldIDs.FinalLayoutField]);
 LayoutDefinition layout = LayoutDefinition.Parse(layoutField.Value);
 DeviceDefinition device = device = layout.GetDevice("{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}"); // Get the default device

 // Get renderings for the device
 var d = layout.GetDevice(device.ID.ToString());
 var ren = d.Renderings;

 // Query the renderings for a specific one - use any logic here
 var rendering = from RenderingDefinition renDef in ren
  where renDef.ItemID == "{CD8C466E-22D3-4791-9871-C230AA57F71B}"
  select renDef;

 if (rendering.FirstOrDefault() != null)
 {
  item.Editing.BeginEdit();
  d.Renderings.Remove(rendering);
  layoutField.Value = layout.ToXml();
  item.Editing.EndEdit();
 }
}
The logic could be expanded to fit the needs of your given requirements.

Wednesday, August 24, 2016

Sitecore tip on the web database prefetch cache

Sitecore has a number of different caching areas which all work together to (hopefully) provide a seamless CMS experience with reasonable load times for content editors and end users alike. Sitecore explains the prefetch caches rather well as:
Prefetch caches are populated at application initialization, based on the information provided in the prefecth configuration files. This results in a smoother user experience after an application restart.However, excessive use of the prefetch cache can increase the time required for the application to restart, giving a negative user experience.
To understand how the prefetch caches are utilized once the application is up and running, a view into how Sitecore caching works is required:
When a database item cache does not contain an entry for an item, Sitecore retrieves the corresponding entry from the database data cache, converts it to a different type, and stores that converted data as an entry in the database item cache. When the database data cache does not contain an entry for an item, Sitecore retrieves the corresponding entry from the database prefetch cache, converts it to a different type, and stores that converted data as an entry in the database data cache. When an entry does not exist for an item in a database prefetch cache, Sitecore retrieves that item from the data provider for that database, converts it to a different type, and stores that converted data as an entry in the database prefetch cache.
What this means is that the prefetch caches are not only populated at initialization, but during the life of the application. This understanding is required when thinking about setting the size of the prefetch caches.
In the Website\App_Config\Prefetch folder of your Sitecore website there are three files which define the prefetch cache for the web, core and master databases. Here the maximum size for the caches is set as well as some other options:
  • <template> — This element tells Sitecore to cache all items based on a given template at application initialization.
  • <children> — This element tells Sitecore to cache all of the children of the specified item. 
By default, the web database prefetch will be configured to cache the default home item and all of it's children. However in many instances that home item is deleted or left unused. Therefore the tip is to investigate which items or templates you want cached for the web database of your Sitecore instance. In many cases, having the main home item and it's children will lead to performance benefits.

Remember that it also leads to a slower application startup which is not as much as a worry for production as development.

Friday, August 19, 2016

Bundling CSS and JavaScript with Sitecore

In terms of optimizing web pages for a better user experience and less load on the web server, bundling of css and javascript files can provide one of the quickest wins. The benefits of bundling the css and javascript are:
  1. Less HTTP requests on the web server - for example a single javascript file request instead of 10+, which is a real help when a server is under load.
  2. Less file size as the bundle is minified.
  3. "Bundling and minification in ASP.NET 4.5 is performed at runtime, so that the process can identify the user agent (for example IE, Mozilla, etc.), and thus, improve the compression by targeting the user browser (for instance, removing stuff that is Mozilla specific when the request comes from IE)."

Setting up bundling in your Sitecore solution (web forms)

  1. Install the Microsoft.AspNet.Web.Optimization nuget package. In this example I installed version 1.1.3
  2. Because the the nuget package above has some dependencies that Sitecore uses, set the WebGrease and Newtonsoft.json references to Copy Local false. As we don't want to overwrite the newer versions installed by Sitecore.
  3. The following dependent assembly changes need to be inside the web.config file
  4. <dependentAssembly>
      <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" />
      <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
      <bindingRedirect oldVersion="0.0.0.0-1.6.5135.21930" newVersion="1.6.5135.21930" />
    </dependentAssembly>
    
    The Newtonsoft one will likely already exist, but the WebGrease one won't. Check the DLL versions installed by your version of Sitecore. I am on 8.1 so my Sitecore versions were newer than those used by System.Web.Optimization
  5. In my case, I will be bundling one bundle of CSS and one bundle of JS (referenced at the body closing tag to stop JavaScript page rendering blocking). My config/definitions of the bundle is as follows.
  6. public class BundleConfig
    {
     public void Process(PipelineArgs args)
     {
      BundleConfig.RegisterBundles(BundleTable.Bundles);
     }
    
     public static void RegisterBundles(BundleCollection bundles)
     {
      bundles.Add(new ScriptBundle("~/bundles/Global").Include(
       "~/assets/plugins/bootstrap/js/bootstrap.min.js",
       "~/assets/plugins/bootstrap-hover-dropdown.min.js",
       "~/assets/plugins/back-to-top.js",
       "~/assets/plugins/jquery-placeholder/jquery.placeholder.js",
       "~/assets/js/main.js"));
    
      bundles.Add(new StyleBundle("~/Content/Global").Include(
       "~/assets/plugins/bootstrap/css/bootstrap.min.css",
       "~/assets/plugins/font-awesome/css/font-awesome.css",
       "~/assets/css/styles.css"));
     }
    }
    
    Normally with bundles you define them inside the global.asax file, however as I don't want to edit the out-of-box Sitecore file, I am hooking into the initialize pipeline (which runs on the start off the application).
  7. The patch file to register the pipeline is:
  8. <?xml version="1.0" encoding="utf-8" ?>
    <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
      <sitecore>
        <pipelines>
          <initialize>
            <processor type="MyProject.Helpers.BundleConfig, MyProject" />
          </initialize>
        </pipelines>
      </sitecore>
    </configuration>
    
  9. Now on the layout or master page you reference the assembly:
  10. <%@ import namespace="System.Web.Optimization" %>
    
  11. Now a runat server needs to be added to the head element
  12. <head runat="server">
    
  13. The CSS is registered in the head like so:
  14. <asp:PlaceHolder runat="server">
        <%: Styles.Render("~/Content/Global") %>
    </asp:PlaceHolder>
    
  15. The JS is registered in the page like so:
  16. <%: Scripts.Render("~/bundles/Global") %>
    
After a deploy your css and js will now be nicely bundled and minified and you should get a good scropt from YSlow!


Monday, August 1, 2016

Sitecore inconsistencies with the reminder feature

In the Sitecore content editor, there is a handy feature which allows content authors to set a reminder on a content item. This feature will then email the user with the reminder message they set at the specified date/time.

The first problem which I encountered was that the same reminder was being sent multiple times (from the master database and the web database). If an item had an older reminder set and you then published to the web database, the reminder would be re-added to the queue and sent again. The fix for these problems has been addressed by Christian Kay Linkhusen and involves overriding the task database to stop reminders from being sent off the web database.

The second problem I encountered was that some reminders were begin sent as expected where others would not. After looking at the tasks table in the core database, I noticed that when I set a reminder on the Content Management server, it would then be added into the table without an instance name. The task database agent is a timed job which is run on both content management and content delivery servers. When this is run (GetPendingTasks - to get pending tasks to process), the first method it calls is AssignTasks, which looks for any tasks without an instance name and assigns them to the current instance. This meant that one of the content delivery servers could then take ownership of a newly created reminder, and the content management server would never see it. Because this item is in the master database, the content delivery server could never access it so the reminder would never send.

The fix for this problem was to take a copy of the GetPendingTasks() method and remove the AssignTasks() method from running. The code to get the pending tasks actually gets all tasks due to run assigned to the current instance, or where the instance is null - so the tasks without the instance names would still send. The fix above actually overrides the method which calls GetPendingTasks() so these two fixes work well together.

using Sitecore;
using Sitecore.Configuration;
using Sitecore.Data;
using Sitecore.Data.Sql;
using Sitecore.Data.SqlServer;
using Sitecore.Diagnostics;
using Sitecore.Diagnostics.PerformanceCounters;
using Sitecore.Reflection;
using Sitecore.Tasks;
using System;
using System.Collections;
using System.Data.SqlTypes;
using Sitecore.Common;

namespace MyProject.Sitecore.Override
{
    public class TaskDatabaseAgent : Sitecore.Tasks.TaskDatabaseAgent
    {
        public new void Run()
        {
            Task[] pendingTasks = GetPendingTasks(); // Call our copy of the method here
            this.LogInfo(string.Concat("Processing tasks (count: ", (int)pendingTasks.Length, ")"));
            Task[] taskArray = pendingTasks;
            for (int i = 0; i < (int)taskArray.Length; i++)
            {
                Task logActivity = taskArray[i];
                try
                {
                    logActivity.LogActivity = this.LogActivity;

                    //Test if the Task is the EmailReminderTask and the current database is not master database
                    if (logActivity.GetType() == typeof(EmailReminderTask) && !logActivity.DatabaseName.ToLower().Equals("master"))
                    {
                        //If its not the master database
                        //Only mark the Task as done, don't execute the reminder
                        Globals.TaskDatabase.MarkDone(logActivity);
                    }
                    else
                    {
                        logActivity.Execute();
                    }

                    JobsCount.TasksTasksExecuted.Increment((long)1);
                }
                catch (Exception exception)
                {
                    Log.Error("Exception in task", exception, logActivity.GetType());
                }
            }
        }
        public Task[] GetPendingTasks()
        {
            //this.AssignTasks(); // Removed as CD servers were assigning master tasks to themselves
            ArrayList arrayLists = new ArrayList();
            int num = 8;
            string str = "SELECT [ID], [NextRun], [taskType], [Parameters], [Recurrence], [ItemID], [Database], [InstanceName] \r\n        FROM [Tasks] \r\n        WHERE [NextRun] <= @now AND [Pending] = 1 AND [Disabled] = 0 AND ([InstanceName] IS NULL OR [InstanceName] = @instanceName) \r\n        ORDER BY [NextRun]";
            object[] utcNow = new object[] { "now", DateTime.UtcNow, "instanceName", Settings.InstanceName };
            object[] array = SqlUtil.GetArray(str, utcNow, Settings.GetConnectionString("core"));
            for (int i = 0; i < (int)array.Length - (num - 1); i = i + num)
            {
                ID d = ID.Parse(array[i]);
                DateTime dateTime = ((DateTime)array[i + 1]).SpecifyKind(DateTimeKind.Utc);
                string str1 = array[i + 2] as string;
                string str2 = array[i + 3] as string;
                string str3 = array[i + 4] as string;
                ID d1 = ID.Parse(array[i + 5]);
                string str4 = array[i + 6] as string;
                string str5 = array[i + 7] as string;
                Task task = this.CreateTask(str1, dateTime);
                if (task != null)
                {
                    task.ID = d;
                    task.Parameters = str2;
                    task.RecurrencePattern = str3;
                    task.ItemID = d1;
                    task.DatabaseName = str4;
                    task.InstanceName = str5;
                    arrayLists.Add(task);
                }
            }
            return arrayLists.ToArray(typeof(Task)) as Task[];
        }

        protected Task CreateTask(string taskType, DateTime taskDate)
        {
            if (taskDate == SqlDateTime.MinValue.Value)
            {
                taskDate = DateTime.MinValue;
            }
            taskDate = DateUtil.ToUniversalTime(taskDate);
            object[] objArray = new object[] { taskDate };
            return ReflectionUtil.CreateObject(taskType, objArray) as Task;
        }

        /// <summary>
        /// Logs the info.
        /// </summary>
        /// <param name="message">The message.</param>
        private void LogInfo(string message)
        {
            if (this.LogActivity)
            {
                Log.Info(message, this);
            }
        }
    }
}
You'll also need to replace the task database agent to use the one above:
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/" xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <sitecore>
    <scheduling>
      <agent type="Sitecore.Tasks.TaskDatabaseAgent" method="Run" interval="00:10:00">
        <patch:attribute name="type" xdt:Transform="Replace" xdt:Locator="Match(type)">Myproject.Override.TaskDatabaseAgent</patch:attribute>
      </agent>
    </scheduling>
  </sitecore>
</configuration>
As with any overrides or replacement of Sitecore code, you will need to check if the code is updated upon installations of CMS updates and update your code accordingly. This code was taken from version8 update 5.

Monday, July 18, 2016

Sitecore commerce required license missing error

After upgrading to Sitecore 8.1 the following error message may start appearing in the content editor:
Required license is missing.
You require one of the following licenses to use the Sitecore Commerce Connect module. 'Sitecore.Commerce.Connect' or 'Sitecore.Commerce.ConnectPartner'

The solution to suppressing this error message involves removing the Sitecore.Commerce.Pipelines.ContentEditorLicenseWarning processcor pipeline. The following patch file will delete the element, however it should be run last so include it in a z folder in App_Config.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <getContentEditorWarnings>
        <processor type="Sitecore.Commerce.Pipelines.ContentEditorLicenseWarning, Sitecore.Commerce">
          <patch:delete />
        </processor>
      </getContentEditorWarnings>
    </pipelines>
  </sitecore>
</configuration>

Friday, July 8, 2016

Sitecore scheduled tasks not running as expected

On a Sitecore implementation some key scheduled tasks (set in the content editor rather than agents), would run around the expected time in most cases. However every so often there would be a major delay in their running (and as emails were sent it was noticeable what time it was kicked off).

After investigating the logs, I noticed that in the periods where the custom scheduled task was not being kicked off, a system task was running. The system task Rebuild Suggested Tests Index which relates to content testing (crawls the database and gives candidates for content testing - a/b testing). It was set to run once daily and due to content volume would take anywhere for 5 to 7 hours to run, of course not begin asynchronous it would hold up all other tasks.

The solution here was to set it to run weekly or to set it to be asynchronous.

Wednesday, July 6, 2016

Sitecore errors on a scaled environment

On a scaled Sitecore 8.1 environment with two content delivery servers and one content management server, I followed the Config_Enable-Disable_Sitecore_8.1_upd3.xlsx spreadsheet to disable/enable the required configuration files. The following errors started appearing in the content delivery logs.
Could not find configuration node: contentSearch/indexConfigurations/defaultFxmLuceneIndexConfiguration
This error required that Sitecore.FXM.Lucene.DomainsSearch.DefaultIndexConfiguration.config (found in App_Config\Include\FXM) be enabled, even though it was marked to be disabled on CD servers.
Invalid cast from 'System.String' to 'Sitecore.Analytics.Aggregation.Data.DataAccess.ICollectionDataProvider2'.
This error may cause images not to load along with other features such as search. The fix for this is to keep Sitecore.Analytics.Reporting.config (found in App_Config\Include) enabled, even though it was marked to be disabled on CD servers.


These issues were found using the Sitecore Habitat solution.

Tuesday, July 5, 2016

Sitecore session errors with MongoDB

An interesting issue was reported on a Sitecore environment where users were able to browse to several pages, but eventually the website would stop responding to requests (and it appeared as if the server was too busy to respond). However if cookies were cleared on the user's machine, they could browse for a bit before not getting requests back again.


In this instance the shared/private sessions were being stored in MongoDB. The following error was appearing in the logs:
ERROR Application error.
Exception: System.Web.HttpException
Message: Exception of type 'System.Web.HttpException' was thrown.
Source: System.Web
   at System.Web.HttpAsyncResult.End()
   at System.Web.HttpApplication.AsyncEventExecutionStep.OnAsyncEventCompletion(IAsyncResult ar)
Nested Exception
Exception: MongoDB.Driver.MongoDuplicateKeyException
Message: WriteConcern detected an error ''. (Response was { "ok" : 1, "code" : 11000, "err" : "E11000 duplicate key error collection: session.sessions index: _id_ dup key: { : { s: \"j0lvazyxndl5ihfldr0zzce2\", a: \"private\" } }", "n" : NumberLong(0), "updatedExisting" : false }).
Source: MongoDB.Driver
In our instance we traced the error back to a corrupted MongoDB instance (which is a post for another day), and we moved sessions over to SQL Server to resolve the issue. However if you are seeing this error these are some configurations to check.
  1. Inside the web.config the <sessionState> element is where private sessions are defined.
  2. Inside Sitecore.Analytics.Tracking.config file is where shared sessions are defined
  3. Inside ConnectionString.config is where the connection string for any session databases (MongoDB or SQL Server) should be.
If any of these session configurations are using InProc in an environment with multiple servers (content delivery scaled environments for example) you will have a problem if the load balancer is not using sticky sessions (ensuring the visitor always visits the same server). In these sorts of environments its recommended to use a shared session source (MongoDB or SQL Server).

Sitecore search inconsistencies in the result URLs

I noticed on a Lucene search implementation in Sitecore that there were some inconsistencies around the URLs being generated. In some cases spaces where replaced by dashes and in other cases they were not. This was a tricky issue because some of the content names (and therefore paths) included dashes.

of course in the Sitecore.config file there was an encode name replacement to replace all dashes in URLs with spaces.
<replace mode="on" find=" " replaceWith="-"/>
However the results weren't showing until the EncodeNames element was set in the UrlOptions when generating the link with the link manager.
itemUrl = Sitecore.Links.LinkManager.GetItemUrl(item, new UrlOptions { AlwaysIncludeServerUrl = true, LanguageEmbedding = LanguageEmbedding.Never, EncodeNames = true});

Sitecore search ashx is appearing in search result URLs

I noticed that in some cases a Lucene search that I had built in Sitecore was generating URLs that contained a .ashx extension for PDF files in some cases and .pdf in others.

There is a setting in the Sitecore.config file which controls which extension will be shown for media library URLs. The setting is Media.RequestExtension and by default will be set to ashx.
<setting name="Media.RequestExtension" value="ashx"/>
Using a patch file (or editing directly), simply set this to an empty value to ensure media library content is served up using the correct extension.

However in my case, this did not resolve the issue... Some items were still coming through with .ashx and there was no content differences that could cause this issue. Ultimately setting IncludeExtension in the MediaUrlOptions did solve it:
itemUrl = MediaManager.GetMediaUrl(item, new MediaUrlOptions { AlwaysIncludeServerUrl = true, IncludeExtension = true});

Friday, July 1, 2016

User/anonymous generated content in Sitecore

User generated content from site visitors might be as simple as a comment on a news article, or more complex such as the submission of structured content (a community venue listing for example). The only question then becomes how to handle the user/anonymous generated content in Sitecore.

Wednesday, June 29, 2016

Sitecore handling content URLs that change between environments

In some Sitecore implementations there can be various integration points to third party (or other internal web sites) which will have URLs (in content) which change based on environment (development, test or production).

An example of this would be the main content web site (in Sitecore) which has a number of links to an online application web site. In production the URL might be app.mydomain.com, and in test app-test.mydomain.com. The problem here is that as content is synced from production to test, the production links will be present in the test environment. This is not ideal as it can lead to test data in production which testers/content users may not notice - it also doesn't allow for fair end to end testing of the integrated systems.

Sitecore caching data when there is no context

When working with areas in Sitecore where the context is not resolving (such as custom index field or pipelines) the standard HttpContext.Current.Cache would not resolve because HttpContext.Current is null. In this case HttpRuntime.Cache will work instead.

I tend to use caching in custom search index fields where configuration is coming from a Sitecore item. This saves a lot calls to Sitecore and processing of the item to get the required data.

Wednesday, June 22, 2016

Glass Mapper error with web forms project

I added the Glass Mapper package to a Sitecore project which was web forms based and the following error was appearing when attempting to load the Sitecore admin area.
Could not resolve type name: Glass.Mapper.Sc.Pipelines.Response.GetModelFromView, Glass.Mapper.Sc.Mvc (method:
Sitecore.Configuration.Factory.CreateFromTypeName(XmlNode configNode, String[] parameters, Boolean assert)).
Looking into the details, Glass Mapper added a config file to my solution: Glass.Mapper.Sc.config. This file then referenced the MVC specific DLL: Glass.Mapper.Sc.Mvc in the pipelines. The fix was to remove the following section from the config file:

<mvc.getModel>
  <processor patch:before="*[@type='Sitecore.Mvc.Pipelines.Response.GetModel.GetFromItem, Sitecore.Mvc']"  type="Glass.Mapper.Sc.Pipelines.Response.GetModel, Glass.Mapper.Sc.Mvc"/>
  <processor patch:before="*[@type='Sitecore.Mvc.Pipelines.Response.GetModel.GetFromItem, Sitecore.Mvc']"  type="Glass.Mapper.Sc.Pipelines.Response.GetModelFromView, Glass.Mapper.Sc.Mvc"/>
</mvc.getModel>

It is a known issue.

Monday, June 20, 2016

Sitecore PXM InDesign is unable to load a project

When inside InDesign and attempting to load a Sitecore Print Experience Manager (PXM) project - via the Load the current item button, the project was not loading for me.


After looking into the printstudio.log older I noticed the following error:
ERROR Access to the path 'C:\inetpub\Jetstream\Website\Projects\sitecore\admin_a1wfr3ls\en\a9880262-14c3-4569-91d0-03aeaed946ed\' is denied.
ERROR BuildPublishXml Object reference not set to an instance of an object.
This was traced back to a setting inside Sitecore.PrintStudio.config - available in App_Config/Include/PXM. The PrintStudio.ProjectsFolder setting needs to be set to the PublishingCache folder rather than the default of projects. The correct setting should be something like: C:\inetpub\wwwroot\Jetstream\PXMPublishing\PublishingCache - which is the folder structure you created manually in the PXM installation.

This error may also trace back to permission/sharing of this folder depending on server/network architecture.

Sunday, June 19, 2016

Sitecore PXM DashBoardServer error - Message Queuing has not been installed

After following the Print Experience Manager installation guide the following error may appear in the DashBoardServerLog file.
ERROR HasJob:
Message Queuing has not been installed on this computer.
at System.Messaging.Interop.SafeNativeMethods.MQPathNameToFormatName(String pathName, StringBuilder formatName, Int32& count)
at System.Messaging.MessageQueue.ResolveFormatNameFromQueuePath(String queuePath, Boolean throwException)
at System.Messaging.MessageQueue.get_FormatName()
at System.Messaging.MessageQueue.GetMessageEnumerator2()
at System.Messaging.MessageQueue.GetAllMessages()
at Sitecore.PrintStudio.DashBoardServer.DashBoardWebService.HasJob(String serviceType)
This error occurs because Message Queuing (or MSMQ) has not been enabled on the server. Simpy go to the Control Panel and select turn Window features on or off. Then enable:

  • Microsoft Message Queue (MSMQ) Server
  • Microsoft Message Queue (MSMQ) Server Core
On windows server this will need to be done through the server manager, enable features.


Sitecore PXM InDesignProcessingService error with DashBoardWebService

When attempting to use PXM (Print Experience Manager) in Sitecore, the following error may appear in the InDesignProcessingService log file.
ERROR JobHandling
Could not connect to http://localhost:8070/DashBoardWebService. TCP error code 10061: No connection could be made because the target machine actively refused it 127.0.0.1:8070.
This is because the Sitecore Print Studio InDesign Processing service is not running (or accessible via network).

Sitecore PXM InDesign Server Service is not loading

In the installation guide for PXM, after starting the InDesign Server Service and assigning it the port of 8081, it should then load up in the browser using a URL such as http://localhost:8081/service?wsdl. However in my case, this was not occurring and the browser was unable to connect.

It turns out this was due to a licensing issue. To check this, the service can be loaded via command line:
  1. Change the command line directory to that of the Adobe InDesign Server path: C:\Program Files\Adobe\Adobe InDesign CC Server 2015 for example.
  2. Run the command: InDesignServer -port 8081
  3. If "Adobe InDesign server is not properly licensed and will now quit" displays, the license has not been installed correctly.
In my case this was due to a trial version, I simply downloaded Adobe_Provisioning_Toolkit_6_2_ALL and installed it to the desktop. Changed directgory to the install folder in the command line and ran the command: adobe_prtk --tool=StartTrial --leid=V7{}InDesignServer-11-Win-GM.

The service now loads correctly in the browser;


Thursday, June 9, 2016

Sitecore LinkManager.GetItemUrl duplicating the host name

I noticed an interesting feature of the LinkManager.GetItemUrl method (used in a custom module) in Sitecore where it was rerunning URLs with the host name twice. For example http://mysite.comhttp://mysite.com/link/to/page.

This was occurring because the module had UrlOptions with AlwaysIncludeServerUrl set to false and would assume that the link returned from LinkManager.GetItemUrl would never return the server URL.

However when the site definition (in Sitecore.config) has the targetHostName attribute set, the LinkManger will always return the server URL (from that setting). Which meant I had to update the module to handle this exception.

Thursday, June 2, 2016

Dynamic placeholders in sitecore

One problem with reusing Sitecore components that contain placeholders (such as a two column row) is that if you place more than one of the component on the page, the child elements will not render correctly - because there are two versions of the same placeholder on the page.

The solution to this issue is to use dynamic placeholder keys so that each version of a given component will have unique placeholders. This will then allow multiple components to work harmoniously together on the same page.

The solution below is for projects which are using web forms, there are plenty of solutions available online for those who are using MVC.

Defining the dynamic placeholder

The code below is a custom implementation of a Sitecore placeholder which will take the placeholder key and append the unique id of the rendering to ensure each key is unqiue.

using System.Collections.Generic;
using System.Linq;
using System.Web.UI;
using Sitecore.Common;
using Sitecore.Layouts;
using Sitecore.Web.UI;
using Sitecore.Web.UI.WebControls;

namespace MyProject.Helper
{
    public class DynamicKeyPlaceholder : WebControl, IExpandable
    {
        protected string _key = Placeholder.DefaultPlaceholderKey;
        protected string _dynamicKey = null;
        protected Placeholder _placeholder;

        public string Key
        {
            get
            {
                return _key;
            }
            set
            {
                _key = value.ToLower();
            }
        }

        protected string DynamicKey
        {
            get
            {
                if (_dynamicKey != null)
                {
                    return _dynamicKey;
                }
                _dynamicKey = _key;
                //find the last placeholder processed, will help us find our parent
                Stack<Placeholder> stack = Switcher<Placeholder, PlaceholderSwitcher>.GetStack(false);
                Placeholder current = stack.Peek();
                //find the rendering reference we are contained in
                var renderings = Sitecore.Context.Page.Renderings.Where(rendering => (rendering.Placeholder == current.ContextKey || rendering.Placeholder == current.Key) && rendering.AddedToPage);
                if (renderings.Count() > 0)
                {
                    //last one added to page defines our parent
                    var rendering = renderings.Last();
                    //use rendering reference unique ID to uniquely and permanently identify the placeholder
                    _dynamicKey = _key + rendering.UniqueId;
                }
                return _dynamicKey;
            }
        }

        protected override void CreateChildControls()
        {
            Sitecore.Diagnostics.Tracer.Debug("DynamicKeyPlaceholder: Adding dynamic placeholder with Key " + DynamicKey);
            _placeholder = new Placeholder();
            _placeholder.Key = this.DynamicKey;
            this.Controls.Add(_placeholder);
            _placeholder.Expand();
        }

        protected override void DoRender(HtmlTextWriter output)
        {
            base.RenderChildren(output);
        }

        #region IExpandable Members

        public void Expand()
        {
            this.EnsureChildControls();
        }

        #endregion
    }
}

Using a dynamic placeholder

Firstly to use the dynamic placeholder, a custom tag needs needs to be defined in the web.config under the controls node (<configuration><system.web><pages><controls>).
lt;add tagPrefix="dkp" namespace="WCC.Internet.UI.Helper" assembly="WCC.Internet.UI" />
Now a dynamic placeholder can be referenced on a sublayout/rendering:
<dkp:DynamicKeyPlaceholder ID="plSingle" runat="server" Key="singlecolumn" />
Don't forget to create placeholder settings for the placeholder key (singlecolumn in this case). Once rendered on the page it will appear unique with an appended GUID.

Allowing for placeholder settings

Now to allow placeholder settings to work with these dynamic placeholders.

public class GetDynamicKeyAllowedRenderings : GetAllowedRenderings
{
 //text that ends in a GUID
 private const string DYNAMIC_KEY_REGEX = @"(.+){[\d\w]{8}\-([\d\w]{4}\-){3}[\d\w]{12}}";

 public new void Process(GetPlaceholderRenderingsArgs args)
 {
  Assert.IsNotNull(args, "args");

  string placeholderKey = args.PlaceholderKey;
  Regex regex = new Regex(DYNAMIC_KEY_REGEX);
  Match match = regex.Match(placeholderKey);
  if (match.Success && match.Groups.Count > 0)
  {
   placeholderKey = match.Groups[1].Value;
  }
  else
  {
   return;
  }
  // Same as Sitecore.Pipelines.GetPlaceholderRenderings.GetAllowedRenderings but with fake placeholderKey
  Item placeholderItem = null;
  if (ID.IsNullOrEmpty(args.DeviceId))
  {
   placeholderItem = Client.Page.GetPlaceholderItem(placeholderKey, args.ContentDatabase,
                args.LayoutDefinition);
  }
  else
  {
   using (new DeviceSwitcher(args.DeviceId, args.ContentDatabase))
   {
    placeholderItem = Client.Page.GetPlaceholderItem(placeholderKey, args.ContentDatabase,
                 args.LayoutDefinition);
   }
  }
  List<Item> collection = null;
  if (placeholderItem != null)
  {
   bool flag;
   args.HasPlaceholderSettings = true;
   collection = this.GetRenderings(placeholderItem, out flag);
   if (flag)
   {
    args.CustomData["allowedControlsSpecified"] = true;
    args.Options.ShowTree = false;
   }
  }
  if (collection != null)
  {
   if (args.PlaceholderRenderings == null)
   {
    args.PlaceholderRenderings = new List<Item>();
   }
   args.PlaceholderRenderings.AddRange(collection);
  }
 }
}
And to have the chrome appear correctly in the page editor.
public class GetDynamicPlaceholderChromeData : GetChromeDataProcessor
{
 //text that ends in a GUID
 private const string DYNAMIC_KEY_REGEX = @"(.+){[\d\w]{8}\-([\d\w]{4}\-){3}[\d\w]{12}}";

 public override void Process(GetChromeDataArgs args)
 {
  Assert.ArgumentNotNull(args, "args");
  Assert.IsNotNull(args.ChromeData, "Chrome Data");
  if ("placeholder".Equals(args.ChromeType, StringComparison.OrdinalIgnoreCase))
  {
   string argument = args.CustomData["placeHolderKey"] as string;

   string placeholderKey = argument;
   Regex regex = new Regex(DYNAMIC_KEY_REGEX);
   Match match = regex.Match(placeholderKey);
   if (match.Success && match.Groups.Count > 0)
   {
    // Is a Dynamic Placeholder
    placeholderKey = match.Groups[1].Value;
   }
   else
   {
    return;
   }

   // Handles replacing the displayname of the placeholder area to the master reference
   Item item = null;
   if (args.Item != null)
   {
    string layout = ChromeContext.GetLayout(args.Item);
    item = Client.Page.GetPlaceholderItem(placeholderKey, args.Item.Database, layout);
    if (item != null)
    {
     args.ChromeData.DisplayName = item.DisplayName;
    }
    if ((item != null) && !string.IsNullOrEmpty(item.Appearance.ShortDescription))
    {
     args.ChromeData.ExpandedDisplayName = item.Appearance.ShortDescription;
    }
   }
  }
 }
}
Along with a patch file to register these two custom pipelines.
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <getPlaceholderRenderings>
        <processor
          type="MyProject.DynamicPlaceholders.GetDynamicKeyAllowedRenderings, MyProject"
          patch:before="processor[@type='Sitecore.Pipelines.GetPlaceholderRenderings.GetAllowedRenderings, Sitecore.Kernel']"/>
      </getPlaceholderRenderings>

      <getChromeData>
        <processor
          type="MyProject.DynamicPlaceholders.GetDynamicPlaceholderChromeData, MyProject"
          patch:after="processor[@type='Sitecore.Pipelines.GetChromeData.GetPlaceholderChromeData, Sitecore.Kernel']"/>
      </getChromeData>
    </pipelines>
  </sitecore>
</configuration>

You should now have dynamic placeholders working correctly. The main dynamic placeholder was taken from Nick Wesselman at Techphoria414. The placeholder settings/chrome was taken from user dunston on Stack Overflow.

Dynamic placeholders not showing in the Sitecore page editor

When working with an implementation of dynamic placeholders in Sitecore 8, I was not able to see my dynamic placeholders in the experience editor (aka page editor), yet I could use them by manually adding controls via the presentation details.

This was actually because of a setting in App_Config\Include\Sitecore.ExperienceEditor.config
WebEdit.PlaceholdersEditableWithoutSettings
Because the implementation of dynamic placeholders I was using at this time did not support placeholder settings, the config setting above (false by default) was stopping these placeholders from showing in page editor mode.

The fixes are to set this config setting to true (not recommended) or to find dynamic placeholders which support placeholder settings.

Tuesday, May 31, 2016

Sitecore error when rebuilding search indexes

In a scaled Sitecore environment, I was getting a combination of the following errors in conjunction with search (Lucene based) not working as expected:
Exception: System.InvalidOperationException

Message: Could not find configuration node: contentSearch/indexConfigurations/indexUpdateStrategies/syncMaster

ERROR One or more exceptions occurred while processing the subscribers to the 'indexing:end:remote' event.
This was because one of the Sitecore indexes which relies on the master was not being removed by the switch master to web configuration patch file. The following index items need to be deleted on CD servers:
<contentSearch>
  <configuration>
 <indexes>
   <index id="sitecore_master_index">
  <patch:delete />
   </index>
   <index id="sitecore_marketing_asset_index_master">
  <patch:delete/>
   </index>
   <index id="sitecore_testing_index">
  <patch:delete/>
   </index>
   <index id="sitecore_suggested_test_index">
  <patch:delete/>
   </index>
   <index id="sitecore_fxm_master_index">
  <patch:delete />
   </index>
   <index id="social_messages_master">
  <patch:delete/>
   </index>
   <index id="sitecore_list_index">
  <patch:delete/>
   </index>
 </indexes>
  </configuration>
  <indexConfigurations>
 <indexUpdateStrategies>
   <syncMaster>
  <patch:delete />
   </syncMaster>
   <intervalAsyncMaster>
  <patch:delete />
   </intervalAsyncMaster>
 </indexUpdateStrategies>
  </indexConfigurations>
</contentSearch>
In particular, my config file was not patching sitecore_list_index out. After this was removed, all was good again.

301 redirects in a Sitecore item resolver

When migrating to Sitecore from an existing web site, one of the biggest concerns is a change in URLs - especially when it comes to search engine optimization. The common solution to this is a mix of a URL redirect module and sitemaps. However when there are a large chunk of content pages where the URL structure is changing in a common way - news articles, or blog posts for example. A Sitecore item resolver can be a good way to handle this, and by using 301 (moved permanently) is an added bonus to SEO.
using Sitecore.Configuration;
using Sitecore.Diagnostics;
using Sitecore.Links;
using Sitecore.Pipelines.HttpRequest;
using System;
using System.Web;

namespace MyProject.ItemResolvers
{
    public class MyItemResolver : HttpRequestProcessor
    {
        public override void Process(HttpRequestArgs args)
        {
            // Return if Sitecore has found the item
            if (Context.Item != null || Context.Database == null || args.Url.ItemPath.Length == 0) return;

            try
   {
    var itemPath = ""; // Business logic here to find the item based on the Context passed through
    var context = Factory.GetDatabase("web");
    var item = context.GetItem(itemPath);

    if (item != null)
    {
     var urlOptions = new UrlOptions
     {
      AlwaysIncludeServerUrl = true
     };

     var itemUrl = Sitecore.Links.LinkManager.GetItemUrl(item, urlOptions);

     HttpResponse Response = HttpContext.Current.Response;
     Response.StatusCode = 301;
     Response.StatusDescription = "Moved Permanently";
     Response.RedirectLocation = itemUrl;
     Response.Flush();
    }
   }
   catch (Exception e)
   {
    Log.Error(e.ToString(), "");
   }
        }
    }
}

Then we simply place this resolver after the default Sitecore one, which means that it will only be called if Sitecore could not find the item.
<processor type="Sitecore.Pipelines.HttpRequest.FileResolver, Sitecore.Kernel"/>
<processor type="MyProject.ItemResolvers.MyItemResolver, MyProject"/>

The only hard part is creating business logic to resolve what the item should be based on the invalid URL. Once this is done and you have found the new item, you get a legitimate 301 redirect to it.

Monday, May 30, 2016

IHttpHandler response redirect shows Object moved to here

While working on a HTTP Handler for PDF files, I found that redirecting to a new URL would display:
Object moved to here.
 This was using the following code:

context.Response.Redirect(pdfUrl, false);
context.Response.StatusCode = 301;
context.Response.End();

This could have been related to the redirect begin inside a try catch block, so the following code works better by flushing a redirect header.

HttpResponse Response = HttpContext.Current.Response;
Response.StatusCode = 301;
Response.StatusDescription = "Moved Permanently";
Response.RedirectLocation = pdfUrl;
Response.Flush();

This then worked as expected.

Thursday, May 19, 2016

Sitecore snippets are great for reusable HTML blocks

During a migration from an older system to Sitecore, I ran into an issue where there were large chunks of HTML being used to render content. These chunks needed to be reusable and inserted by content editors as needed. The solution was using snippets in the rich text editor:

Sitecore rich text editor - insert snippet

These snippets are defined under rich text editor profiles - which are found in the core database under: /sitecore/system/Settings/Html Editor Profiles.

An example snippet is available under the rich text full profile (/sitecore/system/Settings/Html Editor Profiles/Rich Text Full). If you look under this item, there will be a folder called snippets, and in there a sample snippet.

Location of Sitecore sample snippet

This item is of the template HTML Editor Snippet (/sitecore/templates/System/Html Editor Profiles/Html Editor Snippet) and contains 2 fields:

  • Header: What displays in the rich text editor
  • Value: The HTML block to be inserted
Sample Sitecore snippet
An example with HTML is:
Sitecore snippet with HTML
My previous post on rich text editor profiles explains how these profiles can be set by default on al rich text fields or on individual instances of a field. In one multi site instance of Sitecore, I set a different profile on the base content item for each site - this allowed them to have their own custom snippets.