Passed
Push — master ( 0c4770...bd90a5 )
by Damian
32:36 queued 21:07
created

TinyMCEConfig::setContentCSS()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
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 = array(
220
        'fix_list_elements' => true, // https://www.tinymce.com/docs/configure/content-filtering/#fix_list_elements
221
        'friendly_name' => '(Please set a friendly name for this config)',
222
        'priority' => 0, // used for Per-member config override
223
        'browser_spellcheck' => true,
224
        'body_class' => 'typography',
225
        'statusbar' => true,
226
        'elementpath' => true, // https://www.tinymce.com/docs/configure/editor-appearance/#elementpath
227
        'relative_urls' => true,
228
        'remove_script_host' => true,
229
        'convert_urls' => false, // Prevent site-root images being rewritten to base relative
230
        'menubar' => false,
231
        'language' => 'en',
232
        'branding' => false,
233
    );
234
235
    /**
236
     * Holder list of enabled plugins
237
     *
238
     * @var array
239
     */
240
    protected $plugins = array(
241
        'table' => null,
242
        'emoticons' => null,
243
        'paste' => null,
244
        'code' => null,
245
        'importcss' => null,
246
        'lists' => null,
247
    );
248
249
    /**
250
     * Theme name
251
     *
252
     * @var string
253
     */
254
    protected $theme = 'modern';
255
256
    /**
257
     * Get the theme
258
     *
259
     * @return string
260
     */
261
    public function getTheme()
262
    {
263
        return $this->theme;
264
    }
265
266
    /**
267
     * Set the theme name
268
     *
269
     * @param string $theme
270
     * @return $this
271
     */
272
    public function setTheme($theme)
273
    {
274
        $this->theme = $theme;
275
        return $this;
276
    }
277
278
    /**
279
     * Holder list of buttons, organised by line. This array is 1-based indexed array
280
     *
281
     * {@link https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols}
282
     *
283
     * @var array
284
     */
285
    protected $buttons = [
286
        1 => [
287
            'bold', 'italic', 'underline', 'removeformat', '|',
288
            'alignleft', 'aligncenter', 'alignright', 'alignjustify', '|',
289
            'bullist', 'numlist', 'outdent', 'indent',
290
        ],
291
        2 => [
292
            'formatselect', '|',
293
            'paste', 'pastetext', '|',
294
            'table', 'sslink', 'unlink', '|',
295
            'code'
296
        ],
297
        3 => []
298
    ];
299
300
    public function getOption($key)
301
    {
302
        if (isset($this->settings[$key])) {
303
            return $this->settings[$key];
304
        }
305
        return null;
306
    }
307
308
    public function setOption($key, $value)
309
    {
310
        $this->settings[$key] = $value;
311
        return $this;
312
    }
313
314
    public function setOptions($options)
315
    {
316
        foreach ($options as $key => $value) {
317
            $this->settings[$key] = $value;
318
        }
319
        return $this;
320
    }
321
322
    /**
323
     * Get all settings
324
     *
325
     * @return array
326
     */
327
    protected function getSettings()
328
    {
329
        return $this->settings;
330
    }
331
332
    public function getAttributes()
333
    {
334
        return [
335
            'data-editor' => 'tinyMCE', // Register ss.editorWrappers.tinyMCE
336
            'data-config' => Convert::array2json($this->getConfig()),
337
        ];
338
    }
339
340
    /**
341
     * Enable one or several plugins. Will maintain unique list if already
342
     * enabled plugin is re-passed. If passed in as a map of plugin-name to path,
343
     * the plugin will be loaded by tinymce.PluginManager.load() instead of through tinyMCE.init().
344
     * Keep in mind that these externals plugins require a dash-prefix in their name.
345
     *
346
     * @see http://wiki.moxiecode.com/index.php/TinyMCE:API/tinymce.PluginManager/load
347
     *
348
     * If passing in a non-associative array, the plugin name should be located in the standard tinymce
349
     * plugins folder.
350
     *
351
     * If passing in an associative array, the key of each item should be the plugin name.
352
     * The value of each item is one of:
353
     *  - null - Will be treated as a stardard plugin in the standard location
354
     *  - relative path - Will be treated as a relative url
355
     *  - absolute url - Some url to an external plugin
356
     *  - An instance of ModuleResource object containing the plugin
357
     *
358
     * @param string|array $plugin,... a string, or several strings, or a single array of strings - The plugins to enable
359
     * @return $this
360
     */
361
    public function enablePlugins($plugin)
362
    {
363
        $plugins = func_get_args();
364
        if (is_array(current($plugins))) {
365
            $plugins = current($plugins);
366
        }
367
        foreach ($plugins as $name => $path) {
368
            // if plugins are passed without a path
369
            if (is_numeric($name)) {
370
                $name = $path;
371
                $path = null;
372
            }
373
            if (!array_key_exists($name, $this->plugins)) {
374
                $this->plugins[$name] = $path;
375
            }
376
        }
377
        return $this;
378
    }
379
380
    /**
381
     * Enable one or several plugins. Will properly handle being passed a plugin that is already disabled
382
     * @param string|array $plugin,... a string, or several strings, or a single array of strings - The plugins to enable
383
     * @return $this
384
     */
385
    public function disablePlugins($plugin)
386
    {
387
        $plugins = func_get_args();
388
        if (is_array(current($plugins))) {
389
            $plugins = current($plugins);
390
        }
391
        foreach ($plugins as $name) {
392
            unset($this->plugins[$name]);
393
        }
394
        return $this;
395
    }
396
397
    /**
398
     * Gets the list of all enabled plugins as an associative array.
399
     * Array keys are the plugin names, and values are potentially the plugin location,
400
     * or ModuleResource object
401
     *
402
     * @return array
403
     */
404
    public function getPlugins()
405
    {
406
        return $this->plugins;
407
    }
408
409
    /**
410
     * Get list of plugins without custom locations, which is the set of
411
     * plugins which can be loaded via the standard plugin path, and could
412
     * potentially be minified
413
     *
414
     * @return array
415
     */
416
    public function getInternalPlugins()
417
    {
418
        // Return only plugins with no custom url
419
        $plugins = [];
420
        foreach ($this->getPlugins() as $name => $url) {
421
            if (empty($url)) {
422
                $plugins[] = $name;
423
            }
424
        }
425
        return $plugins;
426
    }
427
428
    /**
429
     * Get all button rows, skipping empty rows
430
     *
431
     * @return array
432
     */
433
    public function getButtons()
434
    {
435
        return array_filter($this->buttons);
436
    }
437
438
    /**
439
     * Totally re-set the buttons on a given line
440
     *
441
     * @param int $line The line number to redefine, from 1 to 3
442
     * @param string $buttons,... A string or several strings, or a single array of strings.
443
     * The button names to assign to this line.
444
     * @return $this
445
     */
446
    public function setButtonsForLine($line, $buttons)
447
    {
448
        if (func_num_args() > 2) {
449
            $buttons = func_get_args();
450
            array_shift($buttons);
451
        }
452
        $this->buttons[$line] = is_array($buttons) ? $buttons : array($buttons);
453
        return $this;
454
    }
455
456
    /**
457
     * Add buttons to the end of a line
458
     * @param int $line The line number to redefine, from 1 to 3
459
     * @param string $buttons,... A string or several strings, or a single array of strings.
460
     * The button names to add to this line
461
     * @return $this
462
     */
463
    public function addButtonsToLine($line, $buttons)
464
    {
465
        if (func_num_args() > 2) {
466
            $buttons = func_get_args();
467
            array_shift($buttons);
468
        }
469
        if (!is_array($buttons)) {
470
            $buttons = [$buttons];
471
        }
472
        foreach ($buttons as $button) {
473
            $this->buttons[$line][] = $button;
474
        }
475
        return $this;
476
    }
477
478
    /**
479
     * Internal function for adding and removing buttons related to another button
480
     * @param string $name The name of the button to modify
481
     * @param int $offset The offset relative to that button to perform an array_splice at.
482
     * 0 for before $name, 1 for after.
483
     * @param int $del The number of buttons to remove at the position given by index(string) + offset
484
     * @param mixed $add An array or single item to insert at the position given by index(string) + offset,
485
     * or null for no insertion
486
     * @return bool True if $name matched a button, false otherwise
487
     */
488
    protected function modifyButtons($name, $offset, $del = 0, $add = null)
489
    {
490
        foreach ($this->buttons as &$buttons) {
491
            if (($idx = array_search($name, $buttons)) !== false) {
492
                if ($add) {
493
                    array_splice($buttons, $idx + $offset, $del, $add);
494
                } else {
495
                    array_splice($buttons, $idx + $offset, $del);
496
                }
497
                return true;
498
            }
499
        }
500
        return false;
501
    }
502
503
    /**
504
     * Insert buttons before the first occurance of another button
505
     * @param string $before the name of the button to insert other buttons before
506
     * @param string $buttons,... a string, or several strings, or a single array of strings.
507
     * The button names to insert before that button
508
     * @return bool True if insertion occured, false if it did not (because the given button name was not found)
509
     */
510
    public function insertButtonsBefore($before, $buttons)
511
    {
512
        if (func_num_args() > 2) {
513
            $buttons = func_get_args();
514
            array_shift($buttons);
515
        }
516
        if (!is_array($buttons)) {
517
            $buttons = [$buttons];
518
        }
519
        return $this->modifyButtons($before, 0, 0, $buttons);
520
    }
521
522
    /**
523
     * Insert buttons after the first occurance of another button
524
     * @param string $after the name of the button to insert other buttons before
525
     * @param string $buttons,... a string, or several strings, or a single array of strings.
526
     * The button names to insert after that button
527
     * @return bool True if insertion occured, false if it did not (because the given button name was not found)
528
     */
529
    public function insertButtonsAfter($after, $buttons)
530
    {
531
        if (func_num_args() > 2) {
532
            $buttons = func_get_args();
533
            array_shift($buttons);
534
        }
535
        if (!is_array($buttons)) {
536
            $buttons = [$buttons];
537
        }
538
        return $this->modifyButtons($after, 1, 0, $buttons);
539
    }
540
541
    /**
542
     * Remove the first occurance of buttons
543
     * @param string $buttons,... one or more strings - the name of the buttons to remove
544
     */
545
    public function removeButtons($buttons)
546
    {
547
        if (func_num_args() > 1) {
548
            $buttons = func_get_args();
549
        }
550
        if (!is_array($buttons)) {
551
            $buttons = [$buttons];
552
        }
553
        foreach ($buttons as $button) {
554
            $this->modifyButtons($button, 0, 1);
555
        }
556
    }
557
558
    /**
559
     * Generate the JavaScript that will set TinyMCE's configuration:
560
     * - Parse all configurations into JSON objects to be used in JavaScript
561
     * - Includes TinyMCE and configurations using the {@link Requirements} system
562
     *
563
     * @return array
564
     */
565
    protected function getConfig()
566
    {
567
        $settings = $this->getSettings();
568
569
        // https://www.tinymce.com/docs/configure/url-handling/#document_base_url
570
        $settings['document_base_url'] = Director::absoluteBaseURL();
571
572
        // https://www.tinymce.com/docs/api/class/tinymce.editormanager/#baseURL
573
        $baseResource = $this->getTinyMCEResource();
574
        if ($baseResource instanceof ModuleResource) {
575
            $tinyMCEBaseURL = $baseResource->getURL();
576
        } else {
577
            $tinyMCEBaseURL = Controller::join_links(Director::baseURL(), $baseResource);
578
        }
579
        $settings['baseURL'] = $tinyMCEBaseURL;
580
581
        // map all plugins to absolute urls for loading
582
        $plugins = array();
583
        foreach ($this->getPlugins() as $plugin => $path) {
584
            if ($path instanceof ModuleResource) {
585
                $path = Director::absoluteURL($path->getURL());
586
            } elseif (!$path) {
587
                // Empty paths: Convert to urls in standard base url
588
                $path = Controller::join_links(
589
                    $tinyMCEBaseURL,
590
                    "plugins/{$plugin}/plugin.min.js"
591
                );
592
            } elseif (!Director::is_absolute_url($path)) {
593
                // Non-absolute urls are made absolute
594
                $path = Director::absoluteURL($path);
595
            }
596
            $plugins[$plugin] = $path;
597
        }
598
599
        // https://www.tinymce.com/docs/configure/integration-and-setup/#external_plugins
600
        if ($plugins) {
601
            $settings['external_plugins'] = $plugins;
602
        }
603
604
        // https://www.tinymce.com/docs/configure/editor-appearance/#groupingtoolbarcontrols
605
        $buttons = $this->getButtons();
606
        $settings['toolbar'] = [];
607
        foreach ($buttons as $rowButtons) {
608
            $row = implode(' ', $rowButtons);
609
            if (count($buttons) > 1) {
610
                $settings['toolbar'][] = $row;
611
            } else {
612
                $settings['toolbar'] = $row;
613
            }
614
        }
615
616
        // https://www.tinymce.com/docs/configure/content-appearance/#content_css
617
        $settings['content_css'] = $this->getEditorCSS();
618
619
        // https://www.tinymce.com/docs/configure/editor-appearance/#theme_url
620
        $theme = $this->getTheme();
621
        if (!Director::is_absolute_url($theme)) {
622
            $theme = Controller::join_links($tinyMCEBaseURL, "themes/{$theme}/theme.min.js");
623
        }
624
        $settings['theme_url'] = $theme;
625
626
        // Send back
627
        return $settings;
628
    }
629
630
    /**
631
     * Get location of all editor.css files.
632
     * All resource specifiers are resolved to urls.
633
     *
634
     * @return array
635
     */
636
    protected function getEditorCSS()
637
    {
638
        $editor = [];
639
        $resourceLoader = ModuleResourceLoader::singleton();
640
        foreach ($this->getContentCSS() as $contentCSS) {
641
            $editor[] = $resourceLoader->resolveURL($contentCSS);
642
        }
643
        return $editor;
644
    }
645
646
    /**
647
     * Get list of resource paths to css files.
648
     *
649
     * Will default to `editor_css` config, as well as any themed `editor.css` files.
650
     * Use setContentCSS() to override.
651
     *
652
     * @return string[]
653
     */
654
    public function getContentCSS()
655
    {
656
        // Prioritise instance specific content
657
        if (isset($this->contentCSS)) {
658
            return $this->contentCSS;
659
        }
660
661
        // Add standard editor.css
662
        $editor = [];
663
        $editorCSSFiles = $this->config()->get('editor_css');
664
        if ($editorCSSFiles) {
665
            foreach ($editorCSSFiles as $editorCSS) {
666
                $editor[] = $editorCSS;
667
            }
668
        }
669
670
        // Themed editor.css
671
        $themes = HTMLEditorConfig::getThemes() ?: SSViewer::get_themes();
672
        $themedEditor = ThemeResourceLoader::inst()->findThemedCSS('editor', $themes);
673
        if ($themedEditor) {
674
            $editor[] = $themedEditor;
675
        }
676
        return $editor;
677
    }
678
679
    /**
680
     * Set explicit set of CSS resources to use for `content_css` option.
681
     *
682
     * Note: If merging with default paths, you should call getContentCSS() and merge
683
     * prior to assignment.
684
     *
685
     * @param string[] $css Array of resource paths. Supports module prefix,
686
     * e.g. `silverstripe/admin:client/dist/styles/editor.css`
687
     * @return $this
688
     */
689
    public function setContentCSS($css)
690
    {
691
        $this->contentCSS = $css;
692
        return $this;
693
    }
694
695
    /**
696
     * Generate gzipped TinyMCE configuration including plugins and languages.
697
     * This ends up "pre-loading" TinyMCE bundled with the required plugins
698
     * so that multiple HTTP requests on the client don't need to be made.
699
     *
700
     * @return string
701
     * @throws Exception
702
     */
703
    public function getScriptURL()
704
    {
705
        /** @var TinyMCEScriptGenerator $generator */
706
        $generator = Injector::inst()->get(TinyMCEScriptGenerator::class);
707
        return $generator->getScriptURL($this);
708
    }
709
710
    public function init()
711
    {
712
        // include TinyMCE Javascript
713
        Requirements::javascript($this->getScriptURL());
714
    }
715
716
717
    /**
718
     * Get the current tinyMCE language
719
     *
720
     * @return string Language
721
     */
722
    public static function get_tinymce_lang()
723
    {
724
        $lang = static::config()->get('tinymce_lang');
725
        $locale = i18n::get_locale();
726
        if (isset($lang[$locale])) {
727
            return $lang[$locale];
728
        }
729
        return 'en';
730
    }
731
732
    /**
733
     * Returns the full filesystem path to TinyMCE resources (which could be different from the original tinymce
734
     * location in the module).
735
     *
736
     * Path will be absolute.
737
     *
738
     * @return string
739
     * @throws Exception
740
     */
741
    public function getTinyMCEResourcePath()
742
    {
743
        $resource = $this->getTinyMCEResource();
744
        if ($resource instanceof ModuleResource) {
745
            return $resource->getPath();
746
        }
747
        return Director::baseFolder() . '/' . $resource;
748
    }
749
750
    /**
751
     * Get front-end url to tinymce resources
752
     *
753
     * @return string
754
     * @throws Exception
755
     */
756
    public function getTinyMCEResourceURL()
757
    {
758
        $resource = $this->getTinyMCEResource();
759
        if ($resource instanceof ModuleResource) {
760
            return $resource->getURL();
761
        }
762
        return $resource;
763
    }
764
765
    /**
766
     * Get resource root for TinyMCE, either as a string or ModuleResource instance
767
     * Path will be relative to BASE_PATH if string.
768
     *
769
     * @return ModuleResource|string
770
     * @throws Exception
771
     */
772
    public function getTinyMCEResource()
773
    {
774
        $configDir = static::config()->get('base_dir');
775
        if ($configDir) {
776
            return ModuleResourceLoader::singleton()->resolveResource($configDir);
777
        }
778
779
        throw new Exception(sprintf(
780
            'If the silverstripe/admin module is not installed you must set the TinyMCE path in %s.base_dir',
781
            __CLASS__
782
        ));
783
    }
784
785
    /**
786
     * @deprecated 4.0..5.0
787
     */
788
    public function getTinyMCEPath()
789
    {
790
        Deprecation::notice('5.0', 'use getTinyMCEResourcePath instead');
791
        return $this->getTinyMCEResourcePath();
792
    }
793
794
    /**
795
     * @return Module
796
     * @deprecated 4.0..5.0
797
     */
798
    protected function getAdminModule()
799
    {
800
        Deprecation::notice('5.0', 'Set base_dir or editor_css config instead');
801
        return ModuleLoader::getModule('silverstripe/admin');
802
    }
803
}
804