Customizing Content Editor forms
You can customize the Content Editor interface with JSON overrides. This topic shows you how override sections, field sets, 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.
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:content
content 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.
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
}
}]
}
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
}
}
]
}
}
}
}