Thursday, 4 October 2018

Sitecore PowerShell Extensions - Custom function

Last time I had to prepare a resolution for making changes on the existing content after the deployment. We were adding new components to Standard Values of the page and they had to be configured properly on the content item by setting data source. Change like this couldn’t be synchronised by Unicorn as the page content item was one-time deployment only item (NewItemOnlyEvaluator) and setting data source straight on Standard Value is a really bad idea!

We had three options there:
  1. Set data source manually after synch
  2. Create a Sitecore content package with prepared changes
  3. Prepare Sitecore PowerShell script that will automate the changes we need
As doing things manually is always error-prone and package could overwrite some content changes, we decided to go for third option – PowerShell scripting!

First of all, I needed a way for setting data source on rendering that already exists on a page content item. I found there is not such specific function in SPE core so I wrote a custom one.





To create a custom SPE function we need to have new module created. The easiest it will be by using wizard:





We will need Shared Functions integration, so we can select it just while creating the module.



It can be done after as well just by creating scripts library called Functions. Here's link to documentation: https://doc.sitecorepowershell.com/modules/integration-points/functions

After this we will have our module folder with Functions folder inside. There we will create our custom shared function.


I have named it Set-RenderingDatasource.  Body of the function should be placed inside field described as Script body - Source code written in PowerShell. That's the function I've shown earlier. 

Next thing will be create new PowerShell Script Library item, just on the same level as Functions item. I named it as After Deploy Scripts and created one new script inside - Apply-Datasources:



This script will use our custom shared function to apply datasources to items. Here is the code:



First crucial thing is to import our shared function

Import-Function -Name Set-RenderingDatasource

Then I have added a flag for rolling back changes if needed. That's a subject of decision how we should do the rollback. For this example rollback will be simply setting empty datasources to that items - as they are empty at beginning.

Under I've added variables that will contain ID of the page where we want make our updates, references to rendering items we want to set datasource and ids of datasources. All those things should be known, as we are assuming they will be deployed by, e.g. unicorn (new renderings and datasources - one time deployment) or they are in the content already for some time - the page we want to update.

Then, based on the rollback flag, I'm using our imported function to set up datasources

Set-RenderingDatasource -Page $updatedPage -Rendering $modalRendering -DatasourceId $modalDatasourceId -Device Default -Publish

 Let's roll now! Let's edit our script in ISE!

And run it to see the result:


It's good to log the things script does and say something at the end of execution.

Done! I hope that was interesting lesson of using Sitecore PowerShell to apply changes after deployments. Enjoy!

Saturday, 19 May 2018

SIF Sitecore 9 installation issue

A couple days ago I came across a problem while installing Sitecore 9.0.2 using SIF:

.Net SqlClient Data Provider: Msg 12809, Level 16, State 1, Line 5
You must remove all users with password before setting the containment property to NONE.

I was using MS SQL Server 2016 (13.0.4001.0). After opening Management Studio I realized that some databases were created already because at first run my script went to some point then failed. So the easy fix go go through this issue was just to remove these databases as they were only partially completed.

Saturday, 7 April 2018

Configuring Unicorn on Helix project

Setting up synchronization in Helix based solution is one of the most crucial things. With proper configuration all new projects will be easy to maintain and predictable. In this short post I will describe basics of configuring Unicorn.

Helix structure configuration

First of all we should have our Foundation.Serialization project configured. In this project we should install Unicorn - from the nuget feed.

We should have whole Helix items structure configuration here and this one should be synched as the first in the queue.


There is unicornSourceFolder variable which should be pointing /src/ folder of our app code. We can specify it for example in some patch in Project.Common project.


 

Abstract configurations

It is really good idea to have some basic configuration we can inherit from. It can be placed in for example Foundation.Serialization project.





Then we can use this abstract configuration in our projects




We do it by using extends parameter

extends="SitecoreCoffee.Base"

If our project is relying on some other, e.g. from Foundation layer, then we should specify it by dependencies parameter. Here it depends on whole Foundation layer by using asterix

dependencies="Foundation.*"

...but it could be as well list of comma-delimited configuration names

dependencies="Foundation.Search,Foundation.Extensions"

As I mentioned earlier, Foundation.Serialization configuration should be synchronized as first, so the our patch should go after that one:

patch:after="configuration[@name='Foundation.Serialization']"

Don't forget to put descriptive name and description attributes too.

Project-specific configurations

In Helix every project which requires some serialized Sitecore items, should have it's own Unicorn configuration. Above we created Feature.Search project configuration and there we put only project-specific items in the predicate

<predicate type="Unicorn.Predicates.SerializationPresetPredicate, Unicorn" singleInstance="true">
    <include name="SitecoreCoffee.Feature.Search.Templates" database="master" path="/sitecore/templates/Feature/Search" />
    <include name="SitecoreCoffee.Feature.Search.Renderings" database="master" path="/sitecore/layout/renderings/Feature/Search" />
</predicate>

Even if you're not using Helix you should consider creating several small configurations instead of one monolithic. It will be easier to synch only specific ones in Unicorn synch panel.





Monday, 12 March 2018

Using SitecoreController to change page startup

A few days ago I had to implement functionality that will check if external e-commerce system is healthy before any further page processing. I decided to prepare maintenance page in Sitecore and switch to it if there are problems with API. Here the problem appeared - how to make it clean and use already DIed services there?

I could use httpBeginRequest pipeline and put my processor just after Context.Item is resolved. Sounds alright but I wanted to avoid adding unnecessary processors and patches as much as I could.

Then I found really interesting post written by Marek Musielak:

Sitecore Item Controller - the hidden gem of Sitecore MVC https://www.skillcore.net/sitecore/sitecore-item-controller-and-page-rules
I decided I will go with this approach and utilize SitecoreController. Marek described precisely how to set up that new controller and where to refere it in template. Here I will describe how I used it.

Controller setup

My controller will be responsible for checking external commerce health before any rendering and API call is made. It should extend SitecoreController that exists in Sitecore.Mvc.Controllers namespace (Sitecore.Mvc assembly). I'm using there two services - one that wraps context item switching and second one for API calls. I found out that there should be only one parameterless constructor in this controller. Unfortunately then you can't inject services properly - only way is to use service locator antipattern!

Further, you should override Index() method and do your stuff there. I'm calling heartbeat service and if system is dead I'm switching context page item to maintenance page. I'm checking as well if maintenance mode was explicitly set in site settings item. And if we are in Experience Editor we don't want this switching to happen, so I'm checking for that option as well. At the end we need to call base Index() method.

Be aware: heartbeat check action must be fast as Flash or even faster. You don't want to add much more rendering time to page. Sometimes it's even better to do that check on client side, but that's more SPA approach. Just make sure it finishes in desired period of time and even drop the execution if it takes more! You can use async calls and cancellation tokens - here I found some nice article by Dave Paquette you can begin with, but that's surely something outside Sitecore: https://www.davepaquette.com/archive/2015/07/19/cancelling-long-running-queries-in-asp-net-mvc-and-web-api.aspx

Page template setup

As Marek described, we need to bind our controller with template's Standard Values to make it work.

Template using SitecoreController

And that's it, this functionality will run before any rendering start to be processed.


Whole implementation is available on my Helix examples and experiments project on GitHub:

ReoKzK/SitecoreCoffee https://github.com/ReoKzK/SitecoreCoffee/tree/master/src/Feature/Commerce/code

Monday, 27 November 2017

Custom Remote Events for Cache Rebuild mechanism

A couple of months ago I had to jump into a project for a while and I came across a problem with refreshing custom cache on content delivery servers. There was a custom cache mechanism that was running on each instance separately. It was relying on items based on specific template, every time one of these items was updated or created/removed, cache should be updated. I found out that the best way to refresh that is to prepare custom remote event.

The problem

I had to make sure that after publishing specific items, cache will be rebuilt. My first thought was to use publish:end event, so I could check if one of that items was published. But here the problem with checking came out. There is a parameter of class Publisher that is put to EventArgs on position 0. Unfortunately, it doesn't contain any list of published items, just the root item:

publisher.Options.RootItem

...or maybe fortunately, for huge amount of items published :) Anyway, if we are relying only on this property, we need to go through all items recursively to find the specific one:

publisher.Options.RootItem.Axes.GetDescendants().Any(x => x.TemplateID.ToString() == SpecificTemplateId)
You might see that here, on some publish events, using Axes and GetDescendants() is not very good idea (most of times it's not!). And we need to keep in mind that RootItem will be null if user ran full site publish...

Improve the performance

Okay, maybe some better ideas? Yes. There is an event called publish:itemProcessed that is fired every time an item is processed during publish. We can use it to find if currently processed item is based on specific template and then set some flag.


Flag used here will be static property of the event handler class:

/// <summary>
/// Static flag that indicates if any specific item was published
/// </summary>
protected static bool SpecificItemWasPublished = false;


After all items have been processed, the publish:end event will be raised. Here we can check if flag is set to true and run cache refresh action. This solution will work gracefully on single server application, but on CM/CD separated instances it will work only on Content Management server. We need to work out a better solution using custom remote events.

Using custom remote event

Custom remote event will be the best option to solve this problem. Our cache refreshing system will work like this
  1. While publishing we check if specific item is published - on publish:itemProcessed event. If there is one, we set up the flag.
  2. When publishing finishes (publish:end) we check the flag. If flag is set up, we raise cache rebuild event
  3. Cache rebuild event will raise remote event for cache rebuild.
Using this approach all hard work, like checking specific item published, will take place on the local server, remote servers will just do what first told them. It all works using EventQueue.

1. Class for remote event representation
First thing that we will need will be class that represents an event. There is no base class to derive or interface to implement, it can be plain class but members need to be serializable. In our example we will have a boolean flag that will indicate if full rebuild is needed.

2. Event raiser class
Next we will need a handy class that will be able to raise the event locally and add request to Event Queue for raising it on remote servers.

3. Pipeline processor for initializing event subscription
We need to create a pipeline processor that will subscribe to those remote events.

4. Event handlers class
This class will contain our event handlers. As described earlier OnItemProcessed will check if currently processed item is based on specific template and set up flag if found. OnPublishEnd will check that flag when publishing is finished and raise cache rebuild event using prepared earlier event raiser. Last event handler OnCustomCacheRebuild will do the actual call to index mechanism for cache rebuild.

5. Sitecore config patch
At last we have to bind all event handlers and processors into Sitecore. This Sitecore config patch does that:
 
And that's all, with all these parts our system is ready to use.


Testing solution 

To test our solution we need to set up two Sitecore instances on our local machine, first for CM role, latter for CD role.They need to have different InstanceName properties set in App_Config\Include\ScalabilitySettings.config.

<setting name="InstanceName">
  <patch:attribute name="value">SitecoreCoffeeCM</patch:attribute>
</setting> 

As well those two instances need to use the same databases.

After setting up those instances and publishing item based on specified template on CM server, there will be new record created in Core database in EventQueue table:

Id:            C3386293-8E1B-4EBB-A490-D57DC663E61B
EventType:     SitecoreCoffee.Foundation.RemoteEvents.Events.CacheRebuildEvent, SitecoreCoffee.Foundation.RemoteEvents, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
InstanceType:  SitecoreCoffee.Foundation.RemoteEvents.Events.CacheRebuildEvent, SitecoreCoffee.Foundation.RemoteEvents, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
InstanceData:  {"FullRebuild":true}
InstanceName:  SitecoreCoffeeCM
RaiseLocally:  0
RaiseGlobally: 1
UserName:      sitecore\admin
Stamp:         0x0000000000032E9E
Created:       2017-11-21 23:20:01.267

And as well on those two instances cache will be rebuilt:

ManagedPoolThread #2 23:20:01 INFO  CacheRebuildEventHandler: Rebuilding the cache.
ManagedPoolThread #2 23:20:01 INFO  CacheRebuildService: Cache rebuilt (full: True)
ManagedPoolThread #2 23:20:01 INFO  CacheRebuildEventHandler: Cache rebuilt in 0 seconds.

Summary

Solution described above explains how to use custom events in Sitecore to create efficient mechanism that will rebuild cache on local and remote servers, when specific item is published. It can be used to perform any other type of action that will require additional activity taken on remote servers.

Full code is available in my Helix solution here: https://github.com/ReoKzK/SitecoreCoffee

Happy Sitecoring!

Friday, 31 March 2017

Translating Sitecore Client

Translating Sitecore Client to another language is not so tough issue. Sitecore comes with a pretty clean solution here. In this article we will use Sitecore 8.2 Update-2.

There are some ready-to-install client translations at Sitecore download site (section "Client Translations"): https://dev.sitecore.net/Downloads/Sitecore_Experience_Platform/82/Sitecore_Experience_Platform_82_Update2.aspx. The choice is not too big, there are only for Danish, German and Japanese. Fortunately, we can create our own client translation.

Preparation

First of all, we need to install language in Sitecore on specific database. We will start with core, where most of our client translations are kept. Let's go to Desktop and switch to Core db. After that let's go to Control Panel. Here we should add new language and set language code for it. In my example I will add Polish language

Sitecore - Control Panel - Add new Language

I'm leaving all steps with default values, so my Polish language code will be "pl-PL". After that we can check it in Content Editor under /sitecore/system/Languages:

Sitecore - Content Editor - Languages

Exporting language file

At this point we need to have a file that will be our source for translation. In other words, we need to export language file from one of existing languages in Sitecore. To achieve that, let's go again to Control Panel. There should be Core database selected as earlier. In Control Panel click on the Export languages and select the language you want to base your translation on:

Sitecore - Control Panel - Export language

After clicking next, you can choose which branch to export. For making translations select sitecore root item:

Sitecore - Control Panel - Export language - select item

After that you will be prompted for location of generated file. In the end Sitecore will process all items and language xml file will be generated.

Preparing language file

Let's take a quick glimpse to the exported language file. You can see that this is just xml file with <phrase> elements. Each that element represents single field of item and contains attributes like:
  • path - path of the item
  • itemid - id of the item
  • key - key name of field
  • fieldid - name of the field
  • updated - date of field update
Inside <phrase>, there is another xml element named after the language code. It surrounds the field value we will translate.

Sitecore - Exported language file

Next thing is to replace language elements with our destination language. In my case it will be pl-PL. The easiest and fastest way will be use some text editor and globally replace:
  • <en> to  <pl-PL>
  • </en> to  </pl-PL>

Replace globally


And that's it! File is prepared for sending to some translation services. They just need to know what they have to translate:

<phrase path="/sitecore/client/Speak/Layouts/Renderings/Common" key="Common" itemid="{1F2A25A4-A532-4E9B-9942-1FA6F10F61B0}" fieldid="__Short description" updated="20130814T120321Z">
    <pl-PL>Common controls like buttons, input fields, and lists.</pl-PL>
 </phrase>
In my case it should be translated to:

<phrase path="/sitecore/client/Speak/Layouts/Renderings/Common" key="Common" itemid="{1F2A25A4-A532-4E9B-9942-1FA6F10F61B0}" fieldid="__Short description" updated="20130814T120321Z">
    <pl-PL>Wspólne kontrolki, takie jak przyciski, pola wprowadzania, czy listy.</pl-PL>
 </phrase>

Just trust me ;)

Importing prepared language file

As we have our language file translated we can import it to Sitecore. Before that, it is highly recommended to backup processed database - in our case Core database. After our backup is done we can import our translation file. To achieve that let's go to Control Panel and click Import languages:

Sitecore - Control Panel - Import languages

Then we need to upload our prepared xml file (or put it somewhere in webroot of our Sitecore). After selecting the file, Sitecore should recognize what language we are importing.

Sitecore - Control Panel - Import languages

Then we need to select the database which items we were translating - for us it is the Core database.

Sitecore - Control Panel - Import languages

After a while import will be finished.

Configuring Sitecore client language

After the import we can use our language. One of the ways is to set globally Sitecore client language in the Control Panel. In the My settings section, there is a Region and languages options link. It opens window where we can set viewing formats and Display language:

Sitecore - Control Panel - Region and languages options

After setting it, you will be prompted to refresh browser window - and you can enjoy the translated Sitecore! More info on installing translations can be found on Sitecore documentation: https://dev.sitecore.net/Downloads/Sitecore_Experience_Platform/82/~/link.aspx?_id=D72CBF8CE581436CBBCAEE896C8646F7&_z=z

Another way is to set the language just for the user. We can find it in the User Manager by clicking Edit on selected user and setting Client language on Language settings tab.

Sitecore - User Manager - Language settings

Translating master database

We were translating only core items, but we can also translate master database items. I might not have sense for all Sitecore items, but for short descriptions of main items like content, layouts, templates, etc.


Happy translating!