Sitecore 9 - Json to Forms


Sitecore 9 Forms is a powerful upgrade from WFFM. It provides a simple interface that allows the user to easily create forms on demand, and collect information from their visitors. For longer forms, the creation process can be a bit tedious and repetitive. This prompted me to leverage Sitecore PowerShell to create a script that will parse a JSON file and generate a form. Now the repetitive stuff can be managed via copy/paste. All of the scripts and an installation package can be found over at GitHub: Sitecore - Json To Forms. I will dive into some of the details below.

As with everything in Sitecore, each piece of a form is just a Sitecore item. After creating my form, I took a look at the resulting content tree and determined that PowerShell could certainly make my life easier. First step was to identify the templates utilized for each field type available in the default fields:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$fieldTemplates = @{
    Form                 = '{6ABEE1F2-4AB4-47F0-AD8B-BDB36F37F64C}';
    Button               = '{94A46D66-B1B8-405D-AAE4-7B5A9CD61C5E}';
    Checkbox             = '{2F07293C-077F-456C-B715-FDB791ACB367}';
    Date                 = '{5AC7621E-9F18-4569-BCEC-F5BF8BA1F4D7}';
    DropdownList         = '{9121D435-48B8-4649-9D13-03D680474FAD}';
    Email                = '{886ADEC1-ABF8-40E1-9926-D9189C4E8E1B}';
    Field                = '{84ED565A-BEC9-46D4-9603-6E0516580832}';
    List                 = '{5B672865-55D2-413E-B699-FDFC7E732CCF}';
    ListBox              = '{81FE389A-FDC7-4ECA-A5A9-4BE3ACA0C69A}';
    MultipleLineText     = '{D8386D04-C1E3-4CD3-9227-9E9F86EF3C88}';
    Number               = '{E8D5A5A3-6430-4701-BAAD-1DB1947616CC}';
    Password             = '{05D71800-56BE-4A53-AFAB-3819DA817A4A}';
    PasswordConfirmation = '{52FEC879-7D8D-46D3-BBB2-131293957709}';
    Text                 = '{FC18F915-EAC6-460A-8777-6E1376A9EA09}';
    Page                 = '{CFEE7B51-8505-45CE-B843-9358F827DF87}';
    Section              = '{8CDDB194-F456-4A75-89B7-346F8F39F95C}';
    Input                = '{0908030B-4564-42EA-A6FA-C7A5A2D921A8}';
}

After identifying the necessary field templates, I needed to come up with a JSON structure that provides a sensible method for me to define items in a good hierarchy. All forms start with their root level declaration, leveraging the Form template type. After that, it's just a matter of listing all of the elements in that form. Given the nature of Sections, those elements can have elements of their own. Each field type has its own set of fields that need values. For those, we just need to normalize the field names to be JSON friendly, and we're off to the races. Given all of this, it's very simple to come up with a basic JSON structure:

{
  "name": "Meeting Proposal Test",
  "fieldType": "Form",
  "elements": [
    {
      "name": "Page",
      "fieldType": "Page",
      "elements": [
        {
          "name": "",
          "fieldType": "Section",
          // ... more properties
          "elements": [
            {
              "name": "",
              "fieldType": "Date",
              "title": "",
              // ... more properties
            }
          ]
        },
      ]
    }
  ]
}

For the properties not listed in this blog, I simply converted the title case field names to camel case JSON property names (e.g. Css Class ==> cssClass).

The last thing to do is to put together a script that will take a JSON string, and generate some Sitecore items. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# Recursive method to read elements and import items
function Invoke-ProcessElements(
    [array]$elements,
    [string]$rootPath
) {
 
    $sortOrder = 0
 
    foreach ($element in $elements) {
        $templateTypeId = $fieldTemplates.$($element.fieldTemplate)
 
        $newItem = New-Item -Path "master:$($rootPath)\$($element.Name)" -ItemType $templateTypeId
 
        $itemProperties = @{ }
 
        $isFormField = $false
 
        switch ($element.fieldTemplate) {
            "Form" {
                Set-FormProperties $itemProperties $element
                break;
            }
            "Button" {
                Set-ButtonProperties $itemProperties $element
                CreateSubmitActions $newItem $element
                break;
            }
            "Checkbox" {
                Set-CheckboxProperties $itemProperties $element
                $isFormField = $true
                break;
            }
            "DrowdownList" {
                Set-DropDownListProperties $itemProperties $element
                CreateListOptions $newItem $element
                $isFormField = $true
                break;
            }
            "Email" {
                # Email is just an input with an Email Validator on it
                Set-EmailProperties $itemProperties $element
                $isFormField = $true
                break;
            }
            "Field" {
                break;
            }
            "List" {
                Set-SelectionProperties $itemProperties $element
                CreateListOptions $newItem $element      
                $isFormField = $true
                break;
            }
            "ListBox" {
                break;
            }
            "MultipleLineText" {
                Set-InputProperties $itemProperties $element
                Set-MultipleLineProperties $itemProperties $element
                $isFormField = $true
                break;
            }
            "Number" {
                Set-NumberProperties $itemProperties $element
                $isFormField = $true
                break;
            }
            "Password" {
                Set-PasswordProperties $itemProperties $element
                $isFormField = $true
                break;
            }
            "PasswordConfirmation" {
                Set-PasswordProperties $itemProperties $element
                Set-ConfirmPasswordProperties $itemProperties $element
                $isFormField = $true
                break;
            }
            "Text" {
                Set-TextProperties $itemProperties $element
                break;
            }
            "Date" {
                Set-DateProperties $itemProperties $element
                $isFormField = $true
                break;
            }
            "Input" {
                Set-InputProperties $itemProperties $element
                $isFormField = $true
                break;
            }
        }
 
        Add-StringProperty $itemProperties "Css Class" "cssClass" $element
        Add-FieldTypeProperty $itemProperties $element
 
        $itemProperties.Add("__Sortorder", $sortOrder)
        $sortOrder += 100
 
        if ($isFormField) {
            Set-FormFieldProperties $itemProperties $element
        }
 
        Update-NewItem $newItem $itemProperties
 
        if ($element.elements.count -gt 0) {
            Invoke-ProcessElements $element.elements $newItem.Paths.FullPath
        }
    }
}
 
$formDataFilePath = Read-Host "Enter path to form data configuration file: "
 
$formDataItem = Get-Item -Path master:$($formDataFilePath)
$formJson = $formDataItem.Json
$formData = ConvertFrom-Json $formJson
 
$formItem = New-Item -Path "master:\sitecore\Forms\$($formData.Name)" -ItemType $($fieldTemplates.Form)
$formItemPath = $formItem.Paths.FullPath
 
$formProperties = @{ }
Set-FormProperties $formProperties $formData
Update-NewItem $formItem $formProperties
 
Invoke-ProcessElements $formData.elements $formItemPath

The above code starts by asking the user for the path to a Sitecore item containing the JSON to be imported; the template for this item is included in the package. Once the JSON is read and converted, I immediately read the root level object. It's a known expectation that it's a regular item with an elements array. Import the first item, and then kick off a recursive execution to process the elements thereafter. Each field type has a processor method that will explicitly map values into the appropriate fields. These processor methods are separated out so they can be easily adjusted. Additionally, the processors are smart enough to not alter a value that isn't specified in the JSON. This means that it is only necessary to set the values you need, and all other values will just take on the standard values for their template.

There are some ancillary functions included with the package which are leveraged for updating the items as they are created, as well as ensuring that the values in the JSON are of the expected type. Lastly, there are some extra methods for handling the special fields (e.g. Drop Down Lists), allowing for both dynamic datasource drive options and static options.

Hop on over to the GitHub repo to take a look at the scripts and to download the package. I hope this helps get some of those longer forms out of the way a little bit quicker.

Categories: Sitecore, SPE, Sitecore Forms
Tags: CMS, Sitecore;

SEARCH ARTICLES