Monday, March 11, 2019

Working with external model data in Sitecore SXA variants

SXA and variant definitions

If you are working with SXA, I assume you heard about rendering variants (if not, you're doing it wrong).  There is some documentation on the options you get when creating your own rendering variant: https://doc.sitecore.com/developers/sxa/18/sitecore-experience-accelerator/en/create-a-rendering-variant.html

There is one variant definition option that I would like to highlight here, and that is the "Model".
Model: displays properties from the current Model. The SXA renderings model inherits from Sitecore.XA.Foundation.Mvc.Models.RenderingModelBase. In the Property path field, you can enter the path to the property that you want to display.

 You can use this Model field to display any data you want. I used it to display data from a custom database, but it could also be data from a webservice or any datasource you like. How to do this..

Create your (custom) model

You should start by creating your own component: https://doc.sitecore.com/developers/sxa/18/sitecore-experience-accelerator/en/create-a-new-sxa-module.html  (or copy an existing one and change the data, but that is another topic). What you need is a custom controller, model and probably also a view. Create the necessary items in Sitecore and create your variant definition.

To write the needed code, you can refer to my blog article on custom renderings with variants or to the official docs. The important part here is actually the model class. All (custom) properties you add there will be available in the variants. Fill the data in your repository (called from the controller).
Suppose we have this model:
public class CustomModel: VariantsRenderingModel
{
  public string CustomProperty { get; set; }
  public CustomClass CustomClassProperty { get; set; }
}
and we would like to show the CustomProperty value and a property from the CustomClassProperty as well. Let's create our variant...

Create the variant definition 

In the variant definition that was created for your custom component, you can add a "Model" field.
You can select a tag (just as in other variant fields) and set a css class or other attributes. The Property Path is the really interesting one: here you can define the property from your Model you want to show. As you can see in the screenshots, you just need to add the name of the property as it is in your model class. And you can also get properties from a property..  (like the "Name" property from the CustomClass that was a property of my model).

You will notice that the variant field does not have all properties like a "Field" variant field - e.g. you cannot create a link (which makes sense of course). 

Model data

I am struggling with a few things still..  example: my model has a url and I would like to show that in the variant..  but I can only show the value of the model property - I can't seem to embed this in a "a href..".  I guess there will always be such edge cases that go just that one step further than what is possible ootb. I could go for a quick fix and have my model give me the html for the link..  and if I find a cleaner solution, I'll update the blog :)

Conclusion

Displaying all sorts of external data with variant in SXA was actually easier than expected. Not the first time the product has amazed me in a good way 😃



Wednesday, January 2, 2019

Extending SXA search queries with SearchQueryTokens

Tokens in Sitecore Search

SXA comes with a set of tokens that you can use in search queries to apply additional search filters.

They can be very useful when creating queries that will be used on multiple pages. In my case, I wanted to extend a search scope with some business logic based on the value of a field on the current page.

The "ItemsWithTheSameValueInField" came close to what I needed but not entirely..  but these tokens can be easily extended. Let's implement our own ResolveSearchQueryTokensProcessor!

ResolveSearchQueryTokens Processor

To start my implementation, I had a (dot)peek at the ItemsWithTheSameValueInField source code as mine would be rather similar. First thing to do is define the key for our SXA token:
[SxaTokenKey]
protected override string TokenKey => FormattableString.Invariant(FormattableStringFactory.Create("{0}|FieldName", "YourTokenName"));

Next thing is override the Process method:
public override void Process(ResolveSearchQueryTokensEventArgs args)
{
  if (args.ContextItem == null)
    return;

  for (var index = 0; index < args.Models.Count; ++index)
  {
    var model = args.Models[index];
    if (model.Type.Equals("sxa", StringComparison.OrdinalIgnoreCase) && ContainsToken(model))
    {
      var fieldName = model.Value.Replace(TokenPart, string.Empty).TrimStart('|');
      var field = args.ContextItem.Fields[fieldName];
      if (field?.Value != null)
      {
        args.Models.Insert(index, BuildModel(fieldName, field.Value));
      }

      args.Models.Remove(model);
    }
  }
}

Let's see what is happening here:

Find our SearchStringModel

The code loops over all the SearchStringModel objects in the arguments. Each SearchStringModel defines a part of the query and has a value, a type and an operation.  When looking at the search box interface like in the above image:
  • the type of the SearchStringModel is the first part (in the image above that would be "Sxa") 
  • the value of the model is the second part (e.g. "ItemsWithTheSameValueInField|FieldName")
  • the operation is "must", "should" or "not"

So we search until we find a model of type sxa. The ContainsToken function adds this check to make sure we have our the token that corresponds with our code:
protected override bool ContainsToken(SearchStringModel m)
{
  return Regex.Match(m.Value, FormattableString.Invariant(FormattableStringFactory.Create("{0}\\|[a-zA-Z ]*", "YourTokenName"))).Success;
}

If the model is found, we fetch the provided FieldName and test if the context item has a value in this field. If that is the case, we build a new SearchStringModel to be inserted in the list. The current model is removed as that has been handled.

Build a SearchStringModel

protected virtual SearchStringModel BuildModel(string fieldName, string fieldValue)
{
  var name = SomeBusinessLogicWithFieldName(fieldName);
  var value = SomeBusinessLogicWithFieldValue(fieldValue);
  return new SearchStringModel("custom", FormattableString.Invariant(FormattableStringFactory.Create("{0}|{1}", name, value)))
  {
    Operation = "must"
  };
}

I am building a SearchStringModel based on the field name and value. The model type is "custom" as we are not using predefined Sitecore fields. You can test such queries in a search box as well. If you would use them in a SXA scope, the scope string would look like "+custom:fieldName|fieldValue".


ResolveSearchQueryToken configuration

To tell Sitecore that we have our own tokens, we have to add the processor to a pipeline:
<sitecore>
  <pipelines>
    <resolveSearchQueryTokens>
   <processor type="YourNameSpace.YourType, YourAssembly" resolve="true" patch:before = "*[1]" />
    </resolveSearchQueryTokens>
  </pipelines>
</sitecore>
With this patch I am including our tokens as the first one in the list.

Once this config patch and the code is available, your Sitecore instance will detect the SxaToken and it will be available in the search list once you type "Sxa".


Conclusion / Issues

I have used this succesfully in scopes with SearchResults components to make them aware of the context item (adapting the actual query based on a field value of the current page). You can of course make the code as complex as you would like
  • pass more parameters in
  • create multiple models based on your data
  • ...

I did find one issue though: in the SearchResults component we have a ContextItem. But that is not always the case. Our code will not break (we do check for null) but the extra filtering is not happening in that case. Sxa's checklist facet component -in 1.7.1- does not send the ContextItem and it does make sense that a facet uses the same data as the corresponding results component.
I've created a Sitecore Support ticket to fix this and did get a patch (ref. 301406).

But (again) a very nice extension point for the Sitecore Experience Accelerator.

Thursday, December 20, 2018

SXA Pagination on a custom component

SXA Pagination component


The SXA pagination component adds a paging feature to your lists. Out of the box, it can work together with the (also out-of-the-box) PageList, FileList and EventList components. But what if you want this to work with your own component?

A custom paginable component


Sitecore StackExchange to the rescue! I found this nice answer https://sitecore.stackexchange.com/q/8105/237 that made me think it was going to be easy...

So for starters I have:
  • a controller with an action method
    (and a view with variants and such, but that is not in scope here - I've written another post if you want to know how to create our own rendering with variants)
  • a repository that gets items from the index (based on a Search scope.. might be my next post)
As I am getting too many items at once, I would like to add paging to this component. Preferably the out-of-the-box paging component (so I don't need to write it).

The answer on SSE indicated that a bit of configuration would do it.. but unfortunately it did not. My repository does not inherit from ListRepository (nor implements IListRepository) as that doesn't fit my needs. The steps I had to take:

Configuration in Sitecore

I'm not going to describe how to add your rendering to Sitecore and SXA - that is described in the official docs.

Your rendering does need rendering parameters. The rendering parameters temlate for your component should inherit IPaginable and IPagination (both from the Feature/Experience Accelerator/Page Content/Rendering Parameters folder).

This will give you the necessary parameters to make your component compatible with the pagination.

The parameters are described in the official docs on the pagination subject but your main focus is:
  • Page Size: the number of items on a page
  • List Signature: a string that will be used as signature - the same string needs to be entered in the rendering parameters of the pagination component to tell both renderings that they belong together. Note that this string will also appear in your querystring when paging..

The configuration mentioned in the SSE answer is now moved into the Sitecore item. So, on your rendering item you need to add IsPaginationEnabledRendering with a value true to the OtherProperties.


Changes to the repository

Our repository has a function GetModel to create the model for the controller. It actually calls VariantsRepository.FillBaseProperties and adds the items we fetch from the index to the model - the model inherits from VariantsRenderingModel.

This function needs an extra parameter of type IListPagination. This parameter will provide the Skip and PageSize information that we need to get the right page from the data. So this is the place where we reduce the full data set to the requested page.

Change to the controller

To be compatible with the Pagination component, our controller needs to derive from the PaginableController. We had to override 2 functions:

PaginationConfiguration

protected override IListPagination PaginationConfiguration
{
  get
    {
      var listPagination = ListPaginationContext.GetCurrent().Get(Rendering.Parameters["ListSignature"]);
      if (listPagination != null)
      {
        return listPagination;
      }

      var parameters = Rendering.Parameters;
      var scope = parameters["Scope"];
      return new ListPagination(parameters.ToDictionary(p => p.Key, p => p.Value), searchResultsRepository.GetItems(scope).Count());
    }
}
The first function to override is the one that get the PaginationConfiguration. The only changes made to the original function are the call to the repository - this needs an extra parameter in our case (and is not the default ListRepository as mentioned before).

Once this function is in place, use it to pass the PaginationConfiguration to the repository (GetModel - see above). At this point you should see that your component already can select the correct page from the data.


OnActionExecuting

We couldn't see any pagination yet though. To get that working, we needed to override one more function:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
  if (ListPaginationContext.GetCurrent().Initialized)
  {
    return;
  }

  var paginationService = ServiceLocator.ServiceProvider.GetService<IPaginationService>();
  foreach (var sitecoreRendering in Sitecore.Mvc.Presentation.PageContext.Current.PageDefinition.Renderings.Where(r => paginationService.IsPaginationEnabledRendering(ID.Parse(r.RenderingItemPath))))
  {
    var scope = Rendering.Parameters["Scope"];
    var itemsCount = searchResultsRepository.GetItems(scope).Count();
    ListPagination listPagination;
    if (itemsCount <= 0)
    {
      var parameters = sitecoreRendering.Parameters;
      listPagination = new ListPagination(parameters.ToDictionary(p => p.Key, p => p.Value), searchResultsRepository.GetItems(scope).Count());
    }
    else
    {
      var parameters = sitecoreRendering.Parameters;
      listPagination = new ListPagination(parameters.ToDictionary(p => p.Key, p => p.Value), itemsCount);
    }

    IListPagination pagination = listPagination;
    if (!ListPaginationContext.GetCurrent().Contains(pagination))
    {
      ListPaginationContext.GetCurrent().Add(pagination);
    }
  }

  ListPaginationContext.GetCurrent().Initialized = true;
}
This function will initialize the Pagination Context. But again, by default it will use the ListRepository and we didn't have that one - so we had to override it and use our own repository.

Conclusion

It's no rocket science if you know how to do it.. :)

I did spend some time figuring this out - but next time it will go much much faster and I will have paging on my components in no time. And maybe with this guide you will too..

ps: using SXA 1.7.1 at time of writing

Monday, October 29, 2018

Sitecore 9.1 Forms - Prefilling fields & security

Sitecore 9.1 Forms

The Forms feature has 2 major new features in version 9.1 of the Sitecore platform. You'll get conditional fields and prefilling of fields. It's about that last one I wanted to give you a little bit of advice...

Prefilling fields

Without going into too much details (Sitecore 9.1 is only released in tech preview at the time of writing - release version will be available soon) the prefilling is based on value providers - classes that will get the values based on a parameter defined with the field. 

Prefilling is a nice feature, as it makes the user experience better and the conversion rate of the forms higher - visitors are lazy ;)

But.. before you jump into prefilling everything you think you know about me, read this article: https://www.campaignion.org/blog/all-you-have-know-about-pre-filling-forms

It is a good overview of what you can an can't do when prefilling forms. And there is one option related to Sitecore that I want to address.

Prefilling xConnext data

Sitecore environments usually have (a lot of) information about their visitors in the xDB database. It is very tempting to use that data to prefill the forms. But there might be catch..  

Consider the following scenario (which is not so far fetched) on a website:
  • a form with just an e-mail address to subscribe to the newsletter
  • a contact form (with email, name, address, ...)
  • all forms will identify the contact
  • all forms use prefilling on fields where possible (all ootb xXonnect fields)

So, what is the catch? Well, if I subscribe you (yes, you.. not me) to the newsletter and the system already knows you it will attach my session to your profile. No harm done, until I visit the contact form and see all your contact data. 

How do you prevent this? You probably want to keep the contact identifcation as that gives your marketers the information they want in the xConnect reports. Adding a Captcha to your form will help a bit (and this might be a good idea for other reasons as well). But actually, the only good solution is -as described in the mentioned post above- to only prefill data from a datastore (like xDB) when your user is really identified - meaning logged in or truly identified through an identifier in the querystring. 

Conclusion

Prefilling is a nice new feature (and it looks better than the WFFM version). And I hope the community will start sharing value providers. But be aware of what you do with your visitors data.. after all, you don't want to end up in a next version of Mikkel Rømer "white hat hackers" session.

Wednesday, October 24, 2018

What's new in SXA 1.8

In the very near future, SXA 1.8 will be available for all - together with Sitecore 9.1.
I had the privilege to take an early look and it looks good.  So ..

What's new?

I'll try to go over the new stuff, based on my notes from the Symposium session by Adam, things I heard in the community and the documention - and provide screenshots were possible.

Installation

Let's start in the beginning. The installation guide mentions "Sitecore Experience Platform 9.0 update 2 or 9.1" as prerequisite. So no Sitecore 8 anymore.. 

As the need to support Sitecore 8 limited the SXA team for certain features, this is a good evolution.

For a developer

  • Bootstrap 4

    This one was announced so no big news, but front-end people will still be very happy that this made the release. Bootstrap 4 comes as an extra option for the grid system - bootstrap 3 is also still available (probably for upgrade scenarios).
  • Link field best practices

    This is a Powershell script available on templates (folders) that will verify the source fields of the templates and suggest better options regarding multisite solutions (e.g. the use of the $site token).
    I must admit that when looking for this script, I found even more interesting (already existing) scripts that come with SXA. Nice...

  • Embedded renderings in rendering variants

    Personally I think this is one of the coolest new features in this version. When this was demoed in the Symposium session, quite some people apparently had no clue what to do with this. But it gives us even more options to get the renderings just the way we want them. Yes, no we can add a breadcrumb within a hero section filled with just page content... and our logic stays were it should be (in the breadcrumb component). I'm sure I will use this one! (to be honest, I already did try this on my preview-test environment and it works like a charm.. )
  • Adding modules

    On tenants and sites, we get scripts to add (missing) modules. Running the script will list all missing modules and you can install the selected ones.
    But, we can also do this the other way: adding a (new) module to multiple sites at once. On your module in /sitecore/System/Settings/Foundation/ or /sitecore/System/Settings/Feature/ you'll find the script. 
  • Custom html (views) per site

    The SXA components have their views, located in the Views folder. That remains the default and fallback location. But you can on each site define -on the Settings item- a CustomRenderingViewPath. This path can contain custom views for the renderings in that site. They need to use the same names and folders - if they do not exist the default is used (so you don't need to copy all views if you just want to change one of them).
    Just like the previous topic a nice feature for multi-site solutions.

For an editor

  • Defining the sections of the toolbox

    Up until version 1.7.1, the toolbox sections were determined by the paths in your renderings folder in the layouts section of Sitecore. This was not very user friendly as you want to group your renderings based on their feature (Helix...). That makes sense to the developers but not to the editors. We already had the "available renderings" section for each site but that wasn't used due to .. well, they had their reason in the early versions but that was gone now. So now we have the option to define the sections in the toolbox based on the available renderings. Note that by default the necessary checkbox (on the "available renderings" item in each site) is off.  As I asked for this one, I'm very happy to see it..
  • Updated editing modes

    The wireframe mode got a revamp and is looking quite a bit better. And a new mode was added: grayscale.
  • Highlighting partial designs

    When hovering over a partial design in the Partial Designs dropdown section in the Experience Editor the current partial design is shown in green border(s) in the editor. This makes it clear for editors where the partials are located and what they contain.
  • Cross site linking

    This might seem very trivial but in a multisite SXA environment it's not always so. You now get the option to set on each site what the "linkable" sites are.
    By default it is the current site only, but you can select all linkable sites or all linkable sites from the current tenant. To define a site as "linkable", you should mark the checkbox in your site's "settings/site grouping/site".


For visitors

WCAG 2.0 compatibility


Some additions were made for the accessibility compliance of SXA. On my test environment the "tab" key seemed to work, and I added the "skip" meta component:




Conclusion

I can be very short on this: it seems like a very nice version with some exciting features. And the upgrade guide is only 9 pages... :)

Monday, October 22, 2018

Using tokens in Sitecore SXA variant data attributes

Sitecore SXA variant definitions


Variants are an important part of SXA. Although SXA is so much more, learning how to use these is rather crucial for anyone who want to create sites with SXA. If you need more information on this, a good place to start are these official docs:
You can do a lot with these variants. Especially when you discover the power of the "Reference" in a variant definition, lots of scenario's become possible...  and there are some hidden gems. 

Our case

We had to create a rendering that displayed blocks of content - so far no problem, this is just displaying fields from a list of items. But the editor had to be able to set a background on each individual block (and some other properties). Short version: we had to be able to set a css class on every block. We could have asked our editors to create a rendering for each block they needed and set parameters on that rendering, but as we had reasons not to do that.

So we started looking at the variant definitions options, tried a few things without luck and ended up asking help on Stack Exchange: https://sitecore.stackexchange.com/q/12806/237

As usual, the SXA dev team came to the rescue (- thx Dawid for this one) and showed me a little hidden gem.

Using tokens in data attributes

Apparently you can use some tokens in the data attrbutes that are available in each part of the variant definition. As shown in the answer on SSE, we can use "class" as data attribute and fill that with a value from a field on the current item by using the $(fieldName) notation.

This means we can use a Reference in our variant to loop over a set of items, and for each of those items render a section (e.g. "div") with a class on it. That'll do it..

But.. we had one small issue. The default implementation will work with text fields. But for link-type fields it will generate a link to the target item. We don't want our editors to type values for fields like this - it should be a selection. So we got a patch from Support that extended this functionality to ValueLookupFields to fetch the value.

Now we could give our editors a dropdown selection of styles and use those in our variant. That's what we needed! (as soon as the site goes live, I can add a screenshot of the result)

I had a look at what Support provided for us and although it is not designed to be expanded easily, we can do even more with this (and so we did).

public class RenderSection : Sitecore.XA.Foundation.RenderingVariants.Pipelines.RenderVariantField.RenderSection
{
 protected override string GetAttributeTokenValue(string fieldName, Item item)
 {
   Field field = item.Fields[fieldName];
   if (field == null)
  return string.Empty;
   ....
   return base.GetAttributeTokenValue(fieldName, item);
 }
}
<sitecore>
    <pipelines>
      <renderVariantField>
        <processor patch:instead="*[@type='Sitecore.XA.Foundation.RenderingVariants.Pipelines.RenderVariantField.RenderSection, Sitecore.XA.Foundation.RenderingVariants']" type="Sitecore.Support.XA.Foundation.RenderingVariants.Pipelines.RenderVariantField.RenderSection, Sitecore.Support.00000" resolve="true" />
      </renderVariantField>
    </pipelines>
</sitecore>

We noticed they had overwritten the function that handles the tokens in case of a variant section (if you want this to work on other variant options like text or field, you need to overwrite those too). Where Sitecore/SXA is designed to be extended, they usually make it easier for us :)

So as mentioned, it is not (yet) designed to be expanded but it can. You could write any logic you want in the above funtion based on the "fieldName" (which propable doesn't even need to be an existing field name).

This opens a new set of possibilities to render everything we need with SXA variants!

Just one little request for a future version:  make this easier to extend.. ;)

Wednesday, August 8, 2018

Sitecore SXA, rules engine & containers in partial designs

SXA and partial designs

While working on a Sitecore site with SXA I created several partial designs (as you should when working with SXA). What a partial design is, and how it is used to create page designs that define the layout of your pages in SXA can be read in the official docs.

What you should know about partial designs is that you edit them on the partial design item itself, not on the pages where they are used. On the final page in the experience editor, you can edit the content shown by the partial design but you cannot change the design itself (add, remove or change components or their properties). Still, partial designs are very useful and help you keep the number of components on a page reasonable. On many occasions though, you will want the editors to be able to add components to the page that are not preset in the partial design. This can be done by adding a "container" component in the partial design (note: this is one way - not necessarily the way) on the spot where you want to give the editors a new placeholder (a placeholder is a spot where new components can be added). And so we did...

And then came the designers... and they gave our pages a very nice design. But they had a problem: empty containers!

The containers that were added to the partial design might remain empty. And with our design, this made it hard for the designers to get the required result. So: lets remove those empty containers.

SXA & the Container component

As mentioned, the container component was just added to have a placeholder available. It's a typical "structure" component that adds a wrapper for other renderings.

In the experience editor we want the placeholder to be always available (to be able to add components) but in normal or preview mode we want it gone when no renderings were added. 

The container component generates a placeholder named "container-x" where x is a dynamic placeholder id. 
Hint: when using the container, you should also set the placeholder restrictions for the generated placeholders - as you should do always probably ;)  More info on how to do this with SXA can be found on the official docs.
 To remove the components, I used the Sitecore rules engine. 

Using the rules engine

We want to use conditional rendering to hide the component when the related placeholder is empty. The action to hide a rendering is out-of-the-box available, so we only need the "condition". 

In this condition, we first check the PageMode to make sure we are in normal or preview mode.

We could ask the administrator adding the rule and using our condition to pass the related placeholder as a parameter (the easy way) but we can actually retrieve it.  The rendering (container) has a parameter called DynamicPlaceholderID that contains the generated dynamic placeholder id. In our case we hard-coded the prefix "container-" but you might do something pretty with that as well.

Last thing to do is find out if there is a rendering on the page in that placeholder..  which can be done with a query on the renderings list. 

Let's wrap this up and show the code:
public class WhenPlaceholderIsEmpty<T> : WhenCondition<T> where T : RuleContext
{
    protected override bool Execute(T ruleContext)
    {
        Assert.ArgumentNotNull(ruleContext, "ruleContext");
        if (!Sitecore.Context.PageMode.IsNormal && !Sitecore.Context.PageMode.IsPreview)
        {
            return false;
        }

 var conditionalRenderingsRuleContext = ruleContext as ConditionalRenderingsRuleContext;
        if (conditionalRenderingsRuleContext == null)
        {
            return false;
        }
  
        var dynamicPlaceholderId = HttpUtility.ParseQueryString(conditionalRenderingsRuleContext.Reference.Settings.Parameters).Get("DynamicPlaceholderID");
        if (dynamicPlaceholderId == null)
 {
            return false;
        }

        if (conditionalRenderingsRuleContext.Item.Visualization.GetRenderings(Sitecore.Context.Device, false)
            .ToList().Any(r => r.Placeholder.EndsWith("container-" + dynamicPlaceholderId, StringComparison.OrdinalIgnoreCase)))
        {
            return false;
        }

        return true;
    }
}

To quickly add this to condition to Sitecore, go to /sitecore/system/Settings/Rules/Definitions/Elements and create a new Element Folder.  In the created folder find tags/default and select Conditional Renderings as tag. In the root of the created element folder, add a new Condition. Fill in the Text like "when the placeholder in the container is empty" and add the Type (referal to the class above). Don't forget to publish the created items ;)

Personalization

Go to a partial design that contains a Container rendering. Open the presentation details (in content editor - you might also use the experience editor) and edit the controls. Use the personalization feature on the Container rendering and add a personalization rule using the newly created condition and the standard available "hide" action.  Save & publish.


Conclusion

Partial designs are a great way to organize your pages in SXA. And yes, they do put have some limitations. But it wouldn't be Sitecore if there was no way to do something about that and the rules engine is a rather powerful tool to help you achieve what is needed.