Completed
Branch development (ad897a)
by Elk
06:53 queued 29s
created

loadEditorSmileys()   C

Complexity

Conditions 13
Paths 9

Size

Total Lines 75
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 137.5198

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 33
dl 0
loc 75
rs 6.6166
c 1
b 0
f 0
nc 9
nop 1
ccs 3
cts 31
cp 0.0968
crap 137.5198

How to fix   Long Method    Complexity   

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\User;
20
21
/**
22
 * Creates a box that can be used for richedit stuff like BBC, Smileys etc.
23
 *
24
 * @param mixed[] $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
 * Optionally:
29
 *   - height => height of the initial box
30
 *   - width => width of the box (100%)
31
 *   - force_rich => force wysiwyg to be enabled
32
 *   - disable_smiley_box => boolean to turn off the smiley box
33
 *   - labels => array(
34
 *       - 'post_button' => $txt['for post button'],
35
 *     ),
36
 *   - preview_type => 2 how to act on preview click, see template_control_richedit_buttons
37
 *
38
 * @event integrate_editor_plugins
39
 * @throws \ElkArte\Exceptions\Exception
40
 * @uses GenericControls template
41
 * @uses Post language
42
 */
43
function create_control_richedit($editorOptions)
44
{
45
	global $txt, $modSettings, $options, $context, $settings, $scripturl;
46
47
	// Load the Post language file... for the moment at least.
48
	theme()->getTemplates()->loadLanguageFile('Post');
49
50
	// Every control must have a ID!
51
	assert(isset($editorOptions['id']));
52
	assert(isset($editorOptions['value']));
53
54
	// Is this the first richedit - if so we need to ensure things are initialised and that we load all of the needed files
55
	if (empty($context['controls']['richedit']))
56
	{
57
		// Store the name / ID we are creating for template compatibility.
58
		$context['post_box_name'] = $editorOptions['id'];
59
60
		// Some general stuff.
61
		$settings['smileys_url'] = $context['user']['smiley_path'];
62
63
		// This really has some WYSIWYG stuff.
64
		theme()->getTemplates()->load('GenericControls');
65
		loadCSSFile('jquery.sceditor.css');
66
		if (!empty($context['theme_variant']) && file_exists($settings['theme_dir'] . '/css/' . $context['theme_variant'] . '/jquery.sceditor.elk' . $context['theme_variant'] . '.css'))
67
		{
68
			loadCSSFile($context['theme_variant'] . '/jquery.sceditor.elk' . $context['theme_variant'] . '.css');
69
		}
70
71
		// JS makes the editor go round
72
		loadJavascriptFile(array('jquery.sceditor.bbcode.min.js', 'jquery.sceditor.elkarte.js', 'post.js', 'dropAttachments.js'));
73
		theme()->addJavascriptVar(array(
74
			'post_box_name' => $editorOptions['id'],
75
			'elk_smileys_url' => $settings['smileys_url'],
76
			'bbc_quote_from' => $txt['quote_from'],
77
			'bbc_quote' => $txt['quote'],
78
			'bbc_search_on' => $txt['search_on'],
79
			'ila_filename' => $txt['file'] . ' ' . $txt['name']), true
80
		);
81
82
		// Editor language file
83
		if (!empty($txt['lang_locale']))
84
		{
85
			loadJavascriptFile($scripturl . '?action=jslocale;sa=sceditor', array('defer' => true), 'sceditor_language');
86
		}
87
88
		// Our not so concise shortcut line
89
		$context['shortcuts_text'] = $context['shortcuts_text'] ?? $txt['shortcuts'];
90
91
		// Spellcheck?
92
		$context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new');
93
		if ($context['show_spellchecking'])
94
		{
95
			// Some hidden information is needed in order to make spell check work.
96
			if (!isset($_REQUEST['xml']))
97
			{
98
				$context['insert_after_template'] .= '
99
		<form name="spell_form" id="spell_form" method="post" accept-charset="UTF-8" target="spellWindow" action="' . $scripturl . '?action=spellcheck">
100
			<input type="hidden" id="spellstring" name="spellstring" value="" />
101
			<input type="hidden" id="fulleditor" name="fulleditor" value="" />
102
		</form>';
103
			}
104
			loadJavascriptFile('spellcheck.js', array('defer' => true));
105
		}
106
	}
107
108
	// Start off the editor...
109
	$context['controls']['richedit'][$editorOptions['id']] = array(
110
		'id' => $editorOptions['id'],
111
		'value' => $editorOptions['value'],
112
		'rich_active' => !empty($options['wysiwyg_default']) || !empty($editorOptions['force_rich']) || !empty($_REQUEST[$editorOptions['id'] . '_mode']),
113
		'disable_smiley_box' => !empty($editorOptions['disable_smiley_box']),
114
		'width' => isset($editorOptions['width']) ? $editorOptions['width'] : '100%',
115
		'height' => isset($editorOptions['height']) ? $editorOptions['height'] : '250px',
116
		'form' => isset($editorOptions['form']) ? $editorOptions['form'] : 'postmodify',
117
		'preview_type' => isset($editorOptions['preview_type']) ? (int) $editorOptions['preview_type'] : 1,
118
		'labels' => !empty($editorOptions['labels']) ? $editorOptions['labels'] : array(),
119
		'locale' => !empty($txt['lang_locale']) ? $txt['lang_locale'] : 'en_US',
120 2
		'plugin_addons' => !empty($editorOptions['plugin_addons']) ? $editorOptions['plugin_addons'] : array(),
121 2
		'plugin_options' => !empty($editorOptions['plugin_options']) ? $editorOptions['plugin_options'] : array(),
122
		'buttons' => !empty($editorOptions['buttons']) ? $editorOptions['buttons'] : array(),
123 2
		'hidden_fields' => !empty($editorOptions['hidden_fields']) ? $editorOptions['hidden_fields'] : array(),
124
	);
125
126 2
	// Allow addons an easy way to add plugins, initialization objects, etc to the editor control
127
	call_integration_hook('integrate_editor_plugins', array($editorOptions['id']));
128
129 2
	// Switch between default images and back... mostly in case you don't have an PersonalMessage template, but do have a Post template.
130 2
	$use_defaults = isset($settings['use_default_images']) && $settings['use_default_images'] == 'defaults' && isset($settings['default_template']);
131
	if ($use_defaults)
132
	{
133 2
		$temp = [];
134
		$temp[] = $settings['theme_url'];
135
		$temp[] = $settings['images_url'];
136 2
		$temp[] = $settings['theme_dir'];
137
138
		$settings['theme_url'] = $settings['default_theme_url'];
139 2
		$settings['images_url'] = $settings['default_images_url'];
140
		$settings['theme_dir'] = $settings['default_theme_dir'];
141
	}
142 2
143 2
	// Setup the toolbar, smileys, plugins
144 2
	$context['bbc_toolbar'] = loadEditorToolbar();
145
	$context['smileys'] = loadEditorSmileys($context['controls']['richedit'][$editorOptions['id']]);
146
	$context['plugins'] = loadEditorPlugins($context['controls']['richedit'][$editorOptions['id']]);
147
	$context['plugin_options'] = getPluginOptions($context['controls']['richedit'][$editorOptions['id']], $editorOptions['id']);
148
149
	// Switch the URLs back... now we're back to whatever the main sub template is.  (like folder in PersonalMessage.)
150 2
	if ($use_defaults)
151 2
	{
152 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...
153 2
	}
154 2
155 2
	// Provide some dynamic error checking (no subject, no body, no service!)
156 2
	if (!empty($editorOptions['live_errors']))
157 2
	{
158
		theme()->getTemplates()->loadLanguageFile('Errors');
159
		theme()->addInlineJavascript('
160 2
	error_txts[\'no_subject\'] = ' . JavaScriptEscape($txt['error_no_subject']) . ';
161
	error_txts[\'no_message\'] = ' . JavaScriptEscape($txt['error_no_message']) . ';
162 2
163
	var subject_err = new errorbox_handler({
164
		self: \'subject_err\',
165
		error_box_id: \'post_error\',
166 2
		error_checks: [{
167
			code: \'no_subject\',
168
			efunction: function(box_value) {
169
				if (box_value.length === 0)
170
					return true;
171
				else
172 2
					return false;
173
			}
174 2
		}],
175
		check_id: "post_subject"
176
	});
177
178 2
	var body_err_' . $editorOptions['id'] . ' = new errorbox_handler({
179 2
		self: \'body_err_' . $editorOptions['id'] . '\',
180
		error_box_id: \'post_error\',
181
		error_checks: [{
182
			code: \'no_message\',
183
			efunction: function(box_value) {
184
				if (box_value.length === 0)
185
					return true;
186
				else
187
					return false;
188
			}
189
		}],
190
		editor_id: \'' . $editorOptions['id'] . '\',
191
		editor: ' . JavaScriptEscape('
192
		(function () {
193
			return $editor_data[\'' . $editorOptions['id'] . '\'].val();
194
		});') . '
195 2
	});', true);
196 2
	}
197 2
}
198 2
199 2
/**
200 2
 * Defines plugins to load and loads the JS needed by core plugins.
201 2
 * Will merge in plugins added by addons.  Its the editor that will load
202 2
 * and call the plugins, this just defines what to load
203 2
 *
204 2
 * @param array $editor_context
205 2
 * @return array
206 2
 */
207 2
function loadEditorPlugins($editor_context)
208 2
{
209 2
	global $modSettings;
210 2
211 2
	$plugins = [];
212 2
	$neededJS = [];
213
214
	if (!empty($modSettings['enableSplitTag']))
215
	{
216 2
		$plugins[] = 'splittag';
217
		$neededJS[] = 'splittag.plugin.js';
218
	}
219 2
220
	if (!empty($modSettings['enableUndoRedo']))
221
	{
222
		$plugins[] = 'undo';
223
		$neededJS[] = 'undo.plugin.min.js';
224
	}
225
226
	if (!empty($modSettings['mentions_enabled']))
227
	{
228
		$plugins[] = 'mention';
229
		$neededJS = array_merge($neededJS, ['jquery.atwho.min.js', 'jquery.caret.min.js', 'mentioning.plugin.js']);
230
	}
231 2
232
	if (!empty($neededJS))
233
	{
234
		loadJavascriptFile($neededJS, array('defer' => true));
235
	}
236
237
	// Merge with other plugins added by core features or addons
238 2
	if (!empty($editor_context['plugin_addons']))
239
	{
240
		if (!is_array($editor_context['plugin_addons']))
241
		{
242
			$editor_context['plugin_addons'] = array($editor_context['plugin_addons']);
243 2
		}
244
245
		$plugins = array_filter(array_merge($plugins, $editor_context['plugin_addons']));
246
	}
247
248
	return $plugins;
249
}
250
251
/**
252 2
 * Loads any built in plugin options and merges in any addon defined
253
 * ones.
254
 *
255 2
 * @param array $editor_context
256
 * @param string $editor_id
257
 * @return array
258 2
 */
259 2
function getPluginOptions($editor_context, $editor_id)
260
{
261
	global $modSettings;
262
263
	$plugin_options = [];
264
265 2
	if (!empty($modSettings['mentions_enabled']))
266
	{
267
		$plugin_options[] = '
268 2
			mentionOptions: {
269
				editor_id: \'' . $editor_id . '\',
270
				cache: {
271
					mentions: [],
272
					queries: [],
273
					names: []
274
				}
275
			}';
276
	}
277
278
	// Allow addons to insert additional editor objects
279
	if (!empty($editor_context['plugin_options']) && is_array($editor_context['plugin_options']))
280
	{
281
		$plugin_options = array_merge($plugin_options, $editor_context['plugin_options']);
282
	}
283
284
	return $plugin_options;
285
}
286 2
287 2
/**
288
 * Loads the editor toolbar with just the enabled commands
289 2
 *
290
 * @return array
291 2
 */
292
function loadEditorToolbar()
293
{
294 2
	$bbc_tags = loadToolbarDefaults();
295
	$disabledToolbar = getDisabledBBC();
296
297 2
	// Build our toolbar, taking in to account any bbc codes from integration
298
	$bbcToolbar = [];
299 2
	foreach ($bbc_tags as $row => $tagRow)
300
	{
301
		$bbcToolbar[$row] = $bbcToolbar[$row] ?? [];
302 2
		$tagsRow = [];
303
304 2
		// For each row of buttons defined, lets build our tags
305
		foreach ($tagRow as $tags)
306
		{
307
			foreach ($tags as $tag)
308
			{
309 2
				// Just add this code in the existing grouping
310
				if (!isset($disabledToolbar[$tag]))
311 2
				{
312
					$tagsRow[] = $tag;
313
				}
314
			}
315
316 2
			// If the row is not empty, and the last added tag is not a space, add a space.
317
			if (!empty($tagsRow) && $tagsRow[count($tagsRow) - 1] !== 'space')
318 2
			{
319
				$tagsRow[] = 'space';
320
			}
321
		}
322
323
		// Build that beautiful button row
324 2
		if (!empty($tagsRow))
325
		{
326 2
			$bbcToolbar[$row][] = implode(',', $tagsRow);
327
		}
328
	}
329
330
	return $bbcToolbar;
331
}
332 2
333
/**
334
 * Loads disabled BBC tags from the DB as defined in the ACP.  It
335
 * will then translate BBC to editor commands (b => bold) such that
336
 * disabled BBC will also disable the associated editor command button.
337
 *
338
 * @return array
339
 */
340
function getDisabledBBC()
341
{
342
	global $modSettings;
343
344
	// Generate a list of buttons that shouldn't be shown
345
	$disabled_tags = [];
346
	$disabledToolbar = [];
347
348
	if (!empty($modSettings['disabledBBC']))
349
	{
350
		$disabled_tags = explode(',', $modSettings['disabledBBC']);
351
	}
352
353
	// Map bbc codes to editor toolbar tags
354
	$translate_tags_to_code = ['b' => 'bold', 'i' => 'italic', 'u' => 'underline', 's' => 'strike',
355
							   'img' => 'image', 'url' => 'link', 'sup' => 'superscript', 'sub' => 'subscript', 'hr' => 'horizontalrule'];
356
357
	// Remove the toolbar buttons for any bbc tags that have been turned off in the ACP
358
	foreach ($disabled_tags as $tag)
359
	{
360
		// list is special, its prevents two tags
361
		if ($tag === 'list')
362
		{
363
			$disabledToolbar['bulletlist'] = true;
364
			$disabledToolbar['orderedlist'] = true;
365
		}
366
		elseif (isset($translate_tags_to_code[$tag]))
367
		{
368
			$disabledToolbar[$translate_tags_to_code[$tag]] = true;
369
		}
370
371
		// Tag is the same as the code, like font, color, size etc
372
		$disabledToolbar[trim($tag)] = true;
373
	}
374
375
	return $disabledToolbar;
376
}
377
378
/**
379
 * Loads the toolbar default buttons which defines what editor buttons might show
380
 *
381
 * @event integrate_bbc_buttons
382
 * @return array|mixed
383
 */
384
function loadToolbarDefaults()
385
{
386
	$bbc_tags = [];
387
388
	// The below array is used to show a command button in the editor, the execution
389
	// and display details of any added buttons must be defined in the javascript files
390
	// see jquery.sceditor.elkarte.js under the sceditor.formats.bbcode area
391
	// for examples of how to use the .set command to add codes.  Include your new
392
	// JS with addInlineJavascript() or loadJavascriptFile() in your addon
393
	$bbc_tags['row1'] = array(
394
		array('bold', 'italic', 'underline', 'strike', 'superscript', 'subscript'),
395
		array('left', 'center', 'right', 'pre', 'tt'),
396
		array('font', 'size', 'color'),
397
	);
398
399
	$bbc_tags['row2'] = array(
400
		array('quote', 'code', 'table'),
401
		array('bulletlist', 'orderedlist', 'horizontalrule'),
402
		array('spoiler', 'footnote', 'splittag'),
403
		array('image', 'link', 'email'),
404
		array('undo', 'redo'),
405
	);
406
407
	// Allow mods to add BBC buttons to the toolbar, actions are defined in the JS
408
	call_integration_hook('integrate_bbc_buttons', array(&$bbc_tags));
409
410
	// Show the format and toggle buttons
411
	$bbc_tags['row2'][] = array('removeformat', 'source');
412
413
	return $bbc_tags;
414
}
415
416
/**
417
 * Loads the smileys for use in the required locations (postform or popup)
418
 *
419
 * What it does:
420
 * - Will load the default smileys or custom smileys as defined in ACP
421
 * - Caches the DB call for 8 mins
422
 * - Sorts smileys to proper array positions removing hidden ones
423
 *
424
 * @return array|array[]|mixed
425
 */
426
function loadEditorSmileys($editorOptions)
427
{
428
	global $context, $modSettings;
429
430
	$smileys = [
431 2
		'postform' => [],
432
		'popup' => [],
433
	];
434
435
	// Initialize smiley array... if not loaded before.
436
	if (empty($context['smileys']) && empty($editorOptions['disable_smiley_box']))
437
	{
438
		// Load smileys - don't bother to run a query if we're not using the database's ones anyhow.
439
		if (empty($modSettings['smiley_enable']) && User::$info->smiley_set !== 'none')
440
		{
441
			$smileys['postform'][] = loadDefaultSmileys();
442
443
			return $smileys;
444
		}
445
446
		if (User::$info->smiley_set !== 'none')
447
		{
448
			$temp = [];
449
			if (!Cache::instance()->getVar($temp, 'posting_smileys', 480))
450
			{
451
				$db = database();
452
				$db->fetchQuery('
453
					SELECT 
454
						code, filename, description, smiley_row, hidden
455
					FROM {db_prefix}smileys
456
					WHERE hidden IN (0, 2)
457
					ORDER BY smiley_row, smiley_order',
458
					array()
459
				)->fetch_callback(
460
					function ($row) use (&$smileys) {
461
						$row['filename'] = htmlspecialchars($row['filename'], ENT_COMPAT, 'UTF-8');
462
						$row['description'] = htmlspecialchars($row['description'], ENT_COMPAT, 'UTF-8');
463
464
						$smileys[empty($row['hidden']) ? 'postform' : 'popup'][$row['smiley_row']]['smileys'][] = $row;
465
					}
466
				);
467
468
				foreach ($smileys as $section => $smileyRows)
469
				{
470
					$last_row = null;
471
					foreach ($smileyRows as $rowIndex => $smileRow)
472
					{
473
						$smileys[$section][$rowIndex]['smileys'][count($smileRow['smileys']) - 1]['isLast'] = true;
474
						$last_row = $rowIndex;
475
					}
476
477
					if ($last_row !== null)
478
					{
479
						$smileys[$section][$last_row]['isLast'] = true;
480
					}
481
				}
482
483
				Cache::instance()->put('posting_smileys', $smileys, 480);
484
			}
485 2
			else
486
			{
487
				$smileys = $temp;
488
			}
489
490
			// The smiley popup may take advantage of Jquery UI ....
491
			if (!empty($smileys['popup']))
492 2
			{
493
				$modSettings['jquery_include_ui'] = true;
494
			}
495
496
			return $smileys;
497
		}
498
	}
499
500
	return empty($context['smileys']) ? $smileys : $context['smileys'];
501
}
502
503
/**
504
 * Returns an array of default smileys that are enabled w/o any db
505
 * requirements
506
 *
507
 * @return array
508
 */
509
function loadDefaultSmileys()
510
{
511
	global $txt;
512
513
	return ['smileys' => [
514
		[
515
			'code' => ':)',
516
			'filename' => 'smiley.gif',
517
			'description' => $txt['icon_smiley'],
518
		],
519
		[
520
			'code' => ';)',
521
			'filename' => 'wink.gif',
522
			'description' => $txt['icon_wink'],
523
		],
524
		[
525
			'code' => ':D',
526
			'filename' => 'cheesy.gif',
527
			'description' => $txt['icon_cheesy'],
528
		],
529
		[
530
			'code' => ';D',
531
			'filename' => 'grin.gif',
532
			'description' => $txt['icon_grin']
533
		],
534 2
		[
535
			'code' => '>:(',
536
			'filename' => 'angry.gif',
537
			'description' => $txt['icon_angry'],
538
		],
539
		[
540
			'code' => ':))',
541
			'filename' => 'laugh.gif',
542
			'description' => $txt['icon_laugh'],
543
		],
544
		[
545
			'code' => ':(',
546
			'filename' => 'sad.gif',
547
			'description' => $txt['icon_sad'],
548
		],
549
		[
550
			'code' => ':o',
551
			'filename' => 'shocked.gif',
552
			'description' => $txt['icon_shocked'],
553
		],
554
		[
555
			'code' => '8)',
556
			'filename' => 'cool.gif',
557
			'description' => $txt['icon_cool'],
558
		],
559
		[
560
			'code' => '???',
561
			'filename' => 'huh.gif',
562
			'description' => $txt['icon_huh'],
563
		],
564
		[
565
			'code' => '::)',
566
			'filename' => 'rolleyes.gif',
567
			'description' => $txt['icon_rolleyes'],
568
		],
569
		[
570
			'code' => ':P',
571
			'filename' => 'tongue.gif',
572
			'description' => $txt['icon_tongue'],
573
		],
574
		[
575
			'code' => ':-[',
576
			'filename' => 'embarrassed.gif',
577
			'description' => $txt['icon_embarrassed'],
578
		],
579
		[
580
			'code' => ':-X',
581
			'filename' => 'lipsrsealed.gif',
582
			'description' => $txt['icon_lips'],
583
		],
584
		[
585
			'code' => ':-\\',
586
			'filename' => 'undecided.gif',
587
			'description' => $txt['icon_undecided'],
588
		],
589
		[
590
			'code' => ':-*',
591
			'filename' => 'kiss.gif',
592
			'description' => $txt['icon_kiss'],
593
		],
594
		[
595
			'code' => 'O:)',
596
			'filename' => 'angel.gif',
597
			'description' => $txt['icon_angel'],
598
		],
599
		[
600
			'code' => ':\'(',
601
			'filename' => 'cry.gif',
602
			'description' => $txt['icon_cry'],
603
			'isLast' => true,
604
		],
605
	],
606
			'isLast' => true,
607
	];
608
}