Using Sitecore Form Handler to Validate the Form Request

I am working on a Sitecore 8.2 MVC solution and I implemented the fix for the well-known issue of submitting the right form on a page with multiple custom forms. The solution that I used on all my projects is the one provided by Reinoud van Dalen and described here: http://reinoudvandalen.nl/blog/ultimate-fix-for-multiple-forms-on-one-page-with-sitecore-mvc/ . However, on my current solution, I needed a search box on all the pages, added statically on the layout and without a rendering in Sitecore:

Layout.cshtml

@{ Html.RenderAction("SearchBox", "Search"); }

The solution with the ValidRenderingTokenAttribute did not work because in the RenderingToken() method from the SitecoreHelperExtensions class, the CurrentRendering.UniqueId had the value Guid.Empty:

SearchBox.cshtml

@Html.Sitecore().RenderingToken()

Renders in HTML as:

<input name="__RequestVerificationToken" type="hidden" 
       value="00000000-0000-0000-0000-000000000000">

Furthermore, in the IsValidForRequest() method from the ValidRenderingTokenAttribute class, the renderingContext was null. In this case, I had to look for another method to validate my form request. I found the blog post from Kevin Brechbühl, which apparently came before Reinoud’s solution: https://ctor.io/posting-forms-in-sitecore-controller-renderings-another-perspective/ . I also noticed that using the Sitecore.FormHandler generates two hidden fields with the names of the controller and the action, and I decided to make use of them:

SearchBox.cshtml

@Html.Sitecore().FormHandler("Search", "SearchBox");

Renders in HTML as:

<input id="scController" name="scController" 
       type="hidden" value="Search">
<input id="scAction" name="scAction" type="hidden" 
       value="SearchBox">

Based on Kevin’s solution, I created another attribute class:

public class ValidActionTokenAttribute 
           : ActionMethodSelectorAttribute
{
  public override bool IsValidForRequest
     (ControllerContext controllerContext, 
      System.Reflection.MethodInfo methodInfo)
  {
    // If the request is not a post, then don't check
    if (!controllerContext.HttpContext.Request
        .GetHttpMethodOverride()
        .Equals(HttpVerbs.Post.ToString(), 
                StringComparison.OrdinalIgnoreCase))
    {
      return true;
    }

    var controller = 
        controllerContext.HttpContext.Request
        .Form["scController"] + "Controller";
    var action = 
        controllerContext.HttpContext.Request
        .Form["scAction"];

    return !string.IsNullOrWhiteSpace(controller)
        && !string.IsNullOrWhiteSpace(action)
        && controller == 
            controllerContext.Controller.GetType().Name
        && methodInfo.Name == action;                 
    }
  }
}

Note that the controllerContext.HttpContext.Request.Form[”scController”] has the value ”Search”, whereas the controllerContext.Controller.GetType().Name has the value ”SearchController”, hence the addition of the ”Controller” string.  Now I can use both attributes, ValidRenderingTokenAttribute and ValidActionTokenAttribute, depending on the implementation of the rendering, and in order for this static form to not get posted when submitting other forms from the page, I left the @Html.Sitecore().RenderingToken() in the SearchBox.cshtml view.

Advertisements

Sitecore 8: Changing Renderings Programmatically

I am sure that most of you working with Sitecore were at some point in the situation of upgrading a solution. This is not an easy task, especially if the upgrade does not work by simply going through the upgrade steps from a version to the next one. In my case, I am in the process of upgrading a Sitecore 7.0 solution to Sitecore 8.2, and because of the legacy code that we cannot use anymore,  my team and I decided that we will start from scratch with a clean Sitecore 8.2 solution, we will migrate the data using scripts, and we will completely rebuild the presentation layer. The data migration, though not a subject of this post, is done by adding the old master db in the new solution, thus allowing us to programmatically transfer items from the old master to the new master. Our approach did not pose many problems, but I did have to investigate a bit how to reuse components.

In the old solution, there are many components that contain a lot of content, which we don’t want to lose. Migrating the datasource items is not difficult, but we need to redo the relations between them and the components that have to appear on the pages. The old renderings are gone, but we will have new ones in the new solution. To be more specific, if in the old solution, on a page oldPageItem, there is a component with the rendering oldRendering, assigned to a placeholder oldPh and it has the datasource item oldDsItem, then in the new solution, on the page newPageItem with the same ID as oldPageItem, we will have the new rendering newRendering, assigned to the new placeholder newPh, but with the datasource item newDsItem with the same ID as oldDsItem. In other words, we are keeping the content, but completely changing the presentation layer. To do this automatically, I wrote the following piece of code:

/* get the page in the new master db */
var newPageItem = newMasterDb.GetItem(oldPageItem.ID);
if (newPageItem == null)
{
    return;
}

/* get the rendering list of the old page item */
var renderingsList = 
    oldPageItem.Visualization.GetRenderings(Context.Device, true);
if (renderingsList == null)
{
    return;
}

/* get the layout definition and the current device definition 
   from the new page item; we are working on the final layout */
LayoutField layoutField = 
    new LayoutField(newPageItem.Fields[FieldIDs.FinalLayoutField]);
LayoutDefinition layoutDefinition = 
    LayoutDefinition.Parse(layoutField.Value);
DeviceDefinition deviceDefinition = 
    layoutDefinition.GetDevice(Context.Device.ID.ToString());

/* go through all the old renderings and identify the one used on 
   the component that needs to be added on the new page item */
foreach (var rendering in renderingsList)
{
    /* we can identify the rendering by name 
       (we could also do it by ID) */
    if (rendering.RenderingItem.Name.Equals("oldRendering"))
    {
        /* get the datasource ID from the rendering */
        var datasourceId = rendering.Settings.DataSource;
        if (datasourceId != null)
        {
            var oldPlaceholder = rendering.Placeholder;
            var newPlaceholder = string.Empty;
            var newRenderingId = string.Empty;

            /* the same component could be rendered in more 
               than one placeholder, so check the name of 
               the old placeholder;
               if it is a nested placeholder in the main 
               placeholder, we need to include it in the check */
            if (oldPlaceholder.Equals("/oldMainPh/oldPh"))
            {
                /* assign the name of the new placeholder;                    
                   if it is a nested placeholder in the 
                   main placeholder, we need to include it */
                newPlaceholder = "/newMainPh/newPh";
                
                /* assign the GUID of the new rendering; 
                   this has to exist in the new solution */
                newRenderingId = "<newRenderingID>";
            }

            if (string.IsNullOrEmpty(newPlaceholder) || 
                string.IsNullOrEmpty(newRenderingId))
            {
                continue;
            }

            /* create the new rendering definition and add 
               it to the current device definition */
            RenderingDefinition newRenderingDefinition = 
                new RenderingDefinition();
            newRenderingDefinition.ItemID = newRenderingId;
            newRenderingDefinition.Placeholder = newPlaceholder;
            newRenderingDefinition.Datasource = datasourceId;
            
            deviceDefinition.AddRendering(newRenderingDefinition);
        }                             
    }
}

/* update the presentation layer of the new page item 
   with the layout definition obtained above */
using (new SecurityDisabler())
{
    newPageItem.Editing.BeginEdit();
    layoutField.Value = layoutDefinition.ToXml();
    newPageItem.Editing.EndEdit();
}

The above code assumes we are working on a given page item, but we can iterate through all the page items where we need to make these changes. We also assumed that the migrated content items to the new solution keep the same IDs as in the old solution.

Sitecore 8.1 WFFM with MVC: Insert Form in Content Editor

In the latest versions of Sitecore, the trend is more and more to use MVC. So having an MVC project, built on Sitecore XP 8.1 rev.151003 (Initial Release) version, and wanting to use the Web Forms For Marketers module, version 8.1 rev. 151008, entails that we want to use the MVC Form rendering, instead of the classic Form sublayout. For a developer, this works perfectly well, but as a Content Editor, I encountered the issue that I could not select any placeholders in the Insert Form wizard. At first, I thought that the explanation for my issue is the same as the one in this article: http://blogs.perficient.com/microsoft/2014/04/web-forms-for-marketers-mvc-and-the-sitecore-page-editor/. It mainly has to do with the fact that in the Insert Form wizard, you will need to select the placeholder on which to add the form, but the list of placeholders cannot be created by Sitecore, because it scans the current page looking for sc:Placeholder controls, which are no longer used in MVC. But later on, I found out that this is not the case for my solution. In my case, there was a custom pipeline HttpRequestProcessor that interfered with the Insert Form wizard, so I found a workaround without changing the code from the custom pipeline.

The scenario is this:

1.) First, you open the Insert Form wizard:InsertForm

2.) Enter a name for the form, and click on either Create a blank form or Select a form to copy:

InsertFormWizard

3.) In the Select a Placeholder screen, you have to select something, otherwise, you cannot proceed to the next step. But in this case, instead of the placeholder list you would expect, the current item url is loaded inside the small wizard, which is a showstopper.

The solution to this problem begins with identifying where the request for placeholders is performed. This is done in a javascript file installed with the WFFM module: <website>\sitecore\shell\Applications\Modules\Web Forms for Marketers\script\Sitecore.PlaceholderManager.js. In the getPlaceholders function, we need to replace the Ajax request to the current url with an Ajax request to our own implementation of displaying the placeholder list. For this, we need a new very simple controller and a view in our project:

Controllers/MvcFormController.cs

public class MvcFormController : Controller
{       
    [HttpGet]
    public ActionResult Index()
    {
        return this.View();
    }
}

Views/MvcForm/Index.cshtml

<a id="ph_wffm" href="#" class="scPalettePlaceholder" title="wffm" onclick="Sitecore.PlaceholderManager.onPlaceholderClick(this, event, 'wffm');">
    <div class="scPalettePlaceholderTitle">wffm</div>
    <div class="scPalettePlaceholderTooltip" style="display: none">wffm</div>
</a>

In my case, I only need a single placeholder called ”wffm”. This was previously added in Sitecore, under Placeholder Settings:

RestrictingPlaceholdersScreen

Of course the logic can be taken even further, so that the placeholder list is retrieved dynamically. But in my case, this solution was good enough.

In the Sitecore.PlaceholderManager.js file, in the getPlaceholders function, we can adjust the Ajax request to our action:

this.getPlaceholders = function (url, success, failure) {
    if (url != null && url != '') {
        new Ajax.Request('/api/sitecore/MvcForm/Index', {
            method: 'get',
            asynchronous: false,
            onSuccess: function (transport) {
                success(transport.responseText);
            },
            onFailure: function (transport) {
                failure(null);
            }
        });
    }
    else {
        failure(null);
    }
}

Now, when I perform again the steps from my scenario, I can see the following screen for selecting the placeholder:

SelectPlaceholder

Remember to properly clear the cache of your browser before trying the scenario, so that you make sure that the changes in javascript are taken into consideration.

Custom MVC Controls for WFFM in Sitecore 8.1

The Web Forms For Marketers module is quite customizable, and the styling of the form controls would normally be manipulated from CSS classes, but I found myself in the situation where I had to adjust not only the styles of the controls, but also the html that renders behind. In this case, I investigated how to create my own custom controls and integrate them with the rest of the WFFM module. My project was built on Sitecore XP 8.1 rev.151003 (Initial Release) version, using the WFFM module, version 8.1 rev. 151008. The purpose is to customize a Single-Line Text control so that the field is surrounded by a <div> with a certain CSS class, has a label with a (*) if the field is required, and has some information displayed if filled in by the editor. The same process can then be followed in order to customize any other form control.

The first step is to check how the classic Single-Line Text control is created, and we can start by looking at the code behind. This information can be retrieved from the corresponding item in Sitecore:

SingleLineText

Our custom control will use the same class as the original control, so we do not need to change anything in the ”Assembly” and ”Class” fields. What we are interested to change is the ”MVC Type” used by our control, because we will implement a new rendering and we will need to link it in this field. Using a decompiling tool, we can see how the view model class of this control is implemented in the SingleLineTextField class (namespace Sitecore.Forms.Mvc.ViewModels.Fields, Sitecore.Forms.Mvc.dll). For most of the controls, the view model class has a strong relation with the view containing the actual control rendering, by having the same name (so naming convention is very important, as with everything in MVC). The views, as installed with the WFFM module, can be found in <website>\Views\Form\EditorTemplates. All the view model classes in the namespace Sitecore.Forms.Mvc.ViewModels.Fields inherit from FieldViewModel class (Sitecore.Forms.Mvc.ViewModels). If the view model of a control has a corresponding view with the same name, it will render that one, otherwise it will render the view of the FieldViewModel (implemented in <website>\Views\Form\EditorTemplates\FieldViewModel.cshtml). For example, the view model for the CheckboxField will render the view CheckboxField.cshtml, whereas the view model for SingleLineTextField will render the view FieldViewModel.cshtml.

So let’s create a new view in our project in the same folder as the other WFFM views, and call it MySingleLineTextField.cshtml. This will contain our code for the custom Single-Line Text rendering, and will reference a new view model with the same name. We will add a new ViewModels folder in our project and add the new class MySingleLineTextField.cs there. If we look at the code in FieldViewModel.cshtml, we can see that it makes use of the Html.BeginField() statement, which renders some tags and classes that I want removed and replaced with a different html structure. By removing this code, we need to make sure that we properly manage the label and the validation display. The advantage is that we can completely control how everything will look like by arranging the html and inserting our own CSS classes.

Views/Form/EditorTemplates/MySingleLineTextField.cshtml

@using Sitecore.Forms.Mvc.Html
@using MyProject.ViewModels
@model MySingleLineTextField

<div class="field">
    <label>
        @Html.BootstrapText("Title")
        @if (Model.IsRequired)
        {
            <abbr class="required" title="Required field">*</abbr>
        }
    </label>
    @Html.BootstrapEditor("Value", new string[] { "input-text" })
    @if (!string.IsNullOrEmpty(Model.Information))
    {
        <p class="small-note">
            @Html.BootstrapText("Information")
        </p>
    }
    <p class="small-note">
        @Html.BootstrapValidationMessage("Value")
    </p>
</div>

ViewModels/MySingleLineTextField.cs

using Sitecore.Forms.Mvc.ViewModels.Fields;

namespace MyProject.ViewModels
{
    public class MySingleLineTextField : SingleLineTextField
    {
        public MySingleLineTextField()
        {
        }       

        public override void Initialize()
        {
            base.Initialize();       
        }
    }
}

Now we can create the custom field in Sitecore and link it with the custom rendering. It is easier to copy the existing ”Single-Line Text” item and rename it. We will add the new item under a ”Custom” folder:

MySingleLineText

The custom field can be added on a new or existing form, like in the screenshot below:

CustomFieldOnForm

As a conclusion, we could create variations for any of the existing form controls, customizing the rendering in any way we want, provided that we also implement a view model class with the same name as the rendering. In order to know what to write in the view model class, just look at the decompiled version of the original view model from the Sitecore.Forms.Mvc.dll.