ManagePortalPages_Controller   B
last analyzed

Complexity

Total Complexity 49

Size/Duplication

Total Lines 549
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 1
Metric Value
eloc 274
c 5
b 0
f 1
dl 0
loc 549
rs 8.48
wmc 49

9 Methods

Rating   Name   Duplication   Size   Complexity  
A list_spCountPages() 0 3 1
A action_index() 0 43 2
B action_delete() 0 27 7
A prepareEditor() 0 28 4
C action_edit() 0 111 13
A action_status() 0 25 5
A list_spLoadPages() 0 3 1
F _sportal_admin_page_edit_save() 0 102 15
B action_list() 0 145 1

How to fix   Complexity   

Complex Class

Complex classes like ManagePortalPages_Controller 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 ManagePortalPages_Controller, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @package SimplePortal ElkArte
5
 *
6
 * @author SimplePortal Team
7
 * @copyright 2015-2021 SimplePortal Team
8
 * @license BSD 3-clause
9
 * @version 1.0.0
10
 */
11
12
use BBC\PreparseCode;
13
use ElkArte\Errors\ErrorContext;
14
15
/**
16
 * SimplePortal Page Administration controller class.
17
 *
18
 * - This class handles the adding/editing/listing of pages
19
 */
20
class ManagePortalPages_Controller extends Action_Controller
21
{
22
	/** @var array */
23
	protected $blocks = array();
24
25
	/**
26
	 * Main dispatcher.
27
	 * This function checks permissions and passes control through.
28
	 */
29
	public function action_index()
30
	{
31
		global $context, $txt;
32
33
		// Admin or pages permissions required
34
		if (!allowedTo('sp_admin'))
35
		{
36
			isAllowedTo('sp_manage_pages');
37
		}
38
39
		// Can't do much without our little buddys
40
		require_once(SUBSDIR . '/PortalAdmin.subs.php');
41
		require_once(SUBSDIR . '/Portal.subs.php');
42
		loadTemplate('PortalAdminPages');
43
44
		// The actions we know
45
		$subActions = array(
46
			'list' => array($this, 'action_list'),
47
			'add' => array($this, 'action_edit'),
48
			'edit' => array($this, 'action_edit'),
49
			'status' => array($this, 'action_status'),
50
			'delete' => array($this, 'action_delete'),
51
		);
52
53
		// Start up the controller, provide a hook since we can
54
		$action = new Action('portal_page');
55
56
		// Default to the list action
57
		$subAction = $action->initialize($subActions, 'list');
58
		$context['sub_action'] = $subAction;
59
60
		$context[$context['admin_menu_name']]['tab_data'] = array(
61
			'title' => $txt['sp_admin_pages_title'],
62
			'help' => 'sp_PagesArea',
63
			'description' => $txt['sp_admin_pages_desc'],
64
			'tabs' => array(
65
				'list' => array(),
66
				'add' => array(),
67
			),
68
		);
69
70
		// Go!
71
		$action->dispatch($subAction);
72
	}
73
74
	/**
75
	 * Show page listing of all portal pages in the system
76
	 */
77
	public function action_list()
78
	{
79
		global $context, $scripturl, $txt, $modSettings;
80
81
		// Build the listoption array to display the categories
82
		$listOptions = array(
83
			'id' => 'portal_pages',
84
			'title' => $txt['sp_admin_pages_list'],
85
			'items_per_page' => $modSettings['defaultMaxMessages'],
86
			'no_items_label' => $txt['error_sp_no_pages'],
87
			'base_href' => $scripturl . '?action=admin;area=portalpages;sa=list;',
88
			'default_sort_col' => 'title',
89
			'get_items' => array(
90
				'function' => array($this, 'list_spLoadPages'),
91
			),
92
			'get_count' => array(
93
				'function' => array($this, 'list_spCountPages'),
94
			),
95
			'columns' => array(
96
				'title' => array(
97
					'header' => array(
98
						'value' => $txt['sp_admin_pages_col_title'],
99
					),
100
					'data' => array(
101
						'db' => 'title',
102
					),
103
					'sort' => array(
104
						'default' => 'title',
105
						'reverse' => 'title DESC',
106
					),
107
				),
108
				'namespace' => array(
109
					'header' => array(
110
						'value' => $txt['sp_admin_pages_col_namespace'],
111
					),
112
					'data' => array(
113
						'sprintf' => array(
114
							'format' => '<a href="?page=%1$s">%1$s</a>',
115
							'params' => array(
116
								'page_id' => true
117
							),
118
						),
119
					),
120
					'sort' => array(
121
						'default' => 'namespace',
122
						'reverse' => 'namespace DESC',
123
					),
124
				),
125
				'type' => array(
126
					'header' => array(
127
						'value' => $txt['sp_admin_pages_col_type'],
128
					),
129
					'data' => array(
130
						'db' => 'type',
131
					),
132
					'sort' => array(
133
						'default' => 'type',
134
						'reverse' => 'type DESC',
135
					),
136
				),
137
				'views' => array(
138
					'header' => array(
139
						'value' => $txt['sp_admin_pages_col_views'],
140
						'class' => 'centertext',
141
					),
142
					'data' => array(
143
						'db' => 'views',
144
						'class' => 'centertext',
145
					),
146
					'sort' => array(
147
						'default' => 'views',
148
						'reverse' => 'views DESC',
149
					),
150
				),
151
				'status' => array(
152
					'header' => array(
153
						'value' => $txt['sp_admin_pages_col_status'],
154
						'class' => 'centertext',
155
					),
156
					'data' => array(
157
						'db' => 'status_image',
158
						'class' => 'centertext',
159
					),
160
					'sort' => array(
161
						'default' => 'status',
162
						'reverse' => 'status DESC',
163
					),
164
				),
165
				'action' => array(
166
					'header' => array(
167
						'value' => $txt['sp_admin_pages_col_actions'],
168
						'class' => 'centertext',
169
					),
170
					'data' => array(
171
						'sprintf' => array(
172
							'format' => '<a href="?action=admin;area=portalpages;sa=edit;page_id=%1$s;' . $context['session_var'] . '=' . $context['session_id'] . '" accesskey="e">' . sp_embed_image('edit') . '</a>&nbsp;
173
								<a href="?action=admin;area=portalpages;sa=delete;page_id=%1$s;' . $context['session_var'] . '=' . $context['session_id'] . '" onclick="return confirm(' . JavaScriptEscape($txt['sp_admin_pages_delete_confirm']) . ') && submitThisOnce(this);" accesskey="d">' . sp_embed_image('trash') . '</a>',
174
							'params' => array(
175
								'id' => true,
176
								'page_id' => true
177
							),
178
						),
179
						'class' => 'centertext nowrap',
180
					),
181
				),
182
				'check' => array(
183
					'header' => array(
184
						'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />',
185
						'class' => 'centertext',
186
					),
187
					'data' => array(
188
						'function' => function ($row)
189
						{
190
							return '<input type="checkbox" name="remove[]" value="' . $row['id'] . '" class="input_check" />';
191
						},
192
						'class' => 'centertext',
193
					),
194
				),
195
			),
196
			'form' => array(
197
				'href' => $scripturl . '?action=admin;area=portalpages;sa=remove',
198
				'include_sort' => true,
199
				'include_start' => true,
200
				'hidden_fields' => array(
201
					$context['session_var'] => $context['session_id'],
202
				),
203
			),
204
			'additional_rows' => array(
205
				array(
206
					'class' => 'submitbutton',
207
					'position' => 'below_table_data',
208
					'value' => '<a class="linkbutton" href="?action=admin;area=portalpages;sa=add;' . $context['session_var'] . '=' . $context['session_id'] . '" accesskey="a">' . $txt['sp_admin_pages_add'] . '</a>
209
						<input type="submit" name="remove_pages" value="' . $txt['sp_admin_pages_remove'] . '" />',
210
				),
211
			),
212
		);
213
214
		// Set the context values
215
		$context['page_title'] = $txt['sp_admin_pages_title'];
216
		$context['sub_template'] = 'show_list';
217
		$context['default_list'] = 'portal_pages';
218
219
		// Create the list.
220
		require_once(SUBSDIR . '/GenericList.class.php');
221
		createList($listOptions);
222
	}
223
224
	/**
225
	 * Callback for createList(),
226
	 * Returns the number of articles in the system
227
	 */
228
	public function list_spCountPages()
229
	{
230
		return sp_count_pages();
231
	}
232
233
	/**
234
	 * Callback for createList()
235
	 * Returns an array of articles
236
	 *
237
	 * @param int $start
238
	 * @param int $items_per_page
239
	 * @param string $sort
240
	 *
241
	 * @return array
242
	 */
243
	public function list_spLoadPages($start, $items_per_page, $sort)
244
	{
245
		return sp_load_pages($start, $items_per_page, $sort);
246
	}
247
248
	/**
249
	 * Interface for adding/editing a page
250
	 */
251
	public function action_edit()
252
	{
253
		global $txt, $context;
254
255
		$context['SPortal']['is_new'] = empty($_REQUEST['page_id']);
256
		$context['SPortal']['preview'] = false;
257
258
		$pages_errors = ErrorContext::context('pages', 0);
259
260
		// Some help will be needed
261
		require_once(SUBSDIR . '/Editor.subs.php');
262
		require_once(SUBSDIR . '/Post.subs.php');
263
264
		// Saving the work?
265
		if (!empty($_POST['submit']) && !$pages_errors->hasErrors())
266
		{
267
			checkSession();
268
			$this->_sportal_admin_page_edit_save();
269
		}
270
271
		// Doing a quick look before you save or you messed up?
272
		if (!empty($_POST['preview']) || $pages_errors->hasErrors())
273
		{
274
			$context['SPortal']['page'] = array(
275
				'id' => $_POST['page_id'],
276
				'page_id' => $_POST['namespace'],
277
				'title' => Util::htmlspecialchars($_POST['title'], ENT_QUOTES),
278
				'body' => Util::htmlspecialchars($_POST['content'], ENT_QUOTES),
279
				'type' => $_POST['type'],
280
				'permissions' => $_POST['permissions'],
281
				'styles' => (int) $_POST['styles'],
282
				'status' => !empty($_POST['status']),
283
			);
284
285
			// Fix up bbc errors before we go to the preview
286
			if ($context['SPortal']['page']['type'] === 'bbc')
287
			{
288
				PreparseCode::instance()->preparsecode($context['SPortal']['page']['body'], false);
289
			}
290
291
			loadTemplate('PortalPages');
292
293
			// Showing errors or a preview?
294
			if ($pages_errors->hasErrors())
295
			{
296
				$context['pages_errors'] = array(
297
					'errors' => $pages_errors->prepareErrors(),
298
					'type' => $pages_errors->getErrorType() == 0 ? 'minor' : 'serious',
299
					'title' => $txt['sp_form_errors_detected'],
300
				);
301
			}
302
			else
303
			{
304
				$context['SPortal']['preview'] = true;
305
306
				// The editor will steal focus so we have to delay
307
				addInlineJavascript('setTimeout(() => $("html, body").animate({scrollTop: $("#preview_section").offset().top}, 250), 750);', true);
308
			}
309
		}
310
		// New page, set up with a random page ID
311
		elseif ($context['SPortal']['is_new'])
312
		{
313
			$context['SPortal']['page'] = array(
314
				'id' => 0,
315
				'page_id' => 'page' . random_int(1, 5000),
316
				'title' => $txt['sp_pages_default_title'],
317
				'body' => '',
318
				'type' => 'bbc',
319
				'permissions' => 3,
320
				'styles' => 4,
321
				'status' => 1,
322
			);
323
		}
324
		// Used page :P
325
		else
326
		{
327
			$_REQUEST['page_id'] = (int) $_REQUEST['page_id'];
328
			$context['SPortal']['page'] = sportal_get_pages($_REQUEST['page_id']);
329
		}
330
331
		if ($context['SPortal']['page']['type'] === 'bbc')
332
		{
333
			$context['SPortal']['page']['body'] = PreparseCode::instance()->un_preparsecode($context['SPortal']['page']['body']);
334
			$context['SPortal']['page']['body'] = str_replace(array('"', '<', '>', '&nbsp;'), array('&quot;', '&lt;', '&gt;', ' '), $context['SPortal']['page']['body']);
335
		}
336
337
		// Set up the editor, values, initial state, etc
338
		$this->prepareEditor();
339
340
		// Set the globals, spplugin will set the editor box as needed (editor or textbox, etc)
341
		addConversionJS($context['SPortal']['page']['type']);
342
343
		// Permissions
344
		$context['SPortal']['page']['permission_profiles'] = sportal_get_profiles(null, 1, 'name');
345
		if (empty($context['SPortal']['page']['permission_profiles']))
346
		{
347
			throw new Elk_Exception('error_sp_no_permission_profiles', false);
348
		}
349
350
		// Styles
351
		$context['SPortal']['page']['style_profiles'] = sportal_get_profiles(null, 2, 'name');
352
		if (empty($context['SPortal']['page']['style_profiles']))
353
		{
354
			throw new Elk_Exception('error_sp_no_style_profiles', false);
355
		}
356
357
		// And for the template
358
		$context['SPortal']['page']['style'] = sportal_select_style($context['SPortal']['page']['styles']);
359
		$context['SPortal']['page']['body'] = sportal_parse_content($context['SPortal']['page']['body'], $context['SPortal']['page']['type'], 'return');
360
		$context['page_title'] = $context['SPortal']['is_new'] ? $txt['sp_admin_pages_add'] : $txt['sp_admin_pages_edit'];
361
		$context['sub_template'] = 'pages_edit';
362
	}
363
364
	/**
365
	 * Sets up editor options as needed for SP, temporarily ignores any user options
366
	 * so we can enable it in the proper mode bbc/php/html
367
	 */
368
	private function prepareEditor()
369
	{
370
		global $context, $options;
371
372
		if ($context['SPortal']['page']['type'] !== 'bbc')
373
		{
374
			// No wizzy mode if they don't need it
375
			$temp_editor = !empty($options['wysiwyg_default']);
376
			$options['wysiwyg_default'] = false;
377
		}
378
379
		$editorOptions = array(
380
			'id' => 'content',
381
			'value' => $context['SPortal']['page']['body'],
382
			'width' => '100%',
383
			'height' => '275px',
384
			'preview_type' => 1,
385
		);
386
		$editorOptions['plugin_addons'] = array();
387
		$editorOptions['plugin_addons'][] = 'spplugin';
388
		create_control_richedit($editorOptions);
389
390
		$context['post_box_name'] = $editorOptions['id'];
391
		$context['post_box_class'] = $context['article']['type'] !== 'bbc' ? 'sceditor-container' : 'sp-sceditor-container';
392
393
		if (isset($temp_editor))
394
		{
395
			$options['wysiwyg_default'] = $temp_editor;
396
		}
397
	}
398
399
	/**
400
	 * Does the actual saving of the page data
401
	 *
402
	 * - Validates the data is safe to save
403
	 * - Updates existing pages or creates new ones
404
	 */
405
	private function _sportal_admin_page_edit_save()
406
	{
407
		global $txt, $context, $modSettings;
408
409
		// No errors, yet.
410
		$pages_errors = ErrorContext::context('pages', 0);
411
412
		// Use our standard validation functions in a few spots
413
		require_once(SUBSDIR . '/DataValidator.class.php');
414
		$validator = new Data_Validator();
415
416
		// Clean and Review the post data for compliance
417
		$validator->sanitation_rules(array(
418
			'title' => 'trim|Util::htmlspecialchars',
419
			'namespace' => 'trim|Util::htmlspecialchars',
420
			'permissions' => 'intval',
421
			'type' => 'trim',
422
			'content' => 'trim'
423
		));
424
		$validator->validation_rules(array(
425
			'title' => 'required',
426
			'namespace' => 'alpha_numeric|required',
427
			'type' => 'required',
428
			'content' => 'required'
429
		));
430
		$validator->text_replacements(array(
431
			'title' => $txt['sp_error_page_name_empty'],
432
			'namespace' => $txt['sp_error_page_namespace_empty'],
433
			'content' => $txt['sp_admin_pages_col_body'],
434
		));
435
436
		// If you messed this up, back you go
437
		if (!$validator->validate($_POST))
438
		{
439
			foreach ($validator->validation_errors() as $id => $error)
440
			{
441
				$pages_errors->addError($error);
442
			}
443
444
			$this->action_edit();
445
		}
446
447
		// Can't have the same name in the same space twice
448
		$has_duplicate = sp_check_duplicate_pages($_POST['namespace'], $_POST['page_id']);
449
		if (!empty($has_duplicate))
450
		{
451
			$pages_errors->addError('sp_error_page_namespace_duplicate');
452
		}
453
454
		// Can't have a simple numeric namespace
455
		if (preg_replace('~[0-9]+~', '', $_POST['namespace']) === '')
456
		{
457
			$pages_errors->addError('sp_error_page_namespace_numeric');
458
		}
459
460
		if ($_POST['type'] === 'php' && !allowedTo('admin_forum'))
461
		{
462
			throw new Elk_Exception('cannot_admin_forum', false);
463
		}
464
465
		// Running some php code, then we need to validate its legit code
466
		if ($_POST['type'] === 'php' && !empty($_POST['content']) && empty($modSettings['sp_disable_php_validation']))
467
		{
468
			$validator_php = new Data_Validator();
469
			$validator_php->validation_rules(array('content' => 'php_syntax'));
470
471
			// Bad PHP code
472
			if (!$validator_php->validate(array('content' => $_POST['content'])))
473
			{
474
				$pages_errors->addError($validator_php->validation_errors());
475
			}
476
		}
477
478
		// None shall pass ... with errors
479
		if ($pages_errors->hasErrors())
480
		{
481
			$this->action_edit();
482
		}
483
484
		// The data for the fields
485
		$page_info = array(
486
			'id' => (int) $_POST['page_id'],
487
			'namespace' => Util::htmlspecialchars($_POST['namespace'], ENT_QUOTES),
488
			'title' => Util::htmlspecialchars($_POST['title'], ENT_QUOTES),
489
			'body' => Util::htmlspecialchars($_POST['content'], ENT_QUOTES),
490
			'type' => in_array($_POST['type'], array('bbc', 'html', 'php', 'markdown')) ? $_POST['type'] : 'bbc',
491
			'permissions' => (int) $_POST['permissions'],
492
			'styles' => (int) $_POST['styles'],
493
			'status' => !empty($_POST['status']) ? 1 : 0,
494
		);
495
496
		if ($page_info['type'] === 'bbc')
497
		{
498
			PreparseCode::instance()->preparsecode($page_info['body'], false);
499
		}
500
501
		// Save away
502
		sp_save_page($page_info, $context['SPortal']['is_new']);
503
504
		redirectexit('action=admin;area=portalpages');
505
506
		return true;
507
	}
508
509
	/**
510
	 * Update the page status / active on/off
511
	 */
512
	public function action_status()
513
	{
514
		global $context;
515
516
		checkSession(isset($_REQUEST['xml']) ? '' : 'get');
517
518
		$page_id = !empty($_REQUEST['page_id']) ? (int) $_REQUEST['page_id'] : 0;
519
		$state = sp_changeState('page', $page_id);
520
521
		// Doing this the ajax way?
522
		if (isset($_REQUEST['xml']))
523
		{
524
			$context['item_id'] = $page_id;
525
			$context['status'] = !empty($state) ? 'active' : 'deactive';
526
527
			// Clear out any template layers, add the xml response
528
			loadTemplate('PortalAdmin');
529
			$template_layers = Template_Layers::instance();
530
			$template_layers->removeAll();
531
			$context['sub_template'] = 'change_status';
532
533
			obExit();
534
		}
535
536
		redirectexit('action=admin;area=portalpages');
537
	}
538
539
	/**
540
	 * Delete a page from the system
541
	 */
542
	public function action_delete()
543
	{
544
		$page_ids = array();
545
546
		// Get the page id's to remove
547
		if (!empty($_POST['remove_pages']) && !empty($_POST['remove']) && is_array($_POST['remove']))
548
		{
549
			checkSession();
550
551
			foreach ($_POST['remove'] as $index => $page_id)
552
			{
553
				$page_ids[(int) $index] = (int) $page_id;
554
			}
555
		}
556
		elseif (!empty($_REQUEST['page_id']))
557
		{
558
			checkSession('get');
559
			$page_ids[] = (int) $_REQUEST['page_id'];
560
		}
561
562
		// If we have some to remove ....
563
		if (!empty($page_ids))
564
		{
565
			sp_delete_pages($page_ids);
566
		}
567
568
		redirectexit('action=admin;area=portalpages');
569
	}
570
}
571