Published on April 25, 2016 by Aleksandr Pchelintcev
In this post I'd like to feature a small module I've developed that aims to improve the Magnolia configuration experience. More than a year ago we started an effort called Light Development which, among many things, implied new ways of configuring the essential Magnolia CMS components like templates, dialogs, apps etc. As a result we have gathered the previously not related registries of such components under the common API and introduced the notion of configuration sources. So far, existing sources include the good old JCR and the new YAML implementations.
Configuration via YAML seems to have been warmly welcomed by the community proving itself to be more expressive and concise than JCR version and also easier to manipulate - just open a YAML file in a favorite text editor. However, there is one feature that YAML configuration source still lacks (mostly) - component inheritance, also known as JCR's extends property. Of course, there is there is the !include Yaml tag which allows to inject a verbatim copy of one YAML file into another. However, unfortunately, until now it was not possible to modify the injected data.
With no further ado please meet my light-yaml module which proudly claims to fill this gap and even go beyond it!
(Photo credit: http://www.tor.com/2014/11/07/toy-story-4-announced/)
Light-yaml provides a set of YAML tags making it possible to configure things by extending and tuning other components' configuration from different sources:
!extend- include another YAML file into your own one and alter it as your current project requires in a really similar spirit to JCR's
!extend-jc- a bridge between YAML and JCR, allows to import the configuration data from an arbitrary JCR node and define the missing properties and sub-definitions with YAML.
!extend-def- no need to specify the configuration source - just extend a definition from literally any registry by its id.
!override- causes the same effect as
extends:overridedoes for JCR: for the annotated part of extended configuration do not use the source at all - only rely on current file's contents.
!exclude- completely ignore some part of source configuration.
!include- for compatibility reasons the currently implemented inclusion tag is also supported.
Now we are going to take a look at some well-known dialog configuration samples enhanced with the new YAML capabilities. Let's assume we have a module called e.g.
light-yaml-sample. Upon installation our module bootstraps some field definition samples at
/modules/light-yaml-sample/fields. Roughly the JCR sub-tree structure could look like this:
label: Sample Text Field
label: Sample Select Field
Now it's time to add some reusable data in a couple of YAML files. First the file will pre-configure a sample form tab. The special part here is that the tab includes the field definitions from the JCR sample above!
# The following will populate a field list by including two field definitions (foo and bar) from JCR node and complement them with a third one (baz)
- name: baz
Another snippet will provide some common actions:
Now let's create a custom dialog definition based on the snippets above:
# We extend a sample tab here and add a fourth field definition.
- name: qux
# We also change the type of the field foo from simple text field to a rich-text field
- name: foo
# Actions are reused from the sampple file as
So what we have got so far? We have created a dialog definition with one tab with four fields, two of which come primarily from JCR (foo and bar), one added by the /light-yaml-sample/samples/sampleTab.yaml file (baz) and one that is defined in the resulting file /light-yaml-sample/dialogs/dialogA.yaml (qux), in which we also override a property of field definition from JCR (type of field foo). Pretty powerful already, right?
To complete the picture, let's now create another definition which extends and alters the dialogA via registry reference:
# We specify the type of a definition we are going to extend (in this case it is 'dialog' which automatically maps to the DialogDefinitionRegistry) and the target definition reference in the registry,
# for the case of dialogs it consists of a module name + definition name
- name: extendedTab
# Despite the fact that we extend dialogA definition, the 'extendedTab' will contain only one field definition since we annotate 'fields' list with !override tag
- name: quux
# dialogB is not going to contain the 'commit' action, 'cancel' action is still going to be there
I won't bore you with too many of the implementation details here: for those who are interested - the source code is available in our git repository. However it's worth highlighting several techniques that bring those funky extensions to life:
Easily! Currently the module automatically replaces the current YAML configuration sources with the extended one for all the modules. The old files should still work thanks to the backported !include directive. Otherwise the system should be ready to be extended with the new annotations/ All you need to do is to add the following dependency to a web-app:
That's about it - hope you'll like it! Feel free to contact me with any feedback about the module, the PRs are welcome in the Git repo and the bug reports can land in the JIRA project. Also feel free to talk to me at our conference in June - I'd love to know your opinion and thoughts on how we can push Magnolia configuration capabilities even further.
At the moment the partial extension of a YAML file is not yet implemented - doesn't seem to be too hard, just a matter of time. Also it is an open question whether the annotations should follow the JCR's pattern and be called extends-... or stay as they are right now (without 's' at the end), probably in the next version - both variants will be supported. Stay tuned!
Aleksandr is in the Magnolia development team and is responsible for tasks around UI and core features. In his spare time he likes playing football and reading about technology. His posts will focus on tinkering and extending Magnolia APIs and improving user experience with the Vaadin framework.
See all posts on Aleksandr Pchelintcev