Passed
Push — main ( e2b5f1...353913 )
by Stefan
02:09
created

FormCKEdit::setBrowseFolderLinkURL()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
declare(strict_types=1);
3
4
namespace SKien\Formgenerator;
5
6
/**
7
 * WYSIWYG - HTML input using CKEditor.
8
 * uses CKEditor Version 4.15
9
 * 
10
 * For more information about download, install and integration of the CKEditor, see
11
 * CKEditorIntegration.md
12
 * 
13
 * To enable filebrowsing on the server for the insert mage and insert link functionality
14
 * the RichFilemanager is used. For more information see
15
 * RichFilemanager.md  
16
 * 
17
 * @package Formgenerator
18
 * @author Stefanius <[email protected]>
19
 * @copyright MIT License - see the LICENSE file for details
20
 */
21
class FormCKEdit extends FormTextArea
22
{
23
    /** 'Source' - Button   */
24
    public const TB_SOURCE           = 0x00000002;
25
    /** Basic Styles:  Bold, Italic, Underline, Subscript, Superscript, RemoveFormat    */
26
    public const TB_BASIC_STYLES     = 0x00000004;
27
    /** Paragraph Formation: NumberedList, BulletedList, Outdent, Indent, JustifyLeft, -Center, -Right' */
28
    public const TB_PARAGRAPH        = 0x00000008;
29
    /** Links: link, Unlink */
30
    public const TB_LINKS            = 0x00000010;
31
    /** Insert Image    */
32
    public const TB_IMAGE            = 0x00000020;
33
    /** Colors: Text-, Backgroundcolor  */
34
    public const TB_COLOR            = 0x00000040;
35
    /** insert Table   */
36
    public const TB_TABLE            = 0x00000080;
37
    /** SelectBox for defined Styles    */
38
    public const TB_STYLES_SELECT    = 0x00000100;
39
    /** Select predefined Templates */
40
    public const TB_TEMPLATES        = 0x00000200;
41
    /** SelectBox for Placeholders  */
42
    public const TB_PLACEHOLDERS     = 0x00000400;
43
    /** Insert Codesnippet  */
44
    public const TB_SNIPPET          = 0x00000800;
45
    /** Insert Special Chars  */
46
    public const TB_SPECIAL_CHAR     = 0x00001000;
47
    /** Insert Iframe  */
48
    public const TB_IFRAME           = 0x00002000;
49
    
50
    /** small toolbar (only basic styles)   */
51
    public const TB_SMALL    = 0x00000004; // TB_BASIC_STYLES;
52
    /** insert objects   */
53
    public const TB_INSERT   = 0x000038A0; // TB_IMAGE | TB_TABLE | TB_SNIPPET | TB_SPECIAL_CHAR | TB_IFRAME
54
    /** toolbar for content edit (no colors, templates and objects)   */
55
    public const TB_CONTENT  = 0xfffff53d; // 0xffffffff & ~(TB_COLOR | TB_TEMPLATES | TB_INSERT | TB_SOURCE);
56
    /** full toolbar (no templates)   */
57
    public const TB_FULL     = 0xfffffdfd; // 0xffffffff & ~(TB_TEMPLATES | TB_SOURCE);
58
    
59
    /** custom button only with text   */
60
    public const BTN_TEXT           = 0x01;
61
    /** custom button only with icon   */
62
    public const BTN_ICON           = 0x02;
63
    /** custom button with text and icon  */
64
    public const BTN_TEXT_ICON      = 0x03;
65
    
66
    /** @var string the CSS file used inside the editarea    */
67
    protected string $strContentsCss = '';
68
    /** @var string the id of the editarea   */
69
    protected string $strBodyID;
70
    /** @var array custom button definition ["func" => <buttonhandler>, "name" => <buttonname>]    */
71
    protected array $aCustomBtn = [];
72
    /** @var string allowed content    */
73
    protected string $strAllowedContent = '';
74
    /** @var int toolbar mask    */
75
    protected int $lToolbar;
76
    /** @var string initial folder to expand in filemanager for links   */
77
    protected string $strBrowseFolderLinkURL = '';
78
    /** @var string initial folder to expand in filemanager for images   */
79
    protected string $strBrowseFolderImageURL = '';
80
    /** @var string initial folder to expand in filemanager for image links   */
81
    protected string $strBrowseFolderImageLinkURL = '';
82
    
83
    /**
84
     * Creates a WYSIWYG editor.
85
     * @param string $strName
86
     * @param int $iRows
87
     * @param string $strWidth
88
     * @param int $wFlags
89
     */
90
    public function __construct(string $strName, int $iRows, string $strWidth = '100%', int $wFlags = 0) 
91
    {
92
        // add 2 rows to increase height for toolbar
93
        parent::__construct($strName, 0, $iRows + 2, $strWidth, $wFlags);
94
        $this->strBodyID = 'editarea';
95
        $this->lToolbar = self::TB_CONTENT;
96
    } 
97
98
    /**
99
     * Set the CSS file to use in the edit area.
100
     * @param string $strContentsCss
101
     */
102
    public function setContentsCss(string $strContentsCss) : void
103
    {
104
        $this->strContentsCss = $strContentsCss;
105
    }
106
107
    /**
108
     * Set the ID of the body.
109
     * This is the ID of the 'Container' element in which the text to be edited here 
110
     * should be displayed. This ID is required so that the correct CSS selectors are 
111
     * used for the display here in the editor. 
112
     * @param string $strBodyID
113
     */
114
    public function setBodyID(string $strBodyID) : void 
115
    {
116
        $this->strBodyID = $strBodyID;
117
    }
118
    
119
    /**
120
     * Add custom button to the beginning of the toolbar.
121
     * If icon specified take care it the path is absolute or relative to the script that
122
     * containing this CKEditor.
123
     * @param string $strName       Name (Text) of the Button
124
     * @param string $strFunction   JS-Function to handle click (func gets editor as paramter)
125
     * @param string $strIcon       Icon for the button
126
     * @param int $iType            Type of the button (FormCKEdit::BTN_TEXT, FormCKEdit::BTN_ICON or FormCKEdit::BTN_TXET_ICON)
127
     */
128
    public function addCustomButton(string $strName, string $strFunction, string $strIcon = '', int $iType = self::BTN_TEXT) : void 
129
    {
130
        if (empty($strIcon)) {
131
            $iType = self::BTN_TEXT;
132
        }
133
        $this->aCustomBtn[] = [
134
            'func' => $strFunction, 
135
            'name' => $strName,
136
            'icon' => $strIcon,
137
            'type' => $iType,
138
        ];
139
    }
140
    
141
    /**
142
     * Specify allowed content (see documentation of CKEdit for details)
143
     * @param string $strAllowedContent leave empty to allow everything (default)  
144
     */
145
    public function setAllowedContent(string $strAllowedContent = '') : void
146
    {
147
        $this->strAllowedContent = $strAllowedContent;
148
    }
149
    
150
    /**
151
     * @param string $strBrowseFolderLinkURL
152
     */
153
    public function setBrowseFolderLinkURL(string $strBrowseFolderLinkURL) : void
154
    {
155
        $this->strBrowseFolderLinkURL = $strBrowseFolderLinkURL;
156
    }
157
    
158
    /**
159
     * @param string $strBrowseFolderImageURL
160
     */
161
    public function setBrowseFolderImageURL(string $strBrowseFolderImageURL) : void
162
    {
163
        $this->strBrowseFolderImageURL = $strBrowseFolderImageURL;
164
    }
165
    
166
    /**
167
     * @param string $strBrowseFolderImageLinkURL
168
     */
169
    public function setBrowseFolderImageLinkURL(string $strBrowseFolderImageLinkURL) : void
170
    {
171
        $this->strBrowseFolderImageLinkURL = $strBrowseFolderImageLinkURL;
172
    }
173
    
174
    /**
175
     * Load some configuratin after parent set.
176
     * {@inheritDoc}
177
     * @see \SKien\Formgenerator\FormElement::onParentSet()
178
     */
179
    protected function onParentSet() : void
180
    {
181
        $aCKEditor = [
182
            'Path' => $this->oFG->getConfig()->getString('CKEditor.Path'),
183
            'editorID' => $this->strName,
184
            'editorOptions' => $this->buildEditorOptions(),
185
            'customButtons' => $this->aCustomBtn,
186
        ];
187
        // pass the content through the JSON data
188
        if ($this->oFlags->isSet(FormFlags::SET_JSON_DATA)) {
189
            $aCKEditor['editorData'] = $this->oFG->getData()->getValue($this->strName);
190
        }
191
        $this->oFG->addConfigForJS('CKEditor', $aCKEditor);
192
        
193
        $strRfmPath = $this->oFG->getConfig()->getString('RichFilemanager.Path');
194
        if ($strRfmPath != '') {
195
            if (!file_exists($_SERVER['DOCUMENT_ROOT'] . $strRfmPath)) {
196
                if ($this->oFG->getDebugMode()) {
197
                    trigger_error('Can not find Rich Filemanager at [' . $_SERVER['DOCUMENT_ROOT'] . $strRfmPath . ']', E_USER_WARNING);
198
                }
199
            }
200
            
201
            $strBrowseFolderLinkURL = $this->strBrowseFolderLinkURL ?: $this->oFG->getConfig()->getString('RichFilemanager.expandFolder.browseLinkURL');
202
            $strBrowseFolderImageURL = $this->strBrowseFolderImageURL ?: $this->oFG->getConfig()->getString('RichFilemanager.expandFolder.browseImageURL');
203
            $strBrowseFolderImageLinkURL = $this->strBrowseFolderImageLinkURL ?: $this->oFG->getConfig()->getString('RichFilemanager.expandFolder.browseImageLinkURL');
204
            $strBrowseFolderImageLinkURL = $strBrowseFolderImageLinkURL ?: $strBrowseFolderLinkURL;
205
            $aRFN = [
206
                'Path' => $strRfmPath,
207
                'language' => $this->oFG->getConfig()->getString('RichFilemanager.language'),
208
                'expandFolder' => [
209
                    'browseLinkURL' => $strBrowseFolderLinkURL,
210
                    'browseImageURL' => $strBrowseFolderImageURL,
211
                    'browseImageLinkURL' => $strBrowseFolderImageLinkURL
212
                ]
213
            ];
214
            $this->oFG->addConfigForJS('RichFilemanager', $aRFN);
215
        }
216
    }
217
    
218
    /**
219
     * Build CKEditor specific styles.
220
     * @return string
221
     */
222
    public function getStyle() : string 
223
    {
224
        // If custom toolbar buttons defined, for each button dependent on the his
225
        // type (TEXT, ICON, TEXT+ICON) some styles have to be set.
226
        $strStyle = '';
227
        foreach ($this->aCustomBtn as $aBtn) {
228
            $strBtn = strtolower($aBtn['func']);
229
            $strDisplayLabel = (($aBtn['type'] & self::BTN_TEXT) != 0) ? 'inline' : 'none';
230
            $strDisplayIcon = (($aBtn['type'] & self::BTN_ICON) != 0) ? 'inline' : 'none';
231
            $strStyle .= '.cke_button__' . $strBtn . '_icon { display: ' . $strDisplayIcon . ' !important; }' . PHP_EOL;
232
            $strStyle .= '.cke_button__' . $strBtn . '_label { display: ' . $strDisplayLabel . ' !important; }' . PHP_EOL;
233
        }
234
        
235
        if ($this->oFG->getConfig()->getString('RichFilemanager.Path')) {
236
            $strStyle .= PHP_EOL . 
237
                ".fm-modal {" . PHP_EOL .
238
                "    z-index: 10011; /** Because CKEditor image dialog was at 10010 */" . PHP_EOL .
239
                "    width:80%;" . PHP_EOL .
240
                "    height:80%;" . PHP_EOL .
241
                "    top: 10%;" . PHP_EOL .
242
                "    left:10%;" . PHP_EOL .
243
                "    border:0;" . PHP_EOL .
244
                "    position:fixed;" . PHP_EOL .
245
                "}";
246
        }
247
        
248
        return $strStyle;
249
    }
250
    
251
    /**
252
     * Define toolbar to display.
253
     * @param int $lToolbar
254
     */
255
    public function setToolbar(int $lToolbar) : void
256
    {
257
        $this->lToolbar = $lToolbar;
258
    }
259
    
260
    /**
261
     * Returns currently defined toolbar.
262
     * @return int
263
     */
264
    public function getToolbar() : int
265
    {
266
        return $this->lToolbar;
267
    }
268
    
269
    /**
270
     * Build the options to create the CKEditor instance.
271
     * @return array
272
     */
273
    protected function buildEditorOptions() : array
274
    {
275
        if (strlen($this->strContentsCss) == 0) {
276
            trigger_error('No CSS Stylesheet set!', E_USER_WARNING);
277
        }
278
        $aCKEditor = [
279
            'contentsCss' => $this->strContentsCss,
280
            'bodyId' => $this->strBodyID,
281
            'toolbar' => $this->buildToolbarDef(),
282
            'toolbarCanCollapse' => false,
283
            'uiColor' => $this->oFG->getConfig()->getString('CKEditor.uiColor', '#F8F8F8'),
284
            'pasteFilter' => $this->oFG->getConfig()->getString('CKEditor.pasteFilter', 'plain-text'),
285
            'colorButton_enableAutomatic' => $this->oFG->getConfig()->getBool('CKEditor.colorbutton.enableAutomatic', true),
286
            'colorButton_enableMore' => $this->oFG->getConfig()->getBool('CKEditor.colorbutton.enableMore', true),
287
            'allowedContent' => ($this->strAllowedContent ?: true),
288
            'resize_enabled' => false,
289
        ];
290
        $this->buildSelectableColors($aCKEditor);
291
        $this->buildPlaceholderSelect($aCKEditor);
292
        
293
        return $aCKEditor;
294
    }
295
    
296
    /**
297
     * Build config settings for the selectable colors.
298
     * @param array $aCKEditor
299
     */
300
    protected function buildSelectableColors(array &$aCKEditor) : void
301
    {
302
        $aSelectableColorsRaw = $this->oFG->getConfig()->getArray('CKEditor.colorbutton.selectableColors');
303
        $aSelectableColors = [];
304
        foreach ($aSelectableColorsRaw as $color) {
305
            $aSelectableColors[] = ltrim($color, '#');
306
        }
307
        if (($this->lToolbar & self::TB_COLOR) != 0 && count($aSelectableColors) > 0) {
308
            $strColors = '';
309
            $strSep = '';
310
            foreach ($aSelectableColors as $strColor) {
311
                $strColors .= $strSep . $strColor;
312
                $strSep = ',';
313
            }
314
            $aCKEditor['colorButton_colors'] = $strColors;
315
            $aCKEditor['colorButton_colorsPerRow'] = $this->oFG->getConfig()->getInt('CKEditor.colorbutton.colorsPerRow', 6);
316
        }
317
    }
318
    
319
    /**
320
     * Build config for available placeholders in the placeholder-combobox.
321
     * @param array $aCKEditor
322
     */
323
    protected function buildPlaceholderSelect(array &$aCKEditor) : void
324
    {
325
        $aPlaceholderselect = $this->oFG->getConfig()->getArray('CKEditor.placeholder');
326
        if (($this->lToolbar & self::TB_PLACEHOLDERS) != 0 && count($aPlaceholderselect) > 0) {
327
            $aCKEditor['placeholder_select'] = ['placeholders' => $aPlaceholderselect];
328
        }
329
    }
330
    
331
    /**
332
     * Returns currently defined toolbar as array for JSON-encoding.
333
     * @link https://ckeditor.com/latest/samples/toolbarconfigurator/index.html
334
     * @return array
335
     */
336
    protected function buildToolbarDef() : array
337
    {
338
        $aToolbar = [];
339
        $this->addCustomBtns($aToolbar);
340
        $this->addBasicStyleBtns($aToolbar);
341
        $this->addParagraphBtns($aToolbar);
342
        $this->addLinkBtns($aToolbar);
343
        $this->addInsertBtns($aToolbar);
344
        $this->addColorBtns($aToolbar);
345
        $this->addStyleSelect($aToolbar);
346
        $this->addTemplateSelect($aToolbar);
347
        $this->addPlaceholderSelect($aToolbar);
348
        $this->addSourceBtn($aToolbar);
349
        
350
        return $aToolbar;
351
    }
352
    
353
    /**
354
     * Add all custom buttons at start of the toolbar.
355
     * @param array $aToolbar reference to the toolbar array
356
     */
357
    protected function addCustomBtns(array &$aToolbar) : void
358
    {
359
        foreach ($this->aCustomBtn as $aBtn) {
360
            $aToolbar[] = ['items' => [$aBtn['func']]];
361
        }
362
    }
363
    
364
    /**
365
     * Add button group for basic styles. 
366
     * @param array $aToolbar reference to the toolbar array
367
     */
368
    protected function addBasicStyleBtns(array &$aToolbar) : void
369
    {
370
        if (($this->lToolbar & self::TB_BASIC_STYLES) != 0) {
371
            $aToolbar[] = [
372
                'name' => 'basicstyles', 
373
                'items' => [
374
                    'Bold',
375
                    'Italic',
376
                    'Underline',
377
                    'Subscript',
378
                    'Superscript',
379
                    '-',
380
                    'RemoveFormat',
381
                ]
382
            ];
383
        }
384
    }
385
    
386
    /**
387
     * Add button group for paragraph formating. 
388
     * @param array $aToolbar reference to the toolbar array
389
     */
390
    protected function addParagraphBtns(array &$aToolbar) : void
391
    {
392
        if (($this->lToolbar & self::TB_PARAGRAPH) != 0) {
393
            $aToolbar[] = [
394
                'name' => 'paragraph',
395
                'items' => [
396
                    'NumberedList',
397
                    'BulletedList',
398
                    '-',
399
                    'Outdent', 
400
                    'Indent',
401
                    '-',
402
                    'JustifyLeft',
403
                    'JustifyCenter',
404
                    'JustifyRight',
405
                ]
406
            ];
407
        }
408
    }
409
    
410
    /**
411
     * Add button group for links. 
412
     * @param array $aToolbar reference to the toolbar array
413
     */
414
    protected function addLinkBtns(array &$aToolbar) : void
415
    {
416
        if (($this->lToolbar & self::TB_LINKS) != 0) {
417
            $aToolbar[] = [
418
                'name' => 'links',
419
                'items' => ['Link', 'Unlink']
420
            ];
421
        }
422
    }
423
    
424
    /**
425
     * Add button group to insert objects.
426
     * - Images
427
     * - Snippets
428
     * - Tables
429
     * - Special Chars
430
     * - IFrames 
431
     * @param array $aToolbar reference to the toolbar array
432
     */
433
    protected function addInsertBtns(array &$aToolbar) : void
434
    {
435
        if (($this->lToolbar & self::TB_INSERT) != 0) {
436
            $aInsert = array();
437
            if (($this->lToolbar & self::TB_IMAGE) != 0) {
438
                $aInsert[] = 'Image';
439
            }
440
            if (($this->lToolbar & self::TB_SNIPPET) != 0) {
441
                $aInsert[] = 'CodeSnippet';
442
            }
443
            if (($this->lToolbar & self::TB_TABLE) != 0) {
444
                $aInsert[] = 'Table';
445
            }
446
            if (($this->lToolbar & self::TB_SPECIAL_CHAR) != 0) {
447
                $aInsert[] = 'SpecialChar';
448
            }
449
            if (($this->lToolbar & self::TB_IFRAME) != 0) {
450
                $aInsert[] = 'Iframe';
451
            }
452
            $aToolbar[] = ['name' => 'insert', 'items' => $aInsert];
453
        }
454
    }
455
    
456
    /**
457
     * Add button group for colors 
458
     * @param array $aToolbar reference to the toolbar array
459
     */
460
    protected function addColorBtns(array &$aToolbar) : void
461
    {
462
        if (($this->lToolbar & self::TB_COLOR) != 0) {
463
            $aToolbar[] = ['name' => 'color', 'items' => ['TextColor', 'BGColor']];
464
        }
465
    }
466
    
467
    /**
468
     * Add select list for styles 
469
     * @param array $aToolbar reference to the toolbar array
470
     */
471
    protected function addStyleSelect(array &$aToolbar) : void
472
    {
473
        if (($this->lToolbar & self::TB_STYLES_SELECT) != 0) {
474
            $aToolbar[] = ['items' => ['Styles']];
475
        }
476
    }
477
    
478
    /**
479
     * Add select list for templates 
480
     * @param array $aToolbar reference to the toolbar array
481
     */
482
    protected function addTemplateSelect(array &$aToolbar) : void
483
    {
484
        if (($this->lToolbar & self::TB_TEMPLATES) != 0) {
485
            $aToolbar[] = ['items' => ['Templates']];
486
        }
487
    }
488
    
489
    /**
490
     * Add select list for placeholders 
491
     * @param array $aToolbar reference to the toolbar array
492
     */
493
    protected function addPlaceholderSelect(array &$aToolbar) : void
494
    {
495
        if (($this->lToolbar & self::TB_PLACEHOLDERS) != 0 && count($this->oFG->getConfig()->getArray('CKEditor.placeholder')) > 0) {
496
            $aToolbar[] = ['items' => ['placeholder_select']];
497
        }
498
    }
499
    
500
    /**
501
     * Add button to switch in the source mode 
502
     * @param array $aToolbar reference to the toolbar array
503
     */
504
    protected function addSourceBtn(array &$aToolbar) : void
505
    {
506
        if (($this->lToolbar & self::TB_SOURCE) != 0) {
507
            $aToolbar[] = ['name' => 'document', 'items' => ['Source']];
508
        }
509
    }
510
}
511