Issues (1686)

ElkArte/AdminController/ManagePermissions.php (2 issues)

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