Thursday, November 23, 2017

A look at the new Forms module in Sitecore 9

Longtime users of the Sitecore Experience Platform will be familiar with the Web Forms For Marketers (or WFFM) module. It's main strength was the ability for content editors and marketers to create rich forms, all without developer intervention. Yet, there were often complaints when it came down to visual styling, customization around multi-page forms along with the need for conditional elements (show this text box if the user selects value X in the drop-down list).

With Sitecore 9, comes the new forms modules and when I say module, it come's out of the box with no separate install! Right away that will excite anyone that has ever had to do a Sitecore upgrade and go through the install of multiple WFFM update packages.

The Forms dashboard

After clicking on the Forms shortcut you will be taken to the new forms dashboard.

Sitecore Forms dashboard
No longer do marketers need to manage their forms via the content editor, where it can become especially difficult when there are a reasonable number of forms and even form variations. This dashboard allows for searching, sorting along with the ability to see forms created by you. The option to download all submitted data for a given form, is available here as well.

On the right hand panel, the selected form shows two key areas of information. Overview displays  statistics on creation, edit date along with where the form is actually being used. Performance has statistics and counters around views, abandonment and even error rates. Both of these features are key for quickly finding out which forms are actually being used and where there is potential loss of conversion (abandonment/error rates).

Forms performance for a given form

Creating a form

From the dashboard, it is then possible to create a new form. This could be a blank form (start from scratch) or via a template - which lets you select an existing form and create a new one based off of that.

The form creation page is a much slicker version of the old WFFM module. Here the familiar form elements are available for selection (text boxes, lists, and so on), but the new drag and drop feature makes for a much easier editing experience.

Sitecore forms drag and drop
When a field is added to the form, there are a number of settings that are available. Aside from the standard validation/field lengths, placeholder text is now supported, as is a CSS class for both the field element and it's label.

General settings for a Sitecore form field
If performance tracking has been enabled for a given form field, then the performance statistics will become available for viewing. Much like the form statistics on the Forms dashboard, this shows field level abandonment rates, error rates and average time spent on the field. This data is extremely valuable for a marketer looking to optimize a given form to lead to higher completion rates.

Performance for a form field
Two versions of a given form could also be A/B tested to see which leads to higher completion rate - this would be done by A/B testing against a goal triggered in the form's submit actions.

Once a submit button has been added onto the form, the submit actions can be selected and configured. The standard save data, page redirect and triggering of marketing items (goals, campaign activities and outcomes) is available, but much like WFFM the real power comes in plugging in a custom submit action.

Perhaps the most exciting feature of all, is the ability to add paging to your form! This has been a long requested feature and is a great way to create wizard-like forms and even make a particularly long form more bearable to the user.

The new page form element
Now a form has been created and saved, it can then be added to a page using the standard forms rendering. This of course can be achieved using both the content editor and the experience editor.

A form added to a page using the MVC Form rendering

Customization of Forms

Much like any part of the Sitecore Experience Platform, the ability to extend or customize the forms module is supported. 
  • Submit action - creation of a custom submit action allows for the data entered into the form to be used as required. Common examples of this might be sending the data to a third party CRM, or custom database.
  • Custom validation - this allows for custom and more complex validations to be done in code. A common example of this may be validating a customer number (even taking into account a field containing customer name).
  • Custom form elements - sometimes business requirements will come up that require a custom form element to be created. An example of this may be a color picker, or perhaps a custom CAPTCHA implementation.
Furthermore it is also possible to edit the output of existing fields (such as the HTML output for a text box). This is not best practice as you are editing core Sitecore files (that will be overwritten during upgrade). In some cases, simply adding styles for form elements/labels may not be enough and this option becomes necessary.

Conclusion

Overall the new Forms module available in Sitecore 9 is an impressive release, that provides an easier interface for marketers to create, manage and monitor performance of their forms. It's worth noting that the aim with the initial release of forms was to match the functionality of the current WFFM offering. No doubt in the future we are going to see some exciting features to come.

Tuesday, November 14, 2017

Sitecore 9 SOLR search example

One of the key features that any web site needs is a strong internal search engine. This posts focuses on how to get a minimal working example of an internal Sitecore search using SOLR in version 9 of Sitecore.
  1. The first step is to create a core for SOLR. This core is an instance of a Lucene index and contains dedicated configurations for that core.
  2. The next step is to create an index configuration for SOLR search, this will contain what data is getting indexed and which fields of that data.
  3. The index is now ready to be rebuilt (to get the data added into it). If the Sitecore admin area shows an error the index configuration XML may not be valid. The rebuild will also give a count of items processed into the index.
  4. Now code can be used to access the SOLR search index and query for a given term. The example below is the full code implementation for search in Sitecore with SOLR and will be explained in detail.
using Sitecore.ContentSearch;
using Sitecore.ContentSearch.Linq;
using Sitecore.ContentSearch.Linq.Utilities;
using Sitecore.ContentSearch.SearchTypes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace Sitecore.Base.Helpers
{
    public class SearchHelper
    {
        /// <summary>
        /// Complete a search
        /// </summary>
        /// <param name="searchTerm">Search term</param>
        /// <returns>Search results object</returns>
        public static SearchResults DoSearch(string searchTerm)
        {
            var myResults = new SearchResults
            {
                Results = new List<SearchResult>()
            };

            var searchIndex = ContentSearchManager.GetIndex("sitecore_test_index"); // Get the search index
            var searchPredicate = GetSearchPredicate(searchTerm); // Build the search predicate

            using (var searchContext = searchIndex.CreateSearchContext()) // Get a context of the search index
            {
                var searchResults = searchContext.GetQueryable<SearchModel>().Where(searchPredicate); // Search the index for items which match the predicate

                // This will get all of the results, which is not reccomended
                var fullResults = searchResults.GetResults();

                // This is better and will get paged results - page 1 with 10 results per page
                //var pagedResults = searchResults.Page(1, 10).GetResults();

                foreach (var hit in fullResults.Hits)
                {
                    myResults.Results.Add(new SearchResult
                    {
                        Description = hit.Document.Description,
                        Title = hit.Document.ItemName,
                        Url = hit.Document.ItemUrl
                    });
                }

                return myResults;
            }
        }

        /// <summary>
        /// Search logic
        /// </summary>
        /// <param name="searchTerm">Search term</param>
        /// <returns>Search predicate object</returns>
        public static Expression<Func<SearchModel, bool>> GetSearchPredicate(string searchTerm)
        {
            var predicate = PredicateBuilder.True<SearchModel>(); // Items which meet the predicate

            // Search the whole phrase - LIKE
            predicate = predicate.Or(x => x.DispalyName.Like(searchTerm)).Boost(1.2f);
            predicate = predicate.Or(x => x.Description.Like(searchTerm)).Boost(1.2f);
            predicate = predicate.Or(x => x.Title.Like(searchTerm)).Boost(1.2f);

            // Search the whole phrase - CONTAINS
            predicate = predicate.Or(x => x.DispalyName.Contains(searchTerm)).Boost(2.0f);
            predicate = predicate.Or(x => x.Description.Contains(searchTerm)).Boost(2.0f);
            predicate = predicate.Or(x => x.Title.Contains(searchTerm)).Boost(2.0f);

            return predicate;
        }

        /// <summary>
        /// Search item mapped to SOLR index
        /// </summary>
        public class SearchModel : SearchResultItem
        {
            [IndexField("_name")]
            public virtual string ItemName { get; set; }

            [IndexField("_displayname")]
            public virtual string DispalyName { get; set; }

            [IndexField("itemurl")]
            public virtual string ItemUrl { get; set; }

            [IndexField("description")]
            public virtual string Description { get; set; } // Custom field on my template

            [IndexField("title")]
            public virtual string Title { get; set; } // Custom field on my template
        }

        /// <summary>
        /// Custom search result model for binding to front end
        /// </summary>
        public class SearchResult
        {
            public string Title { get; set; }

            public string Url { get; set; }

            public string Description { get; set; }
        }

        /// <summary>
        /// Custom search result model for binding to front end
        /// </summary>
        public class SearchResults
        {
            public List<SearchResult> Results { get; set; }
        }
    }
}
This code is best explained by breaking it down into sections:
  • SearchModel - this is the model that each item in the index is mapped to. It contains a base class with all of the standard Sitecore fields indexed. The items directly on the model are my custom fields in the index. The itemurl field is a clean implementation of the URL of the item/page that uses a Sitecore computed index field.
  • SearchResults - This is a model containing a list of search results (SearchResult model mapped from the SearchModel  with only the fields we need) which is bound to the view. It would normally contain more information such as total results, current page, etc.
  • GetSearchPredicate - Is where the main search logic is built up into a predicate. This is a simple example where like or contains is run on the whole search term. More advanced logic could be added to split a phrase into multiple terms and so on. A predicate can also be made up of multiple predicates if required.
  • DoSearch - The main piece of code to actually run the search. This gets the search index and queries it with the built up predicate. Any hits are mapped through to the cleaner model assigned to the search results view. In this case p;aging is not implemented, but the code to do this is commented out.
This is just an example of a minimal implementation of SOLR search in Sitecore 9 (from a back-end perspective). This can be expanded upon to provide more advanced concepts such as working out the field which is most relevant to the user's query (and displaying this on the front-end) or highlighting the search terms in the results. 

Sitecore Instance Manager (SIM) installation error

When attempting to install the latest version of Sitecore Instance Manager (or SIM), the following error was appearing after entering the SQL Server details:
The SQL Server is configured to use "NTSERVICE\MSSQLSERVER" account which is not supported by current version of SIM. You need to change the SQL Server's user account and click Grant again. The instruction will be provided when you click OK.
This can be resolved using these instructions.
  1. Open the SQL Server configuration manager, this can be done by entering the following into your apps or start bar:
    1. SQLServerManager13.msc in the case of SQL Server 2016
    2. SQLServerManager12.msc in the case of SQL Server 2014
    3. SQLServerManager11.msc in the case of SQL Server 2012
  2. On the right hand side select SQL Server Services and then on the left select SQL Server (MSSQLSERVER). Then right click and click Properties.
  3. Select the Built-in account radio button and in the drop down box below, Network Service
  4. Click OK to close the box and again when promoted that a restart of SQL Server will occur.
SIM will now install correctly without any issues.

Thursday, November 9, 2017

Sitecore SOLR custom fields are not appearing in the index

After setting up a custom index configuration for SOLR in a Sitecore 9 instance, I noticed that the fields in which I had added to the raw:AddComputedIndexField were not present in the index (when viewed under schema in the SOLR admin site).

The index definition XML was valid and the index would built, yet my fields were not present.

After a bit of playing around I notice that the ordering of documentOptions and fieldMap mattered. I originally had them in this order:
<configuration ref="contentSearch/indexConfigurations/defaultSolrIndexConfiguration">
 <fieldMap ref="contentSearch/indexConfigurations/defaultSolrIndexConfiguration/fieldMap">
   <fieldNames hint="raw:AddFieldByFieldName">
  <field fieldName="title" returnType="text" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" />
  <field fieldName="description" returnType="text" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" />
   </fieldNames>
 </fieldMap>
 <documentOptions ref="contentSearch/indexConfigurations/defaultSolrIndexConfiguration/documentOptions">
   <include hint="list:AddIncludedTemplate">
  <Page>{49E6B85F-02F4-405F-923F-66761933E80F}</Page>
   </include>
   <fields hint="raw:AddComputedIndexField">
  <field fieldName="MyComputedField" returnType="string">Sitecore.Base.Search.MyComputedImageField, Sitecore.Base</field>
   </fields>
 </documentOptions>
</configuration>
Which was incorrect, however swapping to the following order worked.
<configuration ref="contentSearch/indexConfigurations/defaultSolrIndexConfiguration">
 <documentOptions ref="contentSearch/indexConfigurations/defaultSolrIndexConfiguration/documentOptions">
   <include hint="list:AddIncludedTemplate">
  <Page>{49E6B85F-02F4-405F-923F-66761933E80F}</Page>
   </include>
   <fields hint="raw:AddComputedIndexField">
  <field fieldName="MyComputedField" returnType="string">Sitecore.Base.Search.MyComputedImageField, Sitecore.Base</field>
   </fields>
 </documentOptions>
 <fieldMap ref="contentSearch/indexConfigurations/defaultSolrIndexConfiguration/fieldMap">
   <fieldNames hint="raw:AddFieldByFieldName">
  <field fieldName="title" returnType="text" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" />
  <field fieldName="description" returnType="text" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" />
   </fieldNames>
 </fieldMap>
</configuration>
It was a strange one to get working, so hopefully this post is able to help anyone else that comes across this error.

Sitecore SOLR Error getting file length for [segments_1]

After installing Sitecore 9 and deploying a simple site, I noticed that experience analytics were appearing but no experience profiles were visible. After allowing anonymous experience profile data, the experience profiles were still not visible. Looking into the SOLR admin logs there was a stream of the following error message:
Error getting file length for [segments_1]
 These errors were all for the xDB SOLR core.

Sitecore SOLR - error getting file length

Try rebuilding the index

The first step is to try rebuilding the index. In this case, these steps were followed:
  1. Turn the Windows SOLR service off
  2. Delete the data folder for the core with the error
  3. Turn the Windows SOLR service on
  4. Kick off an index rebuld
    1. For the xDB index, a rebuild has a special set of instructions as opposed to the control panel in Sitecore.
In my case, this did not resolve the issues.

Java Runtime Environment version

When installing SOLR, I had installed the latest version of the JRE (version 9). However SOLR prefers a slightly older version of JRE, version 1.8.0_111 aka jre-8u144-windows-x64. To resolve this error the following steps were followed:

  1. Turn off the Windows SOLR service
  2. Install the correct JRE version - re-8u144-windows-x64.exe
  3. Restart the PC
  4. Uninstall the incorrect, newer version of the JRE
  5. Update the Java_Home system variable to the new folder location of the older JRE
  6. Restart the SOLR service
After following these steps, the errors disappeared and the xDB data appeared correctly.

Wednesday, November 8, 2017

Sitecore 9 no experience profile data

After spinning up a new Sitecore instance and browsing the site under several anonymous sessions I noticed that no data was appearing inside experience profiles.

Sitecore 9 no experience profile data
This occurs because by default xConnect will not index and display anonymous contacts. Sitecore documentation has a great article on a setting change which will allow this data to be index. Then after a rebuild of the xDB search index in SOLR, the data will begin to show.

The setting which gets changes is IndexAnonymousContactData which is located inside the sc.Xdb.Collection.IndexerSettings.xml file inside the xConnect website folder (C:\inetpub\wwwroot\sc9test_.xconnect\App_data\jobs\continuous\IndexWorker\App_data\Config\Sitecore\SearchIndexer in my case).

Sitecore SOLR creating a core for your custom search index

After installing SOLR and connecting it to your Sitecore 9 instance, you may notice that a number of default Sitecore default cores have been added. In SOLR a core is an instance of a Lucene index and contains the configurations for that index. Each index should have it's own core, so for each custom index you wish to create for Sitecore, you must create a core.

To create your SOLR core, in the browser navigate to the SOLR instance. In my case the URL is: https://solr-dev.local:8983/solr/, however the hostname is configured at installation. On the left hand navigation menu, select the Core Admin option, here a list of the default cores are listed.

SOLR default Sitecore cores
Instead of using the GUI to create the core, the following URL will work (which allows the creation of cores to be scripted):
https://solr-dev.local:8983/solr/admin/cores?action=CREATE&name=TEST_CORE&instanceDir=TEST_CORE&configSet=basic_configs
 This has the following parameters:

  • Action - action to perform on the given core
  • Name - name of the core
  • InstanceDir - Folder name for the core
  • ConfigSet - allows configurations to be shared between a number of cores. This is the default that Sitecore indexes use.
Once created, your core is now ready to use:

SOLR new core added
Which is referenced in the XML definition of your custom SOLR index:

SOLR index configuration, core element

Friday, November 3, 2017

Sitecore 9 SOLR custom index configuration

I always find it handy to have a clean example of a custom index configuration ready to use during project development. Below is an example of a custom SOLR index configuration ready to use with Sitecore 9. As with any index I create, it contains sections for templates/fields to be added to the index along with computed index fields.

The standard database and root for crawling are of course included as well.
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:search="http://www.sitecore.net/xmlconfig/search/">
  <sitecore role:require="Standalone or ContentDelivery or ContentManagement" search:require="solr">
    <contentSearch>
      <configuration type="Sitecore.ContentSearch.ContentSearchConfiguration, Sitecore.ContentSearch">
        <indexes hint="list:AddIndex">
          <index id="sitecore_test_index" type="Sitecore.ContentSearch.SolrProvider.SolrSearchIndex, Sitecore.ContentSearch.SolrProvider">
            <param desc="name">$(id)</param>
            <param desc="core">sc9test__test_index</param>
            <param desc="propertyStore" ref="contentSearch/indexConfigurations/databasePropertyStore" param1="$(id)" />
              <configuration ref="contentSearch/indexConfigurations/defaultSolrIndexConfiguration">
    <documentOptions ref="contentSearch/indexConfigurations/defaultSolrIndexConfiguration/documentOptions">
      <include hint="list:AddIncludedTemplate">
     <Page>{49E6B85F-02F4-405F-923F-66761933E80F}</Page>
      </include>
      <fields hint="raw:AddComputedIndexField">
     <field fieldName="MyComputedField" returnType="string">Sitecore.Base.Search.MyComputedImageField, Sitecore.Base</field>
     <field fieldName="ItemUrl" returnType="string">Sitecore.ContentSearch.ComputedFields.UrlLink,Sitecore.ContentSearch</field>
      </fields>
    </documentOptions>
    <fieldMap ref="contentSearch/indexConfigurations/defaultSolrIndexConfiguration/fieldMap">
      <fieldNames hint="raw:AddFieldByFieldName">
     <field fieldName="title" returnType="text" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" />
     <field fieldName="description" returnType="text" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" />
      </fieldNames>
    </fieldMap>
     </configuration>
            <strategies hint="list:AddStrategy">
              <strategy ref="contentSearch/indexConfigurations/indexUpdateStrategies/onPublishEndAsyncSingleInstance"/>
            </strategies>
            <locations hint="list:AddCrawler">
              <crawler type="Sitecore.ContentSearch.SitecoreItemCrawler, Sitecore.ContentSearch">
                <Database>web</Database>
                <Root>/sitecore/content/Home</Root>
              </crawler>
            </locations>
            <enableItemLanguageFallback>false</enableItemLanguageFallback>
            <enableFieldLanguageFallback>false</enableFieldLanguageFallback>
          </index>
        </indexes>
      </configuration>
    </contentSearch>
  </sitecore>
</configuration>
This index is named sitecore_test_index and uses the SOLR core sc9test__test_index, don't forget to change these!

The URL of the item is a computed index field called ItemUrl and actually refers to a Sitecore code definition to get the URL.

Sitecore SOLR AddFieldByFieldName The given key was not present in the dictionary

When setting up a new custom index for SOLR in Sitecore 9, I noticed the following error after adding the AddFieldByFieldName section.
The given key was not present in the dictionary
I had incorrectly added the section as a type:
fieldMap type="Sitecore.ContentSearch.SolrProvider.SolrFieldMap, Sitecore.ContentSearch.SolrProvider"
When it should have been added as a ref:
fieldMap ref="contentSearch/indexConfigurations/defaultSolrIndexConfiguration/fieldMap"
Fixing this line up, the index would build correctly and the error disappeared. 

Sitecore SOLR: Could not find add method

With Sitecore 9 I came across the following errors when attempting to move my old Lucene based indexes (from Sitecore 8) into SOLR (6.6.2).
Could not find add method: IncludeTemplate (type: Sitecore.ContentSearch.SolrProvider.SolrIndexConfiguration)
Could not find add method: ExcludeTemplate (type: Sitecore.ContentSearch.SolrProvider.SolrIndexConfiguration)
Could not find add method: IncludeField (type: Sitecore.ContentSearch.SolrProvider.SolrIndexConfiguration)
Could not find add method: ExcludeField (type: Sitecore.ContentSearch.SolrProvider.SolrIndexConfiguration) 
These method names have all been updated and now will appear as follows:

  • include hint="list:AddIncludedTemplate"
  • include hint="list:AddExcludedTemplate"
  • include hint="list:AddIncludedField"
  • include hint="list:AddExcludedField"
The custom index should work as expected with these updates.

Thursday, November 2, 2017

Sitecore report showing visits with company/business name

One of the benefits to signing up to Sitecore's IP Geo-location service is that an IP lookup is done against each visitor in an attempt to discover their company/business name. Although this is not always accurate (in many cases will actually return the name of the ISP), site visitors from larger organisations will have their company name listed.

This information is not displayed (out-of-box) inside the Experience Analytics areas and will actually need to be enabled (and then viewed in a separate area). The reporting feature is included and working with Sitecore (as of 8.2) but a short-cut needs to be added to the desktop area of Sitecore.

Details for adding this shortcut to the desktop menu are available on this post from Nish Tech. It is very simple and does not require a deploy, nor will it require web site downtime.

Sitecore desktop menu with the newly added Engagement Analytics option
Once this menu item has been added, you can now load up the old experience analytics reporting interface. Under the Recent Activity node in the tree, will be the Latest Visits report. I recommend filtering to a desired time frame and then downloading the report to be massaged in Excel.

Sitecore experience analytics showing organization name

Sitecore FXM Error The page does not belong to a tracked website

When attempting to setup FXM on a local sandbox site, I was noticing the following error in the console/network tab of the browser (when browsing the external site containing the FXM beacon).
The page does not belong to a tracked website.
It also showed the message:
400 (DomainInvalid)
The fix for the error is to publish the external site which was created in FXM, as shown in the image below:
Sitecore FXM - publish external site
Quite a simple mistake, but it may trip someone up who is new to FXM.

Wednesday, November 1, 2017

Creating your first Sitecore 9 Visual Studio solution

So you've now got Sitecore 9 up and running, the next step is to create a solution to allow custom code to be deployed. The following steps will show you how to setup a single project solution, but the concepts will work when adding more projects to form a Sitecore Helix solution.
  1. Firstly, create a new a new web project and ensure the target .NET Framework version is 4.6.2 (see here if 4.6.2 is not available).
  2. For a project template select the Empty option and check the MVC checkbox.
  3. Delete the Global.asax file.
  4. Set the Build Action to None for the Web.Config file.
  5. Add the Sitecore Nuget Feed as a source in Visual Studio.
  6. Add the following nuget packages from the Sitecore feed (I like to use the no reference ones, as the dependent references are often not required).
    1. Sitecore.Kernel
    2. Sitecore.Mvc
    3. Sitecore.Mvc.Analytics
  7. In the Views/Web.Config file add the following two lines (in the namespaces section):
<add namespace="Sitecore.Mvc" />
<add namespace="Sitecore.Mvc.Presentation" />

Now your project is ready to build and deploy to your Sitecore instance. Don't forget to follow the Helix principals!

Setting up .NET Framework 4.6.2 in Visual Studio for Sitecore 9

When attempting to create a new Visual Studio solution for Sitecore 9, I noticed that the .NET Framework 4.6.2 was not available to set as the projects framework version.

I downloaded the offline installer for .NET Framework 4.6.2, however the installer notified me that the version (or a later one) was already installed.

It turns out that one more installation needed to occur to have this version available in Visual Studio. The .NET Framework 4.6.2 Developer Pack once installed allows the framework to be set on a project.

.NET Framework version 6.6.2 in Visual Studio