Passed
Push — main ( e33397...22db30 )
by Stefan
02:01 queued 28s
created

FormCKEdit::addSourceBtn()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 4
rs 10
1
<?php
2
namespace SKien\Formgenerator;
3
4
/**
5
 * WYSIWYG - HTML input using CKEditor.
6
 * currently used/supportet CKEditor Version is 4.6.2
7
 * 
8
 * TODO: check, if it make sense to migrate to CKeditor 5 or 'only' update to newest 4.X 
9
 * @link https://ckeditor.com/docs/ckeditor5/latest/builds/guides/migrate.html 
10
 *
11
 * #### History
12
 * - *2020-05-12*   initial version
13
 * - *2021-01-07*   PHP 7.4
14
 * - *2021-01-09*   added support of icons for custom buttons and table, special char and iframe - Button
15
 *
16
 * @package Formgenerator
17
 * @version 1.1.0
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
    /** Table funtions insert and delete rows   */
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 string the content for the editor as JSON   */
71
    protected ?string $strJsonData = null;
72
    /** @var array custom button definition ["func" => <buttonhandler>, "name" => <buttonname>]    */
73
    protected array $aCustomBtn = [];
74
    /** @var string allowed content    */
75
    protected string $strAllowedContent = '';
76
    /** @var int toolbar mask    */
77
    protected int $lToolbar;
78
    
79
    /**
80
     * Creates a WYSIWYG editor.
81
     * @param string $strName
82
     * @param string $strValue
83
     * @param int $iRows
84
     * @param string $strWidth
85
     * @param int $wFlags
86
     */
87
    public function __construct(string $strName, string $strValue, int $iRows, string $strWidth = '95%', int $wFlags = 0) 
88
    {
89
        // TODO: explain differences when using FormInput::SET_JSON_DATA
90
        if (($wFlags & FormInput::SET_JSON_DATA) != 0) {
91
            $this->strJsonData = $strValue;
92
            $strValue = '';
93
        }
94
        // add 2 rows to increase height for toolbar
95
        parent::__construct($strName, $strValue, 0, $iRows + 2, $strWidth, $wFlags);
96
        $this->bCreateScript = true;
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
     * Build the JS script to Load the editor:
156
     * - contentCSS
157
     * - bodyID
158
     * - Toolbar
159
     * - Custom Buttons
160
     */
161
    public function getScript() : string 
162
    {
163
        // define a global instance of the editor
164
        $strScript = "var editor = null;" . PHP_EOL;
165
        
166
        // this function must be called in the html.body.onload() event!
167
        $strScript .= 
168
            "function LoadEditor()" . PHP_EOL .
169
            "{" . PHP_EOL .
170
            "    // get initial size of textarea to replace" . PHP_EOL .
171
            "    var oTA = document.getElementById('" . $this->strName . "');" . PHP_EOL .
172
            "    var iHeight = 80;" . PHP_EOL .
173
            "    if (oTA) {" . PHP_EOL .
174
            "        iHeight = oTA.offsetHeight;" . PHP_EOL .
175
            "        iWidth = oTA.offsetWidth;" . PHP_EOL .
176
            "    }" . PHP_EOL;
177
        
178
        $strScript .= $this->buildEditorCreateScript();
179
        $strScript .= $this->buildCustomBtnScript();
180
        
181
        // resize to desired size
182
        $strScript .= "    CKEDITOR.on('instanceReady', function(event) {" . PHP_EOL;
183
        $strScript .= "            editor.resize(iWidth, iHeight);" . PHP_EOL;
184
        $strScript .= "        });" . PHP_EOL;
185
        
186
        // if data to edit provided in JSON format, set it
187
        if (($this->wFlags & self::SET_JSON_DATA) != 0 && $this->strJsonData != '') {
188
            $strScript .= PHP_EOL;
189
            $strScript .= '    editor.setData(' . json_encode($this->strJsonData, JSON_PRETTY_PRINT) . ');' . PHP_EOL;
190
        }       
191
        $strScript .= "}" . PHP_EOL;
192
        
193
        return $strScript;
194
    }
195
    
196
    /**
197
     * Build CKEditor specific styles.
198
     * @return string
199
     */
200
    public function getStyle() : string 
201
    {
202
        // If custom toolbar buttons defined, for each button dependent on the his
203
        // type (TEXT, ICON, TEXT+ICON) some styles have to be set.
204
        $strStyle = '';
205
        foreach ($this->aCustomBtn as $aBtn) {
206
            $strBtn = strtolower($aBtn['func']);
207
            $strDisplayLabel = (($aBtn['type'] & self::BTN_TEXT) != 0) ? 'inline' : 'none';
208
            $strDisplayIcon = (($aBtn['type'] & self::BTN_ICON) != 0) ? 'inline' : 'none';
209
            $strStyle .= '.cke_button__' . $strBtn . '_icon { display: ' . $strDisplayIcon . ' !important; }' . PHP_EOL;
210
            $strStyle .= '.cke_button__' . $strBtn . '_label { display: ' . $strDisplayLabel . ' !important; }' . PHP_EOL;
211
        }
212
        
213
        return $strStyle;
214
    }
215
    
216
    /**
217
     * Define toolbar to display.
218
     * @param int $lToolbar
219
     */
220
    public function setToolbar(int $lToolbar) : void
221
    {
222
        $this->lToolbar = $lToolbar;
223
    }
224
    
225
    /**
226
     * Returns currently defined toolbar.
227
     * @return int
228
     */
229
    public function getToolbar() : int
230
    {
231
        return $this->lToolbar;
232
    }
233
    
234
    /**
235
     * Build the script that creates the CKEditor instance.
236
     * @return string
237
     */
238
    protected function buildEditorCreateScript() : string
239
    {
240
        if (strlen($this->strContentsCss) == 0) {
241
            trigger_error('No CSS Stylesheet set!', E_USER_WARNING);
242
        }
243
        $aCKEditor = [
244
            'contentsCss' => $this->strContentsCss,
245
            'bodyId' => $this->strBodyID,
246
            'removePlugins' => 'elementspath',
247
            'toolbar' => $this->buildToolbarDef(),
248
            'toolbarCanCollapse' => false,
249
            'pasteFilter' => 'plain-text',
250
            'allowedContent' => ($this->strAllowedContent ?: true),
251
            'resize_enabled' => false,
252
        ];
253
        $strScript = '    editor = CKEDITOR.replace("' . $this->strName . '", ' . json_encode($aCKEditor) . ');';
254
        
255
        return $strScript;
256
    }
257
    
258
    /**
259
     * Build the script tp define all custom buttons.
260
     * @return string
261
     */
262
    protected function buildCustomBtnScript() : string
263
    {
264
        $strScript = '';
265
        // commands for custom buttons
266
        if (is_array($this->aCustomBtn) && count($this->aCustomBtn) > 0) {
267
            reset($this->aCustomBtn);
268
            foreach ($this->aCustomBtn as $aBtn) {
269
                $strScript .= $this->buildBtnScript($aBtn);
270
            }
271
        }
272
        return $strScript;
273
    }
274
    
275
    /**
276
     * Build the script to define a custom Button for the CFKEditor Toolbar.
277
     * @param array $aBtn
278
     * @return string
279
     */
280
    protected function buildBtnScript(array $aBtn) : string
281
    {
282
        $strCommand = 'cmd_' . $aBtn['func'];
283
        $aCommand = [
284
            'exec' => 'function(editor){' . $aBtn['func'] . '(editor);}',
285
        ];
286
        $aButton = [
287
            'label' => $aBtn['name'],
288
            'command' => $strCommand,
289
            'icon' => $aBtn['icon'],
290
        ];
291
        
292
        $strScript  = "    editor.addCommand('" . $strCommand . "', " . json_encode($aCommand) . ");" . PHP_EOL;
293
        $strScript .= "    editor.ui.addButton('" . $aBtn['func'] . "', " . json_encode($aButton) . ");" . PHP_EOL;
294
        
295
        return $strScript;
296
    }
297
    
298
    /**
299
     * Returns currently defined toolbar as array for JSON-encoding.
300
     * @return array
301
     */
302
    protected function buildToolbarDef() : array
303
    {
304
        $aToolbar = [];
305
        $this->addCustomBtns($aToolbar);
306
        $this->addBasicStyleBtns($aToolbar);
307
        $this->addParagraphBtns($aToolbar);
308
        $this->addLinkBtns($aToolbar);
309
        $this->addInsertBtns($aToolbar);
310
        $this->addColorBtns($aToolbar);
311
        $this->addStyleSelect($aToolbar);
312
        $this->addTemplateSelect($aToolbar);
313
        $this->addPlaceholderSelect($aToolbar);
314
        $this->addSourceBtn($aToolbar);
315
        
316
        return $aToolbar;
317
    }
318
    
319
    /**
320
     * Add all custom buttons at start of the toolbar.
321
     * @param array $aToolbar reference to the toolbar array
322
     */
323
    protected function addCustomBtns(array &$aToolbar) : void
324
    {
325
        foreach ($this->aCustomBtn as $aBtn) {
326
            $aToolbar[] = ['items' => [$aBtn['func']]];
327
        }
328
    }
329
    
330
    /**
331
     * Add button group for basic styles. 
332
     * @param array $aToolbar reference to the toolbar array
333
     */
334
    protected function addBasicStyleBtns(array &$aToolbar) : void
335
    {
336
        if (($this->lToolbar & self::TB_BASIC_STYLES) != 0) {
337
            $aToolbar[] = [
338
                'name' => 'basicstyles', 
339
                'items' => [
340
                    'Bold',
341
                    'Italic',
342
                    'Underline',
343
                    'Subscript',
344
                    'Superscript',
345
                    '-',
346
                    'RemoveFormat',
347
                ]
348
            ];
349
        }
350
    }
351
    
352
    /**
353
     * Add button group for paragraph formating. 
354
     * @param array $aToolbar reference to the toolbar array
355
     */
356
    protected function addParagraphBtns(array &$aToolbar) : void
357
    {
358
        if (($this->lToolbar & self::TB_PARAGRAPH) != 0) {
359
            $aToolbar[] = [
360
                'name' => 'paragraph',
361
                'items' => [
362
                    'NumberedList',
363
                    'BulletedList',
364
                    '-',
365
                    'Outdent', 
366
                    'Indent',
367
                    '-',
368
                    'JustifyLeft',
369
                    'JustifyCenter',
370
                    'JustifyRight',
371
                ]
372
            ];
373
        }
374
    }
375
    
376
    /**
377
     * Add button group for links. 
378
     * @param array $aToolbar reference to the toolbar array
379
     */
380
    protected function addLinkBtns(array &$aToolbar) : void
381
    {
382
        if (($this->lToolbar & self::TB_LINKS) != 0) {
383
            $aToolbar[] = [
384
                'name' => 'links',
385
                'items' => ['Link', 'Unlink']
386
            ];
387
        }
388
    }
389
    
390
    /**
391
     * Add button group to insert objects.
392
     * - Images
393
     * - Snippets
394
     * - Tables
395
     * - Special Chars
396
     * - IFrames 
397
     * @param array $aToolbar reference to the toolbar array
398
     */
399
    protected function addInsertBtns(array &$aToolbar) : void
400
    {
401
        if (($this->lToolbar & self::TB_INSERT) != 0) {
402
            $aInsert = array();
403
            if (($this->lToolbar & self::TB_IMAGE) != 0) {
404
                $aInsert[] = 'Image';
405
            }
406
            if (($this->lToolbar & self::TB_SNIPPET) != 0) {
407
                $aInsert[] = 'CodeSnippet';
408
            }
409
            if (($this->lToolbar & self::TB_TABLE) != 0) {
410
                $aInsert[] = 'Table';
411
            }
412
            if (($this->lToolbar & self::TB_SPECIAL_CHAR) != 0) {
413
                $aInsert[] = 'SpecialChar';
414
            }
415
            if (($this->lToolbar & self::TB_IFRAME) != 0) {
416
                $aInsert[] = 'Iframe';
417
            }
418
            $aToolbar[] = ['name' => 'insert', 'items' => $aInsert];
419
        }
420
    }
421
    
422
    /**
423
     * Add button group for colors 
424
     * @param array $aToolbar reference to the toolbar array
425
     */
426
    protected function addColorBtns(array &$aToolbar) : void
427
    {
428
        if (($this->lToolbar & self::TB_COLOR) != 0) {
429
            $aToolbar[] = ['name' => 'color', 'items' => ['TextColor', 'BGColor']];
430
        }
431
    }
432
    
433
    /**
434
     * Add select list for styles 
435
     * @param array $aToolbar reference to the toolbar array
436
     */
437
    protected function addStyleSelect(array &$aToolbar) : void
438
    {
439
        if (($this->lToolbar & self::TB_STYLES_SELECT) != 0) {
440
            $aToolbar[] = ['items' => ['Styles']];
441
        }
442
    }
443
    
444
    /**
445
     * Add select list for templates 
446
     * @param array $aToolbar reference to the toolbar array
447
     */
448
    protected function addTemplateSelect(array &$aToolbar) : void
449
    {
450
        if (($this->lToolbar & self::TB_TEMPLATES) != 0) {
451
            $aToolbar[] = ['items' => ['Templates']];
452
        }
453
    }
454
    
455
    /**
456
     * Add select list for placeholders 
457
     * @param array $aToolbar reference to the toolbar array
458
     */
459
    protected function addPlaceholderSelect(array &$aToolbar) : void
460
    {
461
        if (($this->lToolbar & self::TB_PLACEHOLDERS) != 0) {
462
            $aToolbar[] = ['items' => ['placeholder_select']];
463
        }
464
    }
465
    
466
    /**
467
     * Add button to switch in the source mode 
468
     * @param array $aToolbar reference to the toolbar array
469
     */
470
    protected function addSourceBtn(array &$aToolbar) : void
471
    {
472
        if (($this->lToolbar & self::TB_SOURCE) != 0) {
473
            $aToolbar[] = ['name' => 'document', 'items' => ['Source']];
474
        }
475
    }
476
}
477