Passed
Push — development ( 4b352c...7fab02 )
by Spuds
01:07 queued 20s
created

ManagePermissions::action_index()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 59
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 35
nc 1
nop 0
dl 0
loc 59
ccs 0
cts 32
cp 0
crap 6
rs 9.36
c 0
b 0
f 0

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
 * Handles all possible permission items, permissions by membergroup
5
 * permissions by board, adding, modifying, etc
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 Beta 1
15
 *
16
 */
17
18
namespace ElkArte\AdminController;
19
20
use BBC\ParserWrapper;
21
use ElkArte\AbstractController;
22
use ElkArte\Action;
23
use ElkArte\BoardsTree;
24
use ElkArte\Exceptions\Exception;
25
use ElkArte\Languages\Txt;
26
use ElkArte\Permissions;
27
use ElkArte\SettingsForm\SettingsForm;
28
29
/**
30
 * ManagePermissions handles all possible permission stuff.
31
 *
32
 * @package Permissions
33
 */
34
class ManagePermissions extends AbstractController
35
{
36
	/** @var int|null The profile ID that we are working with */
37
	protected ?int $_pid;
38
39
	/** @var \Elkarte\Permissions Permissions object */
40
	private Permissions $permissionsObject;
41
42
	/** @var string[] */
43
	private array $illegal_permissions = [];
44
45
	/** @var string[] */
46
	private array $illegal_guest_permissions = [];
47
48
	/**
49
	 * Dispatches to the right function based on the given subaction.
50
	 *
51
	 * - Checks the permissions, based on the sub-action.
52
	 * - Called by ?action=managepermissions.
53
	 *
54
	 * @event integrate_sa_manage_permissions used to add new subactions
55
	 * @uses ManagePermissions language file.
56
	 * @see AbstractController::action_index()
57
	 */
58
	public function action_index()
59
	{
60
		global $txt, $context;
61
62
		// Make sure they can't do certain things unless they have the right permissions.
63
		$this->permissionsObject = new Permissions();
64
		$this->illegal_permissions = $this->permissionsObject->getIllegalPermissions();
65
		$this->illegal_guest_permissions = $this->permissionsObject->getIllegalGuestPermissions();
66
67
		Txt::load('ManagePermissions+ManageMembers');
68
		theme()->getTemplates()->load('ManagePermissions');
69
70
		$subActions = [
71
			'board' => [$this, 'action_board', 'permission' => 'manage_permissions'],
72
			'index' => [$this, 'action_list', 'permission' => 'manage_permissions'],
73
			'modify' => [$this, 'action_modify', 'permission' => 'manage_permissions'],
74
			'modify2' => [$this, 'action_modify2', 'permission' => 'manage_permissions'],
75
			'quick' => [$this, 'action_quick', 'permission' => 'manage_permissions'],
76
			'quickboard' => [$this, 'action_quickboard', 'permission' => 'manage_permissions'],
77
			'postmod' => [$this, 'action_postmod', 'permission' => 'manage_permissions', 'disabled' => !featureEnabled('pm')],
78
			'profiles' => [$this, 'action_profiles', 'permission' => 'manage_permissions'],
79
			'settings' => [$this, 'action_permSettings_display', 'permission' => 'admin_forum'],
80
		];
81
82
		// Action controller
83
		$action = new Action('manage_permissions');
84
85
		// Load the subactions, call integrate_sa_manage_permissions
86
		$subAction = $action->initialize($subActions, (allowedTo('manage_permissions') ? 'index' : 'settings'));
87
88
		// Last items needed
89
		$context['page_title'] = $txt['permissions_title'];
90
		$context['sub_action'] = $subAction;
91
92
		// Create the tabs for the template.
93
		$context[$context['admin_menu_name']]['object']->prepareTabData([
94
			'title' => 'permissions_title',
95
			'help' => 'permissions',
96
			'tabs' => [
97
				'index' => [
98
					'description' => $txt['permissions_groups'],
99
				],
100
				'board' => [
101
					'description' => $txt['permission_by_board_desc'],
102
				],
103
				'profiles' => [
104
					'description' => $txt['permissions_profiles_desc'],
105
				],
106
				'postmod' => [
107
					'description' => $txt['permissions_post_moderation_desc'],
108
				],
109
				'settings' => [
110
					'description' => $txt['permission_settings_desc'],
111
				],
112
			],
113
		]);
114
115
		// Call the right function for this sub-action.
116
		$action->dispatch($subAction);
117
	}
118
119
	/**
120
	 * Sets up the permissions by membergroup index page.
121
	 *
122
	 * - Called by ?action=managepermissions
123
	 * - Creates an array of all the groups with the number of members and permissions.
124
	 *
125
	 * @event integrate_list_regular_membergroups_list
126
	 * @event integrate_list_post_count_membergroups_list
127
	 * @uses ManagePermissions language file.
128
	 * @uses ManagePermissions template file.
129
	 * @uses ManageBoards template, permission_index sub-template.
130
	 */
131
	public function action_list(): void
132
	{
133
		global $txt, $context, $modSettings;
134
135
		require_once(SUBSDIR . '/Membergroups.subs.php');
136
		require_once(SUBSDIR . '/Members.subs.php');
137
		require_once(SUBSDIR . '/ManagePermissions.subs.php');
138
139
		$context['page_title'] = $txt['permissions_title'];
140
141
		// pid = profile id
142
		$this->_pid = $this->_req->getQuery('pid', 'intval');
143
144
		// We can modify any permission set apart from the read-only, reply only and no polls ones as they are redefined.
145
		$context['can_modify'] = empty($this->_pid) || $this->_pid === 1 || $this->_pid > 4;
146
147
		// Load all the permissions. We'll need them in the template.
148
		loadAllPermissions();
149
150
		// Also load profiles, we may want to reset.
151
		loadPermissionProfiles();
152
153
		$listOptions = [
154
			'id' => 'regular_membergroups_list',
155
			'title' => $txt['membergroups_regular'],
156
			'base_href' => getUrl('admin', ['action' => 'admin', 'area' => 'permissions', 'sa' => 'index'] + ($this->_req->hasQuery('sort2') ? ['sort2' => $this->_req->getQuery('sort2', 'trim')] : []) + ($this->_pid !== null ? ['pid' => $this->_pid] : [])),
157
			'default_sort_col' => 'name',
158
			'get_items' => [
159
				'file' => SUBSDIR . '/Membergroups.subs.php',
160
				'function' => 'list_getMembergroups',
161
				'params' => [
162
					'all',
163
					$this->user->id,
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
164
					allowedTo('manage_membergroups'),
165
					allowedTo('admin_forum'),
166
					true,
167
					true,
168
					$this->_pid,
169
				],
170
			],
171
			'columns' => [
172
				'name' => [
173
					'header' => [
174
						'value' => $txt['membergroups_name'],
175
					],
176
					'data' => [
177
						'function' => static function ($rowData) {
178
							global $txt;
179
							// Since the moderator group has no explicit members, no link is needed.
180
							// Since guests and regular members are not groups, no link is needed.
181
							if (in_array($rowData['id_group'], [-1, 0, 3], true))
182
							{
183
								$group_name = $rowData['group_name'];
184
							}
185
							else
186
							{
187
								$group_name = sprintf('<a href="' . getUrl('action', ['action' => 'admin', 'area' => 'membergroups', 'sa' => 'members', 'group' => $rowData['id_group']]) . '">%1$s</a>', $rowData['group_name_color']);
188
							}
189
190
							// Add a help option for guests, regular members, moderator and administrator.
191
							if (!empty($rowData['help']))
192
							{
193
								$group_name .= '(<a href="' . getUrl('action', ['action' => 'quickhelp', 'help' => $rowData['help']]) . '" onclick="return reqOverlayDiv(this.href);" class="helpicon i-help"></a>)';
194
							}
195
196
							if (!empty($rowData['children']))
197
							{
198
								$group_name .= '
199
									<br />
200
									<span class="smalltext">' . $txt['permissions_includes_inherited'] . ': &quot;' . implode('&quot;, &quot;', $rowData['children']) . '&quot;</span>';
201
							}
202
203
							return $group_name;
204
						},
205
					],
206
					'sort' => [
207
						'default' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, mg.group_name',
208
						'reverse' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, mg.group_name DESC',
209
					],
210
				],
211
				'members' => [
212
					'header' => [
213
						'value' => $txt['membergroups_members_top'],
214
						'class' => 'grid17',
215
					],
216
					'data' => [
217
						'function' => static function ($rowData) {
218
							global $txt;
219
220
							// No explicit members for guests and the moderator group.
221
							if (in_array($rowData['id_group'], [-1, 3]))
222
							{
223
								return $txt['membergroups_guests_na'];
224
							}
225
226
							if ($rowData['can_search'])
227
							{
228
								return '<a href="' . getUrl('action', ['action' => 'moderate', 'area' => 'viewgroups', 'sa' => 'members', 'group' => $rowData['id_group']]) . '">' . comma_format($rowData['num_members']) . '</a>';
229
							}
230
231
							return comma_format($rowData['num_members']);
232
						},
233
					],
234
					'sort' => [
235
						'default' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, 1',
236
						'reverse' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, 1 DESC',
237
					],
238
				],
239
				'permissions_allowed' => [
240
					'header' => [
241
						'value' => empty($modSettings['permission_enable_deny']) ? $txt['membergroups_permissions'] : $txt['permissions_allowed'],
242
						'class' => 'grid17',
243
					],
244
					'data' => [
245
						'function' => static fn($rowData) => $rowData['num_permissions']['allowed'],
246
					],
247
				],
248
				'permissions_denied' => [
249
					'evaluate' => !empty($modSettings['permission_enable_deny']),
250
					'header' => [
251
						'value' => $txt['permissions_denied'],
252
						'class' => 'grid17',
253
					],
254
					'data' => [
255
						'function' => static fn($rowData) => $rowData['num_permissions']['denied'],
256
					],
257
				],
258
				'modify' => [
259
					'header' => [
260
						'value' => $context['can_modify'] ? $txt['permissions_modify'] : $txt['permissions_view'],
261
						'class' => 'grid17',
262
					],
263
					'data' => [
264
						'function' => function ($rowData) {
265
							global $txt;
266
267
							if ($rowData['id_group'] != 1)
268
							{
269
								return '<a href="' . getUrl('action', ['action' => 'admin', 'area' => 'permissions', 'sa' => 'modify', 'group' => $rowData['id_group']] + ($this->_pid !== null ? ['pid' => $this->_pid] : [])) . '">' . $txt['membergroups_modify'] . '</a>';
270
							}
271
272
							return '';
273
						},
274
					],
275
				],
276
				'check' => [
277
					'header' => [
278
						'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />',
279
						'class' => 'centertext',
280
						'style' => 'width:4%;',
281
					],
282
					'data' => [
283
						'function' => static function ($rowData) {
284
							if ($rowData['id_group'] !== 1)
285
							{
286
								return '<input type="checkbox" name="group[]" value="' . $rowData['id_group'] . '" class="input_check" />';
287
							}
288
289
							return '';
290
						},
291
						'class' => 'centertext',
292
					],
293
				],
294
			],
295
		];
296
297
		createList($listOptions);
298
299
		// The second list shows the post-count-based groups...if enabled
300
		if (!empty($modSettings['permission_enable_postgroups']))
301
		{
302
			$listOptions = [
303
				'id' => 'post_count_membergroups_list',
304
				'title' => $txt['membergroups_post'],
305
				'base_href' => getUrl('admin', ['action' => 'admin', 'area' => 'permissions', 'sa' => 'index'] + ($this->_req->hasQuery('sort') ? ['sort' => $this->_req->getQuery('sort', 'trim')] : []) + ($this->_pid !== null ? ['pid' => $this->_pid] : [])),
306
				'default_sort_col' => 'required_posts',
307
				'request_vars' => [
308
					'sort' => 'sort2',
309
					'desc' => 'desc2',
310
				],
311
				'get_items' => [
312
					'file' => SUBSDIR . '/Membergroups.subs.php',
313
					'function' => 'list_getMembergroups',
314
					'params' => [
315
						'post_count',
316
						$this->user->id,
317
						allowedTo('manage_membergroups'),
318
						allowedTo('admin_forum'),
319
						false,
320
						true,
321
						$this->_pid,
322
					],
323
				],
324
				'columns' => [
325
					'name' => [
326
						'header' => [
327
							'value' => $txt['membergroups_name'],
328
							'class' => 'grid25',
329
						],
330
						'data' => [
331
							'function' => static fn($rowData) => sprintf('<a href="' . getUrl('admin', ['action' => 'admin', 'area' => 'permissions', 'sa' => 'members', 'group' => $rowData['id_group']]) . '">%1$s</a>', $rowData['group_name_color']),
332
						],
333
						'sort' => [
334
							'default' => 'mg.group_name',
335
							'reverse' => 'mg.group_name DESC',
336
						],
337
					],
338
					'required_posts' => [
339
						'header' => [
340
							'value' => $txt['membergroups_min_posts'],
341
							'class' => 'grid25',
342
						],
343
						'data' => [
344
							'db' => 'min_posts',
345
						],
346
						'sort' => [
347
							'default' => 'mg.min_posts',
348
							'reverse' => 'mg.min_posts DESC',
349
						],
350
					],
351
					'members' => [
352
						'header' => [
353
							'value' => $txt['membergroups_members_top'],
354
							'class' => 'grid10',
355
						],
356
						'data' => [
357
							'function' => static function ($rowData) {
358
								if ($rowData['can_search'])
359
								{
360
									return '<a href="' . getUrl('action', ['action' => 'moderate', 'area' => 'viewgroups', 'sa' => 'members', 'group' => $rowData['id_group']]) . '">' . comma_format($rowData['num_members']) . '</a>';
361
								}
362
363
								return comma_format($rowData['num_members']);
364
							},
365
						],
366
						'sort' => [
367
							'default' => '1 DESC',
368
							'reverse' => '1',
369
						],
370
					],
371
					'permissions_allowed' => [
372
						'header' => [
373
							'value' => empty($modSettings['permission_enable_deny']) ? $txt['membergroups_permissions'] : $txt['permissions_allowed'],
374
							'class' => 'grid8',
375
						],
376
						'data' => [
377
							'function' => static fn($rowData) => $rowData['num_permissions']['allowed'],
378
						],
379
					],
380
					'permissions_denied' => [
381
						'evaluate' => !empty($modSettings['permission_enable_deny']),
382
						'header' => [
383
							'value' => $txt['permissions_denied'],
384
							'class' => 'grid8',
385
						],
386
						'data' => [
387
							'function' => static fn($rowData) => $rowData['num_permissions']['denied'],
388
						],
389
					],
390
					'modify' => [
391
						'header' => [
392
							'value' => $txt['modify'],
393
							'class' => 'grid17',
394
						],
395
						'data' => [
396
							'function' => function ($rowData) {
397
								global $txt;
398
399
								if ($rowData['id_parent'] == -2)
400
								{
401
									return '<a href="' . getUrl('admin', ['action' => 'admin', 'area' => 'permissions', 'sa' => 'modify', 'group' => $rowData['id_group']] + ($this->_pid !== null ? ['pid' => $this->_pid] : [])) . '">' . $txt['membergroups_modify'] . '</a>';
402
								}
403
404
								return '<span class="smalltext">' . $txt['permissions_includes_inherited_from'] . '&quot;' . $rowData['parent_name'] . '&quot;</span>
405
										<br />
406
										<a href="' . getUrl('admin', ['action' => 'admin', 'area' => 'permissions', 'sa' => 'modify', 'group' => $rowData['id_parent']] + ($this->_pid !== null ? ['pid' => $this->_pid] : [])) . '">' . $txt['membergroups_modify_parent'] . '</a>';
407
							}
408
						],
409
					],
410
					'check' => [
411
						'header' => [
412
							'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />',
413
							'class' => 'centertext',
414
							'style' => 'width:4%;',
415
						],
416
						'data' => [
417
							'sprintf' => [
418
								'format' => '<input type="checkbox" name="group[]" value="%1$d" class="input_check" />',
419
								'params' => [
420
									'id_group' => false,
421
								],
422
							],
423
							'class' => 'centertext',
424
						],
425
					],
426
				],
427
			];
428
429
			createList($listOptions);
430
		}
431
432
		// pid = profile id
433
		if (!empty($this->_pid))
434
		{
435
			if (!isset($context['profiles'][$this->_pid]))
436
			{
437
				throw new Exception('no_access', false);
438
			}
439
440
			// Change the selected tab to better reflect that this really is a board profile.
441
			$context[$context['admin_menu_name']]['current_subsection'] = 'profiles';
442
443
			$context['profile'] = [
444
				'id' => $this->_pid,
445
				'name' => $context['profiles'][$this->_pid]['name'],
446
			];
447
		}
448
449
		$context['groups'] = array_merge([0 => $txt['membergroups_members']], getInheritableGroups());
450
451
		// Load the proper template.
452
		$context['sub_template'] = 'permission_index';
453
		createToken('admin-mpq');
454
	}
455
456
	/**
457
	 * Handle permissions by board... more or less. :P
458
	 */
459
	public function action_board(): void
460
	{
461
		global $context, $txt;
462
463
		require_once(SUBSDIR . '/ManagePermissions.subs.php');
464
465
		$context['page_title'] = $txt['permissions_boards'];
466
		$context['edit_all'] = $this->_req->hasQuery('edit');
467
468
		// Saving?
469
		if (!empty($this->_req->post->save_changes) && !empty($this->_req->post->boardprofile))
470
		{
471
			checkSession('request');
472
			validateToken('admin-mpb');
473
474
			$changes = [];
475
			foreach ($this->_req->post->boardprofile as $board => $profile)
476
			{
477
				$changes[(int) $profile][] = (int) $board;
478
			}
479
480
			foreach ($changes as $profile => $list_boards)
481
			{
482
				assignPermissionProfileToBoard($profile, $list_boards);
483
			}
484
485
			$context['edit_all'] = false;
486
		}
487
488
		// Load all permission profiles.
489
		loadPermissionProfiles();
490
491
		if (!$context['edit_all'])
492
		{
493
			$js = 'new Array(';
494
			foreach ($context['profiles'] as $id => $profile)
495
			{
496
				$js .= '{name: ' . JavaScriptEscape($profile['name']) . ', id: ' . $id . '},';
497
			}
498
499
			theme()->addJavascriptVar([
500
				'permission_profiles' => substr($js, 0, -1) . ')',
501
				'txt_save' => JavaScriptEscape($txt['save']),
502
			]);
503
		}
504
505
		// Get the board tree.
506
		$boardTree = new BoardsTree(database());
507
		$boardList = $boardTree->getBoardList();
508
		$cat_tree = $boardTree->getCategories();
509
		$boards = $boardTree->getBoards();
510
511
		// Build the list of the boards.
512
		$context['categories'] = [];
513
		$bbc_parser = ParserWrapper::instance();
514
		foreach ($cat_tree as $catid => $tree)
515
		{
516
			$context['categories'][$catid] = [
517
				'name' => &$tree['node']['name'],
518
				'id' => &$tree['node']['id'],
519
				'boards' => []
520
			];
521
			foreach ($boardList[$catid] as $boardid)
522
			{
523
				$boards[$boardid]['description'] = $bbc_parser->parseBoard($boards[$boardid]['description']);
524
525
				if (!isset($context['profiles'][$boards[$boardid]['profile']]))
526
				{
527
					$boards[$boardid]['profile'] = 1;
528
				}
529
530
				$context['categories'][$catid]['boards'][$boardid] = [
531
					'id' => &$boards[$boardid]['id'],
532
					'name' => &$boards[$boardid]['name'],
533
					'description' => &$boards[$boardid]['description'],
534
					'child_level' => &$boards[$boardid]['level'],
535
					'profile' => &$boards[$boardid]['profile'],
536
					'profile_name' => $context['profiles'][$boards[$boardid]['profile']]['name'],
537
				];
538
			}
539
		}
540
541
		$context['sub_template'] = 'by_board';
542
		createToken('admin-mpb');
543
	}
544
545
	/**
546
	 * Handles permission modification actions from the upper part of the
547
	 * permission manager index.
548
	 *
549
	 * @throws Exception
550
	 */
551
	public function action_quick(): void
552
	{
553
		checkSession();
554
		validateToken('admin-mpq', 'quick');
555
556
		// we'll need to init illegal permissions, update permissions, etc.
557
		require_once(SUBSDIR . '/ManagePermissions.subs.php');
558
559
		// Make sure only one of the quick options was selected.
560
		if ((!empty($this->_req->post->predefined) && ((isset($this->_req->post->copy_from) && $this->_req->post->copy_from !== 'empty') || !empty($this->_req->post->permissions))) || (!empty($this->_req->post->copy_from) && $this->_req->post->copy_from !== 'empty' && !empty($this->_req->post->permissions)))
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! empty($this->_req->po...req->post->permissions), Probably Intended Meaning: ! empty($this->_req->pos...eq->post->permissions))
Loading history...
561
		{
562
			throw new Exception('permissions_only_one_option', false);
563
		}
564
565
		if (empty($this->_req->post->group) || !is_array($this->_req->post->group))
566
		{
567
			$this->_req->post->group = [];
568
		}
569
570
		// Only accept numeric values for selected membergroups.
571
		$this->_req->post->group = array_map('intval', $this->_req->post->group);
572
		$this->_req->post->group = array_unique($this->_req->post->group);
573
574
		$this->_pid = $this->_req->getQuery('pid', 'intval', 0);
575
576
		// Fix up the old global to the new default!
577
		$bid = max(1, $this->_pid);
578
579
		// No modifying the predefined profiles.
580
		if ($this->_pid > 1 && $this->_pid < 5)
581
		{
582
			throw new Exception('no_access', false);
583
		}
584
585
		// Clear out any cached authority.
586
		updateSettings(['settings_updated' => time()]);
587
588
		// No groups where selected.
589
		if (empty($this->_req->post->group))
590
		{
591
			redirectexit('action=admin;area=permissions;pid=' . $this->_pid);
592
		}
593
594
		// Set a predefined permission profile.
595
		if (!empty($this->_req->post->predefined))
596
		{
597
			// Make sure it's a predefined permission set we expect.
598
			if (!in_array($this->_req->post->predefined, ['restrict', 'standard', 'moderator', 'maintenance']))
599
			{
600
				redirectexit('action=admin;area=permissions;pid=' . $this->_pid);
601
			}
602
603
			foreach ($this->_req->post->group as $group_id)
604
			{
605
				if (!empty($this->_pid))
606
				{
607
					setPermissionLevel($this->_req->post->predefined, $group_id, $this->_pid);
608
				}
609
				else
610
				{
611
					setPermissionLevel($this->_req->post->predefined, $group_id);
612
				}
613
			}
614
		}
615
		// Set a permission profile based on the permissions of a selected group.
616
		elseif ($this->_req->post->copy_from !== 'empty')
617
		{
618
			// Just checking the input.
619
			if (!is_numeric($this->_req->post->copy_from))
620
			{
621
				redirectexit('action=admin;area=permissions;pid=' . $this->_pid);
622
			}
623
624
			// Make sure the group we're copying to is never included.
625
			$this->_req->post->group = array_diff($this->_req->post->group, [$this->_req->post->copy_from]);
626
627
			// No groups left? Too bad.
628
			if (empty($this->_req->post->group))
629
			{
630
				redirectexit('action=admin;area=permissions;pid=' . $this->_pid);
631
			}
632
633
			if (empty($this->_pid))
634
			{
635
				copyPermission($this->_req->post->copy_from, $this->_req->post->group, $this->illegal_permissions, $this->illegal_guest_permissions);
636
			}
637
638
			// Now do the same for the board permissions.
639
			copyBoardPermission($this->_req->post->copy_from, $this->_req->post->group, $bid, $this->illegal_guest_permissions);
640
641
			// Update any children out there!
642
			$this->permissionsObject->updateChild($this->_req->post->group, $this->_pid);
643
		}
644
		// Set or unset a certain permission for the selected groups.
645
		elseif (!empty($this->_req->post->permissions))
646
		{
647
			// Unpack two variables that were transported.
648
			[$permissionType, $permission] = explode('/', $this->_req->post->permissions);
649
650
			// Check whether our input is within the expected range.
651
			if (!in_array($this->_req->post->add_remove, ['add', 'clear', 'deny']) || !in_array($permissionType, ['membergroup', 'board']))
652
			{
653
				redirectexit('action=admin;area=permissions;pid=' . $this->_pid);
654
			}
655
656
			if ($this->_req->post->add_remove === 'clear')
657
			{
658
				if ($permissionType === 'membergroup')
659
				{
660
					deletePermission($this->_req->post->group, $permission, $this->illegal_permissions);
661
				}
662
				else
663
				{
664
					deleteBoardPermission($this->_req->post->group, $bid, $permission);
665
				}
666
			}
667
			// Add a permission (either 'set' or 'deny').
668
			else
669
			{
670
				$add_deny = $this->_req->post->add_remove === 'add' ? '1' : '0';
671
				$permChange = [];
672
				foreach ($this->_req->post->group as $groupID)
673
				{
674
					if ($groupID == -1 && in_array($permission, $this->illegal_guest_permissions))
675
					{
676
						continue;
677
					}
678
679
					if ($permissionType === 'membergroup' && $groupID != 1 && $groupID != 3 && (empty($this->illegal_permissions) || !in_array($permission, $this->illegal_permissions)))
680
					{
681
						$permChange[] = [$permission, $groupID, $add_deny];
682
					}
683
					elseif ($permissionType !== 'membergroup')
684
					{
685
						$permChange[] = [$permission, $groupID, $add_deny, $bid];
686
					}
687
				}
688
689
				if (!empty($permChange))
690
				{
691
					if ($permissionType === 'membergroup')
692
					{
693
						replacePermission($permChange);
694
					}
695
					// Board permissions go into the other table.
696
					else
697
					{
698
						replaceBoardPermission($permChange);
699
					}
700
				}
701
			}
702
703
			// Another child update!
704
			$this->permissionsObject->updateChild($this->_req->post->group, $this->_pid);
705
		}
706
707
		redirectexit('action=admin;area=permissions;pid=' . $this->_pid);
708
	}
709
710
	/**
711
	 * Initializes the necessary to modify a membergroup's permissions.
712
	 */
713
	public function action_modify(): void
714
	{
715
		global $context, $txt;
716
717
		if (!$this->_req->hasQuery('group'))
718
		{
719
			throw new Exception('no_access', false);
720
		}
721
722
		require_once(SUBSDIR . '/ManagePermissions.subs.php');
723
		$context['group']['id'] = $this->_req->getQuery('group', 'intval', 0);
724
725
		// It's not likely you'd end up here with this setting disabled.
726
		if ($context['group']['id'] === 1)
727
		{
728
			redirectexit('action=admin;area=permissions');
729
		}
730
731
		loadAllPermissions();
732
		loadPermissionProfiles();
733
734
		if ($context['group']['id'] > 0)
735
		{
736
			require_once(SUBSDIR . '/Membergroups.subs.php');
737
738
			$group = membergroupById($context['group']['id'], true);
739
			$context['group']['name'] = $group['group_name'];
740
			$parent = $group['id_parent'];
741
742
			// Cannot edit an inherited group!
743
			if ($parent != -2)
744
			{
745
				throw new Exception('cannot_edit_permissions_inherited');
746
			}
747
		}
748
		elseif ($context['group']['id'] == -1)
749
		{
750
			$context['group']['name'] = $txt['membergroups_guests'];
751
		}
752
		else
753
		{
754
			$context['group']['name'] = $txt['membergroups_members'];
755
		}
756
757
		$context['profile']['id'] = $this->_req->getQuery('pid', 'intval', 0);
758
759
		// If this is a moderator, and they are editing "no profile," then we only do boards.
760
		if ($context['group']['id'] == 3 && empty($context['profile']['id']))
761
		{
762
			// For sanity just check they have no general permissions.
763
			removeModeratorPermissions();
764
765
			$context['profile']['id'] = 1;
766
		}
767
768
		$context['permission_type'] = empty($context['profile']['id']) ? 'membergroup' : 'board';
769
		$context['profile']['can_modify'] = !$context['profile']['id'] || $context['profiles'][$context['profile']['id']]['can_modify'];
770
771
		// Set up things a little nicer for board-related stuff...
772
		if ($context['permission_type'] === 'board')
773
		{
774
			$context['profile']['name'] = $context['profiles'][$context['profile']['id']]['name'];
775
			$context[$context['admin_menu_name']]['current_subsection'] = 'profiles';
776
		}
777
778
		// Fetch the current permissions.
779
		$permissions = [
780
			'membergroup' => ['allowed' => [], 'denied' => []],
781
			'board' => ['allowed' => [], 'denied' => []]
782
		];
783
784
		// General permissions?
785
		if ($context['permission_type'] === 'membergroup')
786
		{
787
			$permissions['membergroup'] = fetchPermissions($context['group']['id']);
788
		}
789
790
		// Fetch current board permissions...
791
		$permissions['board'] = fetchBoardPermissions($context['group']['id'], $context['permission_type'], $context['profile']['id']);
792
793
		// Loop through each permission and set whether it's checked.
794
		foreach ($context['permissions'] as $permissionType => $tmp)
795
		{
796
			foreach ($tmp['columns'] as $position => $permissionGroups)
797
			{
798
				foreach ($permissionGroups as $permissionGroup => $permissionArray)
799
				{
800
					foreach ($permissionArray['permissions'] as $perm)
801
					{
802
						// Create a shortcut for the current permission.
803
						$curPerm = &$context['permissions'][$permissionType]['columns'][$position][$permissionGroup]['permissions'][$perm['id']];
804
805
						if ($perm['has_own_any'])
806
						{
807
							$curPerm['any']['select'] = in_array($perm['id'] . '_any', $permissions[$permissionType]['allowed']) ? 'on' : (in_array($perm['id'] . '_any', $permissions[$permissionType]['denied']) ? 'denied' : 'off');
808
							$curPerm['own']['select'] = in_array($perm['id'] . '_own', $permissions[$permissionType]['allowed']) ? 'on' : (in_array($perm['id'] . '_own', $permissions[$permissionType]['denied']) ? 'denied' : 'off');
809
						}
810
						else
811
						{
812
							$curPerm['select'] = in_array($perm['id'], $permissions[$permissionType]['denied']) ? 'denied' : (in_array($perm['id'], $permissions[$permissionType]['allowed']) ? 'on' : 'off');
813
						}
814
					}
815
				}
816
			}
817
		}
818
819
		$context['sub_template'] = 'modify_group';
820
		$context['page_title'] = $txt['permissions_modify_group'];
821
822
		createToken('admin-mp');
823
	}
824
825
	/**
826
	 * This function actually saves modifications to a membergroup's board permissions.
827
	 */
828
	public function action_modify2(): void
829
	{
830
		checkSession();
831
		validateToken('admin-mp');
832
833
		// We'll need to init illegal permissions, update child permissions, etc.
834
		require_once(SUBSDIR . '/ManagePermissions.subs.php');
835
836
		$current_group_id = (int) $this->_req->query->group;
837
		$this->_pid = $this->_req->getQuery('pid', 'intval');
838
839
		// Cannot modify predefined profiles.
840
		if ($this->_pid > 1 && $this->_pid < 5)
841
		{
842
			throw new Exception('no_access', false);
843
		}
844
845
		// Verify this isn't inherited.
846
		if ($current_group_id === -1 || $current_group_id === 0)
847
		{
848
			$parent = -2;
849
		}
850
		else
851
		{
852
			require_once(SUBSDIR . '/Membergroups.subs.php');
853
			$group = membergroupById($current_group_id, true);
854
			$parent = $group['id_parent'];
855
		}
856
857
		if ($parent != -2)
858
		{
859
			throw new Exception('cannot_edit_permissions_inherited');
860
		}
861
862
		$givePerms = ['membergroup' => [], 'board' => []];
863
864
		// Guest group, we need illegal, guest permissions.
865
		if ($current_group_id === -1)
866
		{
867
			$this->illegal_permissions = array_merge($this->illegal_permissions, $this->illegal_guest_permissions);
868
		}
869
870
		// Prepare all permissions that were set or denied for addition to the DB.
871
		if (isset($this->_req->post->perm) && is_array($this->_req->post->perm))
872
		{
873
			foreach ($this->_req->post->perm as $perm_type => $perm_array)
874
			{
875
				if (is_array($perm_array))
876
				{
877
					foreach ($perm_array as $permission => $value)
878
					{
879
						if ($value === 'on' || $value === 'deny')
880
						{
881
							// Don't allow people to escalate themselves!
882
							if (in_array($permission, $this->illegal_permissions))
883
							{
884
								continue;
885
							}
886
887
							$givePerms[$perm_type][] = [$permission, $current_group_id, $value === 'deny' ? 0 : 1];
888
						}
889
					}
890
				}
891
			}
892
		}
893
894
		// Insert the general permissions.
895
		if ($current_group_id !== 3 && empty($this->_pid))
896
		{
897
			deleteInvalidPermissions($current_group_id, $this->illegal_permissions);
898
899
			if (isset($givePerms['membergroup']) && $givePerms['membergroup'] !== [])
900
			{
901
				replacePermission($givePerms['membergroup']);
902
			}
903
		}
904
905
		// Insert the boardpermissions.
906
		$profileid = max(1, $this->_pid);
907
		deleteAllBoardPermissions([$current_group_id], $profileid);
908
909
		if (isset($givePerms['board']) && $givePerms['board'] !== [])
910
		{
911
			foreach (array_keys($givePerms['board']) as $k)
912
			{
913
				$givePerms['board'][$k][] = $profileid;
914
			}
915
916
			replaceBoardPermission($givePerms['board']);
917
		}
918
919
		// Update any inherited permissions as required.
920
		$this->permissionsObject->updateChild($current_group_id, $this->_pid);
921
922
		// Clear cached privs.
923
		updateSettings(['settings_updated' => time()]);
924
925
		redirectexit('action=admin;area=permissions;pid=' . $this->_pid);
926
	}
927
928
	/**
929
	 * A screen to set some general settings for permissions.
930
	 *
931
	 * @event integrate_save_permission_settings
932
	 */
933
	public function action_permSettings_display(): void
934
	{
935
		global $context, $modSettings, $txt;
936
937
		require_once(SUBSDIR . '/ManagePermissions.subs.php');
938
939
		// Initialize the form
940
		$settingsForm = new SettingsForm(SettingsForm::DB_ADAPTER);
941
942
		// Initialize it with our settings
943
		$settingsForm->setConfigVars($this->_settings());
944
945
		// Some items for the template
946
		$context['page_title'] = $txt['permission_settings_title'];
947
		$context['sub_template'] = 'show_settings';
948
		$context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'permissions', 'save', 'sa' => 'settings']);
949
950
		// Saving the settings?
951
		if ($this->_req->hasQuery('save'))
952
		{
953
			checkSession();
954
			call_integration_hook('integrate_save_permission_settings');
955
			$settingsForm->setConfigValues((array) $this->_req->post);
956
			$settingsForm->save();
957
958
			// Clear all deny permissions...if we want that.
959
			if (empty($modSettings['permission_enable_deny']))
960
			{
961
				clearDenyPermissions();
962
			}
963
964
			// Make sure there are no postgroup-based permissions left.
965
			if (empty($modSettings['permission_enable_postgroups']))
966
			{
967
				clearPostgroupPermissions();
968
			}
969
970
			redirectexit('action=admin;area=permissions;sa=settings');
971
		}
972
973
		// We need this for the in-line permissions
974
		createToken('admin-mp');
975
976
		$settingsForm->prepare();
977
	}
978
979
	/**
980
	 * Simple function to return settings in config_vars format.
981
	 *
982
	 * @event integrate_modify_permission_settings
983
	 */
984
	private function _settings()
985
	{
986
		// All the setting variables
987
		$config_vars = [
988
			['title', 'settings'],
989
			// Inline permissions.
990
			['permissions', 'manage_permissions', 'collapsed' => true],
991
			'',
992
			// A few useful settings
993
			['check', 'permission_enable_deny'],
994
			['check', 'permission_enable_postgroups'],
995
		];
996
997
		// Add new settings with a nice hook, makes them available for admin settings search as well
998
		call_integration_hook('integrate_modify_permission_settings', [&$config_vars]);
999
1000
		return $config_vars;
1001
	}
1002
1003
	/**
1004
	 * Return the permission settings for use in admin search
1005
	 */
1006
	public function settings_search()
1007
	{
1008
		return $this->_settings();
1009
	}
1010
1011
	/**
1012
	 * Add/Edit/Delete profiles.
1013
	 */
1014
	public function action_profiles(): void
1015
	{
1016
		global $context, $txt;
1017
1018
		require_once(SUBSDIR . '/ManagePermissions.subs.php');
1019
1020
		// Set up the template, first for fun.
1021
		$context['page_title'] = $txt['permissions_profile_edit'];
1022
		$context['sub_template'] = 'edit_profiles';
1023
1024
		// If we're creating a new one, do it first.
1025
		if (isset($this->_req->post->create) && trim($this->_req->post->profile_name) !== '')
1026
		{
1027
			checkSession();
1028
			validateToken('admin-mpp');
1029
			copyPermissionProfile($this->_req->post->profile_name, (int) $this->_req->post->copy_from);
1030
		}
1031
		// Renaming?
1032
		elseif (isset($this->_req->post->rename))
1033
		{
1034
			checkSession();
1035
			validateToken('admin-mpp');
1036
1037
			// Just showing the boxes?
1038
			if (!isset($this->_req->post->rename_profile))
1039
			{
1040
				$context['show_rename_boxes'] = true;
1041
			}
1042
			else
1043
			{
1044
				foreach ($this->_req->post->rename_profile as $id => $name)
1045
				{
1046
					renamePermissionProfile($id, $name);
1047
				}
1048
			}
1049
		}
1050 2
		// Deleting?
1051
		elseif (isset($this->_req->post->delete) && !empty($this->_req->post->delete_profile))
1052
		{
1053
			checkSession();
1054 2
			validateToken('admin-mpp');
1055
1056
			$profiles = [];
1057
			foreach ($this->_req->post->delete_profile as $profile)
1058
			{
1059
				if ($profile > 4)
1060
				{
1061
					$profiles[] = (int) $profile;
1062
				}
1063
			}
1064 2
1065
			deletePermissionProfiles($profiles);
1066 2
		}
1067
1068
		// Clearly, we'll need this!
1069
		loadPermissionProfiles();
1070
1071
		// Work out what ones are in use.
1072 2
		$context['profiles'] = permProfilesInUse($context['profiles']);
1073
1074 2
		// What can we do with these?
1075
		$context['can_edit_something'] = false;
1076
		foreach ($context['profiles'] as $id => $profile)
1077
		{
1078
			// Can't delete special ones.
1079
			$context['profiles'][$id]['can_edit'] = !isset($txt['permissions_profile_' . $profile['unformatted_name']]);
1080
			if ($context['profiles'][$id]['can_edit'])
1081
			{
1082
				$context['can_edit_something'] = true;
1083
			}
1084
1085
			// You can only delete it if you can edit it AND it's not in use.
1086
			$context['profiles'][$id]['can_delete'] = $context['profiles'][$id]['can_edit'] && empty($profile['in_use']);
1087
		}
1088
1089
		theme()->addJavascriptVar([
1090
			'txt_permissions_commit' => $txt['permissions_commit'],
1091
			'txt_permissions_profile_rename' => $txt['permissions_profile_rename'],
1092
		], true);
1093
		createToken('admin-mpp');
1094
	}
1095
1096
	/**
1097
	 * Present a nice way of applying post-moderation.
1098
	 *
1099
	 * @event integrate_post_moderation_mapping passed $mappings to add other post-moderation values
1100
	 */
1101
	public function action_postmod(): void
1102
	{
1103
		global $context, $txt;
1104
1105
		require_once(SUBSDIR . '/ManagePermissions.subs.php');
1106
1107
		// Just in case.
1108
		checkSession('get');
1109
1110
		$context['page_title'] = $txt['permissions_post_moderation'];
1111
		$context['sub_template'] = 'postmod_permissions';
1112
		$context['current_profile'] = $this->_req->getPost('pid', 'intval', 1);
1113
1114
		// Load all the permission profiles.
1115
		loadPermissionProfiles();
1116
1117
		// Mappings, our key => array(can_do_moderated, can_do_all)
1118
		$mappings = [
1119
			'new_topic' => ['post_new', 'post_unapproved_topics'],
1120
			'replies_own' => ['post_reply_own', 'post_unapproved_replies_own'],
1121
			'replies_any' => ['post_reply_any', 'post_unapproved_replies_any'],
1122
			'attachment' => ['post_attachment', 'post_unapproved_attachments'],
1123
		];
1124
1125
		call_integration_hook('integrate_post_moderation_mapping', [&$mappings]);
1126
1127
		// Load the groups.
1128
		require_once(SUBSDIR . '/Membergroups.subs.php');
1129
		$context['profile_groups'] = prepareMembergroupPermissions();
1130
1131
		// What are the permissions we are querying?
1132
		$all_permissions = [];
1133
		foreach ($mappings as $perm_set)
1134
		{
1135
			$all_permissions = array_merge($all_permissions, $perm_set);
1136
		}
1137
1138
		// If we're saving the changes, then do just that - save them.
1139
		if (!empty($this->_req->post->save_changes) && ($context['current_profile'] == 1 || $context['current_profile'] > 4))
1140
		{
1141
			validateToken('admin-mppm');
1142
1143
			// Start by deleting all the permissions relevant.
1144
			deleteBoardPermissions($context['profile_groups'], $context['current_profile'], $all_permissions);
1145
1146
			// Do it group by group.
1147
			$new_permissions = [];
1148
			foreach ($context['profile_groups'] as $group)
1149
			{
1150
				foreach ($mappings as $index => $data)
1151
				{
1152
					$temp = $this->_req->post->{$index};
1153
					if (isset($temp[$group['id']]))
1154
					{
1155
						if ($temp[$group['id']] === 'allow')
1156
						{
1157
							// Give them both sets for fun.
1158
							$new_permissions[] = [$context['current_profile'], $group['id'], $data[0], 1];
1159
							$new_permissions[] = [$context['current_profile'], $group['id'], $data[1], 1];
1160
						}
1161
						elseif ($temp[$group['id']] === 'moderate')
1162
						{
1163
							$new_permissions[] = [$context['current_profile'], $group['id'], $data[1], 1];
1164
						}
1165
					}
1166
				}
1167
			}
1168
1169
			// Insert new permissions.
1170
			if (!empty($new_permissions))
1171
			{
1172
				insertBoardPermission($new_permissions);
1173
			}
1174
		}
1175
1176
		// Now get all the permissions!
1177
		$perm = getPermission(array_keys($context['profile_groups']), $context['current_profile'], $all_permissions);
1178
1179
		foreach ($perm as $id_group => $row)
1180
		{
1181
			foreach ($mappings as $key => $data)
1182
			{
1183
				foreach ($data as $index => $perm)
1184
				{
1185
					// Only bother if it's not denied.
1186
					if (empty($row['add']))
1187
					{
1188
						continue;
1189
					}
1190
1191
					if (!in_array($perm, $row['add']))
1192
					{
1193
						continue;
1194
					}
1195
1196
					// Full allowance?
1197
					if ($index == 0)
1198
					{
1199
						$context['profile_groups'][$id_group][$key] = 'allow';
1200
					}
1201
					// Otherwise only bother with moderate if not on an allow.
1202
					elseif ($context['profile_groups'][$id_group][$key] !== 'allow')
1203
					{
1204
						$context['profile_groups'][$id_group][$key] = 'moderate';
1205
					}
1206
				}
1207
			}
1208
		}
1209
1210
		createToken('admin-mppm');
1211
	}
1212
}
1213