14 KiB
title: Rich-text editing (WYSIWYG) summary: SilverStripe's use and configuration of TinyMCE html editor.
Rich-text editing (WYSIWYG)
Editing and formatting content is the bread and butter of every content management system, which is why SilverStripe has a tight integration with our preferred editor library, TinyMCE.
On top of the base functionality, we use our own insertion dialogs to ensure you can effectively select and upload files. In addition to the markup managed by TinyMCE, we use shortcodes to store information about inserted images or media elements.
The framework comes with a HTMLEditorField form field class which encapsulates most of the required functionality. It is usually added through the DataObject::getCMSFields() method:
mysite/code/MyObject.php
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
use SilverStripe\ORM\DataObject;
class MyObject extends DataObject
{
private static $db = [
'Content' => 'HTMLText'
];
public function getCMSFields()
{
return new FieldList(
new HTMLEditorField('Content')
);
}
}
Specify which configuration to use
By default, a config named 'cms' is used in any new HTMLEditorField.
If you have created your own HtmlEditorConfig and would like to use it,
you can call HtmlEditorConfig::set_active('myConfig')
and all subsequently created HTMLEditorField
will use the configuration with the name 'myConfig'.
You can also specify which HtmlEditorConfig to use on a per field basis via the construct argument. This is particularly useful if you need different configurations for multiple HTMLEditorField on the same page or form.
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
use SilverStripe\ORM\DataObject;
class MyObject extends DataObject
{
private static $db = [
'Content' => 'HTMLText',
'OtherContent' => 'HTMLText'
];
public function getCMSFields()
{
return new FieldList([
new HTMLEditorField('Content'),
new HTMLEditorField('OtherContent', 'Other content', $this->OtherContent, 'myConfig')
]);
}
}
In the above example, the 'Content' field will use the default 'cms' config while 'OtherContent' will be using 'myConfig'.
Configuration
To keep the JavaScript editor configuration manageable and extensible, we've wrapped it in a PHP class called
HtmlEditorConfig. The class comes with its own defaults, which are extended through the Configuration API
in the framework (and the cms
module in case you've got that installed).
There can be multiple configs, which should always be created / accessed using HtmlEditorConfig::get(). You can
then set the currently active config using set_active()
.
Adding and removing capabilities
In its simplest form, the configuration of the editor includes adding and removing buttons and plugins.
You can add plugins to the editor using the Framework's HtmlEditorConfig::enablePlugins() method. This will transparently generate the relevant underlying TinyMCE code.
mysite/_config.php
use SilverStripe\Forms\HTMLEditor\HtmlEditorConfig;
HtmlEditorConfig::get('cms')->enablePlugins('media');
Plugins and advanced themes can provide additional buttons that can be added (or removed) through the
configuration. Here is an example of adding a ssmacron
button after the charmap
button:
mysite/_config.php
HtmlEditorConfig::get('cms')->insertButtonsAfter('charmap', 'ssmacron');
Buttons can also be removed:
mysite/_config.php
HtmlEditorConfig::get('cms')->removeButtons('tablecontrols', 'blockquote', 'hr');
Setting options
TinyMCE behaviour can be affected through its configuration options. These options will be passed straight to the editor.
One example of the usage of this capability is to redefine the TinyMCE's whitelist of HTML tags - the tags that will not be stripped from the HTML source by the editor.
mysite/_config.php
// Add start and type attributes for <ol>, add <object> and <embed> with all attributes.
HtmlEditorConfig::get('cms')->setOption(
'extended_valid_elements',
'img[class|src|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name|usemap],' .
'iframe[src|name|width|height|title|align|allowfullscreen|frameborder|marginwidth|marginheight|scrolling],' .
'object[classid|codebase|width|height|data|type],' .
'embed[src|type|pluginspage|width|height|autoplay],' .
'param[name|value],' .
'map[class|name|id],' .
'area[shape|coords|href|target|alt],' .
'ol[start|type]'
);
Writing custom plugins
It is also possible to add custom plugins to TinyMCE, for example toolbar buttons. You can enable them through HtmlEditorConfig::enablePlugins():
mysite/_config.php
HtmlEditorConfig::get('cms')->enablePlugins(['myplugin' => '../../../mysite/javascript/myplugin/editor_plugin.js']);
You can learn how to create a plugin from the TinyMCE documentation.
Image and media insertion
The HtmlEditorField API also handles inserting images and media files into the managed HTML content. It can be used both for referencing files on the webserver filesystem (through the File and Image APIs), as well as hotlinking files from the web.
We use shortcodes to store information about inserted images or media elements. The ShortcodeParser API post-processes the HTML content on rendering, and replaces the shortcodes accordingly. It also takes care of care of placing the shortcode replacements relative to its surrounding markup (e.g. left/right alignment).
oEmbed: Embedding media through external services
The "oEmbed" standard is implemented by many media services around the web, allowing easy representation of files just by referencing a website URL. For example, a content author can insert a playable youtube video just by knowing its URL, as opposed to dealing with manual HTML code.
oEmbed powers the "Insert from web" feature available through HtmlEditorField. Internally, it makes HTTP
queries to a list of external services if it finds a matching URL. These services are described in the
Oembed.providers
configuration. Since these requests are performed on page rendering, they typically have a long
cache time (multiple days).
To disable oEmbed usage, set the Oembed.enabled
configuration property to "false".
Limiting oembed URLs
HtmlEditorField can have whitelists set on both the scheme (default http & https) and domains allowed when inserting files for use with oembed.
This is performed through the config variables RemoteFileFormFactory::$fileurl_scheme_whitelist and RemoteFileFormFactory::$fileurl_domain_whitelist.
Setting these configuration variables to empty arrays will disable the whitelist. Setting them to an array of lower case strings will require the scheme or domain respectively to exactly match one of those strings (no wildcards are currently supported).
Doctypes
Since TinyMCE generates markup, it needs to know which doctype your documents will be rendered in. You can set this
through the element_format configuration variable. It
defaults to the stricter 'xhtml' setting, for example rendering self closing tags like <br/>
instead of <br>
.
In case you want to adhere to HTML4 instead, use the following configuration:
HtmlEditorConfig::get('cms')->setOption('element_format', 'html');
By default, TinyMCE and SilverStripe will generate valid HTML5 markup, but it will strip out HTML5 tags like
<article>
or <figure>
. If you plan to use those, add them to the
valid_elements configuration setting.
Also, the HTMLValue API underpinning the HTML processing parses the markup into a temporary object tree which can be traversed and modified before saving. The built-in parser only supports HTML4 and XHTML syntax. In order to successfully process HTML5 tags, please use the 'silverstripe/html5' module.
Recipes
Customising the "Insert" panels
In the standard installation, you can insert links (internal/external/anchor/email), images as well as flash media files. The forms used for preparing the new content element are rendered by SilverStripe, but there's some JavaScript involved to transfer back and forth between a content representation the editor can understand, present and save.
Example: Remove field for "image captions"
use SilverStripe\Core\Extension;
// File: mysite/code/MyToolbarExtension.php
class MyToolbarExtension extends Extension
{
public function updateFieldsForImage(&$fields, $url, $file)
{
$fields->removeByName('CaptionText');
}
}
// File: mysite/_config.php
use SilverStripe\Admin\ModalController;
ModalController::add_extension('MyToolbarExtension');
Adding functionality is a bit more advanced, you'll most likely need to add some fields to the PHP forms, as well as write some JavaScript to ensure the values from those fields make it into the content elements (and back out in case an existing element gets edited). There's lots of extension points in the ModalController class to get you started.
Security groups with their own editor configuration
Different groups of authors can be assigned their own config, e.g. a more restricted rule set for content reviewers (see the "Security" ) The config is available on each user record through Member::getHtmlEditorConfigForCMS(). The group assignment is done through the "Security" interface for each Group record. Note: The dropdown is only available if more than one config exists.
Using the editor outside of the CMS
Each interface can have multiple fields of this type, each with their own toolbar to set formatting and insert HTML elements. They do share one common set of dialogs for inserting links and other media though, encapsulated in the ModalController class. In the CMS, those dialogs are automatically instantiate, but in your own interfaces outside of the CMS you have to take care of instantiate yourself:
use SilverStripe\Admin\ModalController;
use SilverStripe\Control\Controller;
// File: mysite/code/MyController.php
class MyObjectController extends Controller
{
public function Modals()
{
return ModalController::create($this, "Modals");
}
}
Note: The dialogs rely on CMS-access, e.g. for uploading and browsing files, so this is considered advanced usage of the field.
// File: mysite/_config.php
HtmlEditorConfig::get('cms')->disablePlugins('ssbuttons');
HtmlEditorConfig::get('cms')->removeButtons('sslink', 'ssmedia');
HtmlEditorConfig::get('cms')->addButtonsToLine(2, 'link', 'media');
Developing a wrapper to use a different WYSIWYG editors with HTMLEditorField
WYSIWYG editors are complex beasts, so replacing it completely is a difficult task.
The framework provides a wrapper implementation for the basic required functionality,
mainly around selecting and inserting content into the editor view.
Have a look in HtmlEditorField.js
and the ss.editorWrapper
object to get you started
on your own editor wrapper. Note that the HtmlEditorConfig is currently hardwired to support TinyMCE,
so its up to you to either convert existing configuration as applicable,
or start your own configuration.