Passed
Push — 4 ( 33a283...e59625 )
by Maxime
07:11
created

TinyMCEConfig::initImageSizePresets()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

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