Files
moodle/lib/editor/tinymce/classes/plugin.php
T
2012-08-10 14:57:58 +02:00

324 lines
11 KiB
PHP

<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
/**
* TinyMCE text editor plugin base class.
*
* This is a base class for TinyMCE plugins implemented within Moodle. These
* plugins can optionally provide new buttons/plugins within TinyMCE itself,
* or configure the TinyMCE options.
*
* As well as overridable functions, other utility functions in this class
* can be used when writing the plugins.
*
* Finally, a static function in this class is used to call into all the
* plugins when required.
*
* @package editor_tinymce
* @copyright 2012 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class editor_tinymce_plugin {
/** @var string Plugin folder */
protected $plugin;
/**
* @param string $plugin Name of folder
*/
public function __construct($plugin) {
$this->plugin = $plugin;
}
/**
* Adjusts TinyMCE init parameters for this plugin.
*
* Subclasses must implement this function in order to carry out changes
* to the TinyMCE settings.
*
* @param array $params TinyMCE init parameters array
* @param context $context Context where editor is being shown
* @param array $options Options for this editor
*/
protected abstract function update_init_params(array &$params, context $context,
array $options = null);
/**
* Gets the order in which to run this plugin. Order usually only matters if
* (a) the place you add your button might depend on another plugin, or
* (b) you want to make some changes to layout etc. that should happen last.
* The default order is 100; within that, plugins are sorted alphabetically.
* Return a lower number if you want this plugin to run earlier, or a higher
* number if you want it to run later.
*/
protected function get_sort_order() {
return 100;
}
/**
* Adds a button to the editor, after another button (or at the end).
*
* Specify the location of this button using the $after variable. If you
* leave this blank, the button will be added at the end.
*
* If you want to try different possible locations depending on existing
* plugins you can set $alwaysadd to false and check the return value
* to see if it succeeded.
*
* @param array $params TinyMCE init parameters array
* @param int $row Row to add button to (1 to 3)
* @param string $button Identifier of button/plugin
* @param string $after Adds button directly after the named plugin
* @param bool $alwaysadd If specified $after string not found, add at end
* @return bool True if added
*/
protected function add_button_after(array &$params, $row, $button,
$after = '', $alwaysadd = true) {
$this->check_row($row);
$field = 'theme_advanced_buttons' . $row;
$old = $params[$field];
// Empty = add at end.
if ($after === '') {
$params[$field] = $old . ',' . $button;
return true;
}
// Try to add after given plugin.
$params[$field] = preg_replace('~(,|^)(' . preg_quote($after) . ')(,|$)~',
'$1$2,' . $button . '$3', $old);
if ($params[$field] !== $old) {
return true;
}
// If always adding, recurse to add it empty.
if ($alwaysadd) {
return $this->add_button_after($params, $row, $button);
}
// Otherwise return false (failed to add).
return false;
}
/**
* Adds a button to the editor.
*
* Specify the location of this button using the $before variable. If you
* leave this blank, the button will be added at the start.
*
* If you want to try different possible locations depending on existing
* plugins you can set $alwaysadd to false and check the return value
* to see if it succeeded.
*
* @param array $params TinyMCE init parameters array
* @param int $row Row to add button to (1 to 3)
* @param string $button Identifier of button/plugin
* @param string $before Adds button directly before the named plugin
* @param bool $alwaysadd If specified $after string not found, add at start
* @return bool True if added
*/
protected function add_button_before(array &$params, $row, $button,
$before = '', $alwaysadd = true) {
$this->check_row($row);
$field = 'theme_advanced_buttons' . $row;
$old = $params[$field];
// Empty = add at start.
if ($before === '') {
$params[$field] = $button . ',' . $old;
return true;
}
// Try to add after given plugin.
$params[$field] = preg_replace('~(,|^)(' . preg_quote($before) . ')(,|$)~',
'$1' . $button . ',$2$3', $old);
if ($params[$field] !== $old) {
return true;
}
// If always adding, recurse to add it empty.
if ($alwaysadd) {
return $this->add_button_before($params, $row, $button);
}
// Otherwise return false (failed to add).
return false;
}
/**
* Checks the row value is valid.
*
* @param int $row Row to add button to (1 to 3)
* @throws coding_exception If row value is outside the range 1-3
*/
private function check_row($row) {
if ($row < 1 || $row > 3) {
throw new coding_exception("Invalid row option: $row");
}
}
/**
* Adds a JavaScript plugin into TinyMCE. Note that adding a plugin does
* not by itself add a button; you must do both.
*
* If you leave $pluginname blank (default) it uses the folder name.
*
* @param array $params TinyMCE init parameters array
* @param string $pluginname Identifier for plugin within TinyMCE
* @param string $jsfile Name of JS file (within plugin 'tinymce' directory)
*/
protected function add_js_plugin(&$params, $pluginname='', $jsfile='editor_plugin.js') {
global $CFG;
// Set default plugin name.
if ($pluginname === '') {
$pluginname = $this->plugin;
}
// Add plugin to list in params, so it doesn't try to load it again.
$params['plugins'] .= ',-' . $pluginname;
// Add special param that causes Moodle TinyMCE init to load the plugin.
if (!isset($params['moodle_init_plugins'])) {
$params['moodle_init_plugins'] = '';
} else {
$params['moodle_init_plugins'] .= ',';
}
// Get URL of main JS file and store in params.
$jsurl = $this->get_tinymce_file_url($jsfile, false);
$params['moodle_init_plugins'] .= $pluginname . ':' . $jsurl;
}
/**
* Returns URL to files in the TinyMCE folder within this plugin, suitable
* for client-side use such as loading JavaScript files. (This URL normally
* goes through loader.php and contains the plugin version to ensure
* correct and long-term cacheing.)
*
* @param string $file Filename or path within the folder
* @param bool $absolute Set false to get relative URL from plugins folder
*/
public function get_tinymce_file_url($file='', $absolute=true) {
global $CFG;
// Version number comes from plugin version.php, except in developer
// mode where the special string 'dev' is used (prevents cacheing and
// serves unminified JS).
if (debugging('', DEBUG_DEVELOPER)) {
$version = '-1';
} else {
$version = $this->get_version();
}
// Calculate the JS url (relative to the TinyMCE plugins folder - using
// relative URL saves a few bytes in each HTML page).
if ($CFG->slasharguments) {
// URL is usually from loader.php...
$jsurl = 'loader.php/' . $this->plugin . '/' . $version . '/' . $file;
} else {
// ...except when slash arguments are turned off it serves direct.
// In this situation there is no version details and it is up to
// the browser and server to negotiate cacheing, which will mean
// requesting the JS files frequently (reduced performance).
$jsurl = $this->plugin . '/tinymce/' . $file;
}
if ($absolute) {
$jsurl = $CFG->wwwroot . '/lib/editor/tinymce/plugins/' . $jsurl;
}
return $jsurl;
}
/**
* Obtains version number from version.php for this plugin.
*
* @return string Version number
*/
protected function get_version() {
global $CFG;
$plugin = new stdClass;
require($CFG->dirroot . '/lib/editor/tinymce/plugins/' . $this->plugin . '/version.php');
return $plugin->version;
}
/**
* Calls all available plugins to adjust the TinyMCE init parameters.
*
* @param array $params TinyMCE init parameters array
* @param context $context Context where editor is being shown
* @param array $options Options for this editor
*/
public static function all_update_init_params(array &$params,
context $context, array $options = null) {
global $CFG;
// Get list of plugin directories.
$plugins = get_plugin_list('tinymce');
// Construct all the plugins.
$pluginobjects = array();
foreach ($plugins as $plugin => $dir) {
require_once($dir . '/lib.php');
$classname = 'tinymce_' . $plugin;
$pluginobjects[] = new $classname($plugin);
}
// Sort plugins by sort order and name.
usort($pluginobjects, array('editor_tinymce_plugin', 'compare_plugins'));
// Run the function for each plugin.
foreach ($pluginobjects as $obj) {
$obj->update_init_params($params, $context, $options);
}
}
/**
* Gets a named plugin object. Will cause fatal error if plugin doesn't exist.
*
* @param string $plugin Name of plugin e.g. 'moodleemoticon'
* @return editor_tinymce_plugin Plugin object
*/
public static function get($plugin) {
$dir = get_component_directory('tinymce_' . $plugin);
require_once($dir . '/lib.php');
$classname = 'tinymce_' . $plugin;
return new $classname($plugin);
}
/**
* Compares two plugins.
* @param editor_tinymce_plugin $a
* @param editor_tinymce_plugin $b
* @return Negative number if $a is before $b
*/
public static function compare_plugins(editor_tinymce_plugin $a, editor_tinymce_plugin $b) {
// Use sort order first.
$order = $a->get_sort_order() - $b->get_sort_order();
if ($order != 0) {
return $order;
}
// Then sort alphabetically.
return strcmp($a->plugin, $b->plugin);
}
}