Automatically scale Sitecore on Azure PaaS

Since Sitecore 8.2 update 1, we can simply scale our App Service plans up or out. You can find these options on the App Service Plan as well as the App Service themselves.

Scaling up means selecting a different tier for our subscriptions – more RAM and cores for instance.

scaleup

As you can see, I’m currently running on the S1 tier – which I can’t recommend to anyone as it’s quite slow (of course, it is way below the minimum specs found in the installation guides)…

Our other option is to scale out. That means we can add (or remove) a number of instances based on… something. Let’s take a look.

Continue reading

Advertisements

Campaigns, querystrings and FXM

So I saw that in Sitecore’s Federated Experience Manager (FXM) campaigns get added to a page by adding a page filter. I came across this great article that shows in detail how to set goals, campaigns and the like.

In any case, I found it a bit strange that a campaign would have to be set as a page filter, rather than being able to link to an affiliate site with the campaign tracker in  the URL like we’re used to in our normal use of Sitecore (e.g. use sc_camp=someguid).

Of course, as with most things Sitecore, we can customize FXM and I figured this would be a nice way for me to get some more experience with FXM.

The most obvious step first of course: Adding the site to FXM. This can be done through the Federated Experience Manager link on the Launch Pad, and you can then click ‘add external website’. In my case I’ve added the website Sandbox FXM with the domain sandbox.fxm.

fxm-new-site

FXM will then give you a beacon (in the ‘Generated script’ field) which you can parse into your external site. I’ve just dropped mine right before the closing body tag.

Don’t forget to create a site in IIS and update your hosts file as well to make sure you can browse it.

After I pasted that I ran into my first issue: I kept getting the error “Error: Failed to load resource: the server responded with a status of 400 (DomainInvalid)” when loading the FXM site. This has a simple fix: Publish your FXM site, which you can do from the FXM screen.

So, that resolved we can start to write some code. By looking at the TrafficTypeProcessor I found I need to inherit from ITrackPageVisitProcessor and ITrackPageVisitProcessor<ITrackPageVisitArgs>.

Then, in the code I will grab the URL of the page, get the sc_camp querystring from it and register that campaign

public class RegisterCampaignProcessor : ITrackPageVisitProcessor, ITrackPageVisitProcessor<ITrackPageVisitArgs>
{
    public void Process(ITrackPageVisitArgs args)
    {
        var url = System.Web.HttpContext.Current.Server.UrlDecode(WebUtil.GetRawUrl());
        var campaign = string.Empty;
        if (url.IndexOf("sc_camp=") > 0)
        {
            campaign = url.Substring(url.IndexOf("sc_camp=") + 8, 38);
        }
        if (!string.IsNullOrEmpty(campaign))
        {
            var campaignItem = args.Context.Database.Database.GetItem(campaign);
            if (campaignItem != null)
            {
                CampaignItem item = new CampaignItem(campaignItem);
                args.CurrentPageVisit.TriggerCampaign(item);
            }
        }
    }
}

When we do WebUtil.GetRawUrl() we will actually get back something similar to this:
/sitecore/api/ssc/Beacon/Service/beacon/trackPageVisit/?contactId=6b19088ea2c54bdab775231697a5e0a3|False&sessionId=44ir0jmxxilktj3xiaqlidt5&page=http://sandbox.fxm/?sc_camp={87925D56-34D9-4080-A725-6EC8B3B4CB0A}&referrer=&rt=1453220938704
So I’ll grab the campaign from there – although in a production scenario you might want to be a bit more careful about this because this particular code requires you to have the accolades as part of the campaign id (as well as not having any checks on whether it’s a GUID etc). From that GUID I resolve the campaign item as an Item, create a CampaignItem object from that and trigger that on the campaign.

We can use our Sitecore include files to patch this processor in in the correct place – in my case I’ve patched it right before the TrafficTypeProcessor:

<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <pipelines>
            <group groupName="FXM" name="FXM">
                <pipelines>
                    <tracking.trackpagevisit>
                        <processor patch:before="*[@type='Sitecore.FXM.Pipelines.Tracking.TrackPageVisit.TrafficTypeProcessor, Sitecore.FXM']" type="Sandbox.FXM.TriggerCampaign.RegisterCampaignProcessor, Sandbox" />
                    </tracking.trackpagevisit>
                </pipelines>
            </group>
        </pipelines>
    </sitecore>
</configuration>

So that’s now all well and good but how do I figure out whether it’s working? If I had an empty xDB it would be easy enough but I actually have a lot of dummy data in there so had to be a bit more creative. In Mongo I wrote a query to figure out which data came from FXM:

db.Interactions.find({ "SiteName" : "Sandbox_FXM" }, { "ContactId" : 1 } )

In there, I can see that a campaign has been triggered, but I’d also like to see my xFile, andof course the ContactId is then shown as a binary type 3 value. I’m using MongoVue to view my Mongo data, which actually comes with a very handy option:

mongo-vue-preferences.png

Using that I can find the GUID I had for that visitor. I can then open any random xFile and copy/ paste the GUID of this particular Contact in the URL.

fxm-contact-xfile.png

Although for the people paying attention: The contact ID is also in the URL we had from running WebUtil.GetRawUrl(). Sadly I couldn’t actually use that in my example as it was a new InPrivate window and I had closed it.

Golden nuggets – Reset Blank

During my Sitecore 7 training the trainer, Raul Jimenez, pointed out the ‘Reset Blank’ checkbox on the Template Fields template. 

Image

Before today I’ve never used it, so I had no clue what it was that the checkbox does. I figured this might be the case for a lot of others as well:

If the ‘reset blank’ checkbox is ticked, when that field is saved without a value the field will actually be reset to the default value. This is very handy in situations where it doesn’t make sense to have the field blank.

Slideshows and Page Editor

I want my content editors to be able to do anything in the Page Editor that they can do in the Content Editor, giving them the choice to use what they prefer. However, when the editor wants to change a slideshow on a page they must do this through the Content Editor, because when they try to make a change in the Page Editor they get the following error message (or a message similar to it):

Uncaught DOM element is expected to have a non-empty chromes collection.
    Sitecore.PageModes.Chrome.Base.extend._clickHandler
    c
    d.event.handle
    k.handle.m

I was talking about this with another Sitecore developer during a Sitecore Usergroup, and what he suggested was instead of showing it as a slideshow in Page Editor mode, have the images and text be underneath each other. While that’s certainly a valid option, I don’t really like it because it takes away one of the strengths of the Page Editor: See what the result will be while you are changing it.

What I decided to do was create an additional button in the Page Editor and create my own class that inherits from Sitecore’s FieldEditorCommand class.

Create the command

I’ve created a class which inherits the Sitecore.Shell.Applications.WebEdit.Commands.FieldEditorCommand.

For display purposes I’ve overridden the CommandState as well, to disable the button if the page doesn’t have a slideshow. I simply check this by getting the value of a field called Source:

public override CommandState QueryState(CommandContext context)
{
  if (string.IsNullOrEmpty(Sitecore.Context.Item["Source"]))
  {
    return CommandState.Disabled;
  }

  return base.QueryState(context);
}

The Execute method will call the field editor, so we’ll need to implement that:

public override void Execute(CommandContext context)
{
  Assert.ArgumentNotNull(context, "context");
  if (context.Items.Length >= 1)
  {
    var args = new ClientPipelineArgs(context.Parameters);
    args.Parameters.Add("uri", context.Items[0].Uri.ToString());
    Sitecore.Context.ClientPage.Start(this, "StartFieldEditor", args);
  }
}

Now all that’s left is overriding the GetOptions method. The Slideshow field is a dropdown pointing to an item with a multilistfield called Images. We’ll want to edit the Title, Keywords and Description fields of the respective images:

protected override PageEditFieldEditorOptions GetOptions(ClientPipelineArgs args, NameValueCollection form)
{
  Assert.IsNotNull(args, "args");
  Assert.IsNotNull(form, "form");
  Assert.IsNotNullOrEmpty(args.Parameters["uri"], "uri");

  Sitecore.Data.ItemUri uri = Sitecore.Data.ItemUri.Parse(args.Parameters["uri"]);
  Assert.IsNotNull(uri, "uri");

  var item = Sitecore.Data.Database.GetItem(uri);
  Assert.IsNotNull(item, "item");

  var slideShowItem = Sitecore.Context.ContentDatabase.GetItem(item["Source"]);
  Assert.IsNotNull(slideShowItem, "slideShowItem");

  var fields = new List<Sitecore.Data.FieldDescriptor>();
  fields.Add(new Sitecore.Data.FieldDescriptor(slideShowItem, "Images"));
  var images = (MultilistField)slideShowItem.Fields["Images"];
  foreach (var image in images.GetItems())
  {
    foreach (string fieldName in GetFieldNames())
    {
      fields.Add(new Sitecore.Data.FieldDescriptor(image, image.Fields[fieldName].Name));
    }
  }

  var options = new PageEditFieldEditorOptions(form, fields);
  options.PreserveSections = false;
  options.DialogTitle = "Edit the slideshow");
  options.Icon = item.Appearance.Icon;
  return options;
}

private IEnumberable<string> GetFieldNames()
{
  yield return "Title";
  yield return "Keywords";
  yield return "Description";
}

Note that I’m not only adding the image fields (Title, keywords and description), but also the multilistfield in which the images are selected (as highlighted in line 17).

The one drawback of course is that we need to save the changes we make before the page reflects them, and if we change the Images multilistfield we’ll need to re-open the editor to see that reflected on our popup.

Include the command:

I’ll use an include file to patch my config.

  <?xml version="1.0"?>
  <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
      <commands>
        <command name="pageeditor:slideshow" type="TestApplication.PageEditorSlideshows.PageEditorSlideshows, TestApplication" />
      </commands>
    </sitecore>
  </configuration>

Add the button:

To add a button to the Page Editor we’ll need to open the Core database and navigate to /sitecore/content/Applications/WebEdit/Ribbons/WebEdit. I’ll create my button under the existing ‘Page Editor/Edit’ node:

tree

I’ve added the following field values:

Header:   Edit slideshow
Icon:         Applications/32×32/photo_scenery.png
Click:         pageeditor:slideshow
Tooltip:    Edit the slideshow

Now it’s time to test it!

This is how it looks on a page without a slideshow attached:

ribbon_disabled

We could’ve hidden the button instead (by returning CommandState.Hidden instead of CommandState.Disabled), but I tend to just disable them to keep the ribbon consistent across the board.

This is how it looks on a page which does have a slideshow:

ribbon_enabled

If we then click our new button:

popup

Perfect.

Page Editor JavaScript difficulties

When I was implementing the WeBlog module on one of my projects, I ran into a couple of strange JavaScript errors when using the Page Editor to insert components. I noticed two things about the issues:

  1. They only showed up on WeBlog pages
  2. They only showed up when I tried adding a component to any place except the top – if an existing component was there and I’d add it below it would work.

The errors:

When I clicked Component – Add to here in the Page Editor, I got one of the two following errors when I looked at them in Chrome:
Could not find the rendering in the HTML loaded from server
    Sitecore.PageModes.ChromeTypes.Placeholder.Sitecore.PageModes                                                                  .ChromeTypes.ChromeType.extend._frameLoaded
    c 
    d.event.handle 
    k.handle.m

OR

insertRendering
    Cannot find rendering chrome with unique id: 17A541CA4BC34DCBB60F783861BF3191
    Sitecore.PageModes.ChromeTypes.Placeholder.Sitecore.
PageModes.ChromeTypes.ChromeType.extend.insertRendering
    options.success
    f.resolveWith
    v

In addition to that, whenever I clicked anywhere on the page to change one of the renderings/ a piece of text, I also get:

Cannot parse command parameters

Whichever error I got, I would also get a JavaScript popping up saying:

An error ocurred.

I could however still change things around successfully, whether that’s adding or changing text, image, link or even changing a Datasource using ‘Edit the component properties’.

The solutions:

There were actually two issues I was dealing with here, not one:

  1. The first issue was known issue: The Page Editor did not save renderings inserted to a placeholder if the placeholder contained renderings that specified the placeholder key using mixed case. (367952)
    This issue was actually fixed in SItecore 6.6 Update 4 (as specified on SDN)
    After I changed my placeholder keys from camelcase to lowercase, the popup JavaScript error went away, as did the ‘Could not find the rendering in the HTML loaded from server’ error. The second issue remained, however.
  2. The second error is actually a bug (which has been registered with Sitecore Support).

So the first solution was reasonably simple, the second one a bit confusing. Here’s what was going on:

Widgets in WeBlog are based on the /sitecore/templates/Modules/WeBlog/Blog Sublayout template, rather than the default /sitecore/templates/System/Layout/Renderings/Sublayout template. This was so the output of the widgets can be cached per blog.

The Page Editor checks whether the template of the inserted control is the /sitecore/templates/System/Layout/Renderings/Sublayout template. If the control is not based on that template  the control can not be identified as a Sublayout.

Initially I changed the template of the widgets to the default, after that Sitecore Support did come back to me with a possible workaround: a changed version of the /sitecore/shell/Applications/Page Modes/Utility.js file. I have not implemented this (as I didn’t need the caching per blog), so that hasn’t been tested.

Big kudos to Sitecore Support for helping me figure out the cause for these problems.

SlowCheetah and Sitecore

At my current project we have a lot of different config files. We have multiple:

–        web.config
–        app_config\connectionstrings.config
–        app_config\app_settings.config
–        app_config\include\project.config

We’ve set this up with SlowCheetahwhich is an awesome tool to easily transform config files based on the build we have selected.

When I first installed it, I happily added all my transforms to the files above, and I built. My Visual Studio AfterBuild step copies and pastes all my stuff across to my installed Sitecore instance, and I browsed to it. That’s when I got the following error:

uploadwatcher

Turns out, in my eagerness to deploy this, I forgot I actually copied ALL config files across to my local path, not just the one I’m building for. It seems that Sitecore does not like having multiple files like that in the Include folder. When I deleted those files, it was fixed.

After that, I removed the transforms from the AfterBuild step and now it’s happily building away. 

Custom ordered treelist

On a different blog platform, 3 years ago, I created the following entry, which I thought would be nice to have in my ‘own’ blog platform as well.

On a project I was working on recently all the banners were put into a common folder. In the common folder was a hierarchical structure, which would make it easier to find banners when I wanted to select them in my banners field. On the offside, if you have a lot of banners, it can become very difficult to find the one you want to change.

Current situation

In my case it would be sufficient if I could have the name of the parent item in front of the selected item.

To accomplish this, I needed to complete 3 tasks:

  1. Create a custom class that inherits the TreeList
  2. In the Core database, create the new field type
  3. Set our field type in the template

Create a custom class
This custom class needs to override the OnLoad method, since that calls the method we are actually interested in: RestoreState.

protected override void OnLoad(EventArgs args)
{
  base.OnLoad(args);
  Assert.ArgumentNotNull(args, "args");
  if (!Sitecore.Context.ClientPage.IsEvent)
  {
    this.RestoreState();
  }
}

Then we pretty much keep the original RestoreState the way it was as well, except for a few lines.

private void RestoreState()
{
  Listbox _listBox = (Listbox)FindControl(this.ID = "_selected");
  string[] strArray = this.Value.Split(new char[]{'|'});
  _listBox.Controls.Clear();
  if (strArray.Length > 0)
  {
    Database contentDatabase = Sitecore.Context.ContentDatabase;
    if (!string.IsNullOrEmpty(this.DatabaseName))
    {
      contentDatabase = Factory.GetDatabase(this.DatabaseName);
    }

    for (int i=0; i < strArray.Length; i++)
    {
      string str = strArray[i];
      if (!string.IsNullOrEmpty(str))
      {
        ListItem child = new ListItem();
        child.ID = Sitecore.Web.UI.HtmlControls.Control.GetUniqueID("I");
        _listBox.Controls.Add(child);
        child.Value = child.ID + "|"+ str;
        Item item = contentDatabase.GetItem(str);
        if (item != null)
        {
          child.Header = item.Parent.DisplayName + ":" + item.DisplayName;
        }
        else
        {
          child.Header = str + ' ' + Translate.Text("[Item not found]");
        }
      }
    }

    SheerResponse.Refresh(_listBox);
  }
}

Because the base.OnLoad() runs first, the listbox is already filled. To prevent it from containing double values, we need to clear it first. Then we run through the selected values again, putting the displayname of its parent in front.

Now, all we need to do is add the following to the web.config, in the <controlsources> node.

<source mode="on" namespace="CustomTreeList" assembly="CustomTreeList" prefix="MyProject" />

Create the field type

What I did here was duplicate the TreeList type in the Core database, and set the control field to what we just added in the web.config, followed by the classname.

Set the field type
In the template where we have the field, we can now select the Custom Treelist we just added.

If we then check the banner field again, we get the following:

Thank you for reading, I hope this blog helps you.

If you have any suggestions or a better way, please let me know.

As commented on the original post:

To cause links in this field type to appear in the links database, you may want to duplicate the existing treelist entry in the /App_Config/FieldTypes.config file, and change only the name attribute in the new line to match the name of your custom field type.

Page Editor explorations

I read this StackOverflow question, and that made me wonder where the field buttons actually come from. I’d never looked into this before, so here goes.

The summary of the question is:

Can we disable the buttons on fields in Sitecore’s Page Editor, not only based on some field on some item, but also based on which field we have?

The buttons are added in the <getChromeData> pipeline. My initial answer to the question was that one could override the Process method in the GetFieldChromeData class, but that struck me as a little extreme. Especially because it’s a Sitecore processor, so if the Sitecore code gets upgraded, the override has to be changed as well.

So then I figured one could add a processor to the pipeline. I added:

<processor type=TestApplication.RemoveButtons, TestApplication/>

Directly after:

<processor type=Sitecore.Pipelines.GetChromeData.GetFieldChromeData, Sitecore.Kernel/>

My RemoveButtons class looks like this:

public class RemoveButtons : GetChromeDataProcessor
{
    public override void Process(GetChromeDataArgs args)
    {
Assert.ArgumentNotNull(args, “args”);
Assert.IsNotNull(args.ChromeData, “Chrome Data”);

        if (“field”.Equals(args.ChromeType, StringComparison.OrdinalIgnoreCase))
{
Field argument = args.CustomData[“field”] asField;
Assert.ArgumentNotNull(argument, “CustomData[\”{0}\”]”.FormatWith(new object[] { “field” }));

            if (argument.Name == “Title” && MainUtil.GetBool(argument.Item[“NeedsToBeCheckedToPersonalize”], false))
            {
args.ChromeData.Commands.RemoveAll(delegate(WebEditButton b)
{
return b.Header == “Personalize”;
});
}
}
}
}

First I’m checking whether it’s a field or placeholder or rendering. After that, I instantiate the field from the CustomData. Then, if the current field is the Title field and the checkbox NeedsToBeCheckedToPersonalize (which is, just to be clear, a random checkbox I added) is ticked, remove all buttons that are Personalize.

The result for the Title field:

Image

And my subtitle field:

Image

The two fields are on the same item, and of the same type (both are single-line text).

Placeholder Settings

I was playing around with Placeholder Settings, and thought it would be a nice topic for my next blog post – especially since I wanted to customize it a bit.

Adding placeholder settings

Placeholder settings can be added in different ways. The placeholder settings items are saved in the /sitecore/layout/Placeholder Settings folder.

The ‘global’ placeholder settings need a placeholder settings item to be created, then assigned a placeholder key (which needs to match the placeholder key in the sublayout) and can be assigned its allowed controls.

Another way is to add placeholder settings on the Layout details. The placeholder settings item will end up in the Placeholder Settings folder, but will not have a Placeholder Key set in the item itself. It will be bound to a placeholder on the layout.

Now, I had an article page, on which people could leave their comments through a Comment widget. This widget is enabled on some articles, but Content Editors can also remove it if they don’t want this functionality. This means they’d also have to be able to add it back again if they’ve removed it and changed their minds later – but I only want it on the page once, since it doesn’t make too much sense to have the same widget on the page twice.

When looking in the Core database (We can see all things Page Editor in /sitecore/content/Applications/WebEdit/Ribbons/WebEdit), we can see which command gets triggered by clicking the ‘new component’ button:

Image

Using the Commands.config file, this translates to the following command:

<command name=webedit:addrendering” type=Sitecore.Shell.Applications.WebEdit.Commands.AddRendering,Sitecore.Client/>.

In the AddRendering class there’s a private static void defined called RunGetPlaceholderRenderingsPipeline, which kicks off a pipeline like this:

CorePipeline.Run(“getPlaceholderRenderings”, args);


If we then look in the web.config and see what the getPlaceholderRenderings pipeline does:

<getPlaceholderRenderings>

<processor type=Sitecore.Pipelines.GetPlaceholderRenderings.GetAllowedRenderings, Sitecore.Kernel/>
<processor type=Sitecore.Pipelines.GetPlaceholderRenderings.GetPredefinedRenderings, Sitecore.Kernel/>
<processor type=Sitecore.Pipelines.GetPlaceholderRenderings.RemoveNonEditableRenderings, Sitecore.Kernel/>
<processor type=Sitecore.Pipelines.GetPlaceholderRenderings.GetPlaceholderRenderingsDialogUrl, Sitecore.Kernel/>

</getPlaceholderRenderings>

The easiest solution would then be to add a processor to the pipeline.

A processor needs a Process method, and in our case will expect GetPlaceholderRenderingsArgs as well.

I figured I would start off simple and just make it so I can’t add any control to the placeholder if that control is already there.

Here’s the code I wrote for this:

publicvoid Process(GetPlaceholderRenderingsArgs args)
{
    Assert.IsNotNull(args, “args”);
    List<Item> placeholderRenderings = args.PlaceholderRenderings;

    if (placeholderRenderings != null)
    {
        placeholderRenderings.RemoveAll(x => args.LayoutDefinition.Contains(x.ID.ToString()));
    }
}

Relatively easy – go get PlaceholderRenderings, remove all renderings that are already in the LayoutDefinition. Note that this means we can now only have the control once per page as we are looking at the complete LayoutDefinition and not just the controls added to a specific placeholder.

Then I added my processor before

<processor type=Sitecore.Pipelines.GetPlaceholderRenderings.GetPlaceholderRenderingsDialogUrl, Sitecore.Kernel/> in the <getPlaceholderRenderings>node

in the <getPlaceholderRenderings> node:

<processor type=TestApplication.PlaceholderSettingsTest, TestApplication/>

I then set up my page. In my page I can add either Random Sublayout or Random Rendering.

Image

I added Random Rendering to the page, so I would expect Random Sublayout to be the only addable control.

So – Add component -> Add to here…
Image

Perfect!

Of course, there should be some modification, such as having the ability it have controls only once per placeholder rather than once per page like this example.