Merge pull request #4259 from tractorcow/pulls/3/multi-htmleditor-config

API Support for multiple HTMLEditorConfig per page
This commit is contained in:
Daniel Hensby 2015-06-12 00:53:44 +01:00
commit ad04eeb5c4
7 changed files with 146 additions and 62 deletions

View File

@ -339,8 +339,6 @@ class LeftAndMain extends Controller implements PermissionProvider {
if (Director::isDev()) Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/leaktools.js');
HTMLEditorField::include_js();
$leftAndMainIncludes = array_unique(array_merge(
array(
FRAMEWORK_ADMIN_DIR . '/javascript/LeftAndMain.Layout.js',

View File

@ -31,6 +31,34 @@ functionality. It is usually added through the `[api:DataObject->getCMSFields()]
}
}
### Specify which configuration to use
By default, a config named 'cms' is used in any new `[api:HTMLEditorField]`.
If you have created your own `[api:HtmlEditorConfig]` and would like to use it,
you can call `HtmlEditorConfig::set_active('myConfig')` and all subsequently created `[api:HTMLEditorField]`
will use the configuration with the name 'myConfig'.
You can also specify which `[api:HtmlEditorConfig]` to use on a per field basis via the construct argument.
This is particularly useful if you need different configurations for multiple `[api:HTMLEditorField]` on the same page or form.
:::php
class MyObject extends DataObject {
private static $db = array(
'Content' => 'HTMLText',
'OtherContent' => 'HTMLText'
);
public function getCMSFields() {
return new FieldList(array(
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
@ -41,7 +69,6 @@ There can be multiple configs, which should always be created / accessed using `
then set the currently active config using `set_active()`.
<div class="info" markdown="1">
By default, a config named 'cms' is used in any field created throughout the CMS interface.
</div>
<div class="notice" markdown='1'>

View File

@ -19,6 +19,7 @@
* `FormField::validate` now requires an instance of `Validator`
* Implementation of new "Archive" concept for page removal, which supercedes "delete". Where deletion removed
pages only from draft, archiving removes from both draft and live simultaneously.
* Support for multiple HtmlEditorConfigs on the same page.
#### Deprecated classes/methods removed

View File

@ -39,12 +39,20 @@ class HtmlEditorConfig {
self::$current = $identifier;
}
/**
* Get the currently active configuration identifier
* @return String - the active configuration identifier
*/
public static function get_active_identifier() {
$identifier = self::$current ? self::$current : 'default';
return $identifier;
}
/**
* Get the currently active configuration object
* @return HtmlEditorConfig - the active configuration object
*/
public static function get_active() {
$identifier = self::$current ? self::$current : 'default';
$identifier = self::get_active_identifier();
return self::get($identifier);
}
@ -291,38 +299,81 @@ class HtmlEditorConfig {
}
/**
* Generate the javascript that will set tinyMCE's configuration to that of the current settings of this object
* @return string - the javascript
* Generate the JavaScript that will set TinyMCE's configuration:
* - Parse all configurations into JSON objects to be used in JavaScript
* - Includes TinyMCE and configurations using the {@link Requirements} system
*/
public function generateJS() {
$config = $this->settings;
public static function require_js() {
require_once 'tinymce/tiny_mce_gzip.php';
$useGzip = Config::inst()->get('HtmlEditorField', 'use_gzip');
// plugins
$configs = array();
$externalPlugins = array();
$internalPlugins = array();
$externalPluginsJS = '';
foreach($this->plugins as $plugin => $path) {
if(!$path) {
$internalPlugins[] = $plugin;
} else {
$internalPlugins[] = '-' . $plugin;
$externalPluginsJS .= sprintf(
'tinymce.PluginManager.load("%s", "%s");' . "\n",
$plugin,
$path
);
$languages = array();
foreach (self::$configs as $configID => $config) {
$settings = $config->settings;
// parse plugins
$configPlugins = array();
foreach($config->plugins as $plugin => $path) {
if(!$path) {
$configPlugins[] = $plugin;
$internalPlugins[] = $plugin;
} else {
$configPlugins[] = '-' . $plugin;
if ( !array_key_exists($plugin, $externalPlugins) )
{
$externalPlugins[$plugin] = sprintf(
'tinymce.PluginManager.load("%s", "%s");',
$plugin,
$path
);
}
}
}
}
$config['plugins'] = implode(',', $internalPlugins);
foreach ($this->buttons as $i=>$buttons) {
$config['theme_advanced_buttons'.$i] = implode(',', $buttons);
// save config plugins settings
$settings['plugins'] = implode(',', $configPlugins);
// buttons
foreach ($config->buttons as $i=>$buttons) {
$settings['theme_advanced_buttons'.$i] = implode(',', $buttons);
}
// languages
$languages[] = $config->getOption('language');
// save this config settings
$configs[$configID] = $settings;
}
return "
// tinyMCE JS requirement
if ( $useGzip )
{
$tag = TinyMCE_Compressor::renderTag(array(
'url' => THIRDPARTY_DIR . '/tinymce/tiny_mce_gzip.php',
'plugins' => implode(',', $internalPlugins),
'themes' => 'advanced',
'languages' => implode(",", array_filter($languages))
), true);
preg_match('/src="([^"]*)"/', $tag, $matches);
Requirements::javascript(html_entity_decode($matches[1]));
}
else{
Requirements::javascript(MCE_ROOT . 'tiny_mce_src.js');
}
// prepare external plugins js string
$externalPlugins = array_values($externalPlugins);
$externalPlugins = implode("\n ", $externalPlugins);
// tinyMCE config object and external plugings
$configsJS = "
if((typeof tinyMCE != 'undefined')) {
$externalPluginsJS
var ssTinyMceConfig = " . Convert::raw2json($config) . ";
}
";
$externalPlugins
var ssTinyMceConfig = " . Convert::raw2json($configs) . ";
}";
Requirements::customScript($configsJS, 'htmlEditorConfig');
}
}

View File

@ -27,41 +27,31 @@ class HtmlEditorField extends TextareaField {
private static $sanitise_server_side = false;
protected $rows = 30;
/**
* Includes the JavaScript neccesary for this field to work using the {@link Requirements} system.
* @deprecated since version 3.2
*/
public static function include_js() {
require_once 'tinymce/tiny_mce_gzip.php';
$configObj = HtmlEditorConfig::get_active();
if(Config::inst()->get('HtmlEditorField', 'use_gzip')) {
$internalPlugins = array();
foreach($configObj->getPlugins() as $plugin => $path) if(!$path) $internalPlugins[] = $plugin;
$tag = TinyMCE_Compressor::renderTag(array(
'url' => THIRDPARTY_DIR . '/tinymce/tiny_mce_gzip.php',
'plugins' => implode(',', $internalPlugins),
'themes' => 'advanced',
'languages' => $configObj->getOption('language')
), true);
preg_match('/src="([^"]*)"/', $tag, $matches);
Requirements::javascript(html_entity_decode($matches[1]));
} else {
Requirements::javascript(MCE_ROOT . 'tiny_mce_src.js');
}
Requirements::customScript($configObj->generateJS(), 'htmlEditorConfig');
Deprecation::notice('4.0', 'Use HtmlEditorConfig::require_js() instead');
HtmlEditorConfig::require_js();
}
protected $editorConfig = null;
/**
* Creates a new HTMLEditorField.
* @see TextareaField::__construct()
*/
public function __construct($name, $title = null, $value = '') {
*
* @param string $name The internal field name, passed to forms.
* @param string $title The human-readable field label.
* @param mixed $value The value of the field.
* @param string $config HTMLEditorConfig identifier to be used. Default to the active one.
*/
public function __construct($name, $title = null, $value = '', $config = null) {
parent::__construct($name, $title, $value);
self::include_js();
$this->editorConfig = $config ? $config : HtmlEditorConfig::get_active_identifier();
}
public function getAttributes() {
@ -71,6 +61,7 @@ class HtmlEditorField extends TextareaField {
'tinymce' => 'true',
'style' => 'width: 97%; height: ' . ($this->rows * 16) . 'px', // prevents horizontal scrollbars
'value' => null,
'data-config' => $this->editorConfig
)
);
}
@ -185,6 +176,8 @@ class HtmlEditorField_Toolbar extends RequestHandler {
Requirements::javascript(THIRDPARTY_DIR . '/jquery-ui/jquery-ui.js');
Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js');
Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/ssui.core.js');
HtmlEditorConfig::require_js();
Requirements::javascript(FRAMEWORK_DIR ."/javascript/HtmlEditorField.js");
Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');

View File

@ -332,8 +332,8 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
},
redraw: function() {
// Using a global config (generated through HTMLEditorConfig PHP logic)
var config = ssTinyMceConfig, self = this, ed = this.getEditor();
// Using textarea config ID from global config object (generated through HTMLEditorConfig PHP logic)
var config = ssTinyMceConfig[this.data('config')], self = this, ed = this.getEditor();
ed.init(config);

View File

@ -67,12 +67,26 @@ class HtmlEditorConfigTest extends SapphireTest {
$this->assertNotContains('plugin2', array_keys($plugins));
}
public function testGenerateJSWritesPlugins() {
$c = new HtmlEditorConfig();
$c->enablePlugins(array('plugin1'));
public function testRequireJSIncludesAllExternalPlugins() {
$c = HtmlEditorConfig::get('config');
$c->enablePlugins(array('plugin1' => '/mypath/plugin1'));
$c->enablePlugins(array('plugin2' => '/mypath/plugin2'));
$this->assertContains('plugin1', $c->generateJS());
$this->assertContains('tinymce.PluginManager.load("plugin2", "/mypath/plugin2");', $c->generateJS());
HtmlEditorConfig::require_js();
$js = Requirements::get_custom_scripts();
$this->assertContains('tinymce.PluginManager.load("plugin1", "/mypath/plugin1");', $js);
$this->assertContains('tinymce.PluginManager.load("plugin2", "/mypath/plugin2");', $js);
}
public function testRequireJSIncludesAllConfigs() {
$c = HtmlEditorConfig::get('configA');
$c = HtmlEditorConfig::get('configB');
HtmlEditorConfig::require_js();
$js = Requirements::get_custom_scripts();
$this->assertContains('"configA":{', $js);
$this->assertContains('"configB":{', $js);
}
}