Commit | Line | Data |
---|---|---|
fae91170 | 1 | <?php |
2 | // This file is part of Moodle - http://moodle.org/ | |
3 | // | |
4 | // Moodle is free software: you can redistribute it and/or modify | |
5 | // it under the terms of the GNU General Public License as published by | |
6 | // the Free Software Foundation, either version 3 of the License, or | |
7 | // (at your option) any later version. | |
8 | // | |
9 | // Moodle is distributed in the hope that it will be useful, | |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | // GNU General Public License for more details. | |
13 | // | |
14 | // You should have received a copy of the GNU General Public License | |
15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | defined('MOODLE_INTERNAL') || die(); | |
18 | ||
19 | /** | |
20 | * TinyMCE text editor plugin base class. | |
21 | * | |
22 | * This is a base class for TinyMCE plugins implemented within Moodle. These | |
23 | * plugins can optionally provide new buttons/plugins within TinyMCE itself, | |
24 | * or configure the TinyMCE options. | |
25 | * | |
26 | * As well as overridable functions, other utility functions in this class | |
27 | * can be used when writing the plugins. | |
28 | * | |
29 | * Finally, a static function in this class is used to call into all the | |
30 | * plugins when required. | |
31 | * | |
32 | * @package editor_tinymce | |
33 | * @copyright 2012 The Open University | |
34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
35 | */ | |
36 | abstract class editor_tinymce_plugin { | |
37 | /** @var string Plugin folder */ | |
38 | protected $plugin; | |
39 | ||
116ad39b PS |
40 | /** @var array Plugin settings */ |
41 | protected $config = null; | |
42 | ||
fae91170 | 43 | /** |
44 | * @param string $plugin Name of folder | |
45 | */ | |
46 | public function __construct($plugin) { | |
47 | $this->plugin = $plugin; | |
48 | } | |
49 | ||
116ad39b PS |
50 | /** |
51 | * Makes sure config is loaded and cached. | |
52 | * @return void | |
53 | */ | |
54 | protected function load_config() { | |
55 | if (!isset($this->config)) { | |
56 | $name = $this->get_name(); | |
57 | $this->config = get_config("tinymce_$name"); | |
58 | } | |
59 | } | |
60 | ||
61 | /** | |
62 | * Returns plugin config value. | |
63 | * @param string $name | |
64 | * @param string $default value if config does not exist yet | |
65 | * @return string value or default | |
66 | */ | |
67 | public function get_config($name, $default = null) { | |
68 | $this->load_config(); | |
69 | return isset($this->config->$name) ? $this->config->$name : $default; | |
70 | } | |
71 | ||
72 | /** | |
73 | * Sets plugin config value. | |
74 | * @param string $name name of config | |
75 | * @param string $value string config value, null means delete | |
76 | * @return string value | |
77 | */ | |
78 | public function set_config($name, $value) { | |
79 | $pluginname = $this->get_name(); | |
80 | $this->load_config(); | |
81 | if ($value === null) { | |
82 | unset($this->config->$name); | |
83 | } else { | |
84 | $this->config->$name = $value; | |
85 | } | |
86 | set_config($name, $value, "tinymce_$pluginname"); | |
87 | } | |
88 | ||
89 | /** | |
90 | * Returns name of this tinymce plugin. | |
91 | * @return string | |
92 | */ | |
93 | public function get_name() { | |
94 | // All class names start with "tinymce_". | |
95 | $words = explode('_', get_class($this), 2); | |
96 | return $words[1]; | |
97 | } | |
98 | ||
fae91170 | 99 | /** |
100 | * Adjusts TinyMCE init parameters for this plugin. | |
101 | * | |
102 | * Subclasses must implement this function in order to carry out changes | |
103 | * to the TinyMCE settings. | |
104 | * | |
105 | * @param array $params TinyMCE init parameters array | |
106 | * @param context $context Context where editor is being shown | |
107 | * @param array $options Options for this editor | |
108 | */ | |
109 | protected abstract function update_init_params(array &$params, context $context, | |
110 | array $options = null); | |
111 | ||
112 | /** | |
113 | * Gets the order in which to run this plugin. Order usually only matters if | |
114 | * (a) the place you add your button might depend on another plugin, or | |
115 | * (b) you want to make some changes to layout etc. that should happen last. | |
116 | * The default order is 100; within that, plugins are sorted alphabetically. | |
117 | * Return a lower number if you want this plugin to run earlier, or a higher | |
118 | * number if you want it to run later. | |
119 | */ | |
120 | protected function get_sort_order() { | |
121 | return 100; | |
122 | } | |
123 | ||
124 | /** | |
125 | * Adds a button to the editor, after another button (or at the end). | |
126 | * | |
127 | * Specify the location of this button using the $after variable. If you | |
128 | * leave this blank, the button will be added at the end. | |
129 | * | |
130 | * If you want to try different possible locations depending on existing | |
131 | * plugins you can set $alwaysadd to false and check the return value | |
132 | * to see if it succeeded. | |
133 | * | |
134 | * @param array $params TinyMCE init parameters array | |
135 | * @param int $row Row to add button to (1 to 3) | |
136 | * @param string $button Identifier of button/plugin | |
137 | * @param string $after Adds button directly after the named plugin | |
138 | * @param bool $alwaysadd If specified $after string not found, add at end | |
139 | * @return bool True if added | |
140 | */ | |
141 | protected function add_button_after(array &$params, $row, $button, | |
142 | $after = '', $alwaysadd = true) { | |
143 | $this->check_row($row); | |
144 | ||
145 | $field = 'theme_advanced_buttons' . $row; | |
146 | $old = $params[$field]; | |
147 | ||
148 | // Empty = add at end. | |
149 | if ($after === '') { | |
150 | $params[$field] = $old . ',' . $button; | |
151 | return true; | |
152 | } | |
153 | ||
154 | // Try to add after given plugin. | |
155 | $params[$field] = preg_replace('~(,|^)(' . preg_quote($after) . ')(,|$)~', | |
156 | '$1$2,' . $button . '$3', $old); | |
157 | if ($params[$field] !== $old) { | |
158 | return true; | |
159 | } | |
160 | ||
161 | // If always adding, recurse to add it empty. | |
162 | if ($alwaysadd) { | |
163 | return $this->add_button_after($params, $row, $button); | |
164 | } | |
165 | ||
166 | // Otherwise return false (failed to add). | |
167 | return false; | |
168 | } | |
169 | ||
170 | /** | |
171 | * Adds a button to the editor. | |
172 | * | |
173 | * Specify the location of this button using the $before variable. If you | |
174 | * leave this blank, the button will be added at the start. | |
175 | * | |
176 | * If you want to try different possible locations depending on existing | |
177 | * plugins you can set $alwaysadd to false and check the return value | |
178 | * to see if it succeeded. | |
179 | * | |
180 | * @param array $params TinyMCE init parameters array | |
181 | * @param int $row Row to add button to (1 to 3) | |
182 | * @param string $button Identifier of button/plugin | |
183 | * @param string $before Adds button directly before the named plugin | |
184 | * @param bool $alwaysadd If specified $after string not found, add at start | |
185 | * @return bool True if added | |
186 | */ | |
187 | protected function add_button_before(array &$params, $row, $button, | |
188 | $before = '', $alwaysadd = true) { | |
189 | $this->check_row($row); | |
190 | ||
191 | $field = 'theme_advanced_buttons' . $row; | |
192 | $old = $params[$field]; | |
193 | ||
194 | // Empty = add at start. | |
195 | if ($before === '') { | |
196 | $params[$field] = $button . ',' . $old; | |
197 | return true; | |
198 | } | |
199 | ||
200 | // Try to add after given plugin. | |
201 | $params[$field] = preg_replace('~(,|^)(' . preg_quote($before) . ')(,|$)~', | |
202 | '$1' . $button . ',$2$3', $old); | |
203 | if ($params[$field] !== $old) { | |
204 | return true; | |
205 | } | |
206 | ||
207 | // If always adding, recurse to add it empty. | |
208 | if ($alwaysadd) { | |
209 | return $this->add_button_before($params, $row, $button); | |
210 | } | |
211 | ||
212 | // Otherwise return false (failed to add). | |
213 | return false; | |
214 | } | |
215 | ||
216 | /** | |
217 | * Checks the row value is valid. | |
218 | * | |
219 | * @param int $row Row to add button to (1 to 3) | |
220 | * @throws coding_exception If row value is outside the range 1-3 | |
221 | */ | |
222 | private function check_row($row) { | |
223 | if ($row < 1 || $row > 3) { | |
224 | throw new coding_exception("Invalid row option: $row"); | |
225 | } | |
226 | } | |
227 | ||
228 | /** | |
229 | * Adds a JavaScript plugin into TinyMCE. Note that adding a plugin does | |
230 | * not by itself add a button; you must do both. | |
231 | * | |
232 | * If you leave $pluginname blank (default) it uses the folder name. | |
233 | * | |
234 | * @param array $params TinyMCE init parameters array | |
235 | * @param string $pluginname Identifier for plugin within TinyMCE | |
236 | * @param string $jsfile Name of JS file (within plugin 'tinymce' directory) | |
237 | */ | |
238 | protected function add_js_plugin(&$params, $pluginname='', $jsfile='editor_plugin.js') { | |
239 | global $CFG; | |
240 | ||
241 | // Set default plugin name. | |
242 | if ($pluginname === '') { | |
243 | $pluginname = $this->plugin; | |
244 | } | |
245 | ||
246 | // Add plugin to list in params, so it doesn't try to load it again. | |
247 | $params['plugins'] .= ',-' . $pluginname; | |
248 | ||
249 | // Add special param that causes Moodle TinyMCE init to load the plugin. | |
250 | if (!isset($params['moodle_init_plugins'])) { | |
251 | $params['moodle_init_plugins'] = ''; | |
252 | } else { | |
253 | $params['moodle_init_plugins'] .= ','; | |
254 | } | |
255 | ||
256 | // Get URL of main JS file and store in params. | |
257 | $jsurl = $this->get_tinymce_file_url($jsfile, false); | |
258 | $params['moodle_init_plugins'] .= $pluginname . ':' . $jsurl; | |
259 | } | |
260 | ||
261 | /** | |
262 | * Returns URL to files in the TinyMCE folder within this plugin, suitable | |
263 | * for client-side use such as loading JavaScript files. (This URL normally | |
264 | * goes through loader.php and contains the plugin version to ensure | |
265 | * correct and long-term cacheing.) | |
266 | * | |
267 | * @param string $file Filename or path within the folder | |
268 | * @param bool $absolute Set false to get relative URL from plugins folder | |
269 | */ | |
270 | public function get_tinymce_file_url($file='', $absolute=true) { | |
271 | global $CFG; | |
272 | ||
273 | // Version number comes from plugin version.php, except in developer | |
274 | // mode where the special string 'dev' is used (prevents cacheing and | |
275 | // serves unminified JS). | |
276 | if (debugging('', DEBUG_DEVELOPER)) { | |
277 | $version = '-1'; | |
278 | } else { | |
279 | $version = $this->get_version(); | |
280 | } | |
281 | ||
282 | // Calculate the JS url (relative to the TinyMCE plugins folder - using | |
283 | // relative URL saves a few bytes in each HTML page). | |
284 | if ($CFG->slasharguments) { | |
285 | // URL is usually from loader.php... | |
286 | $jsurl = 'loader.php/' . $this->plugin . '/' . $version . '/' . $file; | |
287 | } else { | |
288 | // ...except when slash arguments are turned off it serves direct. | |
289 | // In this situation there is no version details and it is up to | |
290 | // the browser and server to negotiate cacheing, which will mean | |
291 | // requesting the JS files frequently (reduced performance). | |
292 | $jsurl = $this->plugin . '/tinymce/' . $file; | |
293 | } | |
294 | ||
295 | if ($absolute) { | |
296 | $jsurl = $CFG->wwwroot . '/lib/editor/tinymce/plugins/' . $jsurl; | |
297 | } | |
298 | ||
299 | return $jsurl; | |
300 | } | |
301 | ||
302 | /** | |
303 | * Obtains version number from version.php for this plugin. | |
304 | * | |
305 | * @return string Version number | |
306 | */ | |
307 | protected function get_version() { | |
308 | global $CFG; | |
309 | ||
310 | $plugin = new stdClass; | |
311 | require($CFG->dirroot . '/lib/editor/tinymce/plugins/' . $this->plugin . '/version.php'); | |
312 | return $plugin->version; | |
313 | } | |
314 | ||
315 | /** | |
316 | * Calls all available plugins to adjust the TinyMCE init parameters. | |
317 | * | |
318 | * @param array $params TinyMCE init parameters array | |
319 | * @param context $context Context where editor is being shown | |
320 | * @param array $options Options for this editor | |
321 | */ | |
322 | public static function all_update_init_params(array &$params, | |
323 | context $context, array $options = null) { | |
324 | global $CFG; | |
325 | ||
326 | // Get list of plugin directories. | |
327 | $plugins = get_plugin_list('tinymce'); | |
328 | ||
329 | // Construct all the plugins. | |
330 | $pluginobjects = array(); | |
331 | foreach ($plugins as $plugin => $dir) { | |
332 | require_once($dir . '/lib.php'); | |
333 | $classname = 'tinymce_' . $plugin; | |
334 | $pluginobjects[] = new $classname($plugin); | |
335 | } | |
336 | ||
337 | // Sort plugins by sort order and name. | |
338 | usort($pluginobjects, array('editor_tinymce_plugin', 'compare_plugins')); | |
339 | ||
340 | // Run the function for each plugin. | |
341 | foreach ($pluginobjects as $obj) { | |
342 | $obj->update_init_params($params, $context, $options); | |
343 | } | |
344 | } | |
345 | ||
346 | /** | |
347 | * Gets a named plugin object. Will cause fatal error if plugin doesn't exist. | |
348 | * | |
349 | * @param string $plugin Name of plugin e.g. 'moodleemoticon' | |
350 | * @return editor_tinymce_plugin Plugin object | |
351 | */ | |
352 | public static function get($plugin) { | |
353 | $dir = get_component_directory('tinymce_' . $plugin); | |
354 | require_once($dir . '/lib.php'); | |
355 | $classname = 'tinymce_' . $plugin; | |
356 | return new $classname($plugin); | |
357 | } | |
358 | ||
359 | /** | |
360 | * Compares two plugins. | |
361 | * @param editor_tinymce_plugin $a | |
362 | * @param editor_tinymce_plugin $b | |
363 | * @return Negative number if $a is before $b | |
364 | */ | |
365 | public static function compare_plugins(editor_tinymce_plugin $a, editor_tinymce_plugin $b) { | |
366 | // Use sort order first. | |
367 | $order = $a->get_sort_order() - $b->get_sort_order(); | |
368 | if ($order != 0) { | |
369 | return $order; | |
370 | } | |
371 | ||
372 | // Then sort alphabetically. | |
373 | return strcmp($a->plugin, $b->plugin); | |
374 | } | |
375 | } |