Thursday, June 2, 2016

Sitecore WebApi missing context

Sitecore & WebApi


A lot has already been written about Sitecore and WebApi the last years since your custom WebApi calls didn't work anymore without a little tweaking. We have used the solution by Patrick Delancy a few times now and it worked fine. Until today..
Well, the issue seemed to be in the WebApi but it turned out to be something else. My journey of the day:

WebApi call is missing all Sitecore context

Our starting point was indeed a web api request that had no Sitecore context. The request looked like this: 
"http://website/api/stores/nearby/50.860421/4.422365"

First thing to do was compare configs and code with other projects where it was working, but that didn't help. All was fine there.. 
When I tried to place a language in between the domain and the path (../en/api/...) I got a 404 error from IIS. Weird. Nothing from Sitecore, although this should work. So I had my first conclusion: Sitecore is rejecting the request. 

Inspect the httpRequestBegin pipeline

I started inspecting the httpRequestBegin pipeline and noticed that it was skipped in the first step, the CheckIgnoreFlag. A custom processor placed before this step got hit, the one right behind it didn't. So I had to continue my search in.. 

The preprocessRequest pipeline

The preprocessRequest performs several checks in order to determine whether the request is valid for Sitecore. After staring at it for a while my eye fell on the dot. A simple stupid ".". The web api action was expecting 2 doubles as parameters and as this all should work find, there is a filter in this Sitecore pipeline on extensions: the FilterUrlExtensions. And of course, Sitecore is thinking that our extension is 422365 :)

The fix

Fixing this seemed very simple: just a a trailing slash to the request. And there we had our context again!

So remember when using doubles in web api request: use a trailing slash if it is your last parameter...

Sunday, May 29, 2016

Sitecore WFFM MVC FileUploadField restrictions

Sitecore WFFM FileUploadField

There are already some blogpost around about custom FileUploadFields, but as things have changed a bit in the latest Web Forms for Marketers releases and certainly with the introduction of MVC in WFFM I thought it could be useful to share the custom field we created on a Sitecore 8.1 update-1 environment with MVC forms.

Let's start by creating some validation attributes...

Validation attributes


Validation for file size


We inherit from DynamicValidationBase and override ValidateFieldValue.
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public sealed class LimitFileSizeAttribute : DynamicValidationBase
{
    private const string FileSizeLimitKey = "filesizelimit";
    private const string ErrorMessageKey = "filesizelimiterror";

    protected override ValidationResult ValidateFieldValue(IViewModel model, object value, ValidationContext validationContext)
    {
        // Required attribute should handle null value
        if (value == null)
        {
            return ValidationResult.Success;
        }

        var fileBase = value as HttpPostedFileBase;
        if (fileBase == null)
        {
            return new ValidationResult(ErrorMessage);
        }

        var fileUploadField = validationContext.ObjectInstance as FileUploadField;
        var limit = GetLimit(fileUploadField);

        if (limit == -1) // Limit not set
        {
            return ValidationResult.Success;
        }

        if (fileBase.ContentLength > limit)
        {
             return new ValidationResult(GetErrorMessage(fileUploadField));
        }

        return ValidationResult.Success;
    }
 
    private static int GetLimit(IViewModel field)
    {
        if (field == null || !field.Parameters.ContainsKey(FileSizeLimitKey))
        {
            return -1;
        }

        var parameter = field.Parameters[FileSizeLimitKey];
        int intValue;
        if (int.TryParse(parameter, out intValue))
        {
            return intValue;
        }

        return -1;
    }

    private string GetErrorMessage(IViewModel field)
    {
        if (field != null && field.Parameters.ContainsKey(ErrorMessageKey))
        {
            return field.Parameters[ErrorMessageKey];
        }

        return ErrorMessage;
    }
}

The size limit and the error message are retrieved from the parameters on the field.

Validation for allowed extensions

We can create a similar class to validate the extension by using the Path.GetExtension(fileBase.FileName). Of course, other validations are also possible.


MVC Restricted FileUploadField

We inherit from the base FileUploadField in Sitecore.Forms.Mvc.ViewModels.Fields and override the "Value" property, just to add the custom created attributes.

public class RestrictedUploadField : Sitecore.Forms.Mvc.ViewModels.Fields.FileUploadField
{
    [LimitAllowedExtensions]
    [LimitFileSize]
    public override HttpPostedFileBase Value { get; set; }
}



Register in Sitecore

The new custom field needs to be registered in Sitecore:



Usage
Now we can start using our field in a mvc form. Do not forget to fill in the necessary parameters and/or Localized parameters - in case of our example code with the file size limit these are:
  • Parameters : <FileSizeLimit>10485760</FileSizeLimit>
  • Localized parameters : <FileSizeLimitError>File size must be under 10 mb</FileSizeLimitError>

I did not present you a complete FileUpload module here, but the given examples should provide enough information to get you started in your quest to create a customized file upload field in Sitecore WFFM with MVC.

Thursday, May 26, 2016

Sitecore item buckets view settings

Item buckets and the content editor

You have probably heard and seen item buckets already. The basic information can be found on Sitecore's documentation site. In the content editor, the items in a bucket are hidden by default (the documentation shows you how to view them anyway if you want) and the editor is encouraged to use the search. After searching, the results are displayed and the editor can pick an item to edit. By default, this item opens in a tab:

The tab... nested

Once your editor has selected an item, he will get a tab with a nested view. Inside the tab is the ribbon for the current item. Although this all works, we had some editors that got confused. They used the top ribbon to start a publish, hereby publishing the whole bucket. 
So we went looking for options to tweak this, and we found them. In the box.  

Item bucket settings

At "/sitecore/system/Settings/Buckets/Item Buckets Settings" you will find a field called "Show Search Results In". The default value is "New Tab".
The options are:
  • New Tab : [default value] a selected items opens in a tab
  • New Tab Not Selected: as new tab without selection
  • New Content Editor: this might be the one you want.. it will open the selected item not in a new tab, but in a new editor window. If you are in the desktop mode, you will get this window full size and can switch between the windows through the bottom bar. In the content editor mode the new window will not be full sized when created, and will look more like an overlay.
Note that these settings are for all buckets in the system and for all users, so you might need to talk to multiple editors to ask there preference.

Enjoy, you just might have made your editors happy!

Friday, May 20, 2016

WFFM SendMail message routing

Sitecore WFFM SendMail SaveAction

People who have been working with Sitecore WebForms for Marketers probably all know the SendMail save action to send an email after submitting the form. The mail can contain data entered in the mail and can be send to a fixed address or an address provided in the form. But.. sometimes you do want/need your form to send this email to a different email address based on the value chosen in a dropdown list (e.g. the department the visitor wants to contact).

Custom routing

The code I will share here will demonstrate this routing scenario, where the administrators could define in the list of departments the corresponding email address(es). It can be used as a base for similar situations as well. [We are using Sitecore 8.1 update 1]

The implementation

Our implementation works on a value entered in the "To" field of the mail template. If that value contains a fixed string and a guid pointing towards the dropdown field to identify the recipient we check if we can find the value of that field in a defined folder. The found item will give us the recipients' email-address(es) in one of the fields.

The code

Lets have a look at the code (stripped a bit, I removed the logging to just keep the main points here):

public void Process(ProcessMessageArgs args)
{
 if (args == null || args.To == null) { return; }

 var toEmail = args.To.ToString();
 if (!toEmail.Contains(EmailKey)) { return; }

 var fieldIdValue = toEmail.Replace(EmailKey, string.Empty);
 Guid fieldId;
 if (!Guid.TryParse(fieldIdValue, out fieldId)) { return; }

 var field = args.Fields.GetEntryByID(new ID(fieldId));
 if (field == null) { return; }

 var formDataItem = settingsRepository.GetSettingItem("Forms.FormDataRoot");
 if (formDataItem == null) { return; }

 var result = Sitecore.Context.Database.SelectSingleItem(string.Format(CultureInfo.InvariantCulture, "fast:{0}//*[@@name='{1}']", formDataItem.Paths.FullPath, field.Value));
 if (result == null) { return; }

 var toAddress = result["To"];
 if (string.IsNullOrEmpty(toAddress)) { return; }

 args.To.Clear();
 args.To.Append(toAddress);
}

The steps we perform in this function are:

  1. Check the arguments, especially the "To" value as that is our target
  2. We check if the "To" value contains a constant value EmailKey (if not, we skip)
  3. We check if the remainder of the "To" field is a Guid (if not, we skip)
  4. We try to find a field in the form with this Guid as ID
  5. We (try to) get the root of the values (using our SettingsRepository which is a custom piece of code - it just comes down to fetching an item from Sitecore)
  6. We do a fast query in that root to get the item that corresponds with the selected item (value) in the defined field (if not found, we skip)
  7. We get the value of the "To" field in the found item and use this as the new recipient for the mail by clearing and assigning the "To" field of the arguments. 

The config


<pipelines>
  <processMessage>
    <processor patch:before="processor[@method='SendEmail']" type="xx.Wffm.Pipelines.RouteMessage, xx.Wffm" />
  </processMessage>
</pipelines>

We add our class to the processMessage pipeline just before the mail is send.
And that's it. Nothing more to it.

Tuesday, April 5, 2016

Multiple static Sitecore MVC forms

Multiple Sitecore MVC forms

Reinoud Van Dalen wrote a famous blog post over a year ago about multiple Sitecore MVC forms on a single page. His fix was based on some good work from Kevin Brechbühl and it helped us a lot. But recently we bumped into an issue on a Sitecore 8.1 project with mvc forms that were added on a more static way with @Html.Sitecore.Rendering().

Html.Sitecore.Rendering()

We created controller renderings for our forms in Sitecore and used their id's to display the statically bound rendering using @Html.Sitecore.Rendering(). We noticed that when we add such a rendering with Sitecore in a placeholder the code worked fine and the RenderingToken was set. But once the same rendering was placed statically, the CurrentRendering.UniqueId was an empty guid.

One could wonder why we place our forms statically in a View but we have our reasons. The views containing the static bound form-renderings can be placed in placeholders or also statically, that does not make a difference - the UniqueId stays empty.

A fix

We fixed this by making a small change to the original code:

RenderingToken


public static class SitecoreHelperExtensions
{
    public static MvcHtmlString RenderingToken(this SitecoreHelper helper)
    {
        if (helper?.CurrentRendering == null)
        {
            return null;
        }

        var tagBuilder = new TagBuilder("input");
        tagBuilder.Attributes["type"] = "hidden";
        tagBuilder.Attributes["name"] = "uid";
        tagBuilder.Attributes["value"] = helper.CurrentRendering.UniqueId +
                                            helper.CurrentRendering.RenderingItemPath;

        return new MvcHtmlString(tagBuilder.ToString(TagRenderMode.SelfClosing));
    }
}

ValidRenderingTokenAttribute


[AttributeUsage(AttributeTargets.Method)]
public sealed class ValidRenderingTokenAttribute : ActionMethodSelectorAttribute
{
    public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
    {
        var rendering = RenderingContext.CurrentOrNull;
        if (rendering == null || controllerContext == null)
        {
            return false;
        }

        var id = controllerContext.HttpContext.Request.Form["uid"];
        return id.Equals(rendering.Rendering.UniqueId + rendering.Rendering.RenderingItemPath, StringComparison.OrdinalIgnoreCase);
    }
}

The only thing we did was adding the RenderingItemPath to the hidden token.
The "bonus" from Reinouds code to be able to include the same form multiple times on a page still exists as long as the form is added with Sitecore on a placeholder for at least all but one occasions. It is not possible to add the same form twice statically though.


Thanks again to Reinoud for sharing his code on multiple mvc forms - hopefully this little addition can help people who need to add forms statically.

Tuesday, March 22, 2016

Custom index update strategy

ComputedIndexField with dependencies to other items

Ever had a ComputedIndexField that gathered data from other items than the item currently being indexed? For example, from its children. Or from items referred to..  I just had a situation where we needed (a property of) the children to be included in our ComputedIndexField.
But what happens if you update a child item? The child is re-indexed but the parent is not as this was not changed, not published, ...  We were using the onPublishEndAsync update strategy and didn't want to have a solution that needed a rebuild periodically just to keep the index up to date.

There is a GetDependencies pipeline that can be used to add extra items to the indexable list, but that was no option as this pipeline is for all indexes and we wanted it just for our custom index and preferably configurable per index (thinking about performance as well..)

Extend the onPublishEnd update strategy

So, we started thinking of extending the update strategy. We found some examples on the internet but not in our Sitecore version (we are using Sitecore 8.1) and the examples didn't go far enough.

What we wanted was:
  • the base onPublishEnd strategy (that would still check for rebuilds and so on)
  • an extension that would add the first ascendant of the item of a defined template to the list of indexable items

I had a look at the code of the OnPublishEndAsynchronousStrategy with DotPeek and noticed that this was extendable indeed. 

Let's start by creating our class by doing what a developer is good at: copy/paste :)

[DataContract]
public class OnPublishEndWithAncestorAsynchronousStrategy : OnPublishEndAsynchronousStrategy
{
    public string ParentTemplateId { get; set; }
    public string ChildTemplateId { get; set; }

    public OnPublishEndWithAncestorAsynchronousStrategy(string database) : base(database)
    {
    }
}

We created a class that extends OnPublishEndAsynchronousStrategy and gave it a constructor that needs the database name (which will be passed in the config). We also defined two variables to identify the templates that are affected - both parent (ancestor item to look for) as child (item to start from).

Performance

The child item template(s) are requested because our strategy code is executed before the crawler's root path is checked and before the 'DocumentOptions' are checked (like 'IncludeTemplates'). As this extended strategy is already heavier than the original one we wanted to prevent getting even more performance hits for items we don't need to check. This will become clear later on...

Configuration


<strategies hint="list:AddStrategy">
  <onPublishEndWithAncestorAsync type="Xx.OnPublishEndWithAncestorAsynchronousStrategy, Xx">
    <param desc="database">web</param>
    <ParentTemplateId>{0F5141D6-F264-4D03-B5D2-3505E6F308E7}</ParentTemplateId>
    <ChildTemplateId>{2A993FF2-5F17-4EEA-AD53-5343794F86BB}{066DEA00-31D7-4838-94A6-8D05A7FC690E}</ChildTemplateId>
  </onPublishEndWithAncestorAsync>
</strategies>

In the strategies section where you normally add your strategies by pointing towards the one(s) defined in the default Sitecore index configurations we define our custom strategy by providing the type. We send the database (web) as parameter and define the guids for the templates. In this example code we can send multiple child templates.

The index run

After some investigation, it turned out we only had to override one method: "Run". 
We started by copy/pasting the original code and checked the extension points:
  • if the item queue is empty: we leave the original code 
  • if the item queue is so big a rebuild is suggested: we keep the original code as a rebuild will also update the ancestor we might add
  • else..
We kept the original code to fetch the list of items to refresh. We don't actually get the items as "Item" but as "IndexableInfo" objects. For each entry in this list we call our GetAncestor function. The result is checked for null and added to the original list only if is wasn't already in there.


protected override void Run(List<QueuedEvent> queue, ISearchIndex index)
{
    CrawlingLog.Log.Debug($"[Index={index.Name}] {GetType().Name} executing.");
    if (Database == null)
    {
        CrawlingLog.Log.Fatal($"[Index={index.Name}] OperationMonitor has invalid parameters. Index Update cancelled.");
    }
    else
    {
        queue = queue.Where(q => q.Timestamp > (index.Summary.LastUpdatedTimestamp ?? 0L)).ToList();
        if (queue.Count <= 0)
        {
            CrawlingLog.Log.Debug($"[Index={index.Name}] Event Queue is empty. Incremental update returns");
        }
        else if (CheckForThreshold && queue.Count > ContentSearchSettings.FullRebuildItemCountThreshold())
        {
            CrawlingLog.Log.Warn($"[Index={index.Name}] The number of changes exceeded maximum threshold of '{ContentSearchSettings.FullRebuildItemCountThreshold()}'.");
            if (RaiseRemoteEvents)
            {
                IndexCustodian.FullRebuild(index).Wait();
            }
            else
            {
                IndexCustodian.FullRebuildRemote(index).Wait();
            }
        }
        else
        {
            var list = ExtractIndexableInfoFromQueue(queue).ToList();
            // custom code start here...
            CrawlingLog.Log.Info($"[Index={index.Name}] Found '{list.Count}' items from Event Queue.");
            var result = new List<IndexableInfo>();
            CrawlingLog.Log.Info($"[Index={index.Name}] OnPublishEndWithAncestorAsynchronousStrategy executing.");
            foreach (var itemInfo in list)
            {
                var ancestor = GetAncestor(itemInfo);
                if (ancestor != null)
                {
                    if (list.Any(i => i.IndexableId.Equals(ancestor.IndexableId, StringComparison.OrdinalIgnoreCase)))
                    {
                        CrawlingLog.Log.Info($"[Index={index.Name}] Ancestor already in list '{ancestor.IndexableId}'.");
                    }
                    else
                    {
                        CrawlingLog.Log.Info($"[Index={index.Name}] Adding ancestor '{ancestor.IndexableId}'.");
                        result.Add(ancestor);
                    }
                }
            }

            list.AddRange(result);
            CrawlingLog.Log.Info($"[Index={index.Name}] Updating '{list.Count}' items.");
            IndexCustodian.IncrementalUpdate(index, list).Wait();
        }
    }
}

Job(s)

One of the noticeable things here is that we add the extra indexable items to the existing list called with the incremental update. We could also call the Refresh method on the IndexCustodian but that would create extra (background) jobs so this way seems more efficient.

The ancestor check

Last thing to do is the ancestor check itself. For our requirements we needed to find an ancestor of a defined template but this functions could actually do anything. Just keep in mind the performance as this function will be called a lot.. (any ideas how to further improve this are welcome)

private IndexableInfo GetAncestor(IndexableInfo info)
{
    try
    {
 var childTemplateId = ChildTemplateId.ToLowerInvariant();
 var item = Database.GetItem(((ItemUri)info.IndexableUniqueId.Value).ItemID);
 if (item != null && childTemplateId.Contains(item.TemplateID.Guid.ToString("B")))
 {
     var ancestor = item.Axes.GetAncestors().ToList().FindLast(i => i.TemplateID.Guid.ToString("B").Equals(ParentTemplateId, StringComparison.OrdinalIgnoreCase));
     if (ancestor != null)
     {
  return new IndexableInfo(
                        new SitecoreItemUniqueId(
                            new ItemUri(ancestor.ID, ancestor.Language, ancestor.Version, Database)), 
                            info.Timestamp)
    {
        IsSharedFieldChanged = info.IsSharedFieldChanged
    };
     }
 }
    }
    catch (Exception e)
    {
 CrawlingLog.Log.Error($"[Index] Error getting ancestor for '{info.IndexableId}'.", e);
    }

    return null;
}


Using the child template in the config as well, might seems like a limitation but here it gives us a good performance gain because we limit the number of (slow) ancestor look-ups a lot. We still need to do that first lookup of the actual item to detect the template though.
We catch all exceptions - ok, might be bad practice - just to make sure in our test that one failure doesn't break it all.

Conclusion

As usual, we managed to tweak Sitecore in a fairly easy manor. This example can hopefully lead you towards more optimizations and other implementations of custom index features. Suggestions and/or improvements are welcome...


Tuesday, March 1, 2016

Query.MaxItems in Sitecore 8.1

Small tip for people using Query.MaxItems and upgrading to 8.1


Setting the maximum number of results from a Query

As you might know Sitecore has a setting called "Query.MaxItems". The default value of this setting is 100 (well, in the base config file). Setting this value to 0 will make queries return all results without limit - which might (will) have a negative impact on performance in case of a large result set.

We aware that this setting not only influences your own queries, but also some api calls (Axes, SelectItems, ...) and Sitecore fields that use queries underneath. It does not affect fast queries.


The Query.MaxItems value in Sitecore 8.1 : 260

Using queries is in lots of cases not a good idea and as a fan of indexes I (almost) never use them myself but some questions came up when people were upgrading so I decided to blog this little tip: 
in Sitecore 8.1 the Query.MaxItems value is patched in Sitecore.ExperienceExplorer.config and set to 260
If you patched the value yourself and did not use a separate include file (as you should!) and did not take care that your include file comes at the end (prefix with z is a common trick - using a subfolder starting with z- is a better one) this new value of 260 will overwrite yours.