mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API Support for multiple HTMLEditorConfig per page
This commit is contained in:
parent
f21e59585e
commit
a8ace75341
@ -339,8 +339,6 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
|||||||
|
|
||||||
if (Director::isDev()) Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/leaktools.js');
|
if (Director::isDev()) Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/leaktools.js');
|
||||||
|
|
||||||
HTMLEditorField::include_js();
|
|
||||||
|
|
||||||
$leftAndMainIncludes = array_unique(array_merge(
|
$leftAndMainIncludes = array_unique(array_merge(
|
||||||
array(
|
array(
|
||||||
FRAMEWORK_ADMIN_DIR . '/javascript/LeftAndMain.Layout.js',
|
FRAMEWORK_ADMIN_DIR . '/javascript/LeftAndMain.Layout.js',
|
||||||
|
@ -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
|
## Configuration
|
||||||
|
|
||||||
To keep the JavaScript editor configuration manageable and extensible, we've wrapped it in a PHP class called
|
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()`.
|
then set the currently active config using `set_active()`.
|
||||||
|
|
||||||
<div class="info" markdown="1">
|
<div class="info" markdown="1">
|
||||||
By default, a config named 'cms' is used in any field created throughout the CMS interface.
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="notice" markdown='1'>
|
<div class="notice" markdown='1'>
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
* `FormField::validate` now requires an instance of `Validator`
|
* `FormField::validate` now requires an instance of `Validator`
|
||||||
* Implementation of new "Archive" concept for page removal, which supercedes "delete". Where deletion removed
|
* 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.
|
pages only from draft, archiving removes from both draft and live simultaneously.
|
||||||
|
* Support for multiple HtmlEditorConfigs on the same page.
|
||||||
|
|
||||||
#### Deprecated classes/methods removed
|
#### Deprecated classes/methods removed
|
||||||
|
|
||||||
|
@ -39,12 +39,20 @@ class HtmlEditorConfig {
|
|||||||
self::$current = $identifier;
|
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
|
* Get the currently active configuration object
|
||||||
* @return HtmlEditorConfig - the active configuration object
|
* @return HtmlEditorConfig - the active configuration object
|
||||||
*/
|
*/
|
||||||
public static function get_active() {
|
public static function get_active() {
|
||||||
$identifier = self::$current ? self::$current : 'default';
|
$identifier = self::get_active_identifier();
|
||||||
return self::get($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
|
* Generate the JavaScript that will set TinyMCE's configuration:
|
||||||
* @return string - the javascript
|
* - Parse all configurations into JSON objects to be used in JavaScript
|
||||||
|
* - Includes TinyMCE and configurations using the {@link Requirements} system
|
||||||
*/
|
*/
|
||||||
public function generateJS() {
|
public static function require_js() {
|
||||||
$config = $this->settings;
|
require_once 'tinymce/tiny_mce_gzip.php';
|
||||||
|
$useGzip = Config::inst()->get('HtmlEditorField', 'use_gzip');
|
||||||
|
|
||||||
// plugins
|
$configs = array();
|
||||||
|
$externalPlugins = array();
|
||||||
$internalPlugins = array();
|
$internalPlugins = array();
|
||||||
$externalPluginsJS = '';
|
$languages = array();
|
||||||
foreach($this->plugins as $plugin => $path) {
|
|
||||||
if(!$path) {
|
foreach (self::$configs as $configID => $config) {
|
||||||
$internalPlugins[] = $plugin;
|
$settings = $config->settings;
|
||||||
} else {
|
// parse plugins
|
||||||
$internalPlugins[] = '-' . $plugin;
|
$configPlugins = array();
|
||||||
$externalPluginsJS .= sprintf(
|
foreach($config->plugins as $plugin => $path) {
|
||||||
'tinymce.PluginManager.load("%s", "%s");' . "\n",
|
if(!$path) {
|
||||||
$plugin,
|
$configPlugins[] = $plugin;
|
||||||
$path
|
$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) {
|
// save config plugins settings
|
||||||
$config['theme_advanced_buttons'.$i] = implode(',', $buttons);
|
$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')) {
|
if((typeof tinyMCE != 'undefined')) {
|
||||||
$externalPluginsJS
|
$externalPlugins
|
||||||
var ssTinyMceConfig = " . Convert::raw2json($config) . ";
|
var ssTinyMceConfig = " . Convert::raw2json($configs) . ";
|
||||||
}
|
}";
|
||||||
";
|
Requirements::customScript($configsJS, 'htmlEditorConfig');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,41 +27,31 @@ class HtmlEditorField extends TextareaField {
|
|||||||
private static $sanitise_server_side = false;
|
private static $sanitise_server_side = false;
|
||||||
|
|
||||||
protected $rows = 30;
|
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() {
|
public static function include_js() {
|
||||||
require_once 'tinymce/tiny_mce_gzip.php';
|
Deprecation::notice('4.0', 'Use HtmlEditorConfig::require_js() instead');
|
||||||
|
HtmlEditorConfig::require_js();
|
||||||
$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');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected $editorConfig = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Creates a new HTMLEditorField.
|
||||||
* @see TextareaField::__construct()
|
* @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);
|
parent::__construct($name, $title, $value);
|
||||||
|
|
||||||
self::include_js();
|
$this->editorConfig = $config ? $config : HtmlEditorConfig::get_active_identifier();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAttributes() {
|
public function getAttributes() {
|
||||||
@ -71,6 +61,7 @@ class HtmlEditorField extends TextareaField {
|
|||||||
'tinymce' => 'true',
|
'tinymce' => 'true',
|
||||||
'style' => 'width: 97%; height: ' . ($this->rows * 16) . 'px', // prevents horizontal scrollbars
|
'style' => 'width: 97%; height: ' . ($this->rows * 16) . 'px', // prevents horizontal scrollbars
|
||||||
'value' => null,
|
'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-ui/jquery-ui.js');
|
||||||
Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js');
|
Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js');
|
||||||
Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/ssui.core.js');
|
Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/ssui.core.js');
|
||||||
|
|
||||||
|
HtmlEditorConfig::require_js();
|
||||||
Requirements::javascript(FRAMEWORK_DIR ."/javascript/HtmlEditorField.js");
|
Requirements::javascript(FRAMEWORK_DIR ."/javascript/HtmlEditorField.js");
|
||||||
|
|
||||||
Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
|
Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
|
||||||
|
@ -332,8 +332,8 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
},
|
},
|
||||||
|
|
||||||
redraw: function() {
|
redraw: function() {
|
||||||
// Using a global config (generated through HTMLEditorConfig PHP logic)
|
// Using textarea config ID from global config object (generated through HTMLEditorConfig PHP logic)
|
||||||
var config = ssTinyMceConfig, self = this, ed = this.getEditor();
|
var config = ssTinyMceConfig[this.data('config')], self = this, ed = this.getEditor();
|
||||||
|
|
||||||
ed.init(config);
|
ed.init(config);
|
||||||
|
|
||||||
|
@ -67,12 +67,26 @@ class HtmlEditorConfigTest extends SapphireTest {
|
|||||||
$this->assertNotContains('plugin2', array_keys($plugins));
|
$this->assertNotContains('plugin2', array_keys($plugins));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGenerateJSWritesPlugins() {
|
public function testRequireJSIncludesAllExternalPlugins() {
|
||||||
$c = new HtmlEditorConfig();
|
$c = HtmlEditorConfig::get('config');
|
||||||
$c->enablePlugins(array('plugin1'));
|
$c->enablePlugins(array('plugin1' => '/mypath/plugin1'));
|
||||||
$c->enablePlugins(array('plugin2' => '/mypath/plugin2'));
|
$c->enablePlugins(array('plugin2' => '/mypath/plugin2'));
|
||||||
|
|
||||||
$this->assertContains('plugin1', $c->generateJS());
|
HtmlEditorConfig::require_js();
|
||||||
$this->assertContains('tinymce.PluginManager.load("plugin2", "/mypath/plugin2");', $c->generateJS());
|
$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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user