Passed
Push — main ( fac3c3...502827 )
by Stefan
02:24
created

FormCKEdit   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 485
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 59
eloc 185
dl 0
loc 485
rs 4.08
c 2
b 0
f 0

26 Methods

Rating   Name   Duplication   Size   Complexity  
A addCustomButton() 0 10 2
A setAllowedContent() 0 3 1
A setContentsCss() 0 3 1
A setBodyID() 0 3 1
A addTemplateSelect() 0 4 2
A addSourceBtn() 0 4 2
A setBrowseFolderImageURL() 0 3 1
A getStyle() 0 27 5
A __construct() 0 7 1
A onParentSet() 0 33 5
A buildToolbarDef() 0 15 1
A getToolbar() 0 3 1
A buildSelectableColors() 0 16 5
A addColorBtns() 0 4 2
A addParagraphBtns() 0 15 2
A addPlaceholderSelect() 0 4 3
A setToolbar() 0 3 1
A addCustomBtns() 0 4 2
A buildEditorOptions() 0 21 3
B addInsertBtns() 0 20 7
A setBrowseFolderLinkURL() 0 3 1
A addStyleSelect() 0 4 2
A addBasicStyleBtns() 0 13 2
A setBrowseFolderImageLinkURL() 0 3 1
A addLinkBtns() 0 6 2
A buildPlaceholderSelect() 0 5 3

How to fix   Complexity   

Complex Class

Complex classes like FormCKEdit often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FormCKEdit, and based on these observations, apply Extract Interface, too.

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