Showing posts with label sitecore. Show all posts
Showing posts with label sitecore. Show all posts

Friday, 22 January 2021

Fortis - Unable to find template error

I was upgrading my client's Sitecore instance lately and at some point I've spotted this issue with Fortis:

Fortis | Unable to find template for MyProject.Model.IMyTemplate

After doing some digging in the internet I couldn't find out what was causing it. I decided to compare all config files and I've spotted that my Web.config on new environment is missing these bits:

Declaration of Fortis section in  <configSections>

<configSections>
...
    <section name="fortis" type="System.Configuration.NameValueSectionHandler" />
...
</configSections>

...and the actual section my Model project assembly reference:

<fortis>
    <add key="assembly" value="MyProject.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</fortis>


With these added it all worked. I hope this will help somebody!

Monday, 25 November 2019

Azure Search - Improving speed while getting facets only

If you want to get only facets from your search query - for some purposes as preparing filters list - remember to set limit to 0.

For example like this:

var facetResults = searchQuery.Take(0).GetFacets();

.Take(0) translates to $top=0 in Azure Search query. That will return something like this:

{
    "@odata.context": "https://{YOUR-SEARCH-SERVICE}.search.windows.net/indexes('my-index-web-index-99')/$metadata#docs(*)",
    "@odata.count": 857,
    "@search.facets": {
        "contenttype_facet_1": [
            {
                "count": 146,
                "value": "Content Type 1"
            },
            {
                "count": 134,
                "value": "Content Type 2"
            },
            {
                "count": 118,
                "value": "Content Type 3"
            },
        ],
        "topic_facet_1": [
            {
                "count": 176,
                "value": "Topic 1"
            },
            {
                "count": 70,
                "value": "Topic 2"
            },
            {
                "count": 62,
                "value": "Topic 3"
            },
            {
                "count": 52,
                "value": "Topic 4"
            }
        ],
        "subtopic_facet_1": [
            {
                "count": 49,
                "value": "Subtopic 1"
            },
            {
                "count": 32,
                "value": "Subtopic 2"
            }
        ]
    },
    "value": []
}

So only essential statistics data without actual results

Friday, 1 November 2019

RenderField processor for tooltip links

A couple of days ago I've got a requirement from my client about inline tooltips functionality. Those tooltips needed to have formatted text so RTE field should be used.


I started with creating repository folder for those tooltips




Template for tooltip item was really simple, just one RTE field



To have a link which will open a tooltip I had to write a pipeline processor which
  • Will read RTE field content
  • Find internal links
  • Check if they point to tooltip item
    • If yes then process link



This pipeline processor must be run after field value is read, so after
Sitecore.Pipelines.RenderField.GetFieldValue, Sitecore.Kernel

Tuesday, 19 February 2019

Azure Search provider - Results ordering issue

I was implementing Azure Search lately into my last project and I found some strange issue in the provider (we're using Sitecore 9.0.2 rev. 180604). When I wanted to order my search results using Linq like this:

searchQuery.OrderBy(x => x.JobRole).ThenBy(x => x.LastName)

it was translated to:

$orderby=last_name_s,job_role_i

So it looks like it's taking argument of ThenBy as first and OrderBy as the second. Inverting parameters:

searchQuery.OrderBy(x => x.LastName).ThenBy(x => x.JobRole)

gave me desired query:

$orderby=job_role_i,last_name_s

And search result as well were sorted like I wanted.

Azure Search documentation states nothing unusual, but I had to confirm this:


$orderby=[string] (optional)

A list of comma-separated expressions to sort the results by. When calling via POST, this parameter is named orderby instead of $orderby. Each expression can be either a field name or a call to the geo.distance() function. Each expression can be followed by asc to indicate ascending, and desc to indicate descending. The default is ascending order. Ties will be broken by the match scores of documents. If no $orderby is specified, the default sort order is descending by document match score. There is a limit of 32 clauses for $orderby.
https://docs.microsoft.com/en-us/rest/api/searchservice/search-documents#orderbystring-optional

I have raised an issue on Sitecore Helpdesk and guys from support checked and reported the issue. It has a reference number 309333. Current resolution is to invert parameters like I did.


Sunday, 2 December 2018

Unicorn - disabling automatic items serialization

I've come across a problem with Unicorn last time. We deployed to QA environment and a couple days later testers started to see issues like this:

Unicorn: You cannot have a sparse serialized tree

It was appearing when they were saving edited content. I've decided that we don't really need automatic items serialization on QA and other environments, so it will be best to just switch it off. I took a look into Unicorn.DataProvider.config and the comment there states:

This file configures the Unicorn data provider. The data provider writes updated serialized items to disk when they are changed.
This file should be removed in ANY deployed instance (CE or CD) that does not act as a source for serialized item updates.
Generally speaking that's anywhere other than a developer workstation, so your CI process (you have one, right?) should remove this file during the build.
IMPORTANT EXCEPTION: If you are using Transparent Sync as a deployment mechanism, this file must remain on your CE environment.

That's exactly why it should be turned off on any non-development environments.

As we are using Sitecore 9, we have set localenv variable to Local on our dev instances Web.config

<add key="localenv:define" value="Local" />

Instead of removing Unicorn.DataProvider.config config file after deployment, I have added localenv:require="Local" to root element there:

<sitecore localenv:require="Local">
  <dataProviders>
    <!--
      Register the Unicorn data provider for use. If a database hooks to the Unicorn data provider it will
      automatically write changes to the database that match any configured predicate into the serialization provider.
 
      Changes that only affect Revision, Modified, or any fields ignored by FieldPredicates will be ignored.
    -->
    <unicorn type="Unicorn.Data.DataProvider.Unicorn$(database)DataProvider, Unicorn">
      <param connectionStringName="$(1)"/>
      <Name>$(1)</Name>
    </unicorn>
  </dataProviders>
 
  <!--
    Hook the Unicorn Data Provider into the master and core databases. If you're not
    syncing anything in core you can safely unregister it from here. If you want to
    sync something to another database register it here.
 
    It's safe to remove this config section on any environment where you are not
    collecting item changes, which may mean anywhere other than local development
    sites. This will avoid any performance hit from writing unused serialized files.
  -->
  <databases>
    <database id="master">
      <dataProviders>
        <dataProvider ref="dataProviders/main">
          <patch:attribute name="ref">dataProviders/unicorn</patch:attribute>
        </dataProvider>
      </dataProviders>
    </database>
    <database id="core">
      <dataProviders>
        <dataProvider ref="dataProviders/main">
          <patch:attribute name="ref">dataProviders/unicorn</patch:attribute>
        </dataProvider>
      </dataProviders>
    </database>
  </databases>
</sitecore>

That makes sure the changes made by editors won't be serialized.

Saturday, 17 November 2018

Renderings static binding

Last time I had to prepare a component which behaves like a carousel of specific type of renderings. Instead of copy-pasting the Razor markup or using basic Razor partials I decided to use static binding of renderings - method I've never used before. It appeared to be extremely handy.

Basically you can specify a rendering like this:

@Html.Sitecore().Rendering("{ rendering ID }", new { Cacheable = true, Cache_VaryByData = true, DataSource = "{ DataSource ID}" })

First parameter is rendering ID, second is anonymous object where we can specify additional properties, like caching or even datasource that will be used there.

You can even add your custom properties and read them inside rendering, just like this:

@Html.Sitecore().Rendering(
    Constants.MyRendering.RenderingId.ToString("B"),
    new
    {
        Cacheable = true,
        Cache_VaryByData = true,
        DataSource = myDatasource.Id.ToString("B"),
        MyProperty = "My custom value"
 })

To be more familiar with possible properties of that anonymous object I have decompiled GetRendering method from SitecoreHelper class:


I have created and extension method to easily retrieve rendering properties:

And you can use it in view like this:

var property = Html.Sitecore().GetRenderingProperty("MyProperty");


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!

Tuesday, 11 September 2018

Sitecore Module - Dynamic Sitemap XML version 2.0

I have released updated and rewritten version of my Dynamic Sitemap XML module.Version 2.0 comes with better structure of the code and gives much more flexibility in extending it. Now it uses Sitecore Web index by default to improve the performance.

Whole code is hosted on GitHub just like before at https://github.com/ReoKzK/Sitecore.SharedSource.DynamicSitemap.


It can be added to solution by nuget package:

Install-Package Sitecore.SharedSource.DynamicSitemap -Version 2.0.0

Tuesday, 21 August 2018

HelixComponents - Gallery of frequently used website modules

I have released first part of my HelixComponents project. It's aim is to create reusable components that can be used across Helix-based projects.


Each module will be released as a separate repository, which is basically a Helix project. All of them can be taken from my repo at Github: https://github.com/ReoKzK/HelixComponents.
It is based on Helixbase and contains all of these modules.

Here are the first projects:

HelixComponents.Feature.Recommended

Contains modules used for content promotion

HelixComponents.Feature.Tabs

Tabs components - Work in progress

Soon I'm going to release more and fix any outstanding issues

Sunday, 10 June 2018

Sitecore 9 installation scripts

As Sitecore 9 installation is much different than in previous versions and we have now Sitecore Installation Framework, I would like to share with you my installation PowerShell scripts.

First of all, many thanks to George Chang for the Sitecore 9 install script which I based mine (https://blogs.perficientdigital.com/2017/10/23/how-to-install-sitecore-9-with-the-sitecore-install-framework/).

First thing you will need to do is have Solr installed. It is required from v.9.0 to have Solr running and on SSL. There is a great post by Jeremy Davis about installing Solr easily by his PS script - https://jermdavis.wordpress.com/2017/10/30/low-effort-solr-installs/.

After we have Solr installed and running on https we can start installing our Sitecore 9 instance.

We need to have Sitecore Installation Framework installed to our PowerShell. Here is the script that takes care of it:

 

And here is the Sitecore 9 installation script itself. There is a configuration object as first in the code, you can specify there Sitecore version to install, SQL Server credentials and Solr configuration.


All scripts are available on my GitHub: https://github.com/ReoKzK/Sitecore9-Installer


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!

Friday, 13 January 2017

Razl scripting with PowerShell

Razl, tool from Hedgehog Development, that was designed to transfer items between Sitecore instances and compare them, has some nice scripting features.

I needed to use Razl scripting for Sitecore upgrade process. I created new fresh Sitecore instance (it was big jump, from 6.5 to 8.1) and started preparing what should be moved to new database. Old instance had a lot of datasource items and huge amount of media library items. Transfer all of this things was huge deal and was processing for over a dozen of hours. I decided to make some changes in my approach.

Preparation of Sitecore instances

I was using Razl 3.0.5. Unfortunately I had some problems with my solution that I couldn't connect with this tool to the Sitecore that had all of solution code deployed, so I've created a clean instance of version 6.5 and a clean version of 8.1. I've changed the connection strings of this two to the correct solution databases and I used this two clean instances just for transferring purposes - worked like a charm. You might not have exact problem, but this is how I went through that.

Preparing Razl script file

First you have to prepare the script file in the Razl. You can do it just like in general, by adding tasks to Task List:


Then, instead of running transfer, you can click 'Export Tasks' button. You will be prompted to save the script file. Let's take a look into the one:
<?xml version="1.0" encoding="utf-8"?>
<razl>
  <connection name="SC65VanillaWithSW.Master" readOnly="false" install="true">
    <url>http://sc650vanilla/</url>
    <accessGuid>047c5600-e111-449c-9e2b-179b06f17190</accessGuid>
    <database>master</database>
    <path>C:\inetpub\wwwroot\sc650Vanilla\Website</path>
  </connection>
  <connection name="SC81VanillaForSW.Master" readOnly="false" install="true">
    <url>http://sc81u2vanillaforconnection/</url>
    <accessGuid>8db29355-a8d4-4102-969a-537cc8c54f30</accessGuid>
    <database>master</database>
    <path>C:\inetpub\wwwroot\sc81u2vanillaforconnection\Website</path>
  </connection>
  <operation name="CopyAll" source="SC65VanillaWithSW.Master" target="SC81VanillaForSW.Master">
    <!--Copy with overwrite '/sitecore/templates/SW', all missing parents and all child items from 'SC65VanillaWithSW.Master' to 'SC81VanillaForSW.Master'-->
    <parameter name="itemId">b80ae69f-4212-43e6-af97-2f86ddcddae1</parameter>
    <parameter name="overwrite">True</parameter>
    <parameter name="lightningMode">False</parameter>
  </operation>
  <operation name="CopyAll" source="SC65VanillaWithSW.Master" target="SC81VanillaForSW.Master">
    <!--Copy with overwrite '/sitecore/templates/User Defined', all missing parents and all child items from 'SC65VanillaWithSW.Master' to 'SC81VanillaForSW.Master'-->
    <parameter name="itemId">b29ee504-861c-492f-95a3-0d890b6fca09</parameter>
    <parameter name="overwrite">True</parameter>
    <parameter name="lightningMode">False</parameter>
  </operation>
  <operation name="CopyAll" source="SC65VanillaWithSW.Master" target="SC81VanillaForSW.Master">
    <!--Copy with overwrite '/sitecore/layout/Layouts/SW', all missing parents and all child items from 'SC65VanillaWithSW.Master' to 'SC81VanillaForSW.Master'-->
    <parameter name="itemId">2141e479-3f72-447c-9afa-3b4db1ea283d</parameter>
    <parameter name="overwrite">True</parameter>
    <parameter name="lightningMode">False</parameter>
  </operation>
</razl>

It has a two connections for the instances which will be used in transfer. It comes from the Razl configuration, where firstly I created that connections. Under that elements there is a list of operations with specified source instance, target instance, ID of the item and configuration of the operation type - in my case it will be CopyAll with overwriting the target instance.

As far as I knew at that time, there was no way to edit exported script with Razl, so I was editing it manually.

Preparing PowerShell run script

At last I wanted to have a PowerShell script that will run my transfer script with just one click. I have prepared a PSs that run razl.exe with specified scripts. For core database this is quite simple - set your directory where Razl script are located in:


For master database I decided to split Razl script into smaller parts, so the I was able to comment out some of them to run just specific ones:


Also these scripts log full log into specific files. So everything that happens while transfer is saved for analysis. Here is some example part of the log:

    1 13:00:31,579 INFO  CopyAll Started Id: 563a2ddb-ad89-4182-9706-eb1512abe0b1, Path: /sitecore/system/Field types/List Types/Google Maps Multilist, From: SC65VanillaWithSW.Core, To: SC81VanillaForSW.Core, Overwrite: True
   1 13:00:31,579 INFO  CopyAll Processing /sitecore/system/Field types/List Types/Google Maps Multilist
   1 13:00:31,580 INFO  CopyItem Started Id: 563a2ddb-ad89-4182-9706-eb1512abe0b1, Path: /sitecore/system/Field types/List Types/Google Maps Multilist, From: SC65VanillaWithSW.Core, To: SC81VanillaForSW.Core
   1 13:00:31,847 INFO  CopyItem Finish Id: 563a2ddb-ad89-4182-9706-eb1512abe0b1, Path: /sitecore/system/Field types/List Types/Google Maps Multilist, From: SC65VanillaWithSW.Core, To: SC81VanillaForSW.Core
   1 13:00:31,928 INFO  CopyAll processing children of /sitecore/system/Field types/List Types/Google Maps Multilist
   1 13:00:31,928 INFO  CopyAll Processing /sitecore/system/Field types/List Types/Google Maps Multilist/Menu
   1 13:00:31,929 INFO  CopyItem Started Id: 60daf681-8069-46a2-aa39-5490741f6cec, Path: /sitecore/system/Field types/List Types/Google Maps Multilist/Menu, From: SC65VanillaWithSW.Core, To: SC81VanillaForSW.Core
   1 13:00:32,196 INFO  CopyItem Finish Id: 60daf681-8069-46a2-aa39-5490741f6cec, Path: /sitecore/system/Field types/List Types/Google Maps Multilist/Menu, From: SC65VanillaWithSW.Core, To: SC81VanillaForSW.Core
   1 13:00:32,252 INFO  CopyAll processing children of /sitecore/system/Field types/List Types/Google Maps Multilist/Menu
   1 13:00:32,252 INFO  CopyAll Processing /sitecore/system/Field types/List Types/Google Maps Multilist/Menu/Show Map
   1 13:00:32,252 INFO  CopyItem Started Id: ce073686-11cb-4921-b4db-61c98bc1f54a, Path: /sitecore/system/Field types/List Types/Google Maps Multilist/Menu/Show Map, From: SC65VanillaWithSW.Core, To: SC81VanillaForSW.Core
   1 13:00:32,519 INFO  CopyItem Finish Id: ce073686-11cb-4921-b4db-61c98bc1f54a, Path: /sitecore/system/Field types/List Types/Google Maps Multilist/Menu/Show Map, From: SC65VanillaWithSW.Core, To: SC81VanillaForSW.Core
   1 13:00:32,576 INFO  CopyAll processing children of /sitecore/system/Field types/List Types/Google Maps Multilist/Menu/Show Map
   1 13:00:32,576 INFO  CopyAll Processing /sitecore/system/Field types/List Types/Google Maps Multilist/Menu/Select All
   1 13:00:32,577 INFO  CopyItem Started Id: bbe0cd71-aac7-47bc-971a-49265078d17c, Path: /sitecore/system/Field types/List Types/Google Maps Multilist/Menu/Select All, From: SC65VanillaWithSW.Core, To: SC81VanillaForSW.Core
   1 13:00:33,153 INFO  CopyItem Finish Id: bbe0cd71-aac7-47bc-971a-49265078d17c, Path: /sitecore/system/Field types/List Types/Google Maps Multilist/Menu/Select All, From: SC65VanillaWithSW.Core, To: SC81VanillaForSW.Core
   1 13:00:33,209 INFO  CopyAll processing children of /sitecore/system/Field types/List Types/Google Maps Multilist/Menu/Select All
   1 13:00:33,209 INFO  CopyAll Processing /sitecore/system/Field types/List Types/Google Maps Multilist/Menu/Deselect All
   1 13:00:33,209 INFO  CopyItem Started Id: b54acbc5-0fc0-4d6d-a863-0f8657e50504, Path: /sitecore/system/Field types/List Types/Google Maps Multilist/Menu/Deselect All, From: SC65VanillaWithSW.Core, To: SC81VanillaForSW.Core
   1 13:00:38,480 INFO  CopyItem Finish Id: b54acbc5-0fc0-4d6d-a863-0f8657e50504, Path: /sitecore/system/Field types/List Types/Google Maps Multilist/Menu/Deselect All, From: SC65VanillaWithSW.Core, To: SC81VanillaForSW.Core
   1 13:00:38,537 INFO  CopyAll processing children of /sitecore/system/Field types/List Types/Google Maps Multilist/Menu/Deselect All
   1 13:00:38,593 INFO  CopyAll Finish Id: 563a2ddb-ad89-4182-9706-eb1512abe0b1, Path: /sitecore/system/Field types/List Types/Google Maps Multilist, From: SC65VanillaWithSW.Core, To: SC81VanillaForSW.Core, Overwrite: True
   1 13:00:38,705 INFO  CopyAll Started Id: 5cee31bf-120d-4936-b9c6-930cbf63cb57, Path: /sitecore/system/Field types/Simple Types/Google Maps Single Point, From: SC65VanillaWithSW.Core, To: SC81VanillaForSW.Core, Overwrite: True

More info about Razl scripting is in official Razl documentation: https://hedgehogdevelopment.github.io/razl/script.html.

Tuesday, 10 January 2017

Sitecore Module - Dynamic Sitemap XML

After a lot of work spent on creating project specific sitemap xml implementations I have prepared my own generic sitemap xml module. It is based on original marketplace sitemap module but adds some new great features.


It has two main assumptions:
  • Flexible configuration - to give end user as many as possible configuration options, because it is easier to edit Sitecore items than edit and deploy config files.
  • Extensibility - code functionality should be extensible and override-able. It should be easy to extend or override functionality, without decompiling and copy-pasting a lot of code.

Specifying your own logic

To use or not to use marketplace module - that is the question! Most times main reason to write your own sitemap implementation is thought if there will be some custom tricky functionalities needed. Something like some static html pages or other links that exist outside Sitecore.

 
You can specify your own logic, that can add your custom items to the sitemap.

You should create class that implements IItemsProcessor interface, put it in your assembly and attach your class to the site configuration item, in format MyType, MyAssembly.


In this approach, main functionality of module can be easily changed. More info here https://github.com/ReoKzK/Sitecore.SharedSource.DynamicSitemap/wiki/Specifying-your-own-logic

Separation from content

I also decided to definitely separate sitemap things from the actual content. So deciding whether the item goes to sitemap or nor or setting the change frequency or priority, is set on the sitemap configuration item in Sitecore. This approach will keep your items clean. More info here https://github.com/ReoKzK/Sitecore.SharedSource.DynamicSitemap/wiki/Creating-new-sitemap-configuration

That's all :)

Module is uploaded to official Sitecore Marketplace https://marketplace.sitecore.net/Modules/D/Dynamic_Sitemap_XML_Module.aspx

Whole documentation is available here https://github.com/ReoKzK/Sitecore.SharedSource.DynamicSitemap/wiki

Library is also available at nuget:
Install-Package Sitecore.SharedSource.DynamicSitemap
Hope that my module will be useful and will improve your websites development process.

Tuesday, 6 December 2016

Language Fallback on Final Layout

Last time I was struggling with translating items to different languages. I was using the ClayTablet Sitecore Translation Connector module - it basically sends the content to translator, waits till it will be returned, receives the translated content and creates new version in desired language. Sitecore was in Initial Release of 8.1.

Then I realized, that new versions of items in other languages had missing value in Final Renderings field. Unfortunately solution which I was working on, was using this field widely from the beginning - by the Experience Editor. I tried to set the Language Fallback on that field but in vain.

I have successfully reproduced this behaviour on clean instance of 8.1 Initial Release. I have enabled Language Fallback:

    <sites>
      <site name="shell">
        <patch:attribute name="enableItemLanguageFallback">true</patch:attribute>
        <patch:attribute name="enableFieldLanguageFallback">true</patch:attribute>
      </site>
      <site name="website">
        <patch:attribute name="enableItemLanguageFallback">true</patch:attribute>
        <patch:attribute name="enableFieldLanguageFallback">true</patch:attribute>
      </site>
    </sites>

Also I have set the fallback path in the language item:


After that I have added new rendering from Experience Editor:


Unfortunately, I wasn't able to fallback this field from the base language. I have sent my problem to Sitecore Support and they confirmed, that this feature is missing. Sadly I had nothing more to do but manually set these renderings or use a PowerShell to automate it. Fortunately this bug reported by me was labelled as feature request and now it is present on version 8.2 Update-1!


The public reference number was set to 105306.

I have downloaded the package and started to testing it out. Fresh instance was set up by the SIM.
I went to the Final Renderings field:

/sitecore/templates/System/Templates/Sections/Layout/Layout/__Final Renderings

And I have enabled field level fallback:


Everything works ok now - additional "Sample rendering" added from Experience Editor is present now on other languages that have Fallback set:



Also, the Update-1 comes with really great features like wide Azure support. You should definitely check this out!