Passed
Push — main ( 353913...66eafb )
by Stefan
02:04
created

FormCKEdit::getBrowseFolder()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 6
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->getBrowseFolder($this->strBrowseFolderLinkURL, 'RichFilemanager.expandFolder.browseLinkURL');
202
            $strBrowseFolderImageURL = $this->getBrowseFolder($this->strBrowseFolderImageURL, 'RichFilemanager.expandFolder.browseImageURL');
203
            $strBrowseFolderImageLinkURL = $this->getBrowseFolder($this->strBrowseFolderImageLinkURL, 'RichFilemanager.expandFolder.browseImageLinkURL');
204
            $aRFN = [
205
                'Path' => $strRfmPath,
206
                'language' => $this->oFG->getConfig()->getString('RichFilemanager.language'),
207
                'expandFolder' => [
208
                    'browseLinkURL' => $strBrowseFolderLinkURL,
209
                    'browseImageURL' => $strBrowseFolderImageURL,
210
                    'browseImageLinkURL' => $strBrowseFolderImageLinkURL ?: $strBrowseFolderLinkURL
211
                ]
212
            ];
213
            $this->oFG->addConfigForJS('RichFilemanager', $aRFN);
214
        }
215
    }
216
    
217
    /**
218
     * Get the startfolder for different purposes.
219
     * @param string $strFolder
220
     * @param string $strConfig
221
     * @return string
222
     */
223
    protected function getBrowseFolder(string $strFolder, string $strConfig) : string
224
    {
225
        if (strlen($strFolder) > 0) {
226
            return $strFolder;
227
        }
228
        return $this->oFG->getConfig()->getString($strConfig);
229
    }
230
    
231
    /**
232
     * Build CKEditor specific styles.
233
     * @return string
234
     */
235
    public function getStyle() : string 
236
    {
237
        // If custom toolbar buttons defined, for each button dependent on the his
238
        // type (TEXT, ICON, TEXT+ICON) some styles have to be set.
239
        $strStyle = '';
240
        foreach ($this->aCustomBtn as $aBtn) {
241
            $strBtn = strtolower($aBtn['func']);
242
            $strDisplayLabel = (($aBtn['type'] & self::BTN_TEXT) != 0) ? 'inline' : 'none';
243
            $strDisplayIcon = (($aBtn['type'] & self::BTN_ICON) != 0) ? 'inline' : 'none';
244
            $strStyle .= '.cke_button__' . $strBtn . '_icon { display: ' . $strDisplayIcon . ' !important; }' . PHP_EOL;
245
            $strStyle .= '.cke_button__' . $strBtn . '_label { display: ' . $strDisplayLabel . ' !important; }' . PHP_EOL;
246
        }
247
        
248
        if ($this->oFG->getConfig()->getString('RichFilemanager.Path')) {
249
            $strStyle .= PHP_EOL . 
250
                ".fm-modal {" . PHP_EOL .
251
                "    z-index: 10011; /** Because CKEditor image dialog was at 10010 */" . PHP_EOL .
252
                "    width:80%;" . PHP_EOL .
253
                "    height:80%;" . PHP_EOL .
254
                "    top: 10%;" . PHP_EOL .
255
                "    left:10%;" . PHP_EOL .
256
                "    border:0;" . PHP_EOL .
257
                "    position:fixed;" . PHP_EOL .
258
                "}";
259
        }
260
        
261
        return $strStyle;
262
    }
263
    
264
    /**
265
     * Define toolbar to display.
266
     * @param int $lToolbar
267
     */
268
    public function setToolbar(int $lToolbar) : void
269
    {
270
        $this->lToolbar = $lToolbar;
271
    }
272
    
273
    /**
274
     * Returns currently defined toolbar.
275
     * @return int
276
     */
277
    public function getToolbar() : int
278
    {
279
        return $this->lToolbar;
280
    }
281
    
282
    /**
283
     * Build the options to create the CKEditor instance.
284
     * @return array
285
     */
286
    protected function buildEditorOptions() : array
287
    {
288
        if (strlen($this->strContentsCss) == 0) {
289
            trigger_error('No CSS Stylesheet set!', E_USER_WARNING);
290
        }
291
        $aCKEditor = [
292
            'contentsCss' => $this->strContentsCss,
293
            'bodyId' => $this->strBodyID,
294
            'toolbar' => $this->buildToolbarDef(),
295
            'toolbarCanCollapse' => false,
296
            'uiColor' => $this->oFG->getConfig()->getString('CKEditor.uiColor', '#F8F8F8'),
297
            'pasteFilter' => $this->oFG->getConfig()->getString('CKEditor.pasteFilter', 'plain-text'),
298
            'colorButton_enableAutomatic' => $this->oFG->getConfig()->getBool('CKEditor.colorbutton.enableAutomatic', true),
299
            'colorButton_enableMore' => $this->oFG->getConfig()->getBool('CKEditor.colorbutton.enableMore', true),
300
            'allowedContent' => ($this->strAllowedContent ?: true),
301
            'resize_enabled' => false,
302
        ];
303
        $this->buildSelectableColors($aCKEditor);
304
        $this->buildPlaceholderSelect($aCKEditor);
305
        
306
        return $aCKEditor;
307
    }
308
    
309
    /**
310
     * Build config settings for the selectable colors.
311
     * @param array $aCKEditor
312
     */
313
    protected function buildSelectableColors(array &$aCKEditor) : void
314
    {
315
        $aSelectableColorsRaw = $this->oFG->getConfig()->getArray('CKEditor.colorbutton.selectableColors');
316
        $aSelectableColors = [];
317
        foreach ($aSelectableColorsRaw as $color) {
318
            $aSelectableColors[] = ltrim($color, '#');
319
        }
320
        if (($this->lToolbar & self::TB_COLOR) != 0 && count($aSelectableColors) > 0) {
321
            $strColors = '';
322
            $strSep = '';
323
            foreach ($aSelectableColors as $strColor) {
324
                $strColors .= $strSep . $strColor;
325
                $strSep = ',';
326
            }
327
            $aCKEditor['colorButton_colors'] = $strColors;
328
            $aCKEditor['colorButton_colorsPerRow'] = $this->oFG->getConfig()->getInt('CKEditor.colorbutton.colorsPerRow', 6);
329
        }
330
    }
331
    
332
    /**
333
     * Build config for available placeholders in the placeholder-combobox.
334
     * @param array $aCKEditor
335
     */
336
    protected function buildPlaceholderSelect(array &$aCKEditor) : void
337
    {
338
        $aPlaceholderselect = $this->oFG->getConfig()->getArray('CKEditor.placeholder');
339
        if (($this->lToolbar & self::TB_PLACEHOLDERS) != 0 && count($aPlaceholderselect) > 0) {
340
            $aCKEditor['placeholder_select'] = ['placeholders' => $aPlaceholderselect];
341
        }
342
    }
343
    
344
    /**
345
     * Returns currently defined toolbar as array for JSON-encoding.
346
     * @link https://ckeditor.com/latest/samples/toolbarconfigurator/index.html
347
     * @return array
348
     */
349
    protected function buildToolbarDef() : array
350
    {
351
        $aToolbar = [];
352
        $this->addCustomBtns($aToolbar);
353
        $this->addBasicStyleBtns($aToolbar);
354
        $this->addParagraphBtns($aToolbar);
355
        $this->addLinkBtns($aToolbar);
356
        $this->addInsertBtns($aToolbar);
357
        $this->addColorBtns($aToolbar);
358
        $this->addStyleSelect($aToolbar);
359
        $this->addTemplateSelect($aToolbar);
360
        $this->addPlaceholderSelect($aToolbar);
361
        $this->addSourceBtn($aToolbar);
362
        
363
        return $aToolbar;
364
    }
365
    
366
    /**
367
     * Add all custom buttons at start of the toolbar.
368
     * @param array $aToolbar reference to the toolbar array
369
     */
370
    protected function addCustomBtns(array &$aToolbar) : void
371
    {
372
        foreach ($this->aCustomBtn as $aBtn) {
373
            $aToolbar[] = ['items' => [$aBtn['func']]];
374
        }
375
    }
376
    
377
    /**
378
     * Add button group for basic styles. 
379
     * @param array $aToolbar reference to the toolbar array
380
     */
381
    protected function addBasicStyleBtns(array &$aToolbar) : void
382
    {
383
        if (($this->lToolbar & self::TB_BASIC_STYLES) != 0) {
384
            $aToolbar[] = [
385
                'name' => 'basicstyles', 
386
                'items' => [
387
                    'Bold',
388
                    'Italic',
389
                    'Underline',
390
                    'Subscript',
391
                    'Superscript',
392
                    '-',
393
                    'RemoveFormat',
394
                ]
395
            ];
396
        }
397
    }
398
    
399
    /**
400
     * Add button group for paragraph formating. 
401
     * @param array $aToolbar reference to the toolbar array
402
     */
403
    protected function addParagraphBtns(array &$aToolbar) : void
404
    {
405
        if (($this->lToolbar & self::TB_PARAGRAPH) != 0) {
406
            $aToolbar[] = [
407
                'name' => 'paragraph',
408
                'items' => [
409
                    'NumberedList',
410
                    'BulletedList',
411
                    '-',
412
                    'Outdent', 
413
                    'Indent',
414
                    '-',
415
                    'JustifyLeft',
416
                    'JustifyCenter',
417
                    'JustifyRight',
418
                ]
419
            ];
420
        }
421
    }
422
    
423
    /**
424
     * Add button group for links. 
425
     * @param array $aToolbar reference to the toolbar array
426
     */
427
    protected function addLinkBtns(array &$aToolbar) : void
428
    {
429
        if (($this->lToolbar & self::TB_LINKS) != 0) {
430
            $aToolbar[] = [
431
                'name' => 'links',
432
                'items' => ['Link', 'Unlink']
433
            ];
434
        }
435
    }
436
    
437
    /**
438
     * Add button group to insert objects.
439
     * - Images
440
     * - Snippets
441
     * - Tables
442
     * - Special Chars
443
     * - IFrames 
444
     * @param array $aToolbar reference to the toolbar array
445
     */
446
    protected function addInsertBtns(array &$aToolbar) : void
447
    {
448
        if (($this->lToolbar & self::TB_INSERT) != 0) {
449
            $aInsert = array();
450
            if (($this->lToolbar & self::TB_IMAGE) != 0) {
451
                $aInsert[] = 'Image';
452
            }
453
            if (($this->lToolbar & self::TB_SNIPPET) != 0) {
454
                $aInsert[] = 'CodeSnippet';
455
            }
456
            if (($this->lToolbar & self::TB_TABLE) != 0) {
457
                $aInsert[] = 'Table';
458
            }
459
            if (($this->lToolbar & self::TB_SPECIAL_CHAR) != 0) {
460
                $aInsert[] = 'SpecialChar';
461
            }
462
            if (($this->lToolbar & self::TB_IFRAME) != 0) {
463
                $aInsert[] = 'Iframe';
464
            }
465
            $aToolbar[] = ['name' => 'insert', 'items' => $aInsert];
466
        }
467
    }
468
    
469
    /**
470
     * Add button group for colors 
471
     * @param array $aToolbar reference to the toolbar array
472
     */
473
    protected function addColorBtns(array &$aToolbar) : void
474
    {
475
        if (($this->lToolbar & self::TB_COLOR) != 0) {
476
            $aToolbar[] = ['name' => 'color', 'items' => ['TextColor', 'BGColor']];
477
        }
478
    }
479
    
480
    /**
481
     * Add select list for styles 
482
     * @param array $aToolbar reference to the toolbar array
483
     */
484
    protected function addStyleSelect(array &$aToolbar) : void
485
    {
486
        if (($this->lToolbar & self::TB_STYLES_SELECT) != 0) {
487
            $aToolbar[] = ['items' => ['Styles']];
488
        }
489
    }
490
    
491
    /**
492
     * Add select list for templates 
493
     * @param array $aToolbar reference to the toolbar array
494
     */
495
    protected function addTemplateSelect(array &$aToolbar) : void
496
    {
497
        if (($this->lToolbar & self::TB_TEMPLATES) != 0) {
498
            $aToolbar[] = ['items' => ['Templates']];
499
        }
500
    }
501
    
502
    /**
503
     * Add select list for placeholders 
504
     * @param array $aToolbar reference to the toolbar array
505
     */
506
    protected function addPlaceholderSelect(array &$aToolbar) : void
507
    {
508
        if (($this->lToolbar & self::TB_PLACEHOLDERS) != 0 && count($this->oFG->getConfig()->getArray('CKEditor.placeholder')) > 0) {
509
            $aToolbar[] = ['items' => ['placeholder_select']];
510
        }
511
    }
512
    
513
    /**
514
     * Add button to switch in the source mode 
515
     * @param array $aToolbar reference to the toolbar array
516
     */
517
    protected function addSourceBtn(array &$aToolbar) : void
518
    {
519
        if (($this->lToolbar & self::TB_SOURCE) != 0) {
520
            $aToolbar[] = ['name' => 'document', 'items' => ['Source']];
521
        }
522
    }
523
}
524