Azure-Windows2016-Native-Docker-And-Sitecore8-Setup

In this blog we will setup Sitecore 8 on a Windows Server 2016 Docker container, some basic understanding of Sitecore, Windows server and Docker is required and will be of certain advantage, but if you do not know much about them it’s ok, you are here to learn. so let’s dive in and get started.

  1. Identify the images and download from Docker HUB.

We need to pull/download the images from Docker hub/registry to begin, an image is an inert, immutable, file that’s essentially a snapshot of a container. Images are created with the build command, and they’ll produce a container when started with run.

I have identified following two images for our Sitecore on Docker.

  1. Microsoft/mssql-server-windows-express
  2. Microsoft/iis

If you are aren’t sure about which image you are looking for then either from command line you can go and search for the image, however https://hub.docker.com/explore/ is the best place to start with.

Since I already know the name of the image, I’ll go and pull it from registry.

Registry: A service responsible for hosting and distributing images. The default registry is the Docker Hub. Repository. A collection of related images (usually providing different versions of the same application or service)

If you would like to search on Docker command line, then you can use the following command and it should provide you with Microsoft images listed on Docker hub.

#docker search Microsoft

           

We will select Microsoft/IIS and proceed to pull the image from Docker registry.

  1. Pull/Download image from Docker Hub/Registry.

 

To build the container we will have to download the image or multiple images in our case, because we will have one IIS web server and a backend SQL server.

For Docker images certain mannerism is followed, so I suggest you visit http://docs.docker.com

Let’s back to downloading image from Docker.

  1. Download IIS Webserver

Note: I had already downloaded that image previously hence it says already exists.

  1. Download Microsoft/mssql-server-windows-express

If you are downloading it for first time, here’s what you’ll see on your screen. The time it takes to download it completely dependent on your bandwidth, if you are running your host machine on any cloud platform then it shouldn’t be a problem in downloading and should not take more than few mins.

Here’s the confirmation on images download.

#docker images

So you can see that we have two images being pulled from Docker Hub a. microsoft/mssql-server-windows-express b. microsoft/iis

We will first work with microsoft/iis, make some required changes and then deploy Sitecore.

Let’s build the container from the image and get going.

  1. Build Container from image (IIS Server)

I’ll show you how to build the container from image shortly, however for better understanding of Docker I’d encourage you spend sometime with Docker help before you go ahead.

#Docker –help (two dashes)

Back to building container.

We will be using following command in Docker to build the container from image.

# docker run –rm –d –p 81:80 microsoft/iis

Options explained:

Run: tells Docker to run a command in new Docker container, so it creates a new container to execute all of the following commands on:

–rm: automatically removes the container after existing it and this is actually very important, otherwise it gets cached (Docker keeps it), it will lock up the name and consequently, you’re filling up the hard drive partition of your host

-d: run container in background and print container ID for information.

-p port:port: bind host port 81 to container port 80 so that we can access container outside container/host as well.

For example: if your host machine is having live IP 1.2.3.4 and you associate your host port 81 to container port 80, then using the following url you can access the site from anywhere on internet.

http://1.2.3.4:81

If your command ran successfully then you should be able to see output something similar to above screenshot, the long alpha numeric character are the container ID. You might need part of it in future use.

Secondly, you can also try to access the site outside your host machine if you wish to using the method explained above (http://1.2.3.4:81). Make sure you have allowed port 81 in your host machine firewall, otherwise it won’t work.

Alright, let’s get back to working with our new container, let’s get couple of windows feature enabled.

  1. Enable Windows Feature for ASP.net

So far we have only downloaded and build our container from image, now we have to go and enable some of the windows feature so as to work with IIS/Asp.net

So as to enable some of the windows feature within Container we will be using PowerShell, here’s the Docker command to get PowerShell extension on container.

Just before that make a note of your container ID, because using the container ID you will be able to run the PowerShell.

#docker ps

Now we will run the Docker command to get Powershell prompt for given container.

# docker exec –it 37a6 PowerShell

Before you start enabling windows feature it’s a good practice to see what’s already enabled and what needs to be, I like doing that, so that I can be sure that my commands are working fine and as per expectation.

#Get-Windowsfeature

Make a note of second column with heading as “Name”, there are two feature down the list (not visible in screenshot) which we will have to enable.

  1. NET-Framework-45-ASPNET
  2. Web-ASP-Net45

With PowerShell it’s quite easy to do so and here’s how it’s done.

#Install-windowsfeature net-framework-45-aspnet

Once started here’s what you will see as your first screen and the last screen shot shows you the confirmation.

Collecting data info.

Starting Installation.

Installation done.

Let’s quickly enable the other feature as well.

#Install-windowsfeature Web-Asp-Net45

In our case it’s already enabled so here’s what we get.

  1. Build Container from image (SQL Server)

So let’s quickly go and run the SQL Container in background, since we have already downloaded the image it should be pretty quick and easy, as we already know how to run the container from image.

# docker run –rm –d –p 1433:1433 –e sa_password=P@ssw0rd1234 –e ACCEPT_EULA=Y microsoft/mssql-server-windows-express

Few changes in this Docker command.

  1. We are exporting port 1433 of SQL Server for both host and container
  2. We are specifying sa password for sql
  3. We are accepting SQL end user license agreement, this is important without which SQL won’t start.

Here’s the interesting part:

To restore the Sitecore databases I do not use the command line, it’s much easier to connect your Sql Server Management Studio to container and easily restore the DB’s.

  1. Connecting SSMS to SQL Container

There are couple of ways you can restore your Sitecore DB’s to SQL container however for the learning purpose or maybe in production too, I find it easier to use SQL Server Management Studio connected to container or host machine.

There are two scenarios that I’ll show you to connect your SQL server management studio to either your container or your host machine.

Scenario 1:

  1. Your host machine is already having SSMS installed and you need to connect to your container from host.
  2. In this case find out the IP address of your container
  3. Ping back and forth between host machine and container
  4. If they are able to talk to each other than point your SSMS to container IP and use SA user/password details to login.
  5. To test connectivity login to container PowerShell and ping.

  1. Server name: IP address of your SQL container, you can find out couple of ways
    1. Connect to your SQL Server using Powershell (Refer to section 4) and run following command and it should let you know the local IP address of your container

# ipconfig

  1. Other method to find out the IP address is using following command

# docker inspect containerID

This command will show you the local IP address of container at the end of the output.

  1. If you are like me and would like to test SQL connectivity from different perspective then you can connect from your host machine IP address.

You can run your SSMS outside your host and point it to your live IP, make sure your firewall is allowing it go through, not a very secure way, nonetheless you can define security methods once you are logged in.

For the purpose of this demonstration, connecting to your Container from host machine is good enough.

Now as such you are connected let’s quickly attach your DB’s so that we are done with our SQL part and can move back to Sitecore setup in IIS Container.

Note: through SSMS server you are connecting to your SQL container which means using SSMS GUI when you try to restore the DB the SSMS will try to restore from SQL Container, it will look for those DBS MDF/LDF there in container, so make you copy db on to that SQL Server.

How to copy the DBs to SQL container ?

Let’s look at step 7 quickly.

  1. Copying DBs to SQL Container

You have to using Docker command to copy DBs from your local host machine to SQL Container so as to attach/restore those DBs.

#docker cp Databases grave_perlman:/c:\

Let’s understand this.

cp: simple copy parameter to pass on

Database: Name of the local folder on host machine which contains all the DBs.

grave:Perlman:/c:\ à grave:Perlman is the name of the container and later part is the path location where you want to copy your database folder.

Now you can go ahead use SSMS and restore the DBs, I am not going to show that part here. Once you have restore the DBs then you can move to step 8:

  1. Cleanup IIS Container.

Before you start copying the Sitecore application, I like getting rid of any default IIS setting/website.

So I’ll quickly run

# remove-website –Name “Default web site”

  1. Deploying Sitecore App to IIS Container.

Alright, so the Sitecore App fun begins here. Let’s copy the application folder to IIS container, create new website and define the permissions in coming few steps. There are other config changes that we will have to make to get the Sitecore up and running.

Let’s go step by step.

  1. Copy Sitecore app folder to inetpub path, not that you have to use this but that’s the default path.

Alright, so let’s understand what’s happening here:

  • My current location is c:\docker\sitecore 8.2\wwwroot
  • I am trying to copy everything wwwroot in current location to IIS container under c:\inetpub\wwwroot\
  • Hence the command #docker cp . jolly_stallman:/c:\inetpub\wwwroot\ will copy everything from my current location to remote location in IIS container.

Now let’s deploy new website in IIS using PowerShell

 #New-Website –Name “Sitecore” –Port 80 –PhyscialPath “C:\inetput\wwwroot” –ApplicationPool “.Net v4.5”

Great, so our application is deployed and if you have done your license file and network path setting correctly in Sitecore.config and Connectionstring.config then you should be able to see Sitecore default page.

#docker run –d –p 81:80 microsoft/iis

  1. There’s all possibility that you might be able to see the default page, might also be able to login to admin but when you try to open the control panel, you’ll come across this error.

In such situation we have to go and check the permission of folder where we have copied our Sitecore application.

So let’s first check, then set the policy and recheck the page for final confirmation.

  1. Go to location where you have deployed your application and run following command

#get-acl

 

  1. Set the permission for Network Service as follows and hopefully it should work for you, if doesn’t then it’s definitely to do with permission only.

$acl = Get-Acl c:\inetpub

$permission = “NETWORK SERVICE”,”FullControl”,”Allow”

$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission

$acl.SetAccessRule($accessRule)

$acl | Set-Acl c:\inetpub

 

 

 

 

Sitecore Admin Pages Compatibility

We as a Developer / Architect have always been loving the Sitecore Admin pages. Sitecore 8.2 introduced new admin pages that allows more control and functionalities. There are already very nice posts sharing information about those admin pages. So this post is not repetition of the knowledge already shared, reference to these blog posts are shared at the end of this post in Nice Reads section.

The motive here is to show Sitecore admin page’s that are released in 8.2 compatibility with earlier versions of Sitecore.

Sitecore

Admin Pages

7.0

7.1

7.2

7.5

8.0

8.1

Database Cleanup

Dependency Injection Configuration

x

x

x

x

x

x

EventQueue Statistics

Install language

x

x

x

x

x

Jobs Viewer

x

x

x

x

x

x

Logs

Package Item

PublishQueue statistics

Raw Search

Rebuild Key Behavior Cache

x

x

x

x

x

x

Security Tools

Sql Shell

Support Package

User Info

 

Why Compatibility?

While working on a project with Sitecore 8.1 – update-3 which went live just few weeks back before Sitecore 8.2 was released, we do not have option to upgrade the project with Sitecore 8.2 immediately. We also wanted to take advantage of the newly released Sitecore Admin pages and thought why can’t we have it for Sitecore 8.1?

 

Dumping the new admin pages into Sitecore 8.1 and adding a dll to the bin folder worked. A thought triggered to me and I went a step further and came up with the compatibility table for admin pages.

How to add Admin pages to earlier Sitecore Versions?

1)      Copy the admin pages mentioned in the compatibility table above.

2)      Copy Sitecore.ExperienceContentManagement.Administration.dll to the bin folder of Sitecore Website.

Wow! now you have new Sitecore admin pages from 8.2 till Sitecore 7.0.

Nice Reads

–          Sitecore Admin Pages Cheat Sheet – New Tools by Kamruz Jaman

–          New admin pages in Sitecore 8.2 by Ashish Bansal

–          Sitecore Admin Pages Cheat Sheet by Nikola Gotsev

Sitecore Bucket Structure – Item Field Value Based

Bucketing structure in Sitecore is a rule based where the default structure is derived from this setting

In this blog post I am going to share how we can create a bucket structure based on a field value of the item being created into a bucket as in consider News Articles or Products which has a field “Release Date” / “Posted Date”. It is good to create bucket structure based on these dates as compared to the created date of the item. Why? Content Authors can easily navigate through the bucket as they will have in hand information about when a new articles or product was released rather than remembering the created date of the Sitecore item.

So let’s buckle up seat belt and get hands on to see who we can achieve it,

Create a RuleAction class NewsReleaseDate that inherits from Sitecore.Buckets.Rules.Bucketing.RuleAction

public class NewsReleaseDateBasedPath : RuleAction

where T : BucketingRuleContext

{

#region ...Properties

public string Format { get; set; }

#endregion

#region ...Public Methods

public const string CST = "Central Standard Time";

public override void Apply(T ruleContext)

{

string format = this.Format;

if (string.IsNullOrEmpty(format))

{

//Take the format for creating bucket structure from a custom setting defined in a configuratoin file

format = Sitecore.Configuration.Settings.GetSetting("BucketConfiguration.NewsReleaseDatePathFormat", "yyyy/MM/dd");

}

DateTime releaseDate = ConvertToCst(DateTime.UtcNow);

//master database is referenced as command runs under Core DB context

var item = Sitecore.Data.Database.GetDatabase("master").GetItem(ruleContext.NewItemId);

//if item is null bucket structure will be creadetd in default format

//So pass the yyyy/MM/dd format

if (item == null)

{

ruleContext.ResolvedPath = releaseDate.ToString(format, Context.Culture);

return;

}

//AutoGenerated Model class for new article base

NewsArticleBaseItem newsItem = new NewsArticleBaseItem(item);

if (newsItem == null) return;

//Read the release date field value

if (newsItem.ReleaseDate!=null && !String.IsNullOrEmpty(newsItem.ReleaseDate.ToString()))

{

releaseDate = ConvertToCst(newsItem.ReleaseDate.DateTime.ToUniversalTime());

}

ruleContext.ResolvedPath = releaseDate.ToString(format, Context.Culture);

}

public DateTime ConvertToCst(DateTime utcDate)

{

return TimeZoneInfo.ConvertTimeFromUtc(utcDate, TimeZoneInfo.FindSystemTimeZoneById(CST));

}

#endregion

}

Now make few tweaks to Sitecore,
1) Create Element Folder at path /sitecore/system/Settings/Rules/Definitions/Elements/

NewsReleaseDateBucketing-1

2) Create a new action and update the type field to refer to the code class created earlier.

NewsReleaseDateBucketing-2

3) On Item Buckets Setting at path /sitecore/system/Settings/Buckets/Item Buckets Settings
Add the rule and action for your template

NewsReleaseDateBucketing-3

Click on the Edit Rule to add new rule and action for News Release Date

NewsReleaseDateBucketing-4

You will see Bucketing News Elements folder now available

NewsReleaseDateBucketing-5

Save the Item bucket settings item and we are all set to create a news article which will get stored initially with the created date as you have still not changed the valued of the release date of the new news article. Once the release date field is updated the news article will move to the bucket structure based on the value provided in the release date field.

That’s all for now I have to share keep Sitecoring and keep sharing because “Sharing is Caring”

Sitecore KeepAlive config patching

The heartbeat of Sitecore is it’s keepalive.aspx which keeps Sitecore alive and working continuously. There is a good KB article provided by sitecore “Automatic site recycling after the site has been idle for a period” to understand what a KeepAlive.aspx is all about. Another good read is “Keeping Sitecore alive”.

During an implementation in a scaled environment where we had separate CM, Publishing and Aggregation cum Processing server or multiple CD servers, we need to make sure all the environments are up and running all the time. Adding the url of keepalive.aspx on a server which hits itself does not works all the times as the website might be down due to apppool recycle or the machine itself got restarted and there has been no first request yet made to sitecore. The KeepAlive agent can run only if sitecore is up and a first request has already been made after the appPool recycle.

So one of the option is to make sitecore keealive agent hit url of other servers in the vicinity.

Here’s an example for CD servers hitting each other,


<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">

<sitecore>

<scheduling>

<agent patch:before="*[@type='Sitecore.Tasks.TaskDatabaseAgent']" name="CD01" type="Sitecore.Tasks.UrlAgent" method="Run" interval="00:15:00">

<param desc="url">http://CD1hostname/sitecore/service/keepalive.aspx</param>

<LogActivity>true</LogActivity>

</agent>

<agent patch:after="*[@name='CD01']" type="Sitecore.Tasks.UrlAgent" name="CD02" method="Run" interval="00:15:00">

<param desc="url">http://CD2hostname/sitecore/service/keepalive.aspx</param>

<LogActivity>true</LogActivity>

</agent>

</scheduling>

</sitecore>

</configuration>

Example of CM, Publishing Instance and Aggregation / Processing Server hitting each other,


<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">

<sitecore>

<scheduling>

<agent patch:before="*[@type='Sitecore.Tasks.TaskDatabaseAgent']" name="CM01" type="Sitecore.Tasks.UrlAgent" method="Run" interval="00:15:00">

<param desc="url">http://CM01hostname/sitecore/service/keepalive.aspx</param>

<LogActivity>true</LogActivity>

</agent>

<agent patch:after="*[@name='CM01']" type="Sitecore.Tasks.UrlAgent" name="CD02" method="Run" interval="00:15:00">

<param desc="url">http://CM02hostname/sitecore/service/keepalive.aspx</param>

<LogActivity>true</LogActivity>

</agent>

<agent patch:after="*[@name='CM02']" type="Sitecore.Tasks.UrlAgent" name="PI01" method="Run" interval="00:15:00">

<param desc="url">http://PI01hostname/sitecore/service/keepalive.aspx</param>

<LogActivity>true</LogActivity>

</agent>

<agent patch:after="*[@name='PI01']" type="Sitecore.Tasks.UrlAgent" name="AG01" method="Run" interval="00:15:00">

<param desc="url">http://AG01hostname/sitecore/service/keepalive.aspx</param>

<LogActivity>true</LogActivity>

</agent>

</scheduling>

</sitecore>

</configuration>

 

Let’s validate what we see at /sitecore/admin/showconfig.aspx

KeepAlive-1

You might be wondering why do I need to add “name” attribute to all the agent nodes and use patch:after / patch:before. Her’s the reason why, if we use the below patching sitecore only picks the last node and ignores patching of all the other agent.


<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">

<sitecore>

<scheduling>

<!-- The time format is "HH:MM:SS" -->

<agent type="Sitecore.Tasks.UrlAgent" method="Run" interval="00:05:00">

<!-- Replace [WEBSITE] with the appropriate domain -->

<param desc="url">http://CD01hostname/sitecore/service/keepalive.aspx</param>

<LogActivity>true</LogActivity>

</agent>

<agent type="Sitecore.Tasks.UrlAgent" method="Run" interval="00:05:00">

<!-- Replace [WEBSITE] with the appropriate domain -->

<param desc="url">http://CD02hostname/sitecore/service/keepalive.aspx</param>

<LogActivity>true</LogActivity>

</agent>

</scheduling>

</sitecore>

</configuration>

The resultant config patching if seen using /sitecore/admin/showconfig.aspx would reveal that it only picked the last node of the agent.
KeepAlive-2

Schedule Tasks not executing – Processing Server

Are you trying to figure out why a Sitecore Scheduled tasks in not getting executed? There are good amount of reasons for that and you should be looking at these links to get it working for you.

–          Scheduled task is not getting executed

–          Sitecore Schedule task not running

Oh! You already went through these links still the tasks are not running? Keep reading as I too was haunted with the issue.

Thank you Alok Kadudeshmukh (one of the brighter brains around me who blogs about Sitecore at Learn Sitecore Basics) to point me to the correct direction.

Reason why scheduled tasks were not executing

We configured a separate server for Aggregation and Processing and that server was down due to licensing issues. Drilling further into the details I figured out that the agents which are responsible for executing the scheduled tasks are now a part of Sitecore.Processing.config file.


        core
        /sitecore/system/tasks/schedules
        true
      
      
      
        master
        /sitecore/system/tasks/schedules
        true
       

Hence the scheduled tasks get executed on Processing server and not the Content Management server.

When did Sitecore change this?

Sitecore.Processing.config was introduced in Sitecore Experience Platform 8.0 rev. 150121 (8.0 Update-1)

SOLR DOWN DOMINO EFFECT ON SITECORE CD AND CM SERVER

Is your Sitecore Content Management and Content Delivery Server down? Well the reason being you have configured your Sitecore installation to use SOLR as a search platform and the SOLR server itself is down. What? Seriously…. SOLR server triggers a DOMINO effect on the CM and CD servers if it is down. Something we would not want to happen in a production environment. We faced this issue few days back on one of our QA environment. The best way out was to contact Sitecore Support who then reported it as a bug and provided a solution which I am sharing it here.

solr down

Follow below steps and that would be it all,

  1. Copy ‘Sitecore.Support.391039.dll’ assembly to the ‘\bin’ folder.
  2. Copy ‘Sitecore.Support.391039.config’ file to the ‘\App_Config\Include’ folder.
  3. Update all the Solr index configuration files (for each Solr index):
<configuration type="Sitecore.ContentSearch.ContentSearchConfiguration, Sitecore.ContentSearch">

<indexes hint="list:AddIndex">

. . .

<index id="<index_name>" type="Sitecore.Support.ContentSearch.SolrProvider.SolrSearchIndex, Sitecore.Support.391039">

You will still need to work upon bringing up the SOLR server. This is how this fix works, it checks if Solr is up on Sitecore start. If no, it skips indexes initializing. However, it may lead to exceptions in log files and inconsistencies while working with Sitecore when Solr is down.

Also, there is an agent defined in the ‘Sitecore.Support.391039.config’ that checks and logs the status of Solr connection every minute (interval value should be changed if needed).

If the Solr connection is restored — indexes will be initialized, the corresponding message will be logged and the search and indexing related functionality will work fine.

Making of Sitecore Community Project Templates

Writing this blog post is like sharing an experience about Making a Hollywood movie or talking about behind the scenes for a Hollywood movie, working with Sitecore is a different experience and that is what I am sharing here and as the title says “Making of Sitecore Community Project Templates” is what this post is all about.

This all started with one bright idea from Varun Shringarpure and he was the lone warrior while he initiated his idea into a working copy. I saw him working on this great idea and suggested to help, Varun had some different idea and wanted to work alone. A couple of weeks later Varun wanted me to be part of it and he shared his experience and findings with me. Finally working together for another couple of weeks we were able to release it for Sitecore Community.

Varun started creating the various projects and their structure he followed the approach suggested at Side Waffel. There were lot of configuration needed to be done for us to get the templates generated based on Side Waffel. Later on digging into further it was discovered that there is much easier way to create the visual studio templates and we completely dropped the idea of using Side Waffel.

The approach we followed was to export each visual studio project as a template, zip the exported template and add them to the VSIX project solution. A manual task but much simpler and easier task as compared to Side Waffel configuration. Also it was difficult to troubleshoot the configurations that we created for Side Waffel.

Varun announced the release of Sitecore Community Project Templates, more on the documentation and source can be found by following the link.

If you are interested in knowing more about how it is all setup and few tips if you want to come up with your own visual studio templates keeping reading further as this will help to get you kick-started.

How is it all setup?

Currently we have setup three solutions,

1)      Sitecore.Community.Templates.VSIX

Solution for creating the VSIX file that references templates from other projects.

2)      SitecoreMVCStarterKit

Solution containing a bunch of projects required to setup a Sitecore MVC solution.

3)      SitecoreWebformsStarterKit

Solution containing a bunch of projects required to setup a Sitecore Webforms solution.

Projects

Projects

Creating Visual Studio Project Templates

Firstly we created projects structure for all projects Sitecore.MVC.Extension, Sitecore.MVC.IOC, Sitecore.MVC.Models, Sitecore.MVC.Services, Sitecore.MVC.TDS.Core, Sitecore.MVC.TDS.Master, Sitecore.MVC.Web and Sitecore.MVC.Web.UnitTest. For the projects we Added and removed files, installed nugets and such kind of stuff.

Solution Structure

Solution Structure

Once we had our projects setup correctly we did Export Template for each of the projects in the solution which in the background created a .zip file for being used as a visual studio project template.

 

Export Template

Export Template

Export Template Wizard

Export Template Wizard

Creating VSIX Project

Next step was to create a VSIX project that would help us to deliver the Sitecore Community Project Templates.

 

Creating VSIX Project

Creating VSIX Project

This is how we setup the VSIX project,

 

VSIX project setup

VSIX project setup

You might think why did we created the structure ProjectTemplates/CSharp/Sitecore Community and then added all our project template zips. There’s a reason to do so as we wanted the templates to appear under Templates-> Visual C# and just above Sitecore Rocks.

New Project Dialog

New Project Dialog

Why it’s named Sitecore Community Project Templates?

We discussed few different names like Sitecore Templates (we dropped this name as would like Sitecore owns it), Sitecore R&D Templates, Sitecore Project Templates then came the final name Sitecore Community Project Templates as it was dedicated to the so awesome and vibrant Sitecore Community and anyone can contribute to it.

Releasing Sitecore Community Project Templates

Few things that Varun worked upon for releasing it to the Visual Studio Gallery were creating Readme.txt and it’s content, License.txt, Icons, pushing to github, Adding to Sitecore Marketplace and finally sending it to Visual Studio Gallery.

Tips

1)      Amending project zip files with $safeprojectname$ helped us to achieve the custom project name that one can specify during new project creation.

$projectname$

$projectname$

Watch out this link for more token replacements at Template Parameters

2)      For your project template to appear at a required location in the New Project Dialogue, the VSIX project should reflect the same folder structure and the zip of the project template should then be added to the required folder.

3)      In order to support your VSIX for different versions of Visual Studio, open .vsixmanifest file -> Install Targets and Edit or Add New entry for the Visual Studio Version.

More details for inclusion and exclusion of visual studio versions can be found at VSIX Manifest Designer

Install Targets

Install Targets

4)      MyTemplate.vstemplate

5)      Unzip exported template and rename project with $safeprojectname$

Nice Reads

–          How to Create a Visual Studio 2012 Project Template: Introduction

–          Visual Studio Templates

–          Creating Project and Item Templates

–          Custom Visual Studio Project Templates

What a nice journey to cherish for, Thanks Varun for this endeavor and Pranav Geria for helping out with the Sitecore Icons.

Sitecore Support Package Generator

I recently updated my machine to Windows 10 and didn’t face any problems running all of my installed programs. Exceptions are always expected in a developer’s life so my Sitecore Support Package Generator was not running and every time I try to execute it, it had some issues and died showing me the below screen,

Sitecore Support Package Generator

SSPG

Checking the Event viewer and the SSPG logs files it revealed that it has some access problems with Optimization.Suggestion.Count.config file and was throwing below error.

ERROR SSPG.MainWindow - Couldn't clear IsolatedStorage on startup.
System.UnauthorizedAccessException: Access to the path 'Optimization.SuggestedTests.Count.config' is denied.
   at System.IO.Directory.DeleteHelper(String fullPath, String userPath, Boolean recursive)
   at System.IO.Directory.Delete(String fullPath, String userPath, Boolean recursive)
   at SSPG.Core.FileSystem.RoamingFileSystemProvider.Clear()
   at SSPG.MainWindow.MainWindow_Loaded(Object sender, RoutedEventArgs e)

You can find the SSPG.log file at C:\Users\MyUser\AppData\Roaming\Sitecore\SSPG\Files\Logs.

I then started two help channels and tweeted Alen Pelin who is the contributor to this tool, he asked me to check if I have .NET framework 3.5 installed which I was having. Our small twitter conversation.

The other channels was with the Mighty Ones, guessing who I am referring to? Yes they are Sitecore Support Team. Lilia Silvestrova asked me to delete the files present under C:\Users\<User name>\AppData\Roaming\Sitecore\SSPG\Files\ and yes that is the trick.

So if you are facing such issue I would suggest to check SSPG.Log file and if this blog post resolves your issue you should be thankful to the Mighty Ones.

Sitecore Faceting – Removing Global And Displaying Customs Facets: Part-4

We might want to have more control over the facets that are being displayed on the Search UI. When custom facets are defined they are mingled with the global ones and it is difficult to differentiate between them on the Search UI. The global facets are interpreted as sitecore default facets, but they are global ones meaning they appear on all bucket items. If you are following my Sitecore Faceting series then yes this is the fourth one talking about removing the global facets and displaying the ones that you choose.

Below image shows the custom facets mingled with global facets,

Sitecore Custom Facets Mingled

Sitecore Custom Facets Mingled

Let’s consider a hypothetical case where we need to display custom facets(News Release Date Range and News Category) related to our News bucket and which the content authors can take advantage of instead of using the global ones (Template, Language, Author, Date Range) which might not make sense to them.

The logic that we applied was to display only those facets which are selected on the “__facets” field available in the Indexing section.

Custom_Facets_-_Selected

Custom Facets Selected

 

We will take the same path as mentioned in my third post Sitecore Faceting – Ordering Sitecore Global / Default Facets: Part-3, let’s define a processor that will be patched after “Sitecore.Buckets.Pipelines.Search.GetFacets.RunFacets” processor. This processor can be found at \App_Config\Include\Sitecore.Bucket.config.

  
     
      

Now let’s writeup a class that will inherit from Sitecore.Buckets.Pipelines.Search.GetFacets.GetFacetsProcessor

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sitecore.Buckets.Pipelines.Search.GetFacets;
using Sitecore.ContentSearch.SearchTypes;
using Sitecore.Data.Items;
using Sitecore.Data.Fields;

namespace sc80up2.SitecoreEntities.Facet
{
    public class FacetSortOrder : GetFacetsProcessor
    {
        #region ...Public Methods
        
        public override void Process(GetFacetsArgs args)
        {
            if (args.Result != null)
            {
                //Get bucket item
                Item bucketItem = Sitecore.Data.Database.GetDatabase("master").GetItem(args.LocationFilter);
                if (bucketItem == null) return;

                //Facet field
                MultilistField fieldfacet = bucketItem.Fields["__facets"];
                if (fieldfacet == null) return;

                List<string> lstCustomFacets= new List<string>();
                //Get selected facet on a bucket
                foreach (var selectedfacet in fieldfacet.GetItems())
                {
                    if (selectedfacet.TemplateID.ToString() == TemplateIDs.FacetTemplateID)
                    {
                        //get facet field id, can be stored by comma seperated
                        if (selectedfacet.Fields["parameters"] != null && !string.IsNullOrEmpty(selectedfacet.Fields["parameters"].Value))
                        {
                            foreach (string str in selectedfacet.Fields["parameters"].Value.Split(','))
                            {
                                lstCustomFacets.Add(str);
                            }
                        }
                    }
                }

                IEnumerable<IEnumerable<SitecoreUIFacet>> facetListLists = (IEnumerable<IEnumerable<SitecoreUIFacet>>)args.Result;
                List<IEnumerable<SitecoreUIFacet>> newFacetListLists = new List<IEnumerable<SitecoreUIFacet>>();

                foreach (IEnumerable<SitecoreUIFacet> facetList in facetListLists)
                {
                    foreach (SitecoreUIFacet facet in facetList.ToList())
                    {
                        foreach (string str in lstCustomFacets)
                        {
                            if (facet.ID.ToLower() == str.ToLower())
                            {
                                newFacetListLists.Add(facetList);
                            }
                        }
                    }
                }
                args.Result = newFacetListLists.Distinct().OrderBy(fl => (fl.Count() > 0) ? fl.FirstOrDefault().Type : "");
            }
        }
        #endregion

    }

    public static class TemplateIDs
    {
        public const string FacetTemplateID = "{5C125B6D-C481-4C24-B5B9-9A23FE396BF0}";
    }
}

Code Explanation

Selected facets are first stored in a list (lstCustomFacets), these facets are read from the “__faceting” field. Iterate through all the facets that are passed to the processor via args and trying to match the selected one stored in the lstCustomFacets and return them from the method.
That was simple isn’t it? So the crux of this post is you can have your own processor for faceting.

Below image shows the final outcome where only selected (can be custom or global ones) being displayed on Search UI.

 

Outcome Sitecore Custom Facets

Outcome – Sitecore Custom Facets

Keep faceting and Sitecoring and yes do not forget to share your Sitecore Knowledge and learnings with the Sitecore Community.

Sitecore Faceting – Ordering Sitecore Global / Default Facets: Part-3

The third in a row about sitecore faceting and yet not the last one, I am planning to write two more posts on faceting after this. If you want to refer to the previous two posts below are the links,

1) Sitecore Faceting Novice Learnings: Part-1
2) Sitecore Faceting-List and Link Type Fields:Part-2

In this post I will share how can we Sort the Global (Default) facets available on the Search UI. Sitecore has most of such things either content managed (via Core Database) or via settings, sadly for ordering of global facets you will have to write your own processor. But it’s a simple piece of code so let’s check how to achieve it.

Below is the list and image of the default facets namely,

– Template
– Language
– Author
– Date Range

Sitecore Global Facets

which are available on any sitecore bucket item.

Sitecore uses a processor, “Sitecore.Buckets.Pipelines.Search.GetFacets.RunFacets” to get and display the facets. This processor can be found at \App_Config\Include\Sitecore.Bucket.config.

  
     
      

Now let’s writeup a class for a processor that will inherit from Sitecore.Buckets.Pipelines.Search.GetFacets.GetFacetsProcessor

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sitecore.Buckets.Pipelines.Search.GetFacets;
using Sitecore.ContentSearch.SearchTypes;
using Sitecore.Data.Items;
using Sitecore.Data.Fields;

namespace sc80up2.SitecoreEntities.Facet
{
    public class FacetSortOrder : GetFacetsProcessor
    {
        #region ...Public Methods
        public override void Process(GetFacetsArgs args)
        {
            if (args.Result != null)
            {
                IEnumerable < IEnumerable < SitecoreUIFacet >  > facetListLists = (IEnumerable < IEnumerable < SitecoreUIFacet >  > )args.Result;
                IEnumerable < IEnumerable < SitecoreUIFacet >  >  newFacetListLists = facetListLists.OrderBy(fl => (fl.Count()  >  0) ? fl.FirstOrDefault().DisplayName : "");                
                args.Result = newFacetListLists;
            }
        }
        #endregion
}
}

To summarize the logic of above code, we check if there is a item attached to a facet (fl.Count() > 0) and then sort it based on “DisplayName” .

Add the FacetSortOrder processor after Sitecore’s RunFacets processor. So if you hit /sitecore/admin/showconfig.aspx it should reflect as follow,

Bucket-ShowConfig

Below is an xml that you can place in a Facet.config file for xml patching, make sure you put the Facet.config file in a folder under App_config/Include folder otherwise your processor will not get appended as Sitecore.Bucket.config falls after it chronologically.



  
    
      
        
      
    

  

Below image confirms the changed ordering of Sitecore’s Facets.

Global-Facets-Sorted

In my next post for faceting I will be writing about removing the global facets from the Search UI and displaying only the customs one as selected by a user.

Keep Sharing, Keep Caring!