Author Archives: Toby Gutierrez

About Toby Gutierrez

Solutions Architect | 2015 - 2019 Sitecore Technology MVP

Find Text In Sitecore Files

Using Notepad++ To Find All Text In Sitecore Files In A Folder

Have you ever wanted to search for text in all files of a folder? Windows search will allow you to search through a folder, but will return the keyword in the filename by default. That’s great if you want to find a file in the folder, but what if you want to go deeper and search through all files in those folders for a certain keyword? I ran across this scenario recently, and I wanted to share with the community of Sitecore developers because it can be a very useful tool when performing some Development Operations type of tasks to help you troubleshoot when in certain situations as you will see. If you have seen this trick before then, “Amen”, but if not then make this a part of your toolbox!

Let’s just say you are troubleshooting a CD server in a typical 1 CM | 2 CD distributed environment that is having issues still referencing the “master” database. You simply want to find out where all the “master” references are in the App_Config folder and sub-folders without having to open each file manually and check. Can you imagine having to do that? As Kamruz Jaman would say, “Ain’t nobody got time for that!” Notepad++ to the rescue!

All you have to do is the following:

  1. Download Notepad++
  2. Open up Notepad++
  3. Either do a “Ctrl-Shift-F” OR click on “Search” tab then click on “Find in Files…”

    Find In Files Search Option

    Find In Files Search Option

  4. Once you click on that option you will get this “Find in Files” dialog box where you can set your settings for your search. For this example, we will set to “master” in the “Find what” text box and set the “Directory” to “D:\App_Config”. Lastly, we will just ensure the “In all sub-folders” is checked as well as seen below. Please note that you can also do a “Replace in Files” as well and even search in hidden folders:

    Find in Files Dialog Box

    Find in Files Dialog Box

  5. Click “Find All” and watch the all the goodness that returns to you!

As you will see below you now have that keyword brought to you in all the files in that sub-folder as well as the filename and path.  Not only that but when you double-click on the line item it will appear above in the text editor so you can view and modify.

Find in Files Search Results

Find in Files Search Results

With this knowledge, now you are armed like a boss to find keywords in files throughout your Sitecore development daily grind that will make you a more productive member of the team. Happy coding!

Sitecore Training

Sitecore Training Is Always Valuable

When I first started down the path of doing Sitecore development back in 2008, I was told by the company that I worked for at the time that they had a new CMS system they were going to be using to manage all the customer websites. The company was in the restaurant advertising business, and when they got new a new restaurant we would take the website they had currently, and add them into Sitecore to be managed. Back at that time, CMS systems were just gaining some ground in popularity, and .NET web development was a hot topic. I had been doing some work before with ASP but not a ton of .NET web development much less with enterprise level CMS systems. I was at a Junior level in my development efforts in .NET with lots of knowledge, but not a lot of practical uses of said knowledge in the form of implementations. With that being said, I knew that I had a lot of work ahead of me to learn .NET Web Development, and then learn Sitecore intimately from every angle in order for me to be able to deliver solid solutions. At that time in 2008, the version that had everyone excited was version 6.0. Sitecore, in my opinion, has always been good at providing documentation. However, with that being said, I still had to rely mostly upon Google searches to find most of what I needed to help me out in any way shape or form. It was at this time that John West and his blog posts generally saved the day for most of us that come from that era of Sitecore development, and the reason why he is a lifetime Sitecore MVP. He literally paved the way for us current MVP’s to share our knowledge as back then we were just looking for nuggets anywhere we could get them. From all of us in the community, thank you John West!

Now, back on topic, my company saw that this was a tall order for me to take on, and decided to send me to Sitecore certification training in San Francisco, CA. This turned out to be the best move they could make and money well spent to get me up to speed VERY quickly. I flew out to San Francisco, and for a week learned the nuts and bolts about how to develop a website into the Sitecore CMS system. To be honest, it was a lot to take in and being new to .NET development, some of it was over my head, and I struggled to gain the deep understanding that I very much desired. Luckily, all my documentation I read through before the course and the Google searches paid off because I was more so gaining the validity to what I had been doing on my own versus learning brand new concepts. Days were filled with training and examples, and nights were filled with soaking up what I learned that day and studying for the exam at the end of the course. At the end of the week, I flew back home as a Sitecore Certified Developer.

Armed with my knowledge, certification, and newfound passion for Sitecore, I set out to start building websites. I was able to build my first website in 6.0 that had around 20 micro-sites within it that we were able to manage easily in a very short period of time!

Fast-forward 8+ years later, my previous employer, had an instructor fly out, and provide a group training in SItecore 8.1+ . First, we started out with training on how Content Editors use the system from Content Editor as well as heavy use of the Experience Editor, and all it’s functionality and tools to edit content. Then for the rest of the week, we took a deep dive into how to develop websites into the Sitecore CMS using the latest and greatest tools utilizing best practices for Sitecore development. Having been through Sitecore Certification Training in the past, I was sure that I would not see much new that I didn’t know. To my disbelief, I was blown away at all that I learned that I didn’t know about, in addition to using tools that would help my team’s productivity overall. For instance, in the course, we made extensive use of SIM (Sitecore Instance Manager) and Sitecore Rocks for our development. I have used Sitecore Rocks before, here and there for certain tasks, but not strictly for development forcing me to have to get used to use the tool. It was great see just how easy things were to get what I wanted accomplished without ever leaving Visual Studio! Now, I am using Sitecore Rocks more than ever to help me with my day-to-day Sitecore tasks.

The moral of this story is that Sitecore training is ALWAYS valuable for partners as well as clients! Make no mistake about it, you are only as effective as what your level of knowledge is when it comes to Sitecore. Once you get the training you need in your role, you will be more confident, productive, and ultimately happy working inside the Sitecore CMS. Happy coding!

jQuery JavaScript Language Selector for Sitecore

So I had the need to create a quick language selector, and was surprised that there were not simple JavaScript solutions hanging out with every developers best friend “Google”. So I decided to put out a quick blog post to help out the community with my simplistic JavaScript solution that can be put in your toolbox as reusable code for use with any version of Sitecore.

Below is my HTML:

<li class="SS-LanguageToggle">
     <a id="langSelectEn" href="#">EN</a>
     <a id="langSelectFr" href="#">FR</a>
</li>

First, what I want to happen is when someone clicks the appropriate language, in this case either English or French, I want the language selector to change the site context to the appropriate language not only for that page but for every page thereafter until they either change it back to the other language or clear cookies in the browser (more on that in a bit).

Next, I want to set the class of the selector to the class that will make that selector active. In other words, I want to bold out the selector “EN” or “FR” so that you know it is the active language for the site.

Below is my jQuery snippet in my Main.js file:

/*======================================================== */
/* Switch Language Functionality - Start */
/*======================================================== */
$(function () {
    var currentUrl = window.location.href.toLowerCase();
 
    if (currentUrl.indexOf("en/") > -1 ||
        currentUrl.indexOf("sc_lang=en") > -1) {
        $('a#langSelectEn').addClass("Active");
        $('a#langSelectFr').removeClass("Active");
    }
 
    if (currentUrl.indexOf("fr-ca/") > -1 ||
        currentUrl.indexOf("sc_lang=fr-ca") > -1) {
        $('a#langSelectFr').addClass("Active");
        $('a#langSelectEn').removeClass("Active");
    }
 
    $('a#langSelectEn').on('click', function (e) {
 
        if (currentUrl.indexOf("en/") > -1 ||
            currentUrl.indexOf("sc_lang=en") > -1) {
            currentUrl = currentUrl.replace("en/", "").replace("#", "").replace("?sc_lang=en", "");
        }
        else if (currentUrl.indexOf("fr-ca/") > -1 ||
            currentUrl.indexOf("sc_lang=fr-ca") > -1) {
            currentUrl = currentUrl.replace("fr-ca/", "").replace("#", "").replace("?sc_lang=fr-ca", "");
        }
 
        var switchedUrl = currentUrl + '?sc_lang=en';
        window.open(switchedUrl, '_self');
    });
 
    $('a#langSelectFr').on('click', function (e) {
 
        if (currentUrl.indexOf("en/") > -1 ||
            currentUrl.indexOf("sc_lang=en") > -1) {
            currentUrl = currentUrl.replace("en/", "").replace("#", "").replace("?sc_lang=en", "");
        }
        else if (currentUrl.indexOf("fr-ca/") > -1 ||
            currentUrl.indexOf("sc_lang=fr-ca") > -1) {
            currentUrl = currentUrl.replace("fr-ca/", "").replace("#", "").replace("?sc_lang=fr-ca", "");
        }
 
        var switchedUrl = currentUrl + '?sc_lang=fr-ca';
        window.open(switchedUrl, '_self');
    });
});
/*======================================================== */
/* Switch Language Fuctionality - End */
/*======================================================== */

You will see that in this post, I am mostly using the URL to get/set what I need for the language selector. One other thing I should mention to you is for this example, you will want to have “languageEmbedding” turned on to always. This will give you the “languageIdentifier/” just after your domain name in the URL throughout your site. In this example, it is “en/” or “fr-ca/” as you will see in the JavaScript code above where I am looking that languageEmbedding in the URL.

LinkManager settings that are transformed to Sitecore.config:

<linkManager defaultProvider="sitecore">
    <providers>
      <clear />
      <add name="sitecore" type="Sitecore.Links.LinkProvider, Sitecore.Kernel"
            addAspxExtension="false"
            alwaysIncludeServerUrl="false"
            encodeNames="true"
            languageEmbedding="always"
            languageLocation="filePath"
            lowercaseUrls="true"
            shortenUrls="true"
            useDisplayName="false"
            xdt:Transform="Replace"
            xdt:Locator="Match(name)"/>
    </providers>
  </linkManager>

To set the language for the site, for example, English, you just need to add “en/” after the top-level domain OR add ?sc_lang=en to the end of the URL. Once that is done, the site context is changed for in your browser even if you close it and come back to the site later, it will be in that language. Why and how? Cookies…take a look in the cookies for your browser and you will see that you now have a “fr-ca” cookie after selecting the FR language selector:

Language Cookie

Language Cookie in Firefox

So really all I am doing is just checking for certain strings in the URL and replacing them and then modifying the URL to go to the language that I want using “sc_lang=[LanguageOfChoice]”. Simple and easy!

This code can be used as a baseline to extend if needed of course. My goal here was to give you a good head start depending on your language selection needs and requirements. Happy coding!

Sitecore Developer Toolbox Chunk

Sitecore Developer Toolbox Module for Sitecore 7.2+

Have you ever tried to remember what the URL is to the Show Config or the Cache page in your Sitecore instance when using the Administration Tools?

Did you know that there is a Database Browser that the old-schoolers use to Brute Force work they need to get done with Sitecore OR an XPath building tool you can lean on to help you with Sitecore queries?

If you don’t know what I am speaking of, I am eluding to some old-school tools that can be very useful for every day administration and development. For example, the Administration tools page with links to all kinds of tools from the backend, XPath Builder to help you write and validate queries/fast queries, and File Explorer to help you view Website folders/files on the Sitecore instance you have the Sitecore Developer Toolbox (SDT) installed on. When you install the Sitecore Developer Toolbox, you get quick access to these tools via the Developer tab in the Content Editor.

Here is a look at what the module will add to your Content Editor Contextual Ribbon:

Sitecore Developer Toolbox Chunk

Sitecore Developer Toolbox – Chunk

Administration Tools:

Sitecore Developer Toolbox Admin Tools

Sitecore Developer Toolbox – Admin Tools

XPath Builder:

Sitecore Developer Toolbox XPath Builder

Sitecore Developer Toolbox – XPath Builder

File Explorer:

Sitecore Developer Toolbox File Explorer

Sitecore Developer Toolbox – File Explorer

Here is the link to the module on the Sitecore Marketplace: https://marketplace.sitecore.net/en/Modules/S/Sitecore_Developer_Toolbox.aspx

If you like the module please recommend to the community, provide a rating, and/or review. Thanks and happy coding!

How To Setup, Configure, and Connect to xDB Cloud 2.0 – Part 1

In this post, I am going to run through my experience of setting up, configuring, and connecting to xDB Cloud 2.0. However, this is going to be a multi-post, because there are some steps that you need to take place so you can have the xDB Cloud set provisioned before you can connect. Once we get through the initial setup and provisioning by Sitecore, we will  go a little deeper on the connection that may need to take place depending on your environment.

First, I started my journey off by taking a look at some basic documentation on xDB Cloud and realized that I needed to use Version 2.0 as seen below, since the agreement was signed before July 21st:

Use Version 2 Instead of Version 1

Use Version 2 Instead of Version 1

Once I knew I needed to work with Version 2, I started looking through some documentation on Version 2 specifically here. When looking through the overviews, I needed to find out what xDB Cloud Set was compatible with my version of Sitecore, which is Sitecore 8.1 rev.160302 (Update 2). The xDB Cloud Service Compatibility Tables can be found here. I see that my version is supported for creating new xDB Cloud Sets and keep on trucking.

I move on to the “Connect to xDB Cloud 2.0” documentation. My Prerequisites are met so my next step is to “Request the xDB Cloud Customer Set” by putting in a Sitecore Support ticket with the following information:

  • License ID (Sitecore License ID & xDB License ID)
  • Deployment ID (This is the name you want to give to the set i.e. “companynameprod1”)
  • Sitecore Version (Sitecore 8.1 rev.160302 (Update 2)
  • Preferred Location (West US)

Then I waited for Sitecore Support to get back to me with the connection strings, and reporting service settings.

In Part 2, we will take a look at making the connections, and any other configurations that may need to take place to connect to your xDB Cloud set.

jQuery UI AutoComplete

jQuery AutoComplete using MVC with Lucene Index Computed Fields in Sitecore 8.1

In this post I am going to give an example of how to wire up suggestive search, otherwise known as AutoComplete, using the jQuery UI when using ASP.NET MVC along with Lucene Computed Index Fields in Sitecore 8.1.  An AutoComplete is as quoted from jQuery UI, “Enables users to quickly find and select from a pre-populated list of values as they type, leveraging searching and filtering“.

I started this off by working through the examples out of the most recent book put out by Phil Wicklund and Jason Wilkerson, Professional Sitecore 8 Development.  If you haven’t read this book, get it! It is filled with a lot of knowledge that you can add to your toolbox. I started with some basic examples from that book, and modified to get what I needed to meet my requirements for this task.

My requirement for this task was to create an AutoComplete that had a First Name and Last Name. The problem is that in my indexes they are stored as 2 separate fields in Lucene, as you will see below:

<field fieldName="first name" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
   <Analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
</field>
<field fieldName="last name" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
   <Analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
</field>

In order for me to bring them both together in my AutoComplete I created a Computed Index Field. A Computed Index Field is a custom field that stores data in Lucene that is calculated at index time versus on the fly. I needed to query a full name not a first name field AND a last name field. Below is the code to create a Computed index Field called FullNameField.cs:

FullNameField.cs

using System;
using Sitecore.ContentSearch;
using Sitecore.ContentSearch.ComputedFields;
using Sitecore.Diagnostics;
 
namespace SitecoreSandbox.Website.Search.ComputedFields
{
    public class FullNameField : IComputedIndexField
    {
        public string FieldName { get; set; }
 
        public string ReturnType { get; set; }
 
        public object ComputeFieldValue(IIndexable indexable)
        {
            Assert.ArgumentNotNull(indexable, "indexable");
 
            try
            {
                var indexItem = indexable as SitecoreIndexableItem;
 
                if (indexItem == null)
                    return null;
 
                var item = indexItem.Item;
 
                if (item != null)
                {
                    return $"{item["First Name"]} {item["Last Name"]}";
                }
            }
            catch (Exception ex)
            {
                Log.Error($"An error occurred when indexing {indexable.Id}: {ex.Message}", ex, this);
            }
 
            return null;
        }
    }
}

Now, that I have my Computed Index Field, I need to add it to my index. First, I added my field to the fieldmap>fieldnames node to be included in the index:

<field fieldName="full name" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
   <Analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
</field>

Then, I added my Computed Index Field to the documentOption>fields node to be included as a Computed Index Field:

<field fieldName="full name" storageType="YES" indexType="UNTOKENIZED">SitecoreSandbox.Website.Search.ComputedFields.FullNameField, SitecoreSandbox.Website</field>

You will notice that I added in indexType=”UNTOKENIZED” and that is because I want the value to not be split up. For example, if the name is John Doe, I want the results to come back as “John Doe” (UNTOKENIZED) versus “John” or “Doe” (TOKENIZED).

Now that my index is set up with my “full name” Computed Index Field all I need to do now is publish out to my Website folder and re-index. Once, I re-index I can see my field with the full names now being stored. I can easily view my index using LUKE – Lucene Index Toolbox, which I highly recommend, and it’s free. Below you will see that full names are left out for privacy purposes but you can see the field IS being indexed by Lucene and I assert to you the full names are there:

Luke (Lucene Index Toolbox)

Luke (Lucene Index Toolbox)

After verifying in Luke that my full names are being indexed, I added my new field to my PeopleSearchResultItem class so I can start using in code:

[IndexField("full name")]
public string FullName { get; set; }

Next. I created my interface for what is going to be used to implement my Search Service:

ISearchService.cs

using System.Collections.Generic;
 
namespace SitecoreSandbox.Website.Search.AutoComplete
{
    public interface ISearchService
    {
        IEnumerable<string> GetSearchSuggestions(string searchTerm);
    }
}

Then, I implemented my Interface in my Search Service class:

SearchService.cs

using System.Collections.Generic;
using System.Linq;
 
namespace SitecoreSandbox.Website.Search.AutoComplete
{
    public class SearchService : ISearchService
    {
        private readonly SearchManager _searchManager = new SearchManager();
 
        /// <summary>
        /// Gets the search suggestions from the computed index field for full name
        /// </summary>
        /// <param name="searchTerm"></param>
        /// <returns></returns>
        public IEnumerable<string> GetSearchSuggestions(string searchTerm)
        {
            var suggestions = new List<string>();
 
            var results = _searchManager.GetNamesByLetters(searchTerm);
 
            if (!results.Any())
                return suggestions;
 
            suggestions.AddRange(results.Select(result => result.FullName));
 
            return suggestions;
        }
    }
}

The GetSearchSuggestions method will make a call to my SearchManager class where my Lucene search logic is taking place and return me my results. In this case, I needed to pass in letters as the user types to return me back my results for my AutoComplete. The GetNamesByLetters method is seen below:

/// <summary>
/// Gets the the names with letters used with AutoComplete
/// </summary>
/// <param name="letters"></param>
/// <returns></returns>
public List<PeopleSearchResultItem> GetNamesByLetters(string letters)
{ 
    var searchIndex = ContentSearchManager.GetIndex("people_index");
 
    using (var context = searchIndex.CreateSearchContext())
    {
        return context.GetQueryable<PeopleSearchResultItem>()
            .Where(x => x.FullName.Contains(letters)).ToList();
    }
}

Now, that my logic is in place to return my results I just need to set up my Controller to handle the post and my View to make an AJAX call to the AutoComplete function in the jQuery UI. Below is the Controller logic:

private readonly SearchService _searchService = new SearchService();

[HttpPost]
public JsonResult GetSuggestions(PeopleSearchViewModel viewModel)
{
    if (!string.IsNullOrWhiteSpace(viewModel?.SearchTerm))
    {
        return Json(_searchService.GetSearchSuggestions(viewModel.SearchTerm));
    }
 
    return Json(new {});
}

Below is the jQuery code to handle the event for my field that is utilizing the AutoComplete. I added this to my .cshtml file. Keep in mind that my main layouts are already using jQuery and referencing the library in code so I am not including that script here. Also, the jQuery UI <script> and <link> elements can be found on the jQuery UI website:

    $(function () {
        $('#people-name').autocomplete({
            source: function(request, response) {
                $.ajax({
                    url: "/peoplesearch/getsuggestions",
                    type: "POST",
                    dataType: "json",
                    data: {
                        searchTerm: request.term
                    },
                    success: function(data) {
                        response(data.length === 1 && data[0].length === 0 ? [] : data);
                    }
                });
            }
        });
    });

The input text box HTML is below:

<input id=”people-name” placeholder=”Enter name” type=”text”>

The last thing I needed to do was just make sure that my route was set in the RouteConfig.cs file as such:

// Used with the AutoComplete for People Search
RouteTable.Routes.MapRoute("GetSuggestions", "PeopleSearch/GetSuggestions",
   new {controller = "PeopleSearch", action = "GetSuggestions"});

That’s all it takes to get the jQuery UI AutoComplete plug-in working for you to bring back results from your Lucene indexes. Happy coding!

Lucene Spatial Search Support Module

Lucene Spatial Search Support Module with Sitecore 8.1

I came across a need to implement a search based on zip code, latitude, longitude, and a radius. I quickly found out that this is a tall order in a short amount of time if implementing this type of functionality from scratch. However, the Lucene Spatial Search Support module came to the rescue…or did it? I am implementing a Sitecore 8.1 instance and it looks like the module was only good through 7.5 at the time of this post. There is an option to use SOLR Spatial Search Support module, and that IS updated through 8.1, but I didn’t have a driving need for SOLR on this project since my records being indexed were low in nature. So what is a Sitecore developer to do? Luckily, the Lucene Spatial Search Support module source code was available on GitHub, so I set out to get this module upgraded to 8.1. Time to get our hands dirty!

After cloning the repository from GitHub, I tried to build the project and there were many references missing, so I quickly grabbed a vanilla instance of Sitecore 8.1 rev. 160302 and added the references I needed to build the project. They are below:

Added to Sitecore.ContentSearch.Spatial project:

  • Sitecore.ContentSearch.dll
  • Sitecore.ContentSearch.Linq.dll
  • Sitecore.ContentSearch.Linq.Lucene.dll
  • Sitecore.ContentSearch.LuceneProvider.dll
  • Sitecore.Kernel.dll
  • Sitecore.Logging.dll

Added to Sitecore.ContentSearch.Spatial.DataTypes project:

  • Sitecore.ContentSearch.dll
  • Sitecore.Kernel.dll

I then had a build issue in this constructor in the LuceneSearchWithSpatialContext.cs:

protected LuceneSearchWithSpatialContext(ILuceneProviderIndex index, CreateSearcherOption options = CreateSearcherOption.Writeable, SearchSecurityOptions securityOptions = SearchSecurityOptions.EnableSecurityCheck)
: base(index, options, securityOptions)
{
Assert.ArgumentNotNull(index, "index");
this.index = index;
this.settings = this.index.Locator.GetInstance();
}

The Sitecore community never fails, and I found this on the Sitecore Stack Exchange where another developer simply commented out this constructor. I did the same and rebuilt the project but was missing a reference to a Sitecore.Abstractions.dll:

Sitecore.Abstractions Reference Missing

Sitecore.Abstractions Reference Missing

After adding in the Sitecore.Abstractions.dll to the Sitecore.ContentSearch.Spatial project, my project was successfully building. So I added this to the list of project references in addition to what was listed above:

Added to Sitecore.ContentSearch.Spatial project:

  • Sitecore.Abstractions.dll

Now it was time to make sure that the Sitecore.ContentSearch.Spatial.config was configured properly for 8.1. To my joy, it looks like there is a .config setup for version 8 that is disabled when I pulled from GitHub. I disabled Sitecore.ContentSearch.Spatial.config and enabled Sitecore.ContentSearch.Spatial.v8.config. Next, I added in my template criteria for my locations with the Template ID, LatitudeField, and LongitudeFields and then modified the index to use “sitecore_master_index” since I am testing this out locally and in live mode.

I added in the following .dll’s and .config file to the project I am working on that needs the spatial search feature:

  • Lucene.Net.Contrib.Spatial.dll
  • Sitecore.ContentSearch.Spatial.DataTypes.dll
  • Sitecore.ContentSearch.Spatial.dll
  • Spatial4nCore.dll
  • Sitecore.ContentSearch.Spatial.v8.config

After publishing my files and testing out, I got an error that said, “Current Index is not configured to use Spatial Search.

After some research, I realized that my index was set to use the wrong index earlier in code. Not only that, but one that wasn’t setup properly to for spatial search at all. After pointing to the correct index, publishing from Visual Studio, and then rebuilding my “sitecore_master_index” I was getting results back.

As I stated earlier, you can also perform spatial search with SOLR. If you have a client that has this type of environment (which is most right!?), I would take a hard look at SOLR for your client’s search provider.

You must use Solr if you have a scaled environment. This means you have:

  • two or more content delivery servers
  • two or more content authoring severs
  • separate servers for email, processing, reporting and publishing

Solr supports calls over HTTP(S) which means that the indexes are available to all servers in the environment that require it (content management and processing servers).

Big shout out to Ahmed Okour for the help that he provided for questions I had during the process. Happy coding!

Exposing Sitecore Item Properties when Using Code Generation with Glass.Mapper

When initially setting up TDS and with Glass.Mapper code generation in your solution, by default you get the initial properties from the Sitecore item:

  • Id – The ID for the item
  • Language – The language for the item
  • Version – The version of the item

However, what if you want something like the item name available to you like you have available when going through the standard Sitecore API? Don’t worry, the Glass.Mapper team has you covered and you can extend the GlassBase and IGlassBase respectively to get you your desired results.

After you got your initial setup of Code Generation setup with TDS, you simply go to your “glassv3header.tt” file and there you can extend the GlassBase/IGlassBase. However, if you start coding in this file you will have no Visual Studio Intellisense, so I actually wrote code in my ViewModel so I could see what was available in the SitecoreInfoType object. Below are a few:

SitecoreInfoType.Name
SitecoreInfoType.DisplayName
SitecoreInfoType.ContentPath

Once you can see what is available via Intellisense you can set them in the “glassv3header.tt” file appropriately when generating the code as such.

public partial interface IGlassBase
{
[SitecoreId]
Guid Id{ get; }


[SitecoreInfo(SitecoreInfoType.Language)]
Language Language{ get; }


[SitecoreInfo(SitecoreInfoType.Version)]
int Version { get; }

}

public abstract partial class GlassBase : IGlassBase
{

[SitecoreId]
public virtual Guid Id{ get; private set;}


[SitecoreInfo(SitecoreInfoType.Language)]
public virtual Language Language{ get; private set; }


[SitecoreInfo(SitecoreInfoType.Version)]
public virtual int Version { get; private set; }


[SitecoreInfo(SitecoreInfoType.Url)]
public virtual string Url { get; private set; }


[SitecoreInfo(SitecoreInfoType.Name)]
public virtual string Name { get; private set; }

}

This will work to get the item name. However, I noticed that ASP.NET MVC when using Html.Helpers with names of “name” will pull in the Item name into the textbox as the value. Hence, I had to change from “Name” to “ItemName” as such and I was back happy coding again and relieved myself of regression testing after making this change. Below is my modification to get the item name property:

[SitecoreInfo(SitecoreInfoType.Name)]
public virtual string ItemName { get; private set; }

Glass.Mapper, TDS, and Code Generation is a phenomenal tool and highly recommended for rapid development with Sitecore. Play around with it and you can get what you need every time. Happy Holidays!

Modifying Code to Add a Visitor to an Engagement Plan when upgrading from Sitecore 6.6 to 8.1

In my recent efforts during an upgrade from 6.6 to 8.1 I had to refactor some code to work with Sitecore 8.1 to add a visitor to an engagement plan. Below are my findings:

These were the .dlls that were referenced in the previous solution in 6.6:

* Sitecore.Analytics
* Sitecore.Automation.MarketingAutomation

I added in the Sitecore.Analytics.dll and that wasn’t all I needed apparently as I got some errors upon building the project.

using Sitecore.Analytics;
using Sitecore.Analytics.Automation.Data;

Looked like the Automation in Sitecore.Analytics was no longer there and has been moved. After a quick look through Reflecting on the Analytics libraries I had to add the Sitecore.Analytics.Automation.dll as a reference which would give me the Automation and MarketingAutomation that I needed.

Sitecore 8.1 Analytics

Sitecore 8.1 Analytics Libraries

Once that was in place I was able to refactor the code to work with the new Analytics code libraries for Sitecore 8.1.

Before:

using System;
using System.Collections.Generic;
using Sitecore.Analytics;
using Sitecore.Analytics.Automation.Data;
using Sitecore.Analytics.Data.DataAccess;
using Sitecore.Data;
using Sitecore.Rules;
using Sitecore.Rules.Actions;
using Sitecore.Form.Core.Configuration;
 
namespace Sitecore.Sandbox.Actions
{
    public class AddVisitorToEngagementPlan<T> : RuleAction<T> where T : RuleContext
    {
        public string ItemId { get; set; }
 
        public override void Apply(T ruleContext)
        {
            if (!Tracker.IsActive)
                Tracker.StartTracking();
 
            if (!Settings.IsAnalyticsEnabled || string.IsNullOrEmpty(ItemId)) return;
 
            var engagementPlanState = Sitecore.Context.Database.GetItem(ID.Parse(ItemId));
            if (engagementPlanState.TemplateID != ID.Parse("8CE2707A-3742-4A89-933B-065E5BE02BC9")) return;
 
            var engagementPlan = engagementPlanState.Parent;
 
            var visitor = Tracker.Visitor;
            var visitorLoadOption = new VisitorLoadOptions { Options = VisitorOptions.AutomationStates };
            visitor.Load(visitorLoadOption);
 
            if (Tracker.Visitor.DataSet.AutomationStates.Count > 0) return;
 
            AutomationManager.Provider.CreateAutomationStatesFromBulk(new List<Guid> { Tracker.CurrentVisit.VisitorId }, engagementPlan.ID.Guid, engagementPlanState.ID.Guid);
        }
    }
}

After:

You will notice that Sitecore.Automation.MarketingAutomation changed to Sitecore.Analytics.Automation.MarketingAutomation.

using System.Linq;
using Sitecore;
using Sitecore.Analytics;
using Sitecore.Analytics.Automation.MarketingAutomation;
using Sitecore.Data;
using Sitecore.Rules;
using Sitecore.Rules.Actions;
 
namespace Sitecore.Sandbox.Actions
{
    public class AddVisitorToEngagementPlan<T> : RuleAction<T> where T : RuleContext
    {
        public string ItemId { get; set; }
 
        public override void Apply(T ruleContext)
        {
            if (!Tracker.IsActive)
                Tracker.StartTracking();
 
            if (!Tracker.Enabled || string.IsNullOrEmpty(ItemId)) return;
 
            var engagementPlanState = Context.Database.GetItem(ID.Parse(ItemId));
            if (engagementPlanState.TemplateID != ID.Parse("8CE2707A-3742-4A89-933B-065E5BE02BC9")) return;
 
            var engagementPlan = engagementPlanState.Parent;
 
            Tracker.Current.Session.Identify(Context.User.Profile.UserName);
            var manager = Tracker.Current.Session.CreateAutomationStateManager();
 
            if (!manager.GetAutomationStates().Any()) return;
            manager.EnrollInEngagementPlan(ID.Parse(engagementPlan.ID.Guid), ID.Parse(engagementPlanState.ID.Guid));
        }
    }
}

Thanks goes to Brian Pederson for his blog post, which helped me on this issue: https://briancaos.wordpress.com/2015/01/26/sitecore-8-and-engagement-plans/

TDS Code Generation Best Practice for Images and Links Using Glass.Mapper

Just a few weeks ago, I ran into an issue with TDS where all the “Image” fields in my solution were not generated correctly by TDS during code generation. Hence, I had hundreds of “Image” fields that had no reference.

If you are not familiar with using TDS for code generation with Glass.Mapper, I would highly recommend you take a look here, as it truly is a phenomenal tool for rapid development by generating models from Sitecore items:

http://www.glass.lu/Mapper/Sc/Tutorials/Tutorial24

I was stumped as to why this might have happened. After taking a look around my Sitecore instance, and getting some tips from the Sitecore Community on Slack in room “TDS”, I was able to find out what the problem was. I had an “ImageBase” base template that was originally setup on a template. However, I inadvertently created another Image field on the template itself with the same field title. Hence, when it came time to code generate, TDS threw up an error, but then couldn’t come back from the code generation “Image” fields issue. Basically, I had hundreds of mis-referenced “Image” fields in my code generated models file.

After speaking with Kamruz Jaman, he gave me a pointer to possibly just explicitly reference the Image object. Great Idea right!? So my solution to this issue was by going into my glassv3item.tt file and searching for Image. Once I found it, I changed it from:

case "image":
    return "Image";
 
case "general link":
case "general link with search":
    return "Link";

to

case "image":
    return "Glass.Mapper.Sc.Fields.Image";

case "general link":
case "general link with search":
    return "Glass.Mapper.Sc.Fields.Link";

I did this for both the Image and Link so if me or my development team ever ran into this issue of double creating a field with the same name, working with bases and the template item, that TDS code generation would not break on the referencing of Image and Link fields. Hence, I consider it a best practice to explicitly reference those fields just for safe measure in all your projects with TDS & Glass.Mapper code generation. Happy coding!