Motivation
Magnolia uses CKEditor as it's rich text editor implementation. CKEditor provides a link dialog where the user can define a link to any external resource. This is however not very suitable for creating links to internal Magnolia resources as the user would need to be aware of implementation details of how pages are referenced in Magnolia.
Solution
To solve the problem of hiding details of how pages are referenced we introduced a new button to the rich text editor which opens the Pages App as a dialog window and the user can choose a page from the list. Workflow will be similar with dealing with external links where rich text editor allows to add, modify and remove links. Link to a Magnolia page will be shown with same style as external link and by default it will be named after the page title.
As an implementation side effect an communications scheme with the CKEditor and server was created and this API is published to app developers to make the customisation of the rich text field easier.
Stories
Adding link
- User would click add Magnolia link
- Pages App will be shown embedded in a dialog
- User selects a page from the dialog
- Rich text editor now has link to external resource
Modifying link
- User opens up context menu of selected link
- Context menu offers option to modify or remove the link
- On modifying Pages App will be shown to allow the selection of a link source
Removing link
- User opens up context menu of selected link
- Context menu offers option to modify or remove the link
- On removing link the link nature is removed leaving the link as plain text
Implementation notes
- Creating a link will be handled on server side. Client side will make a request to create link and the server will respond to that with a link.
- Links are stored in HTML formatted rich text as they were stored in Magnolia 4. Client side plugin is necessary to render the appearance.
Dialogs
Editor with Magnolia link button
Choose link dialog
CKEditors default link chose dialog (as a reference)
Extensions
It is possible to extend the underlying CKEditor with custom plugins. Normal CKEditor API is extended to enable communications with server side.
Here an example would construct a rich text field with custom plugin and upon construction sends an event to it. Plugin will react on event and send another event back to server.
MagnoliaRichTextFieldConfig config = new MagnoliaRichTextFieldConfig(); //Under /PATH/TO/JS/ there should be a file called plugin.js that declares a CK editor plugin called "myplugin". config.addPlugin("myplugin", "/PATH/TO/JS/"); //Need to declare what events server side would like to receive. Beware of namespace clashes if you have multiple plugins. config.addListenedEvent("eventFromPlugin"); MagnoliaRichTextField richtexteditor = new MagnoliaRichTextField(config); richtexteditor.addListener(new MagnoliaRichTextField.PluginListener() { @Override public void onPluginEvent(String eventName, String value) { if (eventName.equals("eventFromPlugin")) { //Do something according to event } } }); //You can piggyback JSON for more complex data structures in value parameter. richtexteditor.firePluginEvent("eventToPlugin", "value data to plugin");
In client side declare a CKEditor plugin. Please note that path to plugin.js is relative to the widgetset folder and must not span to another site. This limitation is from the CKEditor.
(function() { //Register plugin to CKEditor CKEDITOR.plugins.add('myplugin', { init: function(editor) { //listen events from server editor.on('eventToPlugin', function(e) { //e.data holds the data server sent. //Do something with it if necessary. //Now send another event back to the server. editor.fire('eventFromPlugin', e.data); }); } }); })();
For developing CKEditor plugins please refer to it's tutorials at http://docs.cksource.com/CKEditor_3.x/Tutorials
Know issues
Problem with iOS and rich-text editing.
CK Editor allows for editing HTML in a WYSIWYG fashion and thus needs a sandbox for it. The standard way of doing such operations is to use an iFrame. Unfortunately, iFrame support in Mobile Safari is far from being ideal. In case of text editing (any <input> tag is affected) within an iFrame - text selection and sometimes text editing is damaged. For instance, selecting the text block in the middle of a paragraph might be problematic - the cursor jumps to either en end or a beginning of the line. The problem appears when there is an 'ontouchstart' listener in the DOM hierarchy above or on the same level as an iFrame. In our case - this is inevitable.
However, the workaround exists - if the user triggers the magnifier glass to appear, it is possible to select the desire text blocks.
SO discussion links:
http://stackoverflow.com/questions/6876706/text-selection-bug-in-mobile-safari-with-iframes-and-ontouchstart
http://stackoverflow.com/questions/6175455/ipad-input-in-iframe
Apple issue report reference:
http://openradar.appspot.com/12967017
1 Comment
Antti Hietala
To do: The second part about extending the CKEditor is something we should document for users. It is an advanced topic. Check with Samuli if the code snippets are usable as such and accurate. (Antti)