YAML inherit and include

This page describes the Magnolia-specific directives !inherit, !include and !override. With these directives, you can reuse YAML file-based configurations. !include allows you to reuse an arbitrary resource, whereas !inherit is useful for extending a definition.

YAML include

Use !include to add a reusable chunk. Include a YAML fragment one level below your new definition, or include a complete YAML definition on top of your new definition. You can also modify the included part of the definition.

Reference the file you want to include by its resource path. The path to such a resource has the following pattern: /<module-name>/path/to/the/reusable/chunk.yaml.

Typically, you include YAML snippets that are not proper definitions. Do not add snippets to folders that are meant for definitions (templates, dialogs, apps). Instead, add these snippets to a folder that is not scanned (for instance, /<your-module>/includes/).

If your !include file is not working, check that the first line in the included YAML file does not have any indentation.

Syntactic variants of the directive

The !include directive has existed since Magnolia 5.4, which introduced configuration by YAML. With the release of 5.5.6, the directive syntax has changed slightly. While the old syntax still works, the new one makes it possible to modify and override the included part of the definition. The new syntax uses a colon instead of a space between !include and the path to the resource.

Syntax Requires version Functions

Deprecated syntax

!include <path/to/a–ressource.yaml>

Magnolia 5.4+

simple include

New syntax

!include:<path/to/a–ressource.yaml>

Magnolia 5.5.6+

simple include, include and modify

Generic recipe

  • Prepare a fragment that can be reused in many item definitions.

  • Include a resource with the !include directive. The included resource can contain a fragment or a complete definition.

  • Modify parts of the included definition.

Example definitions

Simple include

Include one tab and all the action definitions in a dialog definition.

This example works with both the old (deprecated) and the new syntax.

Includable files

/module-a/includes/categorization-tab.yaml
name: tabCategories
label: Categories
fields:
  - name: categories
    label: Categories
    class: info.magnolia.ui.form.field.definition.MultiValueFieldDefinition
    label: Select category
    field:
      name: linkField
      class: info.magnolia.ui.form.field.definition.LinkFieldDefinition
      targetWorkspace: category
      appName: categories
      identifierToPathConverter:
        class: info.magnolia.ui.form.field.converter.BaseIdentifierToPathConverter
  - name: blackOrWhite
    label: Black or white
    class: info.magnolia.ui.form.field.definition.SelectFieldDefinition
    options:
      - name: black
        value: black
        selected: true
        label: Black
      - name: white
        value: white
        label: White
/module-a/includes/common-actions.yaml
commit:
  class: info.magnolia.ui.admincentral.dialog.action.SaveDialogActionDefinition
cancel:
  class: info.magnolia.ui.admincentral.dialog.action.CancelDialogActionDefinition

A dialog file with includes

/module-a/dialogs/components/another-component.yaml
form:
  label: another-component
  tabs:
    - name: tabMain
      label: Main
      fields:
        - name: title
          label: Text
          class: info.magnolia.ui.form.field.definition.TextFieldDefinition
          label: Title

    # an include within a list, add a complete tab
    - !include:/module-a/includes/categorization-tab.yaml

# an include within a map, add the actions for the root (map) item "actions"
actions: !include:/module-a/includes/common-actions.yaml

Include and modify a fragment

Include one tab and all the action definitions in a dialog definition. Additionally, modify the included parts of the definition.

This example works only with the new syntax (see syntactic variants).

Includable files

/module-a/includes/categorization-tab.yaml
name: tabCategories
label: Categories
fields:
  - name: categories
    label: Categories
    class: info.magnolia.ui.form.field.definition.MultiValueFieldDefinition
    label: Select category
    field:
      name: linkField
      class: info.magnolia.ui.form.field.definition.LinkFieldDefinition
      targetWorkspace: category
      appName: categories
      identifierToPathConverter:
        class: info.magnolia.ui.form.field.converter.BaseIdentifierToPathConverter
  - name: blackOrWhite
    label: Black or white
    class: info.magnolia.ui.form.field.definition.SelectFieldDefinition
    options:
      - name: black
        value: black
        selected: true
        label: Black
      - name: white
        value: white
        label: White
/module-a/includes/common-actions.yaml
commit:
  class: info.magnolia.ui.admincentral.dialog.action.SaveDialogActionDefinition
cancel:
  class: info.magnolia.ui.admincentral.dialog.action.CancelDialogActionDefinition

A dialog file with includes

/module-a/dialogs/components/lazy-component.yaml
form:
  label: lazy-component
  tabs:
    # an include within a list, add a complete tab
    - !include:/module-a/includes/categorization-tab.yaml
      label: Yo! 1-tab-only!
      fields:
        # modify properties of the field blackOrWhite, which comes from the include
        - name: blackOrWhite
          label: Choose color
# an include within a map, add the actions for the root (map) item "actions"
actions: !include:/module-a/includes/common-actions.yaml
  # add another action to those imported from the include
  doit:
    label: Same as cancel
    class: info.magnolia.ui.admincentral.dialog.action.CancelDialogActionDefinition
  • Line 6: Modify the label of the included tab.

  • Lines 9 and 10: Modify the label of the blackOrWhite field from the include.

  • Lines 14 to 16: Add another action to the imported actions.

Include a complete file and modify

Create a dialog based on a dialog from the basic page template of the MTK module. Override its second tabMeta tab. This is similar to the example in the Modifying reused configuration section, but here you use !include instead of !inherit.

/module-a/dialogs/pages/aa-page.yaml
!include:/mtk/dialogs/pages/basic.yaml
form:
  tabs:
    - name: tabMeta
      fields: !override

YAML inherit

With this directive, you can inherit from an existing registered definition item in order to create a new definition item. Registered items can originate from YAML files, JCR configurations or even Blossom Java code.

It is not a good idea to inherit from a definition that resides in the same module as the dependent one.

Module files are loaded in order of discovery, which can vary over time. This means that after one reload, a definition might work while the next time you edit the definition or restart your application, it may fail to discover an intra-modular dependency and to load correctly. Due to this behavior, you should not create such dependencies as they are inherently unstable.

You can inherit only at the root level, which means you can inherit a complete app but not a subapp. Modify the new item according to your needs.

Registered definition items are known to a registry and can be seen in the Definitions app. Every registered definition item has an identifier that is unique among all items of the same type within their registry. Some of the items are defined by name, others by ID. To inherit an item, reference it by its identifier.

Items are inherited along with their state as known by the registry. For instance, if an item has been decorated, the registry knows its decorated state. Inheriting a decorated item means inheriting the decorated state of the item.

Item definition type Identifier type Example identifier

Template definitions

ID

mtk:components/link

Dialog definitions

ID

mtk:components/textImage

App definitions

Name

definitions-app

Theme definitions

Name

travel-demo-theme

Renderer definitions

Name

freemarker

Module dependencies due to inherit

When using !inherit, you may rely on a definition item that is configured in another module. In this case, you must take care of the loading order. If you rely on another module, make sure it is loaded upfront. Use module dependencies to define the loading order of the modules.

The module dependencies define the order in which modules are loaded during startup. For example, if module-a depends on module-b, module-b will be loaded before module-a. The order becomes important, for example, if both module-a and module-b decorate the same definition or if module-a inherits a definition from module-b.

Module dependencies can be defined within a module descriptor.

Generic recipe

  • On the first line of your YAML definition, use the !inherit directive followed by a colon and the identifier of the definition you want to inherit from.

  • Modify the inherited definition.

Example definitions

Inherit and override a renderer

A new definition

/module-a/renderers/json.yaml
!inherit:freemarker
contentType: application/json

The new renderer named json inherits everything from the freemarker renderer but has different contentType.

Inherit and override a template definition

The original definition you inherit from

/module-a/templates/components/a-component.yaml
title: a-component
renderType: freemarker
templateScript: /module-a/templates/components/a-component.ftl
dialog: module-a:components/a-component
modelClass: info.magnolia.module.jsmodels.rendering.JavascriptRenderingModel

parameters:
  color: blue
  size: 50

A new definition that inherits

/module-b/templates/components/b-component.yaml
!inherit:module-a:components/a-component
title: b-component
description: This is a b-bombastic component to test cool YAML features!
subtype: dummy-components
parameters: !override
  • Line 1: Inherit.

  • Line 2: Override the title property.

  • Lines 3 and 4: Add more properties.

  • Line 5: Suppress the parameters property.

The module descriptor of module-b

/module-b/module.yaml
version: 1.0
dependencies:
  module-a:
    version: 1.0/*

By setting this dependency, we make sure that module-a is loaded before module-b. As a result, b-component, which depends on a-component, can be initialized properly.

Modifying reused configuration

Reused configuration originating from !inherit or !include (when using the new !include syntax) can be modified. You can:

  • Add and modify properties originating from !include or !inherit. This will merge the included or inherited node with the modifications.

  • Use !override to completely ignore the properties of an inherited or included node. As a consequence, you have to add properties to the given node.

YAML override

The !override directive ignores the properties of the node to which the directive has been applied. You have to specify all of the required properties of the overridden definition node.

Example definitions

A definition to inherit or include

/module-a/templates/pages/l-page.yaml
title: L page
templateScript: /mtk/templates/pages/basic.ftl
renderType: freemarker
dialog: module-a:pages/l-page
visible: true

areas:
  main:
    availableComponents:
      textImage:
        id: mtk:components/textImage
      teaser:
        id: mtk:components/teaser
      image:
        id: mtk:components/image
      link:
        id: mtk:components/link

Inherit

/module-a/templates/pages/xxl-page.yaml
!inherit:module-a:pages/l-page
title: XXL page
dialog: module-a:pages/xxl-page

areas:
  main:
    availableComponents:
      html:
        id: mtk:components/html

The resulting definition merges the inherited definition and the modifications area, rendering five available components in the main area.

Inherit and override

/module-a/templates/pages/xs-page.yaml
!inherit:module-a:pages/l-page
title: XS page
dialog: module-a:pages/xs-page

areas:
  main:
    availableComponents: !override
      link:
        id: mtk:components/html

The resulting definition ignores all the properties of availableComponents in the inherited definition, rendering only one available component in the main area.

Overriding list items

Due to the syntax of a YAML list, you cannot apply !override to a list item. To bypass this constraint, use a YAML map instead of a list to apply !override to an item. Even if the definition class expects a list, the YAML file can define the item with a map.

Example definitions

A definition to inherit or include

/module-a/dialogs/pages/l-page.yaml
form:
  label: Page properties
  tabs:
    - name: tabMain
      label: L page
      fields:
        - name: title
          class: info.magnolia.ui.form.field.definition.TextFieldDefinition
          i18n: true
          label: title
        - name: navigationTitle
          class: info.magnolia.ui.form.field.definition.TextFieldDefinition
          i18n: true
          label: navigation title
        - name: windowTitle
          class: info.magnolia.ui.form.field.definition.TextFieldDefinition
          i18n: true
          label: window title
    - name: tabMeta
      label: Meta Data
      fields:
        - name: keywords
          class: info.magnolia.ui.form.field.definition.TextFieldDefinition
          i18n: true
          label: keywords
          rows: 3
        - name: description
          class: info.magnolia.ui.form.field.definition.TextFieldDefinition
          i18n: true
          label: description
          rows: 5

actions:
  commit:
    class: info.magnolia.ui.admincentral.dialog.action.SaveDialogActionDefinition
  cancel:
    class: info.magnolia.ui.admincentral.dialog.action.CancelDialogActionDefinition

Both tabs and fields are defined here as a list. To define these properties, you can also use the map syntax.

Include and override

/module-a/dialogs/pages/xs-page.yaml
!include:/module-a/dialogs/pages/l-page.yaml
form:
  tabs:
    tabMeta: !override
    tabMain:
      fields:
        title: !override
          required: true
          class: info.magnolia.ui.form.field.definition.RichTextFieldDefinition
          label: Title ...

To apply !override to the tabMeta tab and the title field, we use here the map syntax.

Preserving the order of nodes

The order of nodes as defined in a decoration via !override is preserved. This is useful especially if the only change needed to the existing configuration of nodes is their order. Previously, when providing a new configuration with !override, the configuration would be picked up by the system, but any change in the order of the nodes (at the level of the !override) would not be respected.

For example, the list view in the workbench of the Tours app may be adjusted with the following definition decoration placing the path column before the name column:

/stories-app/decorations/tours/apps/tours.yaml
subApps:
  browser:
    workbench:
      contentViews:
        list:
          columns: !override
            path:
            name:
dev days event sign up
dev days event sign up
Feedback