loadEditorPlugins()   B
last analyzed

Complexity

Conditions 9
Paths 192

Size

Total Lines 58
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 11.7194

Importance

Changes 0
Metric Value
cc 9
eloc 28
c 0
b 0
f 0
nc 192
nop 1
dl 0
loc 58
rs 7.2888
ccs 21
cts 31
cp 0.6774
crap 11.7194

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file contains functions specific to the editing box and is
5
 * generally used for WYSIWYG type functionality.
6
 *
7
 * @package   ElkArte Forum
8
 * @copyright ElkArte Forum contributors
9
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
10
 *
11
 * This file contains code covered by:
12
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
13
 *
14
 * @version 2.0 dev
15
 *
16
 */
17
18
use ElkArte\Cache\Cache;
19
use ElkArte\Languages\Txt;
20
21
/**
22
 * Creates a box that can be used for richedit stuff like BBC, Smileys etc.
23
 *
24
 * @param array $editorOptions associative array of options => value
25
 *  Must contain:
26
 *   - id => unique id for the css
27
 *   - value => text for the editor or blank
28
 *   - smiley_container => ID for where the smileys will be placed
29
 *   - bbc_container => ID for where the toolbar will be placed
30
 * Optionally:
31
 *   - height => height of the initial box
32
 *   - width => width of the box (100%)
33
 *   - force_rich => force wysiwyg to be enabled
34
 *   - disable_smiley_box => boolean to turn off the smiley box
35
 *   - labels => array(
36
 *       - 'post_button' => $txt['for post button'],
37
 *     ),
38
 *   - preview_type => 2 how to act on preview click, see template_control_richedit_buttons
39
 *
40
 * @event integrate_editor_plugins
41
 * @uses GenericControls template
42
 * @uses Post language
43
 */
44
function create_control_richedit($editorOptions)
45
{
46
	global $txt, $options, $context, $settings, $scripturl;
47
48
	// Load the Post language file... for the moment at least.
49
	Txt::load('Post');
50
51
	// Every control must have a ID!
52
	assert(isset($editorOptions['id']));
53
	assert(isset($editorOptions['value']));
54
55
	// Is this the first richedit - if so we need to ensure things are initialised and that we load all needed files
56
	if (empty($context['controls']['richedit']))
57
	{
58
		// Store the name / ID we are creating for template compatibility.
59
		$context['post_box_name'] = $editorOptions['id'];
60
		$context['smiley_box_name'] = $editorOptions['smiley_container'] ?? null;
61
		$context['bbc_box_name'] = $editorOptions['bbc_container'] ?? null;
62
63
		// Don't show the smileys if they are off or not wanted.
64
		$editorOptions['disable_smiley_box'] = !empty($editorOptions['disable_smiley_box'])
65
			|| $GLOBALS['context']['smiley_set'] === 'none'
66
			|| !empty($GLOBALS['options']['show_no_smileys'])
67
			|| empty($editorOptions['smiley_container']);
68
69
		// This really has some WYSIWYG stuff.
70
		theme()->getTemplates()->load('GenericControls');
71
		loadCSSFile('jquery.sceditor.css');
72
		if (!empty($context['theme_variant']) && file_exists($settings['theme_dir'] . '/css/' . $context['theme_variant'] . '/jquery.sceditor.elk' . $context['theme_variant'] . '.css'))
73
		{
74
			loadCSSFile($context['theme_variant'] . '/jquery.sceditor.elk' . $context['theme_variant'] . '.css');
75
		}
76
77
		// JS makes the editor go round
78
		loadJavascriptFile([
79
			'editor/jquery.sceditor.bbcode.min.js',
80
			'editor/jquery.sceditor.elkarte.js',
81
			'post.js',
82
			'editor/dropAttachments.js',
83
			'editor/ilaAttachments.js',
84
			'editor/chunkUpload.js'
85
		]);
86
87
		theme()->addJavascriptVar([
88
			'post_box_name' => $editorOptions['id'],
89
			'elk_smileys_url' => $context['smiley_path'],
90
			'elk_emoji_url' => $context['emoji_path'],
91
			'bbc_quote_from' => $txt['quote_from'],
92
			'bbc_quote' => $txt['quote'],
93
			'bbc_search_on' => $txt['search_on'],
94
			'ila_filename' => $txt['file'] . ' ' . $txt['name']], true
95
		);
96
97
		// Editor language file
98
		if (!empty($txt['lang_locale']))
99
		{
100
			loadJavascriptFile($scripturl . '?action=jslocale;sa=sceditor', ['defer' => true], 'sceditor_language');
101
		}
102
103
		// Our not so concise shortcut line
104
		$context['shortcuts_text'] = $context['shortcuts_text'] ?? $txt['shortcuts'];
105
	}
106
107
	// Start off the editor...
108
	$context['controls']['richedit'][$editorOptions['id']] = [
109
		'id' => $editorOptions['id'],
110
		'value' => $editorOptions['value'],
111
		'rich_active' => !empty($options['wysiwyg_default']) || !empty($editorOptions['force_rich']) || !empty($_REQUEST[$editorOptions['id'] . '_mode']),
112
		'disable_smiley_box' => $editorOptions['disable_smiley_box'],
113
		'width' => $editorOptions['width'] ?? '100%',
114
		'height' => $editorOptions['height'] ?? '250px',
115
		'form' => $editorOptions['form'] ?? 'postmodify',
116
		'preview_type' => isset($editorOptions['preview_type']) ? (int) $editorOptions['preview_type'] : 1,
117
		'labels' => !empty($editorOptions['labels']) ? $editorOptions['labels'] : [],
118
		'locale' => !empty($txt['lang_locale']) ? $txt['lang_locale'] : 'en_US',
119
		'plugin_addons' => !empty($editorOptions['plugin_addons']) ? $editorOptions['plugin_addons'] : [],
120 2
		'plugin_options' => !empty($editorOptions['plugin_options']) ? $editorOptions['plugin_options'] : [],
121 2
		'buttons' => !empty($editorOptions['buttons']) ? $editorOptions['buttons'] : [],
122
		'hidden_fields' => !empty($editorOptions['hidden_fields']) ? $editorOptions['hidden_fields'] : [],
123 2
	];
124
125
	// Allow addons an easy way to add plugins, initialization objects, etc to the editor control
126 2
	call_integration_hook('integrate_editor_plugins', [$editorOptions['id']]);
127
128
	// Switch between default images and back... mostly in case you don't have a PersonalMessage template, but do have a Post template.
129 2
	$use_defaults = isset($settings['use_default_images'], $settings['default_template']) && $settings['use_default_images'] === 'defaults';
130 2
	if ($use_defaults)
131
	{
132
		$temp = [];
133 2
		$temp[] = $settings['theme_url'];
134
		$temp[] = $settings['images_url'];
135
		$temp[] = $settings['theme_dir'];
136 2
137
		$settings['theme_url'] = $settings['default_theme_url'];
138
		$settings['images_url'] = $settings['default_images_url'];
139 2
		$settings['theme_dir'] = $settings['default_theme_dir'];
140
	}
141
142 2
	// Setup the toolbar, smileys, plugins
143 2
	$context['bbc_toolbar'] = loadEditorToolbar();
144 2
	$context['editor_bbc_toolbar'] = buildBBCToolbar($context['bbc_box_name']);
145
	$context['smileys'] = empty($editorOptions['disable_smiley_box']) ? loadEditorSmileys($context['controls']['richedit'][$editorOptions['id']]) : '';
146
	$context['editor_smileys_toolbar'] = buildSmileyToolbar(empty($editorOptions['disable_smiley_box']));
147
	$context['plugins'] = loadEditorPlugins($context['controls']['richedit'][$editorOptions['id']]);
148
	$context['plugin_options'] = getPluginOptions($context['controls']['richedit'][$editorOptions['id']], $editorOptions['id']);
149
150 2
	// Switch the URLs back... now we're back to whatever the main sub template is (like folder in PersonalMessage.)
151 2
	if ($use_defaults)
152 2
	{
153 2
		list($settings['theme_url'], $settings['images_url'], $settings['theme_dir']) = $temp;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $temp does not seem to be defined for all execution paths leading up to this point.
Loading history...
154 2
	}
155 2
156 2
	// Provide some dynamic error checking (no subject, no body, no service!)
157 2
	if (!empty($editorOptions['live_errors']))
158
	{
159
		Txt::load('Errors');
160 2
		theme()->addInlineJavascript('
161
		
162 2
	error_txts[\'no_subject\'] = ' . JavaScriptEscape($txt['error_no_subject']) . ';
163
	error_txts[\'no_message\'] = ' . JavaScriptEscape($txt['error_no_message']) . ';
164
		', true);
165
	}
166 2
}
167
168
/**
169
 *  Defines what editor plugins to load
170
 *
171
 *  What it does:
172 2
 *  - loads the JS needed by core plugins.
173
 *  - loads the CSS needed by core plugins
174 2
 *  - Will merge in plugins added by addons.
175
 *  - The editor will load and init the plugins
176
 *
177
 * @param array $editor_context The editor context containing the plugin addons.
178 2
 * @return array The list of loaded plugins.
179 2
 */
180
function loadEditorPlugins($editor_context)
181
{
182
	global $modSettings;
183
184
	$plugins = [];
185
	$neededJS = [];
186
	$neededCSS = [];
187
188
	$plugins[] = 'initialLoad';
189
	$neededJS[] = 'editor/initialLoad.plugin.js';
190
191
	if (!empty($modSettings['enableSplitTag']))
192
	{
193
		$plugins[] = 'splittag';
194
		$neededJS[] = 'editor/splittag.plugin.js';
195 2
	}
196 2
197 2
	if (!empty($modSettings['enableUndoRedo']))
198 2
	{
199 2
		$plugins[] = 'undo';
200 2
		$neededJS[] = 'editor/undo.plugin.min.js';
201 2
	}
202 2
203 2
	if (!empty($modSettings['mentions_enabled']))
204 2
	{
205 2
		$plugins[] = 'mention';
206 2
		$neededJS = array_merge($neededJS, ['editor/jquery.atwho.min.js', 'editor/jquery.caret.min.js', 'editor/mentioning.plugin.js']);
207 2
	}
208 2
209 2
	if (!empty($modSettings['enableGiphy']))
210 2
	{
211 2
		$plugins[] = 'giphy';
212 2
		$neededJS[] = 'editor/giphy.plugin.js';
213
		$neededCSS[] = 'sceditor.giphy.css';
214
	}
215
216 2
	if (!empty($neededJS))
217
	{
218
		loadJavascriptFile($neededJS, ['defer' => true]);
219 2
	}
220
221
	if (!empty($neededCSS))
222
	{
223
		loadCSSFile($neededCSS);
224
	}
225
226
	// Merge with other plugins added by core features or addons which have loaded JS/CSS as needed
227
	if (!empty($editor_context['plugin_addons']))
228
	{
229
		if (!is_array($editor_context['plugin_addons']))
230
		{
231 2
			$editor_context['plugin_addons'] = [$editor_context['plugin_addons']];
232
		}
233
234
		$plugins = array_filter(array_merge($plugins, $editor_context['plugin_addons']));
235
	}
236
237
	return $plugins;
238 2
}
239
240
/**
241
 * Loads any built in plugin options and merges in any addon defined ones.
242
 *
243 2
 * @param array $editor_context
244
 * @param string $editor_id
245
 * @return array
246
 */
247
function getPluginOptions($editor_context, $editor_id)
248
{
249
	global $modSettings;
250
251
	$plugin_options = [];
252 2
253
	if (!empty($modSettings['mentions_enabled']))
254
	{
255 2
		$plugin_options[] = '
256
			mentionOptions: {
257
				editor_id: \'' . $editor_id . '\',
258 2
				cache: {
259 2
					mentions: [],
260
					queries: [],
261
					names: []
262
				}
263
			}';
264
	}
265 2
266
	// Allow addons to insert additional editor objects
267
	if (!empty($editor_context['plugin_options']) && is_array($editor_context['plugin_options']))
268 2
	{
269
		$plugin_options = array_merge($plugin_options, $editor_context['plugin_options']);
270
	}
271
272
	return $plugin_options;
273
}
274
275
/**
276
 * Loads the editor toolbar with just the enabled commands
277
 *
278
 * Each row will be a comma seperated list of commands that the editor knows (or should)
279
 * bold,italic,quote,.....
280
 *
281
 * @return array
282
 */
283
function loadEditorToolbar()
284
{
285
	$bbc_tags = loadToolbarDefaults();
286 2
	$disabledToolbar = getDisabledBBC();
287 2
288
	// Build our toolbar, taking into account any bbc codes from integration
289 2
	$bbcToolbar = [];
290
	foreach ($bbc_tags as $row => $tagRow)
291 2
	{
292
		$bbcToolbar[$row] = $bbcToolbar[$row] ?? [];
293
		$tagsRow = [];
294 2
295
		// For each row of buttons defined, lets build our tags
296
		foreach ($tagRow as $tags)
297 2
		{
298
			foreach ($tags as $tag)
299 2
			{
300
				// Just add this code in the existing grouping
301
				if (!isset($disabledToolbar[$tag]))
302 2
				{
303
					$tagsRow[] = $tag;
304 2
				}
305
			}
306
307
			// If the row is not empty, and the last added tag is not a space, add a space.
308
			if (!empty($tagsRow) && $tagsRow[count($tagsRow) - 1] !== 'space')
309 2
			{
310
				$tagsRow[] = 'space';
311 2
			}
312
		}
313
314
		// Build that beautiful button row
315
		if (!empty($tagsRow))
316 2
		{
317
			$bbcToolbar[$row][] = implode(',', $tagsRow);
318 2
		}
319
	}
320
321
	return $bbcToolbar;
322
}
323
324 2
/**
325
 * Loads disabled BBC tags from the DB as defined in the ACP.  It
326 2
 * will then translate BBC to editor commands (b => bold) such that
327
 * disabled BBC will also disable the associated editor command button.
328
 *
329
 * @return array
330
 */
331
function getDisabledBBC()
332 2
{
333
	global $modSettings;
334
335
	// Generate a list of buttons that shouldn't be shown
336
	$disabled_tags = [];
337
	$disabledToolbar = [];
338
339
	if (!empty($modSettings['disabledBBC']))
340
	{
341
		$disabled_tags = explode(',', $modSettings['disabledBBC']);
342
	}
343
344
	// Map bbc codes to editor toolbar tags
345
	$translate_tags_to_code = ['b' => 'bold', 'i' => 'italic', 'u' => 'underline', 's' => 'strike',
346
							   'img' => 'image', 'url' => 'link', 'sup' => 'superscript', 'sub' => 'subscript', 'hr' => 'horizontalrule'];
347
348
	// Remove the toolbar buttons for any bbc tags that have been turned off in the ACP
349
	foreach ($disabled_tags as $tag)
350
	{
351
		// list is special, its prevents two tags
352
		if ($tag === 'list')
353
		{
354
			$disabledToolbar['bulletlist'] = true;
355
			$disabledToolbar['orderedlist'] = true;
356
		}
357
		elseif (isset($translate_tags_to_code[$tag]))
358
		{
359
			$disabledToolbar[$translate_tags_to_code[$tag]] = true;
360
		}
361
362
		// Tag is the same as the code, like font, color, size etc
363
		$disabledToolbar[trim($tag)] = true;
364
	}
365
366
	return $disabledToolbar;
367
}
368
369
/**
370
 * Loads the toolbar default buttons which defines what editor buttons might show
371
 *
372
 * @event integrate_bbc_buttons
373
 * @return array|mixed
374
 */
375
function loadToolbarDefaults()
376
{
377
	$bbc_tags = [];
378
379
	// The below array is used to show a command button in the editor, the execution
380
	// and display details of any added buttons must be defined in the javascript files
381
	// see jquery.sceditor.elkarte.js under the sceditor.formats.bbcode area
382
	// for examples of how to use the .set command to add codes.  Include your new
383
	// JS with addInlineJavascript() or loadJavascriptFile() in your addon
384
	$bbc_tags['row1'] = [
385
		['bold', 'italic', 'underline', 'strike'],
386
		['left', 'center', 'right', 'pre'],
387
		['image', 'link', 'giphy'],
388
		['bulletlist', 'orderedlist'],
389
		['source', 'expand'],
390
	];
391
392
	$bbc_tags['row2'] = [
393
		['tt', 'superscript', 'subscript'],
394
		['spoiler', 'footnote', 'removeformat'],
395
		['quote', 'code', 'table', 'horizontalrule', 'email'],
396
		['font', 'size', 'color'],
397
		['splittag', 'undo', 'redo'],
398
	];
399
400
	// Allow mods to add BBC buttons to the toolbar, actions are defined in the JS
401
	call_integration_hook('integrate_bbc_buttons', [&$bbc_tags]);
402
403
	return $bbc_tags;
404
}
405
406
/**
407
 * Loads the smileys for use in the required locations (postform or popup)
408
 *
409
 * What it does:
410
 * - Will load the default smileys or custom smileys as defined in ACP
411
 * - Caches the DB call for 10 mins
412
 * - Sorts smileys to proper array positions removing hidden ones
413
 *
414
 * @return array|array[]|mixed
415
 */
416
function loadEditorSmileys($editorOptions)
417
{
418
	global $context, $modSettings;
419
420
	$smileys = [
421
		'postform' => [],
422
		'popup' => [],
423
	];
424
425
	// Initialize smiley array... if not loaded before.
426
	if (empty($context['smileys']) && empty($editorOptions['disable_smiley_box']))
427
	{
428
		$temp = [];
429
		if (!Cache::instance()->getVar($temp, 'posting_smileys', 600))
430
		{
431 2
			require_once(SUBSDIR . '/Smileys.subs.php');
432
			$smileys = getEditorSmileys();
433
			foreach ($smileys as $section => $smileyRows)
434
			{
435
				$last_row = null;
436
				foreach ($smileyRows as $rowIndex => $smileRow)
437
				{
438
					$smileys[$section][$rowIndex]['smileys'][count($smileRow['smileys']) - 1]['isLast'] = true;
439
					$last_row = $rowIndex;
440
				}
441
442
				if ($last_row !== null)
443
				{
444
					$smileys[$section][$last_row]['isLast'] = true;
445
				}
446
			}
447
448
			Cache::instance()->put('posting_smileys', $smileys, 600);
449
		}
450
		else
451
		{
452
			$smileys = $temp;
453
		}
454
455
		// The smiley popup may take advantage of Jquery UI ....
456
		if (!empty($smileys['popup']))
457
		{
458
			$modSettings['jquery_include_ui'] = true;
459
		}
460
461
		return $smileys;
462
	}
463
464
	return empty($context['smileys']) ? $smileys : $context['smileys'];
465
}
466
467
function buildSmileyToolbar($useSmileys)
468
{
469
	global $context;
470
471
	if (!$useSmileys)
472
	{
473
		return ', emoticons: {}';
474
	}
475
476
	$emoticons = ',
477
		emoticons: {';
478
479
	$countLocations = count($context['smileys']);
480
	foreach ($context['smileys'] as $location => $smileyRows)
481
	{
482
		$countLocations--;
483
		if ($location === 'postform')
484
		{
485 2
			$emoticons .= '
486
				dropdown: {';
487
		}
488
489
		if ($location === 'popup')
490
		{
491
			$emoticons .= '
492 2
				popup: {';
493
		}
494
495
		$numRows = count($smileyRows);
496
497
		// This is needed because otherwise the editor will remove all the duplicate (empty)
498
		// keys and leave only 1 additional line
499
		$emptyPlaceholder = 0;
500
		foreach ($smileyRows as $smileyRow)
501
		{
502
			foreach ($smileyRow['smileys'] as $smiley)
503
			{
504
				$emoticons .= '
505
					' . JavaScriptEscape($smiley['code']) . ': {url: ' . (JavaScriptEscape((isset($smiley['emoji']) ? $context['emoji_path'] : $context['smiley_path']) . $smiley['filename'])) . ', tooltip: ' . JavaScriptEscape($smiley['description']) . '}' . (empty($smiley['isLast']) ? ',' : '');
506
			}
507
508
			if (empty($smileyRow['isLast']) && $numRows !== 1)
509
			{
510
				$emoticons .= ",'-" . $emptyPlaceholder++ . "': '',";
511
			}
512
		}
513
514
		$emoticons .= '
515
			}' . ($countLocations !== 0 ? ',' : '');
516
	}
517
518
	return $emoticons . '
519
	}';
520
}
521
522
/**
523
 *  Builds the BBC toolbar configuration for the editor.
524
 *
525
 *  What it does:
526
 *  - If the $bbcContainer parameter is null, it returns the configuration for the source mode toolbar.
527
 *  - Otherwise, it builds the toolbar configuration based on the buttons in the $context['bbc_toolbar'] array.
528
 *
529
 * @param mixed|null $bbcContainer The container of the editor. Null for source mode, otherwise the container element.
530
 * @return string The configuration for the BBC toolbar.
531
 */
532
function buildBBCToolbar($bbcContainer)
533
{
534 2
	global $context;
535
536
	if ($bbcContainer === null)
537
	{
538
		return ', 
539
			toolbar: "source"';
540
	}
541
542
	// Show all the editor command buttons
543
	$toolbar = ',
544
		toolbar: "';
545
546
	// Create the tooltag rows to display the buttons in the editor
547
	foreach ($context['bbc_toolbar'] as $buttonRow)
548
	{
549
		$toolbar .= $buttonRow[0] . '|';
550
	}
551
552
	$toolbar .= '"';
553
554
	return $toolbar;
555
}
556