The Little Book of Sitecore® Tips volume 1 now available

sitecore_netI’m pleased to announce the launch of volume 1 of The Little Book of Sitecore® Tips

The book was created after converting a rather large amount of notes (that were created during my day to day workings with Sitecore) into an initial book, and what will soon become an ongoing series of books.

The main purpose of The Little Book of Sitecore Tips is a light-hearted look at some useful Sitecore tips. These tips are targeted at all levels of user but whethersitecore_book you’re recapping or learning, I hope you find the material either useful, or at least of some entertainment value.

ALL royalties are donated to worthy causes, so if you take nothing from the book at least you’ll know that your purchase wasn’t in vain. So have some fun, support some charity and order yourself a paperback copy from the likes of Amazon or Barnes & Noble – The book is also available as an eBook on the iBooks StoreAmazon Kindle Store, and Google Play.

Thanks is due to the technical reviewers Adam Conn and Radosław Kozłowski – and a big thanks to Tamas Varga who also supplied a great deal of help, support, and the following review:

sitecore_rocket

“This is the book I should have written 5 years ago! Having a book with tips is something we haven’t got so far in the Sitecore community. It goes beyond just Sitecore tips, but some of the best practices the community created and collected during the last couple of years. It not only helps your daily work with Sitecore, but also demonstrates the power and flexibility of it. All the tips in this book is really helpful and the nice illustrations helps you remember them.”

 

amazon-appsstore-us-black-v2Get_it_on_iBooks_Badge_US_1114google-play-badge

Advertisements

Using voice commands to add items to Sitecore. IFTTT: Complete walk through

Having purchased a few Google Home devices my mind turned to getting them to work with Sitecore. Wouldn’t it be cool to tell your Google Home or mobile device to interact with Sitecore?! So I setup a POC to do just that, by using a custom route in an API controller we can create Sitecore items by asking Google Home to “Create Sitecore item [ItemName]“:

First visit the IFTTT Platform where we can add our Applet. By selecting ‘New Applet’ we’re first asked to create a Trigger, for this we select ‘Google Assistant’

Google assistant

This will present us with some options:

google trigger

We’ll be using a phrase with a text ingredient, this allows us to say something like “Hey Google, add [ingredient] to my request”. For the POC this ingredient will be an item name which will be used to add an item to Sitecore under /sitecore/content/Home/Google Home

Now we’ll tell Google that our phrase will be “Create Sitecore item [ItemName]:

google assitant sitecore

And we need a reply from the device we’re speaking to:

google assistant sitecore response

Great, so we’ve got an “if this”, now it’s time for a “then that”. Our devices know what we’re going to say and know how to respond, so we need to create our API controller in Sitecore that’s going to create the item for us. The rough example I used is below:

    public class GoogleTestController: SitecoreController
    {
        public ActionResult IFTTT(string itemName)
        {
            using (new Sitecore.SecurityModel.SecurityDisabler())
            {
                var masterDb = Sitecore.Data.Database.GetDatabase("master");

                var template = masterDb.GetItem("/sitecore/templates/System/Templates/Standard template");

                var parentItem = masterDb.GetItem("/sitecore/content/Home/Google Home");

                parentItem.Add(itemName, new Sitecore.Data.Items.TemplateItem(template));

                return Content($"{itemName} added");
            }
        }
    }

And we’ll need our custom route

        public void Process(PipelineArgs args)
        {
            RouteTable.Routes.MapRoute("Feature.GoogleTest", "GoogleTest/{itemName}",
                new { controller = "GoogleTest", action = "IFTTT" });
        }

That’s our Sitecore code setup, now we need to add a webhook so that Google Assistant can talk to our Sitecore API. Under ‘then’ we select ‘Add action’ and select ‘Webhooks’:

ifttt then

This presents us with a dropdown and we can select ‘Make a web request’. These fields should be self explanatory, so the one I’ll focus on is the URL. In the below I have used a sample URL but obviously this needs to be a publicly accessible server with the Sitecore item Web API enabled:

Sitecore google api

Now we’re calling our Sitecore API controller, and sending in the name of the item that the user has asked to create. We could stop there and deploy but let’s select ‘Add action’ again and choose ‘notifications’. We want a phone notification when an item has been added to Sitecore:

ifttt notification sitecore

Give the Applet a title and description and save it. The final step is to enable this Applet on our IFTTT devices, simply select ‘Turn on’:

sitecore ifttt applet.png

Once it’s on we can talk to any of the Google devices on our account. I tested this by saying the following to my Google Home… “Ok Google, create Sitecore item test item” I hear the response “Ok, creating item test item on your Sitecore instance”, you can hear her response on this recording. We can then confirm that our device has added the item to Sitecore:

test item sitecore

We also get a nice phone notification:

sitecore-item-created-ifttt

The possibilities for this one are endless – Sitecore, it’s invading our homes!

Using the OData Item Service in Sitecore 9 – Step by step

Pre Sitecore 9 if we wanted to retrieve Item data we could make use of RESTful services easily with a simple GET HTTP Request. If for example we wanted to get children of an item we could call an endpoint such as:

http://YourInstance/sitecore/api/ssc/item/Some_Guid/children?fields=ItemId,ItemName,TemplateName

In Sitecore 9 we also have the introduction of the  OData Item Service so I thought I’d take us through a step by step.

First go to the following item in the Core database:

/sitecore/system/Settings/Services/API Key

Right click the API Key folder and insert an ‘OData Item API Key’. An explanation of the fields for this item can be found in the documentation but here’s my setup with an explanation:

ODataItemAPIKey

Database – fairly self explanatory, the Sitecore database you’re retrieving an item from.

Search Filter – we can apply search options using an OData filter format, I’ve left this as the default which will return the latest version of items.

CORS Origins – which origins are allowable? Add your required origins separated with a semicolon. As I’m testing this on Helix Base I have added the relevant origin.

AllowedControllers – you may wish to restrict this API Key to certain controllers and can list those here separated with a semicolon. I’m allowing all so have opted for the wildcard *

Impersonation User – you could leave this blank in which case Sitecore will use the default from your Sitecore.Services.AnonymousUser setting. The default is most likely sitecore\ServicesAPI but I’ve added this to the field anyway.

Once we’ve saved the item it’s time to copy the item Id which we can then use in our request headers, or on the endpoint. For example if my item Id is 1234 I could use a Key of sc_apikey  and value 1234 in my headers, or apply it to the endpoint as follows:

http://demo.helixbase/sitecore/api/ssc/aggregate/content/Items(‘/sitecore/content/Home’)?sc_apikey=1234

Using either approach we can then see the fruits of our labour…

SitecorePostman

To see a list of endpoints for the OData Item Service see the Sitecore documentation

Full list of Sitecore field types and their C# type mapping

I thought it would be useful to have a list of Sitecore fields and their relevant C# Sitecore.*.Type mapping. I couldn’t find a list so have created one below:

‘Analytics’ fields:

Profile Card Value:
TBC
Profile Cards:
TBC
Tracking:
Sitecore.Analytics.Data.TrackingField

‘Simple Types’ fields:

Checkbox:
Sitecore.Data.Fields.CheckboxField
Date:
Sitecore.Data.Fields.DateField
Datetime:
Sitecore.Data.Fields.DateField
File:
Sitecore.Data.Fields.FileField
Image:
Sitecore.Data.Fields.ImageField
Integer:
TBC
Multi-Line Text:
Sitecore.Data.Fields.TextField
Number:
TBC
Password:
TBC
Rich Text:
Sitecore.Data.Fields.HtmlField
Single-Line Text:
Sitecore.Data.Fields.TextField
Word Document:
Sitecore.Data.Fields.WordDocumentField

‘Social’ fields:

Accounts Multilist:
Sitecore.Data.Fields.MultilistField
Campaign Tree:
Sitecore.Data.Fields.ReferenceField
Countable Edit:
Sitecore.Data.Fields.TextField

‘List Types’ fields:

Checklist:
Sitecore.Data.Fields.MultilistField
Droplist:
Sitecore.Data.Fields.ValueLookupField
Grouped Droplink:
Sitecore.Data.Fields.GroupedDroplinkField
Grouped Droplist:
Sitecore.Data.Fields.GroupedDroplistField
Multilist:
Sitecore.Data.Fields.MultilistField
Multilist with Search:
Sitecore.Data.Fields.MultilistField
Name Lookup Value List:
Sitecore.Data.Fields.NameValueListField
Name Value List:
Sitecore.Data.Fields.NameValueListField
Treelist:
Sitecore.Data.Fields.MultilistField
TreelistEx:
Sitecore.Data.Fields.MultilistField

‘Link Types’ fields:

Droplink:
Sitecore.Data.Fields.LookupField
Droptree:
Sitecore.Data.Fields.ReferenceField
General Link:
Sitecore.Data.Fields.LinkField
General Link with Search:
Sitecore.Data.Fields.LinkField
Version Identity:
TBC
Version Link:
Sitecore.Data.Fields.VersionLinkField

‘Developer Types’ fields:

Icon:
TBC
IFrame:
TBC
Tristate:
TBC
Sitecore User:
TBC

‘System Types’ fields:

Attachment:
TBC
Custom:
Sitecore.Data.Fields.CustomCustomField
Datasource:
Sitecore.Data.Fields.DatasourceField
File Drop Area:
Sitecore.Data.Fields.FileDropAreaField
Internal Link:
Sitecore.Data.Fields.InternalLinkField

Page Preview:
Sitecore.Data.Fields.PagePreviewField
Query Builder:
TBC
Query Datasource:
TBC
Rendering Datasource:
Sitecore.Data.Fields.RenderingDatasourceField
Rules:
Sitecore.Data.Fields.RulesField
Security:
Sitecore.Data.Fields.TextField
Template Field Source:
Sitecore.Data.Fields.TemplateFieldSourceField
Thumbnail:
Sitecore.Data.Fields.ThumbnailField
VisitorFilterRules:
TBC

‘Deprecated Types’ fields:

html:
Sitecore.Data.Fields.HtmlField
link:
Sitecore.Data.Fields.LinkField
lookup:
Sitecore.Data.Fields.LookupField
memo:
Sitecore.Data.Fields.TextField
reference:
Sitecore.Data.Fields.ReferenceField
server file:
TBC
text:
Sitecore.Data.Fields.TextField
tree:
Sitecore.Data.Fields.ReferenceField
tree list:
Sitecore.Data.Fields.MultilistField
Treelist with Search:
Sitecore.Data.Fields.MultilistField
valuelookup:
Sitecore.Data.Fields.ValueLookupField
Single-Line Text:
Sitecore.Data.Fields.WordDocumentField

Glass fluent configuration and composite reuse principle

If you’re a fan of the flat structure of composition, rather than the hierarchy of inheritance you will be looking to make an implementation on your Glass Sitecore models and you can read more information about the composition pattern with Glass. However, previously Glass could only handle this by using attribute mapping. For those of us who prefer fluent configuration I created a pull request.

So what does this allow us to do? Let’s look at a very basic example using inheritance, below you can see IBase implemented by IDerived:

using Glass.Mapper.Sc.Fields;
using Helixbase.Foundation.Models.BaseItem;

namespace Helixbase.Feature.Test.Models
{
    public interface IDerived : IBase
    {
        Image SomeImage { get; set; }
    }
}

This class could now make use of composition:

using Glass.Mapper.Sc.Fields;
using Helixbase.Foundation.Models.BaseItem;

namespace Helixbase.Feature.Test.Models
{
    public interface IDerived
    {
        IBase BaseComposition { get; set; }
        Image SomeImage { get; set; }
    }
}

We then register our mapping using the new ‘Self’ extension method as follows:

using Glass.Mapper.Sc.Maps;
using Helixbase.Feature.Hero.Models;

namespace Helixbase.Feature.Hero.ORM
{
    public class GlassMappings : SitecoreGlassMap<IDerived>
    {
        public override void Configure()
        {
            Map(config =>
            {
                config.AutoMap();
                config.TemplateId(Templates.IDerived.TemplateId);
                config.Self(f => f.BaseComposition);
                config.Field(f => f.SomeImage).FieldName("Some Image");
            });
        }
    }
}

With the new method we can use the composite reuse principle in Sitecore with Glass fluent configuration for our mappings.

Cleaning up your Sitecore Helix webroot with gulp

There’s a common problem when publishing a Sitecore solution whether it’s Helix compliant or not… how we handle old files in our webroot. For example, you may change an assembly name or a view, but both the dll and cshtml files will remain in the webroot which causes obvious problems. Immutable deployments are the answer when deploying to environments other than a localhost, but we need an approach when developing locally.

One way to handle this problem is using a gulp publish and binding a cleanup gulp task to our Visual Studio publish. This is one of the areas that the Helix principles show their worth, if folders are organised on disk we can easily setup a gulp task to delete certain folders rather than using wildcards.

So the first step is to add a cleanup task which will be called by our gulp publish task. The cleanup task should remove the Foundation, Feature, and Project config folders from App_Config before deploying any files. For the dll’s we will need a wildcard:

var cleanProjectFiles = function (layerName) {
    const filesToDelete = [
        gulpConfig.webRoot + '/bin/Helixbase.' + layerName + '.*',
        gulpConfig.webRoot + '/App_Config/Include/' + layerName
    ];
    console.log("Removing " + layerName + " configs/binaries");
    return gulp.src(filesToDelete, { read: false })
        .pipe(clean({ force: true }));
};

The task will clear out the old configs/dll’s before publishing our solution but what about views? Helix doesn’t stipulate that views are organised on disk according to layer or context and the default view engine leaves us with a difficulty. We could use gulp to delete everything except for the Shared folder, but this could be problematic if any view folders change in a future Sitecore update. Another option is to have a whitelist of every view to delete but this isn’t ideal either. While old view files are less problematic it’s a good idea to create a custom multisite View Engine. This would give us options for organising our views on disk in a way that our gulp cleanup task can easily handle, examples being:

Views\Helixbase\SomeModule\Someview.cshtml – then delete the context folder in gulp.

Views\Layer\SomeModule\SomeView.cshtml – then delete the layer folder in gulp.

Finally, when it comes to deploying across environments we need an approach other than transform files if we want to easily use tokens/octopus variables etc. To do this involves adding config files per environment. If we’re following a similar approach we need a way to delete the unwanted config folders using gulp. This can be achieved with a post build cleanup task.

// Note: intended to be called after publishing
gulp.task("Clean-Transform-Configs", function (layerName) {
    const filesToDelete = [
        gulpConfig.webRoot + '/App_Config/Include/' + layerName + '/CD',
        gulpConfig.webRoot + '/App_Config/Include/' + layerName + '/CM'
    ];
    console.log("Removing transform configs");
    return gulp.src(filesToDelete, { read: false })
        .pipe(clean({ force: true }));
});

We would want to call this task after each gulp publish, and can also bind it to the Visual Studio publish:

/// <binding AfterBuild='Clean-Transform-Configs' />

But remember if we’re binding to the Visual Studio publish, we’d need to be able to pass in a layerName. As this cannot be achieved, we could add a condition to the gulp task if the layerName var was empty.

Unicorn config issues – Data at the root level is invalid. Line 1, Position 1.

Strictly speaking this isn’t an issue that relates to the use of Unicorn alone, but as Unicorn uses config files you may notice the following error when creating patch configs on some machines, then trying to access unicorn.aspx:

Data invalid

This issue relates to Byte order mark and can be resolved by opening your config in notepad and saving it in ANSI encoding. Then reopen the config in notepad and you will notice the following:

Data invalid ANSI

Delete the ? character and save your config to resolve the issue.