Customizing Content Editor forms

October 8, 2024

You can customize the Content Editor interface with JSON overrides. This topic shows you how override sections, field sets, and fields.

Note: For examples of overrides, see Examples of content definition JSON overrides. For information on Content Editor interface elements and layout, see Understanding Content Editor forms and fields.

Section overrides

This section shows how to define static forms, how Jahia applies inheritance to sections, and provides an example of applying section overrides.

Defining static forms in Jahia modules

You can override the static definition of the forms and field sets by adding JSON files in the META-INF/jahia-content-editor-forms folder of your Jahia module, and then in the forms or fieldsets subdirectory. The files should have meaningful names and to ensure consistency between your definitions and your overrides. We recommend replacing the colon (:) between the namespace and the content type with an underscore. For example, the override of the qant:myFields  node type should be named qant_myFields.json.

Here's an example of a JSON static form definition:

{
    "nodeType": "qant:myFields",
    "priority": 1.0,
    "sections": [
        {
            "name": "content",
            "expanded": true,
            "fieldSets": [
                {
                    "name": "mix:title",
                    "displayName": "qant_myFields.title.label",
                    "rank": 0.0
                }
            ]
        },
        {
            "name": "layout"
        }
    ]
}

With this definition, when you create or edit a node of the type  qant:myFields, a form with a section named layout displays. 

Other examples can be found in our Content Editor test folder.

Checking proper registration of static forms

If you want to check if your form or field set definitions are properly deployed, check the Jahia logs for lines that look like this:

2022-12-12 15:43:48,490: INFO  [StaticDefinitionsRegistry] - Successfully loaded static fieldSets for name jmix:description from bundle://132.0:0/META-INF/jahia-content-editor-forms/fieldsets/jmix_description_move_section.json
2022-12-12 15:43:48,501: INFO  [StaticDefinitionsRegistry] - Successfully loaded static form for name jnt:category from bundle://138.0:0/META-INF/jahia-content-editor-forms/forms/jnt_category.json

Checking form generation

It can be helpful to know how to query a form generation to validate it is generated as expected. This can be done using the GraphQL API directly with the following query:

{
  forms {
    editForm(
      uiLocale: "en"
      locale: "en"
      uuidOrPath: "/sites/digitall/home/area-main"
    ) {
      sections {
        name
        displayName
        fieldSets {
          name
          displayName
          fields {
            name
            selectorType
            i18n
            readOnly
            multiple
            mandatory
            valueConstraints {
              displayValue
              value {
                type
                string
              }
              properties {
                name
                value
              }
            }
            defaultValues {
              type
              string
            }
          }
        }
      }
    }
  }
}

This will produce the JSON for the form for the specified node type, resulting from the merging of CND generation and all the associated static field set and form definitions.

How sections are inherited

By default, you can redefine all properties available in the JSON override:

  • hasPreview
    Enables or disables preview
  • sections
    The list of sections to display in the form

Jahia applies inheritance in the following way:

  • Jahia first gets form definitions for all superTypes, in the inverse inheritance order.
  • Jahia then gets form definitions for all mixins set on the node, in no specific order.
  • If two forms definitions are on the same nodeType, the priority is used. A higher priority value will mean this will be the most important and override all others.
  • Jahia merges all forms definitions in that order and any properties (hasPreview and sections) are overridden.
  • If two forms definitions share the same nodeType and priority, one is ignored.
Note: Section inheritance is intentionally similar to the JCR definition of node type inheritance to bring consistency and extensibility to the content editing UI.

Example of section overrides and inheritance

You can use JSON overrides to define the section and preview behavior for a form. This example shows how to perform section and preview overrides of the jnt:contentcontent definition and how to disable preview for the qant:myFieldsRequired content type.

With form overrides, you can define the sections and preview behavior for a nodetype such as jnt:content.

[qant:myFieldsRequired] > jnt:content, qamix:qaContent, jmix:editorialContent
  - sharedSmallText (string) mandatory
  - smallText (string) i18n mandatory

First, provide a JSON form definition for jnt:content that:

  • Displays sections content and metadata only
  • Sets preview to true

The JSON override (added to /your_module/src/main/resources/META-INF/jahia-content-editor-forms/forms/jnt_content.json) is:

{
 "nodeType": "jnt:content",
 "priority": 1.0,
 "hasPreview": true,
 "sections": [
   {
     "name": "content",
     "labelKey": "label.engineTab.content",
     "requiredPermission": "viewContentTab"
   },
   {
     "name": "metadata",
     "labelKey": "label.engineTab.metadata",
     "requiredPermission": "viewMetadataTab"
   }
 ]
}

This creates a global rule and JSON override for jnt:content that is the default structure for all jnt:content and every node type that inherits from jnt:content.

But you would like to disable the preview for the qant:myFieldsRequired content type. You can override only hasPreview in a separate JSON override for qant:myFieldsRequired because, at the end, JSON overrides are merged together.

Here is the additional JSON override (added to /your_module/src/main/resources/META-INF/jahia-content-editor-forms/forms/qant_myFieldsRequired.json) to override qant:myFieldsRequired.

{
 "nodeType": "qant:myFieldsRequired",
 "priority": 1.0,
 "hasPreview": false
}

This way, only qant:myFieldsRequired will have preview disabled and all other jnt:content will have the preview enabled.

Section label keys

You may have noticed the labelKey setting for a section. This values contains a resource bundle key that will be looked up based on the format of its value. If it doesn't contain the @ character it will be looked up in the Jahia global server resource bundle that should not be modified by user. This could however be useful to re-use system keys (such as in the above examples). If you want to specify your own keys in your own module, you must use the following syntax:

basename@key

where the basename will be the name of the resource bundle (the base name of the file that will be located in the src/main/resources/resources directory in the site template module) and the key will be the key inside the file. Here's an example:

mySiteTemplate@myLabel

will look for the myLabel key inside the src/main/resources/resources/mySiteTemplate_*.properties file in the site template module. If it cannot be found, it will try to look it up in the main Jahia system resource bundle.

Field set override for a specific node type

In case you want to change the ordering of field sets, their name, etc, you can do something like that:

{
    "nodeType": "qant:myFields",
    "priority": 1.0,
    "sections": [
        {
            "name": "content",
            "expanded": true,
            "fieldSets": [
                {
                    "name": "mix:title",
                    "displayName": "qant_myFields.title.label",
                    "rank": 0.0
                }
            ]
        },
        {
            "name": "layout"
        }
    ]
}

Field set overrides

Field set overrides allow you to add a new field set or edit fields within an existing field set. This sections explains how you can manipulate fields in field sets or sections. The next section shows you how to manipulate fields.

Structure of field set overrides

Field set overrides are JSON files that are stored in the following directory:

/your_module/src/main/resources/META-INF/jahia-content-editor-forms/fieldsets

The JSON file has the following format:

{
 "name": "jmix:myType",
 "fields": ...
  ...
  }

The Form builder builds forms from the definition. Then, for each inherited type the builder merges the current form with the overrides that match each type, according to their priority.

For example, for page content the Form builder:

  • Reads the definition of jnt:page and the overrides of jnt:page ordered by priority.
  • Gets inherited types of jnt:page and merges overrides for each.
  • Gets types applied to the node (mixins) and merges overrides for each.
  • Gets types that extend jnt:page (mixins with the extend attribute set to jnt:page) and merges overrides for each.

Field set properties

The following properties define a field set:

  • name (string)
    Defines the nodetype on which the field set override applies
  • displayName (string)
    Sets the display name for the field set. Internationalization is not supported in JSON, but if generated from  CND it will use the module's resource bundle
  • description (string)
    Sets a description for the field set. Internationalization is not supported in JSON, but if generated from  CND it will use the module's resource bundle
  • rank (decimal)
    Sets the position of the field set in the form. The default value is 0.0.
  • priority (decimal)
    Defines the priority between field set of the same type. The default value is 1.0.
  • removed (boolean) If true, the field set is removed from the generated form. The default value is false.
  • dynamic (boolean)
    If true, the field set is dynamic. Not overridable yet. The default value is false.
  • activated (boolean)
    if true, the field set is enabled for a dynamic field set. Not overridable yet. The default value is false.
  • displayed (boolean)
    If false, the field set is part of the generated form, but is not displayed . Not overridable yet. The default value is true.
  • readOnly (boolean)
    If true, the field set is marked as read only. Not overridable yet. The default value is false.
  • fields (array)
    Array of fields that compose the field set.

Field overrides

The section describes field overrides, lists field properties that you can override, and provides examples of field overrides.

Field override basics

JSON overrides can override existing fields or add new fields. You can define field overrides in the fields section of field set override. The override applies to the node type defined by the name property of the field set.

{
 "name": "jmix:myType",
 "fields": [
   {
     "name": "fieldName",
     "mandatory": false,
     ...
   }
}

Field properties

Here the properties that can be overridden or used to create a field:

  • name (string)
    To override an existing property, it must match the property name. Mandatory.
  • displayName (string)
    Value displayed in the form. Internationalization is not supported in JSON, but if generated from  CND it will use the module's resource bundle
  • description (string)
    Description of the field. Internationalization is not supported in JSON, but if generated from  CND it will use the module's resource bundle
  • errorMessage (string)
    Error message that displays when field validation fails.
  • selectorType (string)
    Defines the selector type (input) use for the field.
  • readOnly (boolean)
    If true the field is set as read only. The default value is false.
  • mandatory (boolean)
    If true the field is marked as mandatory. The default value is false.
  • valueConstraints (array)
    Array of constraints applied on the field.
  • defaultValues (array)
    Array of default values.
  • removed (boolean)
    If true, the field will be removed from the form. The default value is false.
  • target (object)
    This field defines the target where to display the field. Used to move a field to another location than its default one (see Moving fields).

Known limitations

  • With versions prior to Content Editor 4.3, extensions are global to the platform
    • Starting from Content Editor 4.3, it is needed to enable the module providing the json override on the desired sites: this allows more flexibility when multiple sites are managed on a Jahia platform
  • Currently only the direct properties of a type can have their fields overridden. It is not possible to override a field brought by a mixin on a specific type.
  • The properties multiple and I18n can not be overridden by JSON definition. As the definition does not allow to have multiple values or internationalized values, there will be an error when you will try to save the data in the JCR.

Moving fields

Field has a target property that allows you to move fields. You move fields by defining the section, field set, and ranking.

Field target structure

You can move a field to a given target, The target format is:

  • sectionName
    Name of the section that displays the field
  • fieldSetName
    Name of the field set that displays the field. Only if sectionName is filled.
  • rank (decimal)
    Defines the rank of the field within the fields of the field set. A value of 0.0 ranks the field at the top and higher values rank the field lower. The default value is 0.
Warning: If there is already an json override on a field that you want to move to another section, it's necessary to specify the target field set name of the field. For example in content editor the nt:base field set already have an override with the system name, so if you want to move the system name in another place than the option section, you will have to specify the new section name and the new field set name

Moving a field in the same field set

The following override moves the template name field above the other fields in a page.

{
 "name": "jnt:page",
 "priority": 1.1,
 "fields": [
   {
     "name": "j:templateName",
     "target": {
       "rank": -1
     }
   }]
}

 

moving-field-in-fieldset.png

Debugging the overrides

This section shows how to debug the json overrides on a content by providing the following information:

  • the priority of the override
  • the module providing the override
  • the file name of the override
  • the nodetype associated with the override
  • if permissions are used in the override
{
  forms {
    editForm(uuidOrPath: "/sites/digitall/home/area-main", uiLocale: "en", locale: "en") {
      mergedItems {
        priority
        bundleName
        filename
        condition {
          nodeType
          withPermission
        }
      }
    }
  }
} 

It will return the following payload for /sites/digitall/home/area-main:

{
  "data": {
    "forms": {
      "editForm": {
        "mergedItems": [
          {
            "priority": 1,
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/nt_base.json",
            "condition": {
              "nodeType": "nt:base",
              "withPermission": null
            }
          },
          {
            "priority": 1,
            "bundleName": "site-settings-seo",
            "filename": "/META-INF/jahia-content-editor-forms/forms/jmix_description_move_section.json",
            "condition": {
              "nodeType": "jmix:description",
              "withPermission": null
            }
          },
          {
            "priority": 1,
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/fieldsets/jmix_skinnable.json",
            "condition": {
              "nodeType": "jmix:skinnable",
              "withPermission": null
            }
          },
          {
            "priority": 1,
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/fieldsets/jmix_cache.json",
            "condition": {
              "nodeType": "jmix:cache",
              "withPermission": null
            }
          },
          {
            "priority": 1.1,
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/nt_base_orderable.json",
            "condition": {
              "nodeType": "nt:base",
              "withPermission": null
            }
          },
          {
            "priority": 2,
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/mix_title.json",
            "condition": {
              "nodeType": "mix:title",
              "withPermission": null
            }
          },
          {
            "priority": 2,
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/jmix_channelSelection.json",
            "condition": {
              "nodeType": "jmix:channelSelection",
              "withPermission": null
            }
          },
          {
            "priority": 2,
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/jmix_tagged.json",
            "condition": {
              "nodeType": "jmix:tagged",
              "withPermission": null
            }
          },
          {
            "priority": 2,
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/jmix_contributeMode.json",
            "condition": {
              "nodeType": "jmix:contributeMode",
              "withPermission": null
            }
          },
          {
            "priority": 2,
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/jmix_listSizeLimit.json",
            "condition": {
              "nodeType": "jmix:listSizeLimit",
              "withPermission": null
            }
          }
        ]
      }
    }
  }
}

It's also possible to get the full details of the item:

{
  forms {
    editForm(uuidOrPath: "/sites/digitall/home/area-main", uiLocale: "en", locale: "en") {
      mergedItems {
        bundleName
        filename
        form {
          name
          sections {
            name
            fieldSets {
              fields {
                name
              }
            }
          }
        }
        fieldSet {
          name
          fields {
            name
          }
        }
      }
    }
  }
}

It will return the following payload:

{
  "data": {
    "forms": {
      "editForm": {
        "mergedItems": [
          {
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/nt_base.json",
            "form": {
              "name": "nt:base",
              "sections": [
                {
                  "name": "content",
                  "fieldSets": []
                },
                {
                  "name": "classification",
                  "fieldSets": []
                },
                {
                  "name": "metadata",
                  "fieldSets": []
                },
                {
                  "name": "layout",
                  "fieldSets": []
                },
                {
                  "name": "options",
                  "fieldSets": [
                    {
                      "fields": [
                        {
                          "name": "ce:systemName"
                        }
                      ]
                    }
                  ]
                },
                {
                  "name": "seo",
                  "fieldSets": [
                    {
                      "fields": [
                        {
                          "name": "jcr:description"
                        }
                      ]
                    }
                  ]
                },
                {
                  "name": "listOrdering",
                  "fieldSets": []
                },
                {
                  "name": "visibility",
                  "fieldSets": []
                },
                {
                  "name": "permissions",
                  "fieldSets": []
                }
              ]
            },
            "fieldSet": null
          },
          {
            "bundleName": "site-settings-seo",
            "filename": "/META-INF/jahia-content-editor-forms/forms/jmix_description_move_section.json",
            "form": {
              "name": "jmix:description",
              "sections": [
                {
                  "name": "seo",
                  "fieldSets": [
                    {
                      "fields": [
                        {
                          "name": "jcr:description"
                        }
                      ]
                    }
                  ]
                }
              ]
            },
            "fieldSet": null
          },
          {
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/fieldsets/jmix_skinnable.json",
            "form": null,
            "fieldSet": {
              "name": "jmix:skinnable",
              "fields": []
            }
          },
          {
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/fieldsets/jmix_cache.json",
            "form": null,
            "fieldSet": {
              "name": "jmix:cache",
              "fields": []
            }
          },
          {
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/nt_base_orderable.json",
            "form": {
              "name": "nt:base",
              "sections": [
                {
                  "name": "listOrdering",
                  "fieldSets": [
                    {
                      "fields": [
                        {
                          "name": "ce:manualOrdering"
                        }
                      ]
                    }
                  ]
                }
              ]
            },
            "fieldSet": null
          },
          {
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/mix_title.json",
            "form": {
              "name": "mix:title",
              "sections": [
                {
                  "name": "content",
                  "fieldSets": [
                    {
                      "fields": [
                        {
                          "name": "jcr:title"
                        },
                        {
                          "name": "ce:systemName"
                        }
                      ]
                    }
                  ]
                }
              ]
            },
            "fieldSet": null
          },
          {
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/jmix_channelSelection.json",
            "form": {
              "name": "jmix:channelSelection",
              "sections": [
                {
                  "name": "visibility",
                  "fieldSets": [
                    {
                      "fields": [
                        {
                          "name": "j:channelSelection"
                        },
                        {
                          "name": "j:channelIncludeOrExclude"
                        }
                      ]
                    }
                  ]
                }
              ]
            },
            "fieldSet": null
          },
          {
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/jmix_tagged.json",
            "form": {
              "name": "jmix:tagged",
              "sections": [
                {
                  "name": "classification",
                  "fieldSets": [
                    {
                      "fields": [
                        {
                          "name": "j:tagList"
                        }
                      ]
                    }
                  ]
                }
              ]
            },
            "fieldSet": null
          },
          {
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/jmix_contributeMode.json",
            "form": {
              "name": "jmix:contributeMode",
              "sections": [
                {
                  "name": "listOrdering",
                  "fieldSets": [
                    {
                      "fields": [
                        {
                          "name": "j:editableInContribution"
                        },
                        {
                          "name": "j:contributeTypes"
                        }
                      ]
                    }
                  ]
                }
              ]
            },
            "fieldSet": null
          },
          {
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/jmix_listSizeLimit.json",
            "form": {
              "name": "jmix:listSizeLimit",
              "sections": [
                {
                  "name": "listOrdering",
                  "fieldSets": [
                    {
                      "fields": []
                    }
                  ]
                }
              ]
            },
            "fieldSet": null
          }
        ]
      }
    }
  }
}

And by adding a filter, get quickly which file is modifying a specific value:

{
  forms {
    editForm(uuidOrPath: "/sites/digitall", uiLocale: "en", locale: "en") {
      hasPreview
      mergedItems(filter:{filters:{fieldName:"form.hasPreview", evaluation:NOT_EMPTY}}) {
        bundleName
        filename
        form {
          hasPreview
        }
      }
    }
  }
} 

It will return the following payload:

{
  "data": {
    "forms": {
      "editForm": {
        "hasPreview": false,
        "mergedItems": [
          {
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/nt_base.json",
            "form": {
              "hasPreview": true
            }
          },
          {
            "bundleName": "jcontent",
            "filename": "/META-INF/jahia-content-editor-forms/forms/jnt_virtualsite.json",
            "form": {
              "hasPreview": false
            }
          }
        ]
      }
    }
  }
}