WYSIWYG text edit

Description

WYSIWYG text field editor programming in Plone.

Introduction

Plone supports TinyMCE (default), Kupu and CKEditor.

TinyMCE and Plone integration is distributed in Products.TinyMCE package.

Content linking

Plone offers many kind of support and enhancements in site internal content linking

The recommended method for linking the content is Linking by UID since Products.TinyMCE version 1.3.

  • When the text is saved in TinyMCE all relative links are converted to UID links in the saved HTML payload
  • When the text is displayed again, the HTML is run through output filter and UID links are converted back to human readable links

This solves issues with earlier Plone versions where the link targets become invalid when a HTML textfield with relative links where shown on the other page as the original context.

Note

You might need to turn on Linking by UID setting on in the site setup if you are migrating from older Plone sites.

Editor preferences

Plone supports user text changeable editor. The active editor is stored in the user preferences.

The user can fallback to hand-edited HTML by setting active editor to none.

The rich text widget can also support optional input formats besides HTML: structured text and so on.

Text format selector

The format selector itself is rendered by wysiwyg_support.pt macros which is Plone core

Applying styles only edit view

You can use TinyMCE body selector make your CSS class have different styles in view and edit modes (inside TinyMCE)

/* Break columns in two column layout
 *
 * https://developer.mozilla.org/en/css3_columns
 *
 */

.column-breaker {
        column-break-before: always;
        display: block;
}

.mceContentBody .column-breaker {
        color: red;
        border: 1px dashed red;
        display: block;
}

Note

Firefox does not actually support column breaks, so this was useful headaching experience.

Customizing TinyMCE options

In your add-on code, all TinyMCE options in the control panel can be exported and imported using GenericSetup, portal_setup and tinymce.xml.

Hacking TinyMCE Javascript

Plone ships with pre-compressed TinyMCE source code enabled by default.

If you want to toy around with TinyMCE source code itself, you might want to first enable the usage of debug version of TinyMCE source.

In Products.TinyMCE.skins open tiny_mce_src.js and copy-paste its content into tiny_mce.js.

Note

Replacing tiny_mce.js with tiny_mce_src.js in portal_javascripts doesn't seem to work as it breaks TinyMCE plug-in loading.

TinyMCE plug-ins

TinyMCE consists of plug-ins. Existing plug-ins can be overlaid with your custom version by loading Javascript after core TinyMCE load.

  • Default TinyMCE plug-in Javascript files can be found under Products.TinyMCE/skins/tinymce/plugins
  • Usually TinyMCE plug-ins dialogs load in <iframe> and HTML code loads separate CSS and JS files from the main site

Adding a new plug-in

Here are instructions how to add new buttons to TinyMCE

Some rules

  • Plug-in id goes to tinymce.xml
  • Your plug-in must be in a file called editor_plugin.js in skins layer
  • You must have a skins layer folder named after your plug-in id
  • You don't register plug-in Javascript portal_javascripts
  • TinyMCE button row is in the main document. However, the edit area itself is in <iframe>. Also, many of TinyMCE dialogs are launched in <iframe> and they load a hardcoded set of Javascript files (they don't use any kind of Plone master template or <head> section).

So in the end you'll have a file:

yourcompany.app./yourcompany/app/skins/tinymce_plugin_flowplayer/flowplayer/editor_plugin.js

Why all this? I don't know. And honestly, in this point, I don't care.

Register your specially named skin layer in skins.xml:

<?xml version="1.0"?>
<object name="portal_skins" meta_type="Plone Skins Tool">


 <object name="tinymce_plugin_flowplayer"
    meta_type="Filesystem Directory View"
    directory="your.app:skins/tinymce_plugin_flowplayer"/>

 <skin-path name="*">
  <layer name="tinymce_plugin_flowplayer"
     insert-after="custom"/>
 </skin-path>

</object>

Register your plugin in tinymce.xml GenericSetup install profile

<?xml version="1.0"?>
<object>

 <toolbar>
  <customtoolbarbuttons purge="False">
    <element value="flowplayer"/>
  </customtoolbarbuttons>
 </toolbar>

 <resourcetypes>

  <customplugins purge="False">
    <element value="flowplayer"/>
  </customplugins>

Then finally drop a editor_plugin.js to your plug-in folder

/**
 * a TinyMCE plug-in for opening a dialog asking a video link and creating Flowplayer code out of it
 *
 */

(function() {

    tinymce.create('tinymce.plugins.FlowplayerPlugin', {

        init : function (ed, url) {

            var t = this;
            t.url = url;
            t.editor = ed;
            t.docs = false;

            ed.addButton('flowplayer', {
                title : 'Video',
                cmd : 'flowplayer',
                image : url + '/img/flowplayer.gif'
            });

            ed.addCommand('flowplayer', function (val, page) {
                var url = prompt("Copy-paste URL to MP4 video file", "");
                // note: flowplayer link must not have text inside
                html = '<a class="flow-player tinymce-flow-player" href="' + url + '" />';
                ed.execCommand('mceInsertContent', false, html);
            });

            //ed.onPostRender.add(t._setupTOC, t);
        },

        getInfo : function () {
            return {
                longname : 'collective.flowplayer video insert plug-in ',
                author : 'Mikko Ohtamaa',
                authorurl : 'http://webandmobile.mfabrik.com',
                infourl : 'http://webandmobile.mfabrik.com',
                version : "1.0"
            };
        }
   });

   tinymce.PluginManager.add('flowplayer', tinymce.plugins.FlowplayerPlugin);
})();

Media resources

TinyMCE exposes URL to your plug-in base folder, where editor_plugin.js is, as plug-in init() parameter.

You can construct relative URLs to set media resources in init().

ed.addButton('flowplayer', {
    title : 'Video',
    cmd : 'video',
    image : url + '/img/placegallery.gif'
});

Language resources

TinyMCE does not directly accept strings as labels, but uses its own internal translation mechanism which is not gettext.

  • Create folder langs under plug-in base folder
  • There create file en.js

Sample content

tinyMCE.addI18n('en.placegallery',{
    desc : 'Placegallery button'
});

More info

Customizing existing plug-in

The recommended way is to customize TinyMCE

  • Re-register plug-in by simply including a plug-in JS code in a separate Javascript file loaded after tinymce.js
  • override existing individual TinyMCE files using jbot.

Overriding plug-in

Create a duplicate of plug-in JS file (table.js), register it as a custom Javascript from your add-on resource folder.

TinyMCE overrides previous plug-in registrations with new ones and you can just re-register your own plug-in version to override the existing version.

  • Refer it in portal_javascripts
<!-- TinyMCE customizations -->
<javascript
        id="++resource++your.app/tiny_mce_special.js"
        authenticated="True"
        cacheable="True" compression="safe" cookable="True" insert-after="tinymce.js"
        enabled="True" expression=""
        inline="False"
        />

Overriding plug-in resources

Yoiu can also override CSS, HTML (.htm.pt templates) with z3c.jbot as instructed above.

Example:

jbot/Products.TinyMCE.skins.tinymce.plugins.table.js.table.js

Warning

Since there resources are loaded in <iframe> the normal browser refresh does not trigger reload for them. Right click <iframe>, choose Refresh from context menu.




Edit this document

The source code of this file is hosted on GitHub. Everyone can update and fix errors in this document with few clicks - no downloads needed.

  1. Go to WYSIWYG text edit on GitHub.
  2. Press Fork and edit this file button.
  3. Edit file contents using GitHub's text editor in your web browserm
  4. Fill in the Commit message text box at the end of the page telling why you did the changes. Press Propose file change button next to it when done.
  5. On Send a pull request page you don't need to fill in text anymore. Just press Send pull request button.
  6. Your changes are now queued for review under project's Pull requests tab on Github.

For basic information about updating this manual and Sphinx format please see Writing and updating the manual guide.