Extending RichText field type in eZ Platform

April 20, 2017

by Serhey Dolgushev

We at Contextual Code were keen to see eZ Platform in action, and decided that updating our own website / blog would provide a perfect opportunity to get deeper into it. During testing of the new admin interface we discovered that a completely new WYSIWYG editor is now being used, but there is currently no easy way to develop eZ XML custom tags. That was the main reason why we decided to spend some time investigating eZ Platform’s rich text field type, looking to find possible ways of extending it.

The basic flow for the RichText field type “lifecycle” is as follows:

 

RichText Editor

Alloy Editor has replaced the eZ XML Editor, and it is used for editing the content of RichText type fields. It comes with some built-in features like object embeds, images, text styles and so on. It receives HTML code from eZ Publish, provides an interface for it’s editing and sends edited HTML code back to the eZ Publish REST API. It is possible to extend it with new functionality (new UI buttons) and in this blog post I will provide an example of doing that. It is also possible to modify the HTML content before it is sent to the eZ Publish REST API using editor content processors. This is useful because we do not really need to send the exact same HTML which is used internally by Alloy Editor to eZ Publish REST API -  for example we can remove empty tags, and unnecessary attributes which are used only by the editor and which have no affect on displayed content.

Submitting HTML to eZ Publish

When RichText field content is submitted to the eZ Publish REST API it goes through following steps:

  1. It is normalized by ezpublish.fieldType.ezrichtext.normalizer.input serivce
  2.  It is validated by ezpublish.fieldType.ezrichtext.validator.input.dispatcher service
  3. It is converted by ezpublish.fieldType.ezrichtext.converter.input.dispatcher service 

All these steps are pretty straightforward and you can replace them with your own custom services.

Again the main reason for this process is to transform Alloy Editor HTML input to XML. For example a div element with a data-ezelement="ezembed" attribute is used by Alloy Edtior for embed objects, but it need to be converted to an embed XML element (with all it’s attributes).

Editing stored XML

Each time a RichText field is edited in Alloy Editor, content stored in eZ Publish as XML need to be converted to HTML that Alloy can understand. The ezpublish_rest.field_type_processor.ezrichtext service is responsible for it. It uses ezpublish.fieldType.ezrichtext.converter.edit.xhtml5 service (https://github.com/ezsystems/ezpublish-kernel/blob/master/eZ/Bundle/EzPublishCoreBundle/FieldType/RichText/Converter/Html5Edit.php) for performing XSLT processing of XML to HTML. In order to customize it, you can use your own service instead of any services listed above or just use custom XSL template.

Output stored XML

For generating final front-end HTML the ezpublish.fieldType.ezrichtext.converter.output.xhtml5.core service is used (https://github.com/ezsystems/ezpublish-kernel/blob/master/eZ/Bundle/EzPublishCoreBundle/FieldType/RichText/Converter/Html5.php). It works in a very similar way to the ezpublish.fieldType.ezrichtext.converter.edit.xhtml5 service. For customization purposes you can implement your own service, or just modify the fieldtypes.ezrichtext.output_custom_xsl parameter, which should be an easier solution. Again, it should be clearly stated that these services/XSL templates are completely independent of the Alloy Editor and they are only used to transform content stored as XML in eZ Publish to the HTML which will be displayed on front-end.

Example

Let’s have a look at a simple RichText editor extension, which allows for the insertion of a  “mark” HTML tag (<mark>...some text…</mark>) into RichText field contents.

First of all we need to create a new bundle. You can create your own or grab it from https://gitlab.com/contextualcode/RichTextEditorCustomTagsBundle. This bundle need to be enabled in your kernel, and it is important to enable it before the Platform UI Assets bundle to avoid problems with adding custom CSS later on. So your app/AppKernel.php file should look like:

 

Then we need to add a new “Marked” button to the Alloy Editor toolbar. In order to do it we need to:

  1. Add it’s YUI module: https://gitlab.com/contextualcode/RichTextEditorCustomTagsBundle/blob/master/Resources/config/yui.yml#L5-7
  2. Implement it’s YUI module: https://gitlab.com/contextualcode/RichTextEditorCustomTagsBundle/blob/master/Resources/public/js/alloyeditor/buttons/marked.js. Our code here is based on other Alloy Editor buttons, as per https://github.com/ezsystems/PlatformUIBundle/tree/master/Resources/public/js/alloyeditor/buttons. To be able use this button in editor toolbar configuration, it should have static key: And we are going to insert a new div element with a data-ezelement=”marked” attribute in the editable area:
  3. We then need to add it’s YUI module (step#1) to the requirement list for the ez-richtext-editview YUI module: https://gitlab.com/contextualcode/RichTextEditorCustomTagsBundle/blob/master/Resources/config/yui.yml#L50
  4. Also we need to add the Marked button to the editor toolbar config: https://gitlab.com/contextualcode/RichTextEditorCustomTagsBundle/blob/master/Resources/public/js/views/fields/ez-richtext-editview.js#L440. This is the main reason why we use a custom JavaScript source for ez-richtext-editview YUI module (https://gitlab.com/contextualcode/RichTextEditorCustomTagsBundle/blob/master/Resources/config/yui.yml#L51)
  5. Then we can add custom CSS styles for our Marked button:

After completing this steps, there should be a new “Marked” button in Alloy Editor:

And a new editable div element with a data-ezelement=”marked” attribute will be injected into the editable area after it’s clicked. Next we need to add some XSL rules to convert this div to a valid XML element when our input is submitted during the publishing process (submitting HTML to eZ Publish), and convert this XML element back to our div when the eZ Publish REST API returns it for future editing in the editor (editing stored XML).To do that we need to use the fieldtypes.ezrichtext.input_custom_xsl and fieldtypes.ezrichtext.edit_custom_xsl parameters accordingly. Needless to say, fieldtypes.ezrichtext.input_custom_xsl contains our custom XSL template: https://gitlab.com/contextualcode/RichTextEditorCustomTagsBundle/blob/master/Resources/xsl/input_custom.xsl. But in the case of fieldtypes.ezrichtext.edit_custom_xsl we need to copy the default file and add our custom template in it.

Now it will be possible to submit/edit a new “Marked” tag to eZ Publish for storage.

The final part is displaying our new tag on the front-end. To do that we need to change fieldtypes.ezrichtext.output_custom_xsl parameter. It works in a similar way to fieldtypes.ezrichtext.edit_custom_xsl, so we need to copy default file and add our custom template to it.

Now our new “Marked” tag will be displayed as a <mark> HTML element on the front-end.

Conclusions

Extending the RichText field type is a bit harder than creating eZ Publish 4.x custom tags. But we really hope, that this blog post will help you with this task and it brought some shine on this topic. Please don’t hesitate to ask any questions in the comments, or correct us if we were wrong on some points.

This post covers simple example, when you are adding the root tag. Nested tags case is a bit more complicated and requires additional development. It will be covered in further posts. Stay tuned!