Passed
Push — master ( bad1ac...1cdfe7 )
by Robbie
15:25 queued 05:45
created

TinyMCEConfig::getConfigSchemaData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Forms\HTMLEditor;
4
5
use Exception;
6
use SilverStripe\Control\Controller;
7
use SilverStripe\Control\Director;
8
use SilverStripe\Core\Convert;
9
use SilverStripe\Core\Injector\Injector;
10
use SilverStripe\Core\Manifest\Module;
11
use SilverStripe\Core\Manifest\ModuleLoader;
12
use SilverStripe\Core\Manifest\ModuleResource;
13
use SilverStripe\Core\Manifest\ModuleResourceLoader;
14
use SilverStripe\Dev\Deprecation;
15
use SilverStripe\i18n\i18n;
16
use SilverStripe\View\Requirements;
17
use SilverStripe\View\SSViewer;
18
use SilverStripe\View\ThemeResourceLoader;
19
20
/**
21
 * Default configuration for HtmlEditor specific to tinymce
22
 */
23
class TinyMCEConfig extends HTMLEditorConfig
24
{
25
    /**
26
     * @config
27
     * @var array
28
     */
29
    private static $tinymce_lang = [
0 ignored issues
show
introduced by
The private property $tinymce_lang is not used, and could be removed.
Loading history...
30
        'ar_EG' => 'ar',
31
        'ca_AD' => 'ca',
32
        'ca_ES' => 'ca',
33
        'cs_CZ' => 'cs',
34
        'cy_GB' => 'cy',
35
        'da_DK' => 'da',
36
        'da_GL' => 'da',
37
        'de_AT' => 'de_AT',
38
        'de_BE' => 'de',
39
        'de_CH' => 'de',
40
        'de_DE' => 'de',
41
        'de_LI' => 'de',
42
        'de_LU' => 'de',
43
        'de_BR' => 'de',
44
        'de_US' => 'de',
45
        'el_CY' => 'el',
46
        'el_GR' => 'el',
47
        'es_AR' => 'es',
48
        'es_BO' => 'es',
49
        'es_CL' => 'es',
50
        'es_CO' => 'es',
51
        'es_CR' => 'es',
52
        'es_CU' => 'es',
53
        'es_DO' => 'es',
54
        'es_EC' => 'es',
55
        'es_ES' => 'es',
56
        'es_GQ' => 'es',
57
        'es_GT' => 'es',
58
        'es_HN' => 'es',
59
        'es_MX' => 'es_MX',
60
        'es_NI' => 'es',
61
        'es_PA' => 'es',
62
        'es_PE' => 'es',
63
        'es_PH' => 'es',
64
        'es_PR' => 'es',
65
        'es_PY' => 'es',
66
        'es_SV' => 'es',
67
        'es_UY' => 'es',
68
        'es_VE' => 'es',
69
        'es_AD' => 'es',
70
        'es_BZ' => 'es',
71
        'es_US' => 'es',
72
        'fa_AF' => 'fa_IR',
73
        'fa_IR' => 'fa_IR',
74
        'fa_PK' => 'fa_IR',
75
        'fr_BE' => 'fr_FR',
76
        'fr_BF' => 'fr_FR',
77
        'fr_BI' => 'fr_FR',
78
        'fr_BJ' => 'fr_FR',
79
        'fr_CA' => 'fr_FR',
80
        'fr_CF' => 'fr_FR',
81
        'fr_CG' => 'fr_FR',
82
        'fr_CH' => 'fr_FR',
83
        'fr_CI' => 'fr_FR',
84
        'fr_CM' => 'fr_FR',
85
        'fr_DJ' => 'fr_FR',
86
        'fr_DZ' => 'fr_FR',
87
        'fr_FR' => 'fr_FR',
88
        'fr_GA' => 'fr_FR',
89
        'fr_GF' => 'fr_FR',
90
        'fr_GN' => 'fr_FR',
91
        'fr_GP' => 'fr_FR',
92
        'fr_HT' => 'fr_FR',
93
        'fr_KM' => 'fr_FR',
94
        'fr_LU' => 'fr_FR',
95
        'fr_MA' => 'fr_FR',
96
        'fr_MC' => 'fr_FR',
97
        'fr_MG' => 'fr_FR',
98
        'fr_ML' => 'fr_FR',
99
        'fr_MQ' => 'fr_FR',
100
        'fr_MU' => 'fr_FR',
101
        'fr_NC' => 'fr_FR',
102
        'fr_NE' => 'fr_FR',
103
        'fr_PF' => 'fr_FR',
104
        'fr_PM' => 'fr_FR',
105
        'fr_RE' => 'fr_FR',
106
        'fr_RW' => 'fr_FR',
107
        'fr_SC' => 'fr_FR',
108
        'fr_SN' => 'fr_FR',
109
        'fr_SY' => 'fr_FR',
110
        'fr_TD' => 'fr_FR',
111
        'fr_TG' => 'fr_FR',
112
        'fr_TN' => 'fr_FR',
113
        'fr_VU' => 'fr_FR',
114
        'fr_WF' => 'fr_FR',
115
        'fr_YT' => 'fr_FR',
116
        'fr_GB' => 'fr_FR',
117
        'fr_US' => 'fr_FR',
118
        'he_IL' => 'he_IL',
119
        'hu_HU' => 'hu_HU',
120
        'hu_AT' => 'hu_HU',
121
        'hu_RO' => 'hu_HU',
122
        'hu_RS' => 'hu_HU',
123
        'it_CH' => 'it',
124
        'it_IT' => 'it',
125
        'it_SM' => 'it',
126
        'it_FR' => 'it',
127
        'it_HR' => 'it',
128
        'it_US' => 'it',
129
        'it_VA' => 'it',
130
        'ja_JP' => 'ja',
131
        'ko_KP' => 'ko_KR',
132
        'ko_KR' => 'ko_KR',
133
        'ko_CN' => 'ko_KR',
134
        'nb_NO' => 'nb_NO',
135
        'nb_SJ' => 'nb_NO',
136
        'nl_AN' => 'nl',
137
        'nl_AW' => 'nl',
138
        'nl_BE' => 'nl',
139
        'nl_NL' => 'nl',
140
        'nl_SR' => 'nl',
141
        'pl_PL' => 'pl',
142
        'pl_UA' => 'pl',
143
        'pt_AO' => 'pt_PT',
144
        'pt_BR' => 'pt_BR',
145
        'pt_CV' => 'pt_PT',
146
        'pt_GW' => 'pt_PT',
147
        'pt_MZ' => 'pt_PT',
148
        'pt_PT' => 'pt_PT',
149
        'pt_ST' => 'pt_PT',
150
        'pt_TL' => 'pt_PT',
151
        'ro_MD' => 'ro',
152
        'ro_RO' => 'ro',
153
        'ro_RS' => 'ro',
154
        'ru_BY' => 'ru',
155
        'ru_KG' => 'ru',
156
        'ru_KZ' => 'ru',
157
        'ru_RU' => 'ru',
158
        'ru_SJ' => 'ru',
159
        'ru_UA' => 'ru',
160
        'sk_SK' => 'sk',
161
        'sk_RS' => 'sk',
162
        'sv_FI' => 'sv_SE',
163
        'sv_SE' => 'sv_SE',
164
        'tr_CY' => 'tr',
165
        'tr_TR' => 'tr_TR',
166
        'tr_DE' => 'tr',
167
        'tr_MK' => 'tr',
168
        'uk_UA' => 'uk_UA',
169
        'vi_VN' => 'vi_VN',
170
        'vi_US' => 'vi_VN',
171
        'zh_CN' => 'zh_CN',
172
        'zh_HK' => 'zh_CN',
173
        'zh_MO' => 'zh_CN',
174
        'zh_SG' => 'zh_CN',
175
        'zh_TW' => 'zh_TW',
176
        'zh_ID' => 'zh_CN',
177
        'zh_MY' => 'zh_CN',
178
        'zh_TH' => 'zh_CN',
179
        'zh_US' => 'zh_CN',
180
    ];
181
182
    /**
183
     * Location of module relative to BASE_DIR. This must contain the following dirs
184
     * - plugins
185
     * - themes
186
     * - skins
187
     *
188
     * Supports vendor/module:path
189
     *
190
     * @config
191
     * @var string
192
     */
193
    private static $base_dir = null;
0 ignored issues
show
introduced by
The private property $base_dir is not used, and could be removed.
Loading history...
194
195
    /**
196
     * Extra editor.css file paths.
197
     *
198
     * Supports vendor/module:path syntax
199
     *
200
     * @config
201
     * @var array
202
     */
203
    private static $editor_css = [];
0 ignored issues
show
introduced by
The private property $editor_css is not used, and could be removed.
Loading history...
204
205
    /**
206
     * List of content css files to use for this instance, or null to default to editor_css config.
207
     *
208
     * @var string[]|null
209
     */
210
    protected $contentCSS = null;
211
212
    /**
213
     * TinyMCE JS settings
214
     *
215
     * @link https://www.tinymce.com/docs/configure/
216
     *
217
     * @var array
218
     */
219
    protected $settings = [
220
        'fix_list_elements' => true, // https://www.tinymce.com/docs/configure/content-filtering/#fix_list_elements
221
        'formats' => [
222
            'alignleft' => [
223
                [
224
                    'selector' => 'p,h1,h2,h3,h4,h5,h6,td,th,li',
225
                    'classes' =>'text-left'
226
                ],
227
                [
228
                    'selector' => 'div,ul,ol,table,img,figure',
229
                    'classes' =>'left'
230
                ]
231
            ],
232
            'aligncenter' => [
233
                [
234
                    'selector' => 'p,h1,h2,h3,h4,h5,h6,td,th,li',
235
                    'classes' =>'text-center'
236
                ],
237
                [
238
                    'selector' => 'div,ul,ol,table,img,figure',
239
                    'classes' =>'center'
240
                ]
241
            ],
242
            'alignright' => [
243
                [
244
                    'selector' => 'p,h1,h2,h3,h4,h5,h6,td,th,li',
245
                    'classes' =>'text-right'
246
                ],
247
                [
248
                    'selector' => 'div,ul,ol,table,img,figure',
249
                    'classes' =>'right'
250
                ]
251
            ],
252
            'alignjustify' => [
253
                [
254
                    'selector' => 'p,h1,h2,h3,h4,h5,h6,td,th,li',
255
                    'classes' =>'text-justify'
256
                ],
257
            ],
258
        ],
259
        'friendly_name' => '(Please set a friendly name for this config)',
260
        'priority' => 0, // used for Per-member config override
261
        'browser_spellcheck' => true,
262
        'body_class' => 'typography',
263
        'statusbar' => true,
264
        'elementpath' => true, // https://www.tinymce.com/docs/configure/editor-appearance/#elementpath
265
        'relative_urls' => true,
266
        'remove_script_host' => true,
267
        'convert_urls' => false, // Prevent site-root images being rewritten to base relative
268
        'menubar' => false,
269
        'language' => 'en',
270
        'branding' => false,
271
    ];
272
273
    /**
274
     * Holder list of enabled plugins
275
     *
276
     * @var array
277
     */
278
    protected $plugins = array(
279
        'table' => null,
280
        'emoticons' => null,
281
        'paste' => null,
282
        'code' => null,
283
        'importcss' => null,
284
        'lists' => null,
285
    );
286
287
    /**
288
     * Theme name
289
     *
290
     * @var string
291
     */
292
    protected $theme = 'modern';
293
294
    /**
295
     * Get the theme
296
     *
297
     * @return string
298
     */
299
    public function getTheme()
300
    {
301
        return $this->theme;
302
    }
303
304
    /**
305
     * Set the theme name
306
     *
307
     * @param string $theme
308
     * @return $this
309
     */
310
    public function setTheme($theme)
311
    {
312
        $this->theme = $theme;
313
        return $this;
314
    }
315
316
    /**
317
     * Holder list of buttons, organised by line. This array is 1-based indexed array
318
     *
319
     * {@link https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols}
320
     *
321
     * @var array
322
     */
323
    protected $buttons = [
324
        1 => [
325
            'bold', 'italic', 'underline', 'removeformat', '|',
326
            'alignleft', 'aligncenter', 'alignright', 'alignjustify', '|',
327
            'bullist', 'numlist', 'outdent', 'indent',
328
        ],
329
        2 => [
330
            'formatselect', '|',
331
            'paste', 'pastetext', '|',
332
            'table', 'sslink', 'unlink', '|',
333
            'code'
334
        ],
335
        3 => []
336
    ];
337
338
    public function getOption($key)
339
    {
340
        if (isset($this->settings[$key])) {
341
            return $this->settings[$key];
342
        }
343
        return null;
344
    }
345
346
    public function setOption($key, $value)
347
    {
348
        $this->settings[$key] = $value;
349
        return $this;
350
    }
351
352
    public function setOptions($options)
353
    {
354
        foreach ($options as $key => $value) {
355
            $this->settings[$key] = $value;
356
        }
357
        return $this;
358
    }
359
360
    /**
361
     * Get all settings
362
     *
363
     * @return array
364
     */
365
    protected function getSettings()
366
    {
367
        return $this->settings;
368
    }
369
370
    public function getAttributes()
371
    {
372
        return [
373
            'data-editor' => 'tinyMCE', // Register ss.editorWrappers.tinyMCE
374
            'data-config' => Convert::array2json($this->getConfig()),
375
        ];
376
    }
377
378
    /**
379
     * Enable one or several plugins. Will maintain unique list if already
380
     * enabled plugin is re-passed. If passed in as a map of plugin-name to path,
381
     * the plugin will be loaded by tinymce.PluginManager.load() instead of through tinyMCE.init().
382
     * Keep in mind that these externals plugins require a dash-prefix in their name.
383
     *
384
     * @see http://wiki.moxiecode.com/index.php/TinyMCE:API/tinymce.PluginManager/load
385
     *
386
     * If passing in a non-associative array, the plugin name should be located in the standard tinymce
387
     * plugins folder.
388
     *
389
     * If passing in an associative array, the key of each item should be the plugin name.
390
     * The value of each item is one of:
391
     *  - null - Will be treated as a stardard plugin in the standard location
392
     *  - relative path - Will be treated as a relative url
393
     *  - absolute url - Some url to an external plugin
394
     *  - An instance of ModuleResource object containing the plugin
395
     *
396
     * @param string|array $plugin,... a string, or several strings, or a single array of strings - plugins to enable
397
     * @return $this
398
     */
399
    public function enablePlugins($plugin)
400
    {
401
        $plugins = func_get_args();
402
        if (is_array(current($plugins))) {
403
            $plugins = current($plugins);
404
        }
405
        foreach ($plugins as $name => $path) {
406
            // if plugins are passed without a path
407
            if (is_numeric($name)) {
408
                $name = $path;
409
                $path = null;
410
            }
411
            if (!array_key_exists($name, $this->plugins)) {
412
                $this->plugins[$name] = $path;
413
            }
414
        }
415
        return $this;
416
    }
417
418
    /**
419
     * Enable one or several plugins. Will properly handle being passed a plugin that is already disabled
420
     * @param string|array $plugin,... a string, or several strings, or a single array of strings - plugins to enable
421
     * @return $this
422
     */
423
    public function disablePlugins($plugin)
424
    {
425
        $plugins = func_get_args();
426
        if (is_array(current($plugins))) {
427
            $plugins = current($plugins);
428
        }
429
        foreach ($plugins as $name) {
430
            unset($this->plugins[$name]);
431
        }
432
        return $this;
433
    }
434
435
    /**
436
     * Gets the list of all enabled plugins as an associative array.
437
     * Array keys are the plugin names, and values are potentially the plugin location,
438
     * or ModuleResource object
439
     *
440
     * @return array
441
     */
442
    public function getPlugins()
443
    {
444
        return $this->plugins;
445
    }
446
447
    /**
448
     * Get list of plugins without custom locations, which is the set of
449
     * plugins which can be loaded via the standard plugin path, and could
450
     * potentially be minified
451
     *
452
     * @return array
453
     */
454
    public function getInternalPlugins()
455
    {
456
        // Return only plugins with no custom url
457
        $plugins = [];
458
        foreach ($this->getPlugins() as $name => $url) {
459
            if (empty($url)) {
460
                $plugins[] = $name;
461
            }
462
        }
463
        return $plugins;
464
    }
465
466
    /**
467
     * Get all button rows, skipping empty rows
468
     *
469
     * @return array
470
     */
471
    public function getButtons()
472
    {
473
        return array_filter($this->buttons);
474
    }
475
476
    /**
477
     * Totally re-set the buttons on a given line
478
     *
479
     * @param int $line The line number to redefine, from 1 to 3
480
     * @param string $buttons,... A string or several strings, or a single array of strings.
481
     * The button names to assign to this line.
482
     * @return $this
483
     */
484
    public function setButtonsForLine($line, $buttons)
485
    {
486
        if (func_num_args() > 2) {
487
            $buttons = func_get_args();
488
            array_shift($buttons);
489
        }
490
        $this->buttons[$line] = is_array($buttons) ? $buttons : array($buttons);
491
        return $this;
492
    }
493
494
    /**
495
     * Add buttons to the end of a line
496
     * @param int $line The line number to redefine, from 1 to 3
497
     * @param string $buttons,... A string or several strings, or a single array of strings.
498
     * The button names to add to this line
499
     * @return $this
500
     */
501
    public function addButtonsToLine($line, $buttons)
502
    {
503
        if (func_num_args() > 2) {
504
            $buttons = func_get_args();
505
            array_shift($buttons);
506
        }
507
        if (!is_array($buttons)) {
508
            $buttons = [$buttons];
509
        }
510
        foreach ($buttons as $button) {
511
            $this->buttons[$line][] = $button;
512
        }
513
        return $this;
514
    }
515
516
    /**
517
     * Internal function for adding and removing buttons related to another button
518
     * @param string $name The name of the button to modify
519
     * @param int $offset The offset relative to that button to perform an array_splice at.
520
     * 0 for before $name, 1 for after.
521
     * @param int $del The number of buttons to remove at the position given by index(string) + offset
522
     * @param mixed $add An array or single item to insert at the position given by index(string) + offset,
523
     * or null for no insertion
524
     * @return bool True if $name matched a button, false otherwise
525
     */
526
    protected function modifyButtons($name, $offset, $del = 0, $add = null)
527
    {
528
        foreach ($this->buttons as &$buttons) {
529
            if (($idx = array_search($name, $buttons)) !== false) {
530
                if ($add) {
531
                    array_splice($buttons, $idx + $offset, $del, $add);
532
                } else {
533
                    array_splice($buttons, $idx + $offset, $del);
534
                }
535
                return true;
536
            }
537
        }
538
        return false;
539
    }
540
541
    /**
542
     * Insert buttons before the first occurance of another button
543
     * @param string $before the name of the button to insert other buttons before
544
     * @param string $buttons,... a string, or several strings, or a single array of strings.
545
     * The button names to insert before that button
546
     * @return bool True if insertion occured, false if it did not (because the given button name was not found)
547
     */
548
    public function insertButtonsBefore($before, $buttons)
549
    {
550
        if (func_num_args() > 2) {
551
            $buttons = func_get_args();
552
            array_shift($buttons);
553
        }
554
        if (!is_array($buttons)) {
555
            $buttons = [$buttons];
556
        }
557
        return $this->modifyButtons($before, 0, 0, $buttons);
558
    }
559
560
    /**
561
     * Insert buttons after the first occurance of another button
562
     * @param string $after the name of the button to insert other buttons before
563
     * @param string $buttons,... a string, or several strings, or a single array of strings.
564
     * The button names to insert after that button
565
     * @return bool True if insertion occured, false if it did not (because the given button name was not found)
566
     */
567
    public function insertButtonsAfter($after, $buttons)
568
    {
569
        if (func_num_args() > 2) {
570
            $buttons = func_get_args();
571
            array_shift($buttons);
572
        }
573
        if (!is_array($buttons)) {
574
            $buttons = [$buttons];
575
        }
576
        return $this->modifyButtons($after, 1, 0, $buttons);
577
    }
578
579
    /**
580
     * Remove the first occurance of buttons
581
     * @param string $buttons,... one or more strings - the name of the buttons to remove
582
     */
583
    public function removeButtons($buttons)
584
    {
585
        if (func_num_args() > 1) {
586
            $buttons = func_get_args();
587
        }
588
        if (!is_array($buttons)) {
589
            $buttons = [$buttons];
590
        }
591
        foreach ($buttons as $button) {
592
            $this->modifyButtons($button, 0, 1);
593
        }
594
    }
595
596
    /**
597
     * Generate the JavaScript that will set TinyMCE's configuration:
598
     * - Parse all configurations into JSON objects to be used in JavaScript
599
     * - Includes TinyMCE and configurations using the {@link Requirements} system
600
     *
601
     * @return array
602
     */
603
    protected function getConfig()
604
    {
605
        $settings = $this->getSettings();
606
607
        // https://www.tinymce.com/docs/configure/url-handling/#document_base_url
608
        $settings['document_base_url'] = Director::absoluteBaseURL();
609
610
        // https://www.tinymce.com/docs/api/class/tinymce.editormanager/#baseURL
611
        $baseResource = $this->getTinyMCEResource();
612
        if ($baseResource instanceof ModuleResource) {
613
            $tinyMCEBaseURL = $baseResource->getURL();
614
        } else {
615
            $tinyMCEBaseURL = Controller::join_links(Director::baseURL(), $baseResource);
616
        }
617
        $settings['baseURL'] = $tinyMCEBaseURL;
618
619
        // map all plugins to absolute urls for loading
620
        $plugins = array();
621
        foreach ($this->getPlugins() as $plugin => $path) {
622
            if ($path instanceof ModuleResource) {
623
                $path = Director::absoluteURL($path->getURL());
624
            } elseif (!$path) {
625
                // Empty paths: Convert to urls in standard base url
626
                $path = Controller::join_links(
627
                    $tinyMCEBaseURL,
628
                    "plugins/{$plugin}/plugin.min.js"
629
                );
630
            } elseif (!Director::is_absolute_url($path)) {
631
                // Non-absolute urls are made absolute
632
                $path = Director::absoluteURL($path);
633
            }
634
            $plugins[$plugin] = $path;
635
        }
636
637
        // https://www.tinymce.com/docs/configure/integration-and-setup/#external_plugins
638
        if ($plugins) {
639
            $settings['external_plugins'] = $plugins;
640
        }
641
642
        // https://www.tinymce.com/docs/configure/editor-appearance/#groupingtoolbarcontrols
643
        $buttons = $this->getButtons();
644
        $settings['toolbar'] = [];
645
        foreach ($buttons as $rowButtons) {
646
            $row = implode(' ', $rowButtons);
647
            if (count($buttons) > 1) {
648
                $settings['toolbar'][] = $row;
649
            } else {
650
                $settings['toolbar'] = $row;
651
            }
652
        }
653
654
        // https://www.tinymce.com/docs/configure/content-appearance/#content_css
655
        $settings['content_css'] = $this->getEditorCSS();
656
657
        // https://www.tinymce.com/docs/configure/editor-appearance/#theme_url
658
        $theme = $this->getTheme();
659
        if (!Director::is_absolute_url($theme)) {
660
            $theme = Controller::join_links($tinyMCEBaseURL, "themes/{$theme}/theme.min.js");
661
        }
662
        $settings['theme_url'] = $theme;
663
664
        // Send back
665
        return $settings;
666
    }
667
668
    /**
669
     * Get location of all editor.css files.
670
     * All resource specifiers are resolved to urls.
671
     *
672
     * @return array
673
     */
674
    protected function getEditorCSS()
675
    {
676
        $editor = [];
677
        $resourceLoader = ModuleResourceLoader::singleton();
678
        foreach ($this->getContentCSS() as $contentCSS) {
679
            $editor[] = $resourceLoader->resolveURL($contentCSS);
680
        }
681
        return $editor;
682
    }
683
684
    /**
685
     * Get list of resource paths to css files.
686
     *
687
     * Will default to `editor_css` config, as well as any themed `editor.css` files.
688
     * Use setContentCSS() to override.
689
     *
690
     * @return string[]
691
     */
692
    public function getContentCSS()
693
    {
694
        // Prioritise instance specific content
695
        if (isset($this->contentCSS)) {
696
            return $this->contentCSS;
697
        }
698
699
        // Add standard editor.css
700
        $editor = [];
701
        $editorCSSFiles = $this->config()->get('editor_css');
702
        if ($editorCSSFiles) {
703
            foreach ($editorCSSFiles as $editorCSS) {
704
                $editor[] = $editorCSS;
705
            }
706
        }
707
708
        // Themed editor.css
709
        $themes = HTMLEditorConfig::getThemes() ?: SSViewer::get_themes();
710
        $themedEditor = ThemeResourceLoader::inst()->findThemedCSS('editor', $themes);
711
        if ($themedEditor) {
712
            $editor[] = $themedEditor;
713
        }
714
        return $editor;
715
    }
716
717
    /**
718
     * Set explicit set of CSS resources to use for `content_css` option.
719
     *
720
     * Note: If merging with default paths, you should call getContentCSS() and merge
721
     * prior to assignment.
722
     *
723
     * @param string[] $css Array of resource paths. Supports module prefix,
724
     * e.g. `silverstripe/admin:client/dist/styles/editor.css`
725
     * @return $this
726
     */
727
    public function setContentCSS($css)
728
    {
729
        $this->contentCSS = $css;
730
        return $this;
731
    }
732
733
    /**
734
     * Generate gzipped TinyMCE configuration including plugins and languages.
735
     * This ends up "pre-loading" TinyMCE bundled with the required plugins
736
     * so that multiple HTTP requests on the client don't need to be made.
737
     *
738
     * @return string
739
     * @throws Exception
740
     */
741
    public function getScriptURL()
742
    {
743
        /** @var TinyMCEScriptGenerator $generator */
744
        $generator = Injector::inst()->get(TinyMCEScriptGenerator::class);
745
        return $generator->getScriptURL($this);
746
    }
747
748
    public function init()
749
    {
750
        // include TinyMCE Javascript
751
        Requirements::javascript($this->getScriptURL());
752
    }
753
754
    public function getConfigSchemaData()
755
    {
756
        $data = parent::getConfigSchemaData();
757
        $data['editorjs'] = $this->getScriptURL();
758
        return $data;
759
    }
760
761
    /**
762
     * Get the current tinyMCE language
763
     *
764
     * @return string Language
765
     */
766
    public static function get_tinymce_lang()
767
    {
768
        $lang = static::config()->get('tinymce_lang');
769
        $locale = i18n::get_locale();
770
        if (isset($lang[$locale])) {
771
            return $lang[$locale];
772
        }
773
        return 'en';
774
    }
775
776
    /**
777
     * Returns the full filesystem path to TinyMCE resources (which could be different from the original tinymce
778
     * location in the module).
779
     *
780
     * Path will be absolute.
781
     *
782
     * @return string
783
     * @throws Exception
784
     */
785
    public function getTinyMCEResourcePath()
786
    {
787
        $resource = $this->getTinyMCEResource();
788
        if ($resource instanceof ModuleResource) {
789
            return $resource->getPath();
790
        }
791
        return Director::baseFolder() . '/' . $resource;
792
    }
793
794
    /**
795
     * Get front-end url to tinymce resources
796
     *
797
     * @return string
798
     * @throws Exception
799
     */
800
    public function getTinyMCEResourceURL()
801
    {
802
        $resource = $this->getTinyMCEResource();
803
        if ($resource instanceof ModuleResource) {
804
            return $resource->getURL();
805
        }
806
        return $resource;
807
    }
808
809
    /**
810
     * Get resource root for TinyMCE, either as a string or ModuleResource instance
811
     * Path will be relative to BASE_PATH if string.
812
     *
813
     * @return ModuleResource|string
814
     * @throws Exception
815
     */
816
    public function getTinyMCEResource()
817
    {
818
        $configDir = static::config()->get('base_dir');
819
        if ($configDir) {
820
            return ModuleResourceLoader::singleton()->resolveResource($configDir);
821
        }
822
823
        throw new Exception(sprintf(
824
            'If the silverstripe/admin module is not installed you must set the TinyMCE path in %s.base_dir',
825
            __CLASS__
826
        ));
827
    }
828
829
    /**
830
     * @deprecated 4.0.0:5.0.0
831
     */
832
    public function getTinyMCEPath()
833
    {
834
        Deprecation::notice('5.0', 'use getTinyMCEResourcePath instead');
835
        return $this->getTinyMCEResourcePath();
836
    }
837
838
    /**
839
     * @return Module
840
     * @deprecated 4.0.0:5.0.0
841
     */
842
    protected function getAdminModule()
843
    {
844
        Deprecation::notice('5.0', 'Set base_dir or editor_css config instead');
845
        return ModuleLoader::getModule('silverstripe/admin');
846
    }
847
}
848