Completed
Push — fix-2494 ( 3153ee )
by Sam
07:19
created

TinyMCEConfig::getScriptURL()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 16
nc 3
nop 0
dl 0
loc 26
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Forms\HTMLEditor;
4
5
use SilverStripe\Core\Convert;
6
use SilverStripe\Control\Controller;
7
use SilverStripe\Control\Director;
8
use SilverStripe\Core\Manifest\ModuleLoader;
9
use SilverStripe\i18n\i18n;
10
use SilverStripe\View\Requirements;
11
use SilverStripe\View\SSViewer;
12
use SilverStripe\View\ThemeResourceLoader;
13
use TinyMCE_Compressor;
14
use Exception;
15
16
/**
17
 * Default configuration for HtmlEditor specific to tinymce
18
 */
19
class TinyMCEConfig extends HTMLEditorConfig
20
{
21
    /**
22
     * @config
23
     * @var array
24
     */
25
    private static $tinymce_lang = [
26
        'ar_EG' => 'ar',
27
        'ca_AD' => 'ca',
28
        'ca_ES' => 'ca',
29
        'cs_CZ' => 'cs',
30
        'cy_GB' => 'cy',
31
        'da_DK' => 'da',
32
        'da_GL' => 'da',
33
        'de_AT' => 'de',
34
        'de_BE' => 'de',
35
        'de_CH' => 'de',
36
        'de_DE' => 'de',
37
        'de_LI' => 'de',
38
        'de_LU' => 'de',
39
        'de_BR' => 'de',
40
        'de_US' => 'de',
41
        'el_CY' => 'el',
42
        'el_GR' => 'el',
43
        'es_AR' => 'es',
44
        'es_BO' => 'es',
45
        'es_CL' => 'es',
46
        'es_CO' => 'es',
47
        'es_CR' => 'es',
48
        'es_CU' => 'es',
49
        'es_DO' => 'es',
50
        'es_EC' => 'es',
51
        'es_ES' => 'es',
52
        'es_GQ' => 'es',
53
        'es_GT' => 'es',
54
        'es_HN' => 'es',
55
        'es_MX' => 'es',
56
        'es_NI' => 'es',
57
        'es_PA' => 'es',
58
        'es_PE' => 'es',
59
        'es_PH' => 'es',
60
        'es_PR' => 'es',
61
        'es_PY' => 'es',
62
        'es_SV' => 'es',
63
        'es_UY' => 'es',
64
        'es_VE' => 'es',
65
        'es_AD' => 'es',
66
        'es_BZ' => 'es',
67
        'es_US' => 'es',
68
        'fa_AF' => 'fa',
69
        'fa_IR' => 'fa',
70
        'fa_PK' => 'fa',
71
        'fi_FI' => 'fi',
72
        'fi_SE' => 'fi',
73
        'fr_BE' => 'fr',
74
        'fr_BF' => 'fr',
75
        'fr_BI' => 'fr',
76
        'fr_BJ' => 'fr',
77
        'fr_CA' => 'fr_ca',
78
        'fr_CF' => 'fr',
79
        'fr_CG' => 'fr',
80
        'fr_CH' => 'fr',
81
        'fr_CI' => 'fr',
82
        'fr_CM' => 'fr',
83
        'fr_DJ' => 'fr',
84
        'fr_DZ' => 'fr',
85
        'fr_FR' => 'fr',
86
        'fr_GA' => 'fr',
87
        'fr_GF' => 'fr',
88
        'fr_GN' => 'fr',
89
        'fr_GP' => 'fr',
90
        'fr_HT' => 'fr',
91
        'fr_KM' => 'fr',
92
        'fr_LU' => 'fr',
93
        'fr_MA' => 'fr',
94
        'fr_MC' => 'fr',
95
        'fr_MG' => 'fr',
96
        'fr_ML' => 'fr',
97
        'fr_MQ' => 'fr',
98
        'fr_MU' => 'fr',
99
        'fr_NC' => 'fr',
100
        'fr_NE' => 'fr',
101
        'fr_PF' => 'fr',
102
        'fr_PM' => 'fr',
103
        'fr_RE' => 'fr',
104
        'fr_RW' => 'fr',
105
        'fr_SC' => 'fr',
106
        'fr_SN' => 'fr',
107
        'fr_SY' => 'fr',
108
        'fr_TD' => 'fr',
109
        'fr_TG' => 'fr',
110
        'fr_TN' => 'fr',
111
        'fr_VU' => 'fr',
112
        'fr_WF' => 'fr',
113
        'fr_YT' => 'fr',
114
        'fr_GB' => 'fr',
115
        'fr_US' => 'fr',
116
        'he_IL' => 'he',
117
        'hu_HU' => 'hu',
118
        'hu_AT' => 'hu',
119
        'hu_RO' => 'hu',
120
        'hu_RS' => 'hu',
121
        'is_IS' => 'is',
122
        'it_CH' => 'it',
123
        'it_IT' => 'it',
124
        'it_SM' => 'it',
125
        'it_FR' => 'it',
126
        'it_HR' => 'it',
127
        'it_US' => 'it',
128
        'it_VA' => 'it',
129
        'ja_JP' => 'ja',
130
        'ko_KP' => 'ko',
131
        'ko_KR' => 'ko',
132
        'ko_CN' => 'ko',
133
        'mi_NZ' => 'mi_NZ',
134
        'nb_NO' => 'nb',
135
        'nb_SJ' => 'nb',
136
        'nl_AN' => 'nl',
137
        'nl_AW' => 'nl',
138
        'nl_BE' => 'nl',
139
        'nl_NL' => 'nl',
140
        'nl_SR' => 'nl',
141
        'nn_NO' => 'nn',
142
        'pl_PL' => 'pl',
143
        'pl_UA' => 'pl',
144
        'pt_AO' => 'pt',
145
        'pt_BR' => 'pt',
146
        'pt_CV' => 'pt',
147
        'pt_GW' => 'pt',
148
        'pt_MZ' => 'pt',
149
        'pt_PT' => 'pt',
150
        'pt_ST' => 'pt',
151
        'pt_TL' => 'pt',
152
        'ro_MD' => 'ro',
153
        'ro_RO' => 'ro',
154
        'ro_RS' => 'ro',
155
        'ru_BY' => 'ru',
156
        'ru_KG' => 'ru',
157
        'ru_KZ' => 'ru',
158
        'ru_RU' => 'ru',
159
        'ru_SJ' => 'ru',
160
        'ru_UA' => 'ru',
161
        'si_LK' => 'si',
162
        'sk_SK' => 'sk',
163
        'sk_RS' => 'sk',
164
        'sq_AL' => 'sq',
165
        'sr_BA' => 'sr',
166
        'sr_ME' => 'sr',
167
        'sr_RS' => 'sr',
168
        'sv_FI' => 'sv',
169
        'sv_SE' => 'sv',
170
        'tr_CY' => 'tr',
171
        'tr_TR' => 'tr',
172
        'tr_DE' => 'tr',
173
        'tr_MK' => 'tr',
174
        'uk_UA' => 'uk',
175
        'vi_VN' => 'vi',
176
        'vi_US' => 'vi',
177
        'zh_CN' => 'zh-cn',
178
        'zh_HK' => 'zh-cn',
179
        'zh_MO' => 'zh-cn',
180
        'zh_SG' => 'zh-cn',
181
        'zh_TW' => 'zh-tw',
182
        'zh_ID' => 'zh-cn',
183
        'zh_MY' => 'zh-cn',
184
        'zh_TH' => 'zh-cn',
185
        'zh_US' => 'zn-cn',
186
    ];
187
188
    /**
189
     * Location of module relative to BASE_DIR. This must contain the following dirs
190
     * - plugins
191
     * - themes
192
     * - skins
193
     *
194
     * If left blank defaults to [admin dir]/tinyme
195
     *
196
     * @config
197
     * @var string
198
     */
199
    private static $base_dir = null;
200
201
    /**
202
     * TinyMCE JS settings
203
     *
204
     * @link https://www.tinymce.com/docs/configure/
205
     *
206
     * @var array
207
     */
208
    protected $settings = array(
209
        'fix_list_elements' => true, // https://www.tinymce.com/docs/configure/content-filtering/#fix_list_elements
210
        'friendly_name' => '(Please set a friendly name for this config)',
211
        'priority' => 0, // used for Per-member config override
212
        'browser_spellcheck' => true,
213
        'body_class' => 'typography',
214
        'elementpath' => false, // https://www.tinymce.com/docs/configure/editor-appearance/#elementpath
215
        'relative_urls' => true,
216
        'remove_script_host' => true,
217
        'convert_urls' => false, // Prevent site-root images being rewritten to base relative
218
        'menubar' => false,
219
        'language' => 'en',
220
    );
221
222
    /**
223
     * Holder list of enabled plugins
224
     *
225
     * @var array
226
     */
227
    protected $plugins = array(
228
        'table' => null,
229
        'emoticons' => null,
230
        'paste' => null,
231
        'code' => null,
232
        'link' => null,
233
        'importcss' => null,
234
    );
235
236
    /**
237
     * Theme name
238
     *
239
     * @var string
240
     */
241
    protected $theme = 'modern';
242
243
    /**
244
     * Get the theme
245
     *
246
     * @return string
247
     */
248
    public function getTheme()
249
    {
250
        return $this->theme;
251
    }
252
253
    /**
254
     * Set the theme name
255
     *
256
     * @param string $theme
257
     * @return $this
258
     */
259
    public function setTheme($theme)
260
    {
261
        $this->theme = $theme;
262
        return $this;
263
    }
264
265
    /**
266
     * Holder list of buttons, organised by line. This array is 1-based indexed array
267
     *
268
     * {@link https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols}
269
     *
270
     * @var array
271
     */
272
    protected $buttons = array(
273
        1 => array(
274
            'bold', 'italic', 'underline', 'removeformat', '|',
275
            'alignleft', 'aligncenter', 'alignright', 'alignjustify', '|',
276
            'bullist', 'numlist', 'outdent', 'indent',
277
        ),
278
        2 => array(
279
            'formatselect', '|',
280
            'paste', 'pastetext', '|',
281
            'table', 'sslink', 'unlink', '|',
282
            'code'
283
        ),
284
        3 => array()
285
    );
286
287
    public function getOption($key)
288
    {
289
        if (isset($this->settings[$key])) {
290
            return $this->settings[$key];
291
        }
292
        return null;
293
    }
294
295
    public function setOption($key, $value)
296
    {
297
        $this->settings[$key] = $value;
298
        return $this;
299
    }
300
301
    public function setOptions($options)
302
    {
303
        foreach ($options as $key => $value) {
304
            $this->settings[$key] = $value;
305
        }
306
        return $this;
307
    }
308
309
    /**
310
     * Get all settings
311
     *
312
     * @return array
313
     */
314
    protected function getSettings()
315
    {
316
        return $this->settings;
317
    }
318
319
    public function getAttributes()
320
    {
321
        return [
322
            'data-editor' => 'tinyMCE', // Register ss.editorWrappers.tinyMCE
323
            'data-config' => Convert::array2json($this->getConfig())
324
        ];
325
    }
326
327
    /**
328
     * Enable one or several plugins. Will maintain unique list if already
329
     * enabled plugin is re-passed. If passed in as a map of plugin-name to path,
330
     * the plugin will be loaded by tinymce.PluginManager.load() instead of through tinyMCE.init().
331
     * Keep in mind that these externals plugins require a dash-prefix in their name.
332
     *
333
     * @see http://wiki.moxiecode.com/index.php/TinyMCE:API/tinymce.PluginManager/load
334
     *
335
     * If passing in a non-associative array, the plugin name should be located in the standard tinymce
336
     * plugins folder.
337
     *
338
     * If passing in an associative array, the key of each item should be the plugin name.
339
     * The value of each item is one of:
340
     *  - null - Will be treated as a stardard plugin in the standard location
341
     *  - relative path - Will be treated as a relative url
342
     *  - absolute url - Some url to an external plugin
343
     *
344
     * @param string|array $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...
345
     * @return $this
346
     */
347
    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...
348
    {
349
        $plugins = func_get_args();
350
        if (is_array(current($plugins))) {
351
            $plugins = current($plugins);
352
        }
353
        foreach ($plugins as $name => $path) {
354
            // if plugins are passed without a path
355
            if (is_numeric($name)) {
356
                $name = $path;
357
                $path = null;
358
            }
359
            if (!array_key_exists($name, $this->plugins)) {
360
                $this->plugins[$name] = $path;
361
            }
362
        }
363
        return $this;
364
    }
365
366
    /**
367
     * Enable one or several plugins. Will properly handle being passed a plugin that is already disabled
368
     * @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...
369
     * @return $this
370
     */
371
    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...
372
    {
373
        $plugins = func_get_args();
374
        if (is_array(current($plugins))) {
375
            $plugins = current($plugins);
376
        }
377
        foreach ($plugins as $name) {
378
            unset($this->plugins[$name]);
379
        }
380
        return $this;
381
    }
382
383
    /**
384
     * Gets the list of all enabled plugins as an associative array.
385
     * Array keys are the plugin names, and values are potentially the plugin location
386
     *
387
     * @return array
388
     */
389
    public function getPlugins()
390
    {
391
        return $this->plugins;
392
    }
393
394
    /**
395
     * Get list of plugins without custom locations, which is the set of
396
     * plugins which can be loaded via the standard plugin path, and could
397
     * potentially be minified
398
     *
399
     * @return array
400
     */
401
    public function getInternalPlugins()
402
    {
403
        // Return only plugins with no custom url
404
        $plugins = [];
405
        foreach ($this->getPlugins() as $name => $url) {
406
            if (empty($url)) {
407
                $plugins[] = $name;
408
            }
409
        }
410
        return $plugins;
411
    }
412
413
    /**
414
     * Get all button rows, skipping empty rows
415
     *
416
     * @return array
417
     */
418
    public function getButtons()
419
    {
420
        return array_filter($this->buttons);
421
    }
422
423
    /**
424
     * Totally re-set the buttons on a given line
425
     *
426
     * @param int $line The line number to redefine, from 1 to 3
427
     * @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...
428
     * The button names to assign to this line.
429
     * @return $this
430
     */
431
    public function setButtonsForLine($line, $buttons)
432
    {
433
        if (func_num_args() > 2) {
434
            $buttons = func_get_args();
435
            array_shift($buttons);
436
        }
437
        $this->buttons[$line] = is_array($buttons) ? $buttons : array($buttons);
438
        return $this;
439
    }
440
441
    /**
442
     * Add buttons to the end of a line
443
     * @param int $line The line number to redefine, from 1 to 3
444
     * @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...
445
     * The button names to add to this line
446
     * @return $this
447
     */
448
    public function addButtonsToLine($line, $buttons)
449
    {
450
        if (func_num_args() > 2) {
451
            $buttons = func_get_args();
452
            array_shift($buttons);
453
        }
454
        if (!is_array($buttons)) {
455
            $buttons = [$buttons];
456
        }
457
        foreach ($buttons as $button) {
458
            $this->buttons[$line][] = $button;
459
        }
460
        return $this;
461
    }
462
463
    /**
464
     * Internal function for adding and removing buttons related to another button
465
     * @param string $name The name of the button to modify
466
     * @param int $offset The offset relative to that button to perform an array_splice at.
467
     * 0 for before $name, 1 for after.
468
     * @param int $del The number of buttons to remove at the position given by index(string) + offset
469
     * @param mixed $add An array or single item to insert at the position given by index(string) + offset,
470
     * or null for no insertion
471
     * @return bool True if $name matched a button, false otherwise
472
     */
473
    protected function modifyButtons($name, $offset, $del = 0, $add = null)
474
    {
475
        foreach ($this->buttons as &$buttons) {
476
            if (($idx = array_search($name, $buttons)) !== false) {
477
                if ($add) {
478
                    array_splice($buttons, $idx + $offset, $del, $add);
479
                } else {
480
                    array_splice($buttons, $idx + $offset, $del);
481
                }
482
                return true;
483
            }
484
        }
485
        return false;
486
    }
487
488
    /**
489
     * Insert buttons before the first occurance of another button
490
     * @param string $before the name of the button to insert other buttons before
491
     * @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...
492
     * The button names to insert before that button
493
     * @return bool True if insertion occured, false if it did not (because the given button name was not found)
494
     */
495 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...
496
    {
497
        if (func_num_args() > 2) {
498
            $buttons = func_get_args();
499
            array_shift($buttons);
500
        }
501
        if (!is_array($buttons)) {
502
            $buttons = [$buttons];
503
        }
504
        return $this->modifyButtons($before, 0, 0, $buttons);
505
    }
506
507
    /**
508
     * Insert buttons after the first occurance of another button
509
     * @param string $after the name of the button to insert other buttons before
510
     * @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...
511
     * The button names to insert after that button
512
     * @return bool True if insertion occured, false if it did not (because the given button name was not found)
513
     */
514 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...
515
    {
516
        if (func_num_args() > 2) {
517
            $buttons = func_get_args();
518
            array_shift($buttons);
519
        }
520
        if (!is_array($buttons)) {
521
            $buttons = [$buttons];
522
        }
523
        return $this->modifyButtons($after, 1, 0, $buttons);
524
    }
525
526
    /**
527
     * Remove the first occurance of buttons
528
     * @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...
529
     */
530 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...
531
    {
532
        if (func_num_args() > 1) {
533
            $buttons = func_get_args();
534
        }
535
        if (!is_array($buttons)) {
536
            $buttons = [$buttons];
537
        }
538
        foreach ($buttons as $button) {
539
            $this->modifyButtons($button, 0, 1);
540
        }
541
    }
542
543
    /**
544
     * Generate the JavaScript that will set TinyMCE's configuration:
545
     * - Parse all configurations into JSON objects to be used in JavaScript
546
     * - Includes TinyMCE and configurations using the {@link Requirements} system
547
     *
548
     * @return array
549
     */
550
    protected function getConfig()
551
    {
552
        $settings = $this->getSettings();
553
554
        // https://www.tinymce.com/docs/configure/url-handling/#document_base_url
555
        $settings['document_base_url'] = Director::absoluteBaseURL();
556
557
        // https://www.tinymce.com/docs/api/class/tinymce.editormanager/#baseURL
558
        $tinyMCEBaseURL = Controller::join_links(
559
            Director::absoluteBaseURL(),
560
            $this->getTinyMCEPath()
561
        );
562
        $settings['baseURL'] = $tinyMCEBaseURL;
563
564
        // map all plugins to absolute urls for loading
565
        $plugins = array();
566
        foreach ($this->getPlugins() as $plugin => $path) {
567
            if (!$path) {
568
                // Empty paths: Convert to urls in standard base url
569
                $path = Controller::join_links(
570
                    $tinyMCEBaseURL,
571
                    "plugins/{$plugin}/plugin.min.js"
572
                );
573
            } elseif (!Director::is_absolute_url($path)) {
574
                // Non-absolute urls are made absolute
575
                $path = Director::absoluteURL($path);
576
            }
577
            $plugins[$plugin] = $path;
578
        }
579
580
        // https://www.tinymce.com/docs/configure/integration-and-setup/#external_plugins
581
        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...
582
            $settings['external_plugins'] = $plugins;
583
        }
584
585
        // https://www.tinymce.com/docs/configure/editor-appearance/#groupingtoolbarcontrols
586
        $buttons = $this->getButtons();
587
        $settings['toolbar'] = [];
588
        foreach ($buttons as $rowButtons) {
589
            $row = implode(' ', $rowButtons);
590
            if (count($buttons) > 1) {
591
                $settings['toolbar'][] = $row;
592
            } else {
593
                $settings['toolbar'] = $row;
594
            }
595
        }
596
597
        // https://www.tinymce.com/docs/configure/content-appearance/#content_css
598
        $settings['content_css'] = $this->getEditorCSS();
599
600
        // https://www.tinymce.com/docs/configure/editor-appearance/#theme_url
601
        $theme = $this->getTheme();
602
        if (!Director::is_absolute_url($theme)) {
603
            $theme = Controller::join_links($tinyMCEBaseURL, "themes/{$theme}/theme.min.js");
604
        }
605
        $settings['theme_url'] = $theme;
606
607
        // Send back
608
        return $settings;
609
    }
610
611
    /**
612
     * Get location of all editor.css files
613
     *
614
     * @return array
615
     */
616
    protected function getEditorCSS()
617
    {
618
        $editor = array();
619
620
        // Add standard editor.css
621
        $editor[] = Director::absoluteURL(ltrim($this->getAdminPath() . '/client/dist/styles/editor.css', '/'));
622
623
        // Themed editor.css
624
        $themedEditor = ThemeResourceLoader::instance()->findThemedCSS('editor', SSViewer::get_themes());
625
        if ($themedEditor) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $themedEditor of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
626
            $editor[] = Director::absoluteURL($themedEditor, Director::BASE);
627
        }
628
629
        return $editor;
630
    }
631
632
    /**
633
     * Generate gzipped TinyMCE configuration including plugins and languages.
634
     * This ends up "pre-loading" TinyMCE bundled with the required plugins
635
     * so that multiple HTTP requests on the client don't need to be made.
636
     *
637
     * @return string
638
     */
639
    public function getScriptURL()
640
    {
641
        // If gzip is disabled just return core script url
642
        $useGzip = HTMLEditorField::config()->get('use_gzip');
643
        if (!$useGzip) {
644
            return $this->getTinyMCEPath() . '/tinymce.min.js';
645
        }
646
647
        // tinyMCE JS requirement
648
        $gzipPath = BASE_PATH . '/' . $this->getTinyMCEPath() . '/tiny_mce_gzip.php';
649
        if (!file_exists($gzipPath)) {
650
            throw new Exception("HTMLEditorField.use_gzip enabled, but file $gzipPath does not exist!");
651
        }
652
653
        require_once $gzipPath;
654
655
        $tag = TinyMCE_Compressor::renderTag(array(
656
            'url' => $this->getTinyMCEPath() . '/tiny_mce_gzip.php',
657
            'plugins' => implode(',', $this->getInternalPlugins()),
658
            'themes' => $this->getTheme(),
659
            'languages' => $this->getOption('language')
660
        ), true);
661
        preg_match('/src="([^"]*)"/', $tag, $matches);
662
663
        return html_entity_decode($matches[1]);
664
    }
665
666
    public function init()
667
    {
668
        // include TinyMCE Javascript
669
        Requirements::javascript($this->getScriptURL());
670
    }
671
672
673
    /**
674
     * Get the current tinyMCE language
675
     *
676
     * @return string Language
677
     */
678
    public static function get_tinymce_lang()
679
    {
680
        $lang = static::config()->get('tinymce_lang');
681
        $locale = i18n::get_locale();
682
        if (isset($lang[$locale])) {
683
            return $lang[$locale];
684
        }
685
        return 'en';
686
    }
687
688
    /**
689
     * @return string|false
690
     */
691
    public function getAdminPath()
692
    {
693
        $module = $this->getAdminModule();
694
        if ($module) {
695
            return $module->getRelativePath();
696
        }
697
        return false;
698
    }
699
700
    /**
701
     * @return string|false
702
     */
703
    public function getTinyMCEPath()
704
    {
705
        $configDir = static::config()->get('base_dir');
706
        if ($configDir) {
707
            return $configDir;
708
        }
709
710
        if ($admin = $this->getAdminModule()) {
711
            return $admin->getResourcePath('thirdparty/tinymce');
712
        }
713
714
        throw new Exception(sprintf(
715
            'If the silverstripe/admin module is not installed, 
716
            you must set the TinyMCE path in %s.base_dir',
717
            __CLASS__
718
        ));
719
    }
720
721
    /**
722
     * @return \SilverStripe\Core\Manifest\Module
723
     */
724
    protected function getAdminModule()
725
    {
726
        return ModuleLoader::instance()->getManifest()->getModule('silverstripe/admin');
727
    }
728
}
729