Completed
Push — createendpointfetcher-test ( b088ef )
by Sam
08:07
created

TinyMCEConfig::getEditorCSS()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 17
rs 9.4285
cc 3
eloc 12
nc 3
nop 0
1
<?php
2
3
/**
4
 * Default configuration for HtmlEditor specific to tinymce
5
 */
6
class TinyMCEConfig extends HtmlEditorConfig {
7
8
	/**
9
	 * Location of module relative to BASE_DIR. This must contain the following dirs
10
	 * - plugins
11
	 * - themes
12
	 * - skins
13
	 *
14
	 * @config
15
	 * @var string
16
	 */
17
	private static $base_dir = 'framework/thirdparty/tinymce';
18
19
	/**
20
	 * TinyMCE JS settings
21
	 *
22
	 * @link https://www.tinymce.com/docs/configure/
23
	 *
24
	 * @var array
25
	 */
26
    protected $settings = array(
27
		'fix_list_elements' => true, // https://www.tinymce.com/docs/configure/content-filtering/#fix_list_elements
28
		'friendly_name' => '(Please set a friendly name for this config)',
29
		'priority' => 0, // used for Per-member config override
30
		'browser_spellcheck' => true,
31
		'body_class' => 'typography',
32
        'elementpath' => false, // https://www.tinymce.com/docs/configure/editor-appearance/#elementpath
33
		'relative_urls' => true,
34
		'remove_script_host' => true,
35
		'convert_urls' => false, // Prevent site-root images being rewritten to base relative
36
		'menubar' => false,
37
		'language' => 'en',
38
    );
39
40
	/**
41
	 * Holder list of enabled plugins
42
     *
43
     * @var array
44
	 */
45
	protected $plugins = array(
46
		'table' => null,
47
		'emoticons' => null,
48
		'paste' => null,
49
		'code' => null,
50
		'link' => null,
51
		'importcss' => null,
52
	);
53
54
	/**
55
	 * Theme name
56
	 *
57
	 * @var string
58
	 */
59
	protected $theme = 'modern';
60
61
	/**
62
	 * Get the theme
63
	 *
64
	 * @return string
65
	 */
66
	public function getTheme() {
67
		return $this->theme;
68
	}
69
70
	/**
71
	 * Set the theme name
72
	 *
73
	 * @param string $theme
74
	 * @return $this
75
	 */
76
	public function setTheme($theme) {
77
		$this->theme = $theme;
78
		return $this;
79
	}
80
81
	/**
82
	 * Holder list of buttons, organised by line. This array is 1-based indexed array
83
     *
84
     * {@link https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols}
85
     *
86
     * @var array
87
	 */
88
	protected $buttons = array(
89
		1 => array(
90
            'bold', 'italic', 'underline', 'removeformat', '|',
91
			'alignleft', 'aligncenter', 'alignright', 'alignjustify', '|',
92
			'bullist', 'numlist', 'outdent', 'indent',
93
        ),
94
		2 => array(
95
            'formatselect', '|',
96
			'paste', 'pastetext', '|',
97
			'table', 'ssmedia', 'sslink', 'unlink', '|',
98
			'code'
99
        ),
100
		3 => array()
101
	);
102
103
	public function getOption($key) {
104
		if(isset($this->settings[$key])) {
105
            return $this->settings[$key];
106
        }
107
        return null;
108
	}
109
110
	public function setOption($key,$value) {
111
		$this->settings[$key] = $value;
112
		return $this;
113
	}
114
115
	public function setOptions($options) {
116
		foreach ($options as $key => $value) {
117
			$this->settings[$key] = $value;
118
		}
119
		return $this;
120
	}
121
122
    /**
123
     * Get all settings
124
     *
125
     * @return array
126
     */
127
    protected function getSettings() {
128
        return $this->settings;
129
    }
130
131
    public function getAttributes() {
132
        return [
133
            'data-editor' => 'tinyMCE', // Register ss.editorWrappers.tinyMCE
134
            'data-config' => Convert::array2json($this->getConfig())
135
        ];
136
    }
137
138
	/**
139
	 * Enable one or several plugins. Will maintain unique list if already
140
	 * enabled plugin is re-passed. If passed in as a map of plugin-name to path,
141
	 * the plugin will be loaded by tinymce.PluginManager.load() instead of through tinyMCE.init().
142
	 * Keep in mind that these externals plugins require a dash-prefix in their name.
143
	 *
144
	 * @see http://wiki.moxiecode.com/index.php/TinyMCE:API/tinymce.PluginManager/load
145
	 *
146
	 * If passing in a non-associative array, the plugin name should be located in the standard tinymce
147
	 * plugins folder.
148
	 *
149
	 * If passing in an associative array, the key of each item should be the plugin name.
150
	 * The value of each item is one of:
151
	 *  - null - Will be treated as a stardard plugin in the standard location
152
	 *  - relative path - Will be treated as a relative url
153
	 *  - absolute url - Some url to an external plugin
154
	 *
155
	 * @param string $plugin,... a string, or several strings, or a single array of strings - The plugins to enable
0 ignored issues
show
Documentation introduced by
There is no parameter named $plugin,.... Did you maybe mean $plugin?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
156
	 * @return $this
157
	 */
158
	public function enablePlugins($plugin) {
0 ignored issues
show
Unused Code introduced by
The parameter $plugin is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
159
		$plugins = func_get_args();
160
		if (is_array(current($plugins))) {
161
            $plugins = current($plugins);
162
        }
163
		foreach ($plugins as $name => $path) {
164
			// if plugins are passed without a path
165
			if(is_numeric($name)) {
166
				$name = $path;
167
				$path = null;
168
			}
169
			if (!array_key_exists($name, $this->plugins)) {
170
                $this->plugins[$name] = $path;
171
            }
172
		}
173
		return $this;
174
	}
175
176
	/**
177
	 * Enable one or several plugins. Will properly handle being passed a plugin that is already disabled
178
	 * @param string $plugin,... a string, or several strings, or a single array of strings - The plugins to enable
0 ignored issues
show
Documentation introduced by
There is no parameter named $plugin,.... Did you maybe mean $plugin?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
179
	 * @return $this
180
	 */
181
	public function disablePlugins($plugin) {
0 ignored issues
show
Unused Code introduced by
The parameter $plugin is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
182
		$plugins = func_get_args();
183
		if (is_array(current($plugins))) {
184
            $plugins = current($plugins);
185
        }
186
		foreach ($plugins as $name) {
187
			unset($this->plugins[$name]);
188
		}
189
		return $this;
190
	}
191
192
	/**
193
     * Gets the list of all enabled plugins as an associative array.
194
     * Array keys are the plugin names, and values are potentially the plugin location
195
     *
196
	 * @return array
197
	 */
198
	public function getPlugins() {
199
		return $this->plugins;
200
	}
201
202
	/**
203
	 * Get list of plugins without custom locations, which is the set of
204
	 * plugins which can be loaded via the standard plugin path, and could
205
	 * potentially be minified
206
	 *
207
	 * @return array
208
	 */
209
	public function getInternalPlugins() {
210
		// Return only plugins with no custom url
211
		$plugins = [];
212
		foreach($this->getPlugins() as $name => $url) {
213
			if(empty($url)) {
214
				$plugins[] = $name;
215
			}
216
		}
217
		return $plugins;
218
	}
219
220
    /**
221
     * Get all button rows, skipping empty rows
222
     *
223
     * @return array
224
     */
225
    public function getButtons() {
226
        return array_filter($this->buttons);
227
    }
228
229
	/**
230
	 * Totally re-set the buttons on a given line
231
	 *
232
	 * @param int $line The line number to redefine, from 1 to 3
233
	 * @param string $buttons,... A string or several strings, or a single array of strings.
0 ignored issues
show
Documentation introduced by
There is no parameter named $buttons,.... Did you maybe mean $buttons?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
234
     * The button names to assign to this line.
235
	 * @return $this
236
	 */
237
	public function setButtonsForLine($line, $buttons) {
238
		if (func_num_args() > 2) {
239
			$buttons = func_get_args();
240
			array_shift($buttons);
241
		}
242
		$this->buttons[$line] = is_array($buttons) ? $buttons : array($buttons);
243
		return $this;
244
	}
245
246
	/**
247
	 * Add buttons to the end of a line
248
	 * @param int $line The line number to redefine, from 1 to 3
249
	 * @param string $buttons,... A string or several strings, or a single array of strings.
0 ignored issues
show
Documentation introduced by
There is no parameter named $buttons,.... Did you maybe mean $buttons?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
250
     * The button names to add to this line
251
	 * @return $this
252
	 */
253
	public function addButtonsToLine($line, $buttons) {
254
        if(func_num_args() > 2) {
255
            $buttons = func_get_args();
256
            array_shift($buttons);
257
        }
258
        if(!is_array($buttons)) {
259
            $buttons = [$buttons];
260
        }
261
		foreach ($buttons as $button) {
262
			$this->buttons[$line][] = $button;
263
		}
264
		return $this;
265
	}
266
267
	/**
268
	 * Internal function for adding and removing buttons related to another button
269
	 * @param string $name The name of the button to modify
270
	 * @param int $offset The offset relative to that button to perform an array_splice at.
271
     * 0 for before $name, 1 for after.
272
	 * @param int $del The number of buttons to remove at the position given by index(string) + offset
273
	 * @param mixed $add An array or single item to insert at the position given by index(string) + offset,
274
	 * or null for no insertion
275
	 * @return bool True if $name matched a button, false otherwise
276
	 */
277
	protected function modifyButtons($name, $offset, $del=0, $add=null) {
278
		foreach ($this->buttons as &$buttons) {
279
			if (($idx = array_search($name, $buttons)) !== false) {
280
				if ($add) {
281
                    array_splice($buttons, $idx + $offset, $del, $add);
282
                } else {
283
                    array_splice($buttons, $idx + $offset, $del);
284
                }
285
				return true;
286
			}
287
		}
288
		return false;
289
	}
290
291
	/**
292
	 * Insert buttons before the first occurance of another button
293
	 * @param string $before the name of the button to insert other buttons before
294
	 * @param string $buttons,... a string, or several strings, or a single array of strings.
0 ignored issues
show
Documentation introduced by
There is no parameter named $buttons,.... Did you maybe mean $buttons?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
295
     * The button names to insert before that button
296
	 * @return bool True if insertion occured, false if it did not (because the given button name was not found)
297
	 */
298 View Code Duplication
	public function insertButtonsBefore($before, $buttons) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
299
        if(func_num_args() > 2) {
300
            $buttons = func_get_args();
301
            array_shift($buttons);
302
        }
303
        if(!is_array($buttons)) {
304
            $buttons = [$buttons];
305
        }
306
		return $this->modifyButtons($before, 0, 0, $buttons);
307
	}
308
309
	/**
310
	 * Insert buttons after the first occurance of another button
311
	 * @param string $after the name of the button to insert other buttons before
312
	 * @param string $buttons,... a string, or several strings, or a single array of strings.
0 ignored issues
show
Documentation introduced by
There is no parameter named $buttons,.... Did you maybe mean $buttons?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
313
     * The button names to insert after that button
314
	 * @return bool True if insertion occured, false if it did not (because the given button name was not found)
315
	 */
316 View Code Duplication
	public function insertButtonsAfter($after, $buttons) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
317
		if(func_num_args() > 2) {
318
            $buttons = func_get_args();
319
            array_shift($buttons);
320
        }
321
        if(!is_array($buttons)) {
322
            $buttons = [$buttons];
323
        }
324
		return $this->modifyButtons($after, 1, 0, $buttons);
325
	}
326
327
	/**
328
	 * Remove the first occurance of buttons
329
	 * @param string $buttons,... one or more strings - the name of the buttons to remove
0 ignored issues
show
Documentation introduced by
There is no parameter named $buttons,.... Did you maybe mean $buttons?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
330
	 * @return null
331
	 */
332 View Code Duplication
	public function removeButtons($buttons) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
333
        if(func_num_args() > 1) {
334
            $buttons = func_get_args();
335
        }
336
        if(!is_array($buttons)) {
337
            $buttons = [$buttons];
338
        }
339
		foreach ($buttons as $button) {
340
			$this->modifyButtons($button, 0, 1);
341
		}
342
	}
343
344
	/**
345
	 * Generate the JavaScript that will set TinyMCE's configuration:
346
	 * - Parse all configurations into JSON objects to be used in JavaScript
347
	 * - Includes TinyMCE and configurations using the {@link Requirements} system
348
     *
349
     * @return array
350
	 */
351
	protected function getConfig() {
352
        $settings = $this->getSettings();
353
354
		// https://www.tinymce.com/docs/configure/url-handling/#document_base_url
355
        $settings['document_base_url'] = Director::absoluteBaseURL();
356
357
		// https://www.tinymce.com/docs/api/class/tinymce.editormanager/#baseURL
358
		$tinyMCEBaseURL = Controller::join_links(
359
			Director::absoluteBaseURL(),
360
			$this->config()->base_dir
361
		);
362
		$settings['baseURL'] = $tinyMCEBaseURL;
363
364
        // map all plugins to absolute urls for loading
365
		$plugins = array();
366
        foreach($this->getPlugins() as $plugin => $path) {
367
			if(!$path) {
368
				// Empty paths: Convert to urls in standard base url
369
				$path = Controller::join_links(
370
					$tinyMCEBaseURL,
371
					"plugins/{$plugin}/plugin.min.js"
372
				);
373
			} elseif(!Director::is_absolute_url($path)) {
374
				// Non-absolute urls are made absolute
375
				$path = Director::absoluteURL($path);
376
			}
377
            $plugins[$plugin] = $path;
378
        }
379
380
        // https://www.tinymce.com/docs/configure/integration-and-setup/#external_plugins
381
        if($plugins) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $plugins of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
382
            $settings['external_plugins'] = $plugins;
383
        }
384
385
        // https://www.tinymce.com/docs/configure/editor-appearance/#groupingtoolbarcontrols
386
        $buttons = $this->getButtons();
387
        $settings['toolbar'] = [];
388
        foreach($buttons as $rowButtons) {
389
            $row = implode(' ', $rowButtons);
390
            if(count($buttons) > 1) {
391
                $settings['toolbar'][] = $row;
392
            } else {
393
                $settings['toolbar'] = $row;
394
            }
395
        }
396
397
        // https://www.tinymce.com/docs/configure/content-appearance/#content_css
398
		$settings['content_css'] = $this->getEditorCSS();
399
400
		// https://www.tinymce.com/docs/configure/editor-appearance/#theme_url
401
		$theme = $this->getTheme();
402
		if(!Director::is_absolute_url($theme)) {
403
			$theme = Controller::join_links($tinyMCEBaseURL, "themes/{$theme}/theme.min.js");
404
		}
405
		$settings['theme_url'] = $theme;
406
407
        // Send back
408
        return $settings;
409
	}
410
411
    /**
412
     * Get location of all editor.css files
413
     *
414
     * @return array
415
     */
416
    protected function getEditorCSS() {
417
        $editor = array();
418
		$editor[] = Controller::join_links(
419
			Director::absoluteBaseURL(),
420
			FRAMEWORK_ADMIN_DIR . '/css/editor.css'
421
		);
422
		if($theme = SSViewer::get_theme_folder()) {
423
			$editorDir = $theme . '/css/editor.css';
424
			if(file_exists(BASE_PATH . '/' . $editorDir)) {
425
				$editor[] = Controller::join_links(
426
					Director::absoluteBaseURL(),
427
					$editorDir
428
				);
429
			}
430
		}
431
		return $editor;
432
	}
433
434
	/**
435
	 * Generate gzipped TinyMCE configuration including plugins and languages.
436
	 * This ends up "pre-loading" TinyMCE bundled with the required plugins
437
	 * so that multiple HTTP requests on the client don't need to be made.
438
	 *
439
	 * @return string
440
	 */
441
	public function getScriptURL() {
442
		// If gzip is disabled just return core script url
443
		$useGzip = Config::inst()->get('HtmlEditorField', 'use_gzip');
444
		if(!$useGzip) {
445
			return THIRDPARTY_DIR . '/tinymce/tinymce.min.js';
446
		}
447
448
		// tinyMCE JS requirement
449
		require_once THIRDPARTY_PATH . '/tinymce/tiny_mce_gzip.php';
450
		$tag = TinyMCE_Compressor::renderTag(array(
451
			'url' => THIRDPARTY_DIR . '/tinymce/tiny_mce_gzip.php',
452
			'plugins' => implode(',', $this->getInternalPlugins()),
453
			'themes' => $this->getTheme(),
454
			'languages' => $this->getOption('language')
455
		), true);
456
		preg_match('/src="([^"]*)"/', $tag, $matches);
457
		return html_entity_decode($matches[1]);
458
	}
459
460
	public function init() {
461
		// These should be 'provides' by bundle-dist.js
462
		Requirements::javascript(FRAMEWORK_DIR . "/thirdparty/jquery/jquery.js");
463
		Requirements::javascript(THIRDPARTY_DIR . '/jquery-ui/jquery-ui.js');
464
		Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js');
465
		Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/dist/ssui.core.js');
466
467
		// include TinyMCE Javascript
468
		Requirements::javascript($this->getScriptURL());
469
		Requirements::javascript(FRAMEWORK_DIR ."/javascript/dist/HtmlEditorField.js");
470
471
		Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
472
	}
473
}
474