ManageMembergroups   F
last analyzed

Complexity

Total Complexity 117

Size/Duplication

Total Lines 888
Duplicated Lines 0 %

Test Coverage

Coverage 1.83%

Importance

Changes 0
Metric Value
eloc 416
c 0
b 0
f 0
dl 0
loc 888
ccs 6
cts 327
cp 0.0183
rs 2
wmc 117

8 Methods

Rating   Name   Duplication   Size   Complexity  
A settings_search() 0 3 1
F action_add() 0 147 33
A action_groupSettings_display() 0 27 2
A action_index() 0 53 2
F action_edit() 0 304 66
A action_delete() 0 9 1
C action_list() 0 230 11
A _settings() 0 11 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
/**
4
 * Handles the administration page for membergroups.
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * This file contains code covered by:
11
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
12
 *
13
 * @version 2.0 dev
14
 *
15
 */
16
17
namespace ElkArte\AdminController;
18
19
use ElkArte\AbstractController;
20
use ElkArte\Action;
21
use ElkArte\Controller\Groups;
22
use ElkArte\Exceptions\Exception;
23
use ElkArte\Helper\DataValidator;
24
use ElkArte\Languages\Txt;
25
use ElkArte\Permissions;
26
use ElkArte\SettingsForm\SettingsForm;
27
28
/**
29
 * ManageMembergroups controller, administration page for membergroups.
30
 *
31
 * @package Membergroups
32
 */
33
class ManageMembergroups extends AbstractController
34
{
35
	/**
36
	 * Main dispatcher, the en\trance point for all 'Manage Membergroup' actions.
37
	 *
38
	 * What it does:
39
	 *
40
	 * - It forwards to a function based on the given subaction, default being subaction 'index', or, without manage_membergroup
41
	 * permissions, then 'settings'.
42
	 * - Called by ?action=admin;area=membergroups.
43
	 * - Requires the manage_membergroups or the admin_forum permission.
44
	 *
45
	 * @event integrate_sa_manage_membergroups Used to add more sub actions
46
	 * @uses ManageMembergroups template.
47
	 * @uses ManageMembers language file.
48
	 * @see AbstractController::action_index()
49
	 */
50
	public function action_index()
51
	{
52
		global $context, $txt;
53
54
		// Language and template stuff, the usual.
55
		Txt::load('ManageMembers');
56
		theme()->getTemplates()->load('ManageMembergroups');
57
58
		$subActions = array(
59
			'add' => array(
60
				'controller' => $this,
61
				'function' => 'action_add',
62
				'permission' => 'manage_membergroups'),
63
			'delete' => array(
64
				'controller' => $this,
65
				'function' => 'action_delete',
66
				'permission' => 'manage_membergroups'),
67
			'edit' => array(
68
				'controller' => $this,
69
				'function' => 'action_edit',
70
				'permission' => 'manage_membergroups'),
71
			'index' => array(
72
				'controller' => $this,
73
				'function' => 'action_list',
74
				'permission' => 'manage_membergroups'),
75
			'members' => array(
76
				'controller' => Groups::class,
77
				'function' => 'action_index',
78
				'permission' => 'manage_membergroups'),
79
			'settings' => array(
80
				'controller' => $this,
81
				'function' => 'action_groupSettings_display',
82
				'permission' => 'admin_forum'),
83
		);
84
85
		$action = new Action('manage_membergroups');
86
87
		// Setup the admin tabs.
88
		$context[$context['admin_menu_name']]['object']->prepareTabData([
89
			'title' => 'membergroups_title',
90
			'description' => 'membergroups_description',
91
			'help' => 'membergroups']
92
		);
93
94
		// Set that subaction, call integrate_sa_manage_membergroups
95
		$subAction = $action->initialize($subActions, allowedTo('manage_membergroups') ? 'index' : 'settings');
96
97
		// Final items for the template
98
		$context['page_title'] = $txt['membergroups_title'];
99
		$context['sub_action'] = $subAction;
100
101
		// Call the right function.
102
		$action->dispatch($subAction);
103
	}
104
105
	/**
106
	 * Shows an overview of the current membergroups.
107
	 *
108
	 * What it does:
109
	 *
110
	 * - Called by ?action=admin;area=membergroups.
111
	 * - Requires the manage_membergroups permission.
112
	 * - Splits the membergroups in regular ones and post count based groups.
113
	 * - It also counts the number of members part of each membergroup.
114
	 *
115
	 * @event integrate_list_regular_membergroups_list
116
	 * @event integrate_list_post_count_membergroups_list
117
	 * @uses ManageMembergroups template, main.
118
	 */
119
	public function action_list()
120
	{
121
		global $txt, $context;
122
123
		$context['page_title'] = $txt['membergroups_title'];
124
125
		// The first list shows the regular membergroups.
126
		$listOptions = array(
127
			'id' => 'regular_membergroups_list',
128
			'title' => $txt['membergroups_regular'],
129
			'base_href' => getUrl('admin', ['action' => 'admin', 'area' => 'membergroups'] + (isset($this->_req->query->sort2) ? ['sort2' => urlencode($this->_req->query->sort2)] : [])),
130
			'default_sort_col' => 'name',
131
			'get_items' => array(
132
				'file' => SUBSDIR . '/Membergroups.subs.php',
133
				'function' => 'list_getMembergroups',
134
				'params' => array(
135
					'regular',
136
					$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...
137
					allowedTo('manage_membergroups'),
138
					allowedTo('admin_forum'),
139
				),
140
			),
141
			'columns' => array(
142
				'name' => array(
143
					'header' => array(
144
						'value' => $txt['membergroups_name'],
145
					),
146
					'data' => array(
147
						'function' => static function ($rowData) {
148
							// Since the moderator group has no explicit members, no link is needed.
149
							if ($rowData['id_group'] === 3)
150
							{
151
								$group_name = $rowData['group_name'];
152
							}
153
							else
154
							{
155
								$group_name = sprintf('<a href="' . getUrl('admin', ['action' => 'admin', 'area' => 'membergroups', 'sa' => 'members', 'group' => $rowData['id_group']]) . '">%1$s</a>', $rowData['group_name_color']);
156
							}
157
158
							// Add a help option for moderator and administrator.
159
							if ($rowData['id_group'] === 1)
160
							{
161
								$group_name .= ' <a href="' . getUrl('action', ['action' => 'quickhelp', 'help' => 'membergroup_administrator']) . '" onclick="return reqOverlayDiv(this.href);" class="helpicon i-help"></a>';
162
							}
163
							elseif ($rowData['id_group'] === 3)
164
							{
165
								$group_name .= ' <a href="' . getUrl('action', ['action' => 'quickhelp', 'help' => 'membergroup_moderator']) . '" onclick="return reqOverlayDiv(this.href);" class="helpicon i-help"></a>';
166
							}
167
							return $group_name;
168
						},
169
					),
170
					'sort' => array(
171
						'default' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, mg.group_name',
172
						'reverse' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, mg.group_name DESC',
173
					),
174
				),
175
				'icons' => array(
176
					'header' => array(
177
						'value' => $txt['membergroups_icons'],
178
					),
179
					'data' => array(
180
						'function' => static function ($rowData) {
181
							global $settings;
182
183
							if (empty($rowData['icons'][0]))
184
							{
185
								return '';
186
							}
187
188
							if (empty($rowData['icons'][1]))
189
							{
190
								return '';
191
							}
192
193
							return str_repeat('<img src="' . $settings['images_url'] . '/group_icons/' . $rowData['icons'][1] . '" alt="*" />', $rowData['icons'][0]);
194
						},
195
					),
196
					'sort' => array(
197
						'default' => 'mg.icons',
198
						'reverse' => 'mg.icons DESC',
199
					)
200
				),
201
				'members' => array(
202
					'header' => array(
203
						'value' => $txt['membergroups_members_top'],
204
					),
205
					'data' => array(
206
						'function' => static function ($rowData) {
207
							global $txt;
208
							// No explicit members for the moderator group.
209
							return $rowData['id_group'] === 3 ? $txt['membergroups_guests_na'] : comma_format($rowData['num_members']);
210
						},
211
					),
212
					'sort' => array(
213
						'default' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, 1',
214
						'reverse' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, 1 DESC',
215
					),
216
				),
217
				'modify' => array(
218
					'header' => array(
219
						'value' => $txt['modify'],
220
					),
221
					'data' => array(
222
						'sprintf' => array(
223
							'format' => '<a href="' . getUrl('admin', ['action' => 'admin', 'area' => 'membergroups', 'sa' => 'edit', 'group' => '']) . '%1$d">' . $txt['membergroups_modify'] . '</a>',
224
							'params' => array(
225
								'id_group' => false,
226
							),
227
						),
228
					),
229
				),
230
			),
231
			'additional_rows' => array(
232
				array(
233
					'position' => 'below_table_data',
234
					'class' => 'submitbutton',
235
					'value' => '<a class="linkbutton" href="' . getUrl('admin', ['action' => 'admin', 'area' => 'membergroups', 'sa' => 'add', 'generalgroup']) . '">' . $txt['membergroups_add_group'] . '</a>',
236
				),
237
			),
238
		);
239
240
		createList($listOptions);
241
242
		// The second list shows the post count based groups.
243
		$listOptions = array(
244
			'id' => 'post_count_membergroups_list',
245
			'title' => $txt['membergroups_post'],
246
			'base_href' => getUrl('admin', ['action' => 'admin', 'area' => 'membergroups'] + (isset($this->_req->query->sort) ? ['sort' => urlencode($this->_req->query->sort)] : [])),
247
			'default_sort_col' => 'required_posts',
248
			'request_vars' => array(
249
				'sort' => 'sort2',
250
				'desc' => 'desc2',
251
			),
252
			'get_items' => array(
253
				'file' => SUBSDIR . '/Membergroups.subs.php',
254
				'function' => 'list_getMembergroups',
255
				'params' => array(
256
					'post_count',
257
					$this->user->id,
258
					allowedTo('manage_membergroups'),
259
					allowedTo('admin_forum'),
260
				),
261
			),
262
			'columns' => array(
263
				'name' => array(
264
					'header' => array(
265
						'value' => $txt['membergroups_name'],
266
					),
267
					'data' => array(
268
						'function' => static fn($rowData) => sprintf('<a href="' . getUrl('admin', ['action' => 'admin', 'area' => 'membergroups', 'sa' => 'members', 'group' => $rowData['id_group']]) . '">%1$s</a>', $rowData['group_name_color']),
269
					),
270
					'sort' => array(
271
						'default' => 'mg.group_name',
272
						'reverse' => 'mg.group_name DESC',
273
					),
274
				),
275
				'icons' => array(
276
					'header' => array(
277
						'value' => $txt['membergroups_icons'],
278
					),
279
					'data' => array(
280
						'function' => static function ($rowData) {
281
							global $settings;
282
283
							if (empty($rowData['icons'][0]))
284
							{
285
								return '';
286
							}
287
288
							if (empty($rowData['icons'][1]))
289
							{
290
								return '';
291
							}
292
293
							return str_repeat('<img src="' . $settings['images_url'] . '/group_icons/' . $rowData['icons'][1] . '" alt="*" />', $rowData['icons'][0]);
294
						},
295
					),
296
					'sort' => array(
297
						'default' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, icons',
298
						'reverse' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, icons DESC',
299
					)
300
				),
301
				'members' => array(
302
					'header' => array(
303
						'value' => $txt['membergroups_members_top'],
304
					),
305
					'data' => array(
306
						'db' => 'num_members',
307
					),
308
					'sort' => array(
309
						'default' => '1 DESC',
310
						'reverse' => '1',
311
					),
312
				),
313
				'required_posts' => array(
314
					'header' => array(
315
						'value' => $txt['membergroups_min_posts'],
316
					),
317
					'data' => array(
318
						'db' => 'min_posts',
319
					),
320
					'sort' => array(
321
						'default' => 'mg.min_posts',
322
						'reverse' => 'mg.min_posts DESC',
323
					),
324
				),
325
				'modify' => array(
326
					'header' => array(
327
						'value' => $txt['modify'],
328
					),
329
					'data' => array(
330
						'sprintf' => array(
331
							'format' => '<a href="' . getUrl('admin', ['action' => 'admin', 'area' => 'membergroups', 'sa' => 'edit', 'group' => '']) . '%1$d">' . $txt['membergroups_modify'] . '</a>',
332
							'params' => array(
333
								'id_group' => false,
334
							),
335
						),
336
					),
337
				),
338
			),
339
			'additional_rows' => array(
340
				array(
341
					'position' => 'below_table_data',
342
					'class' => 'submitbutton',
343
					'value' => '<a class="linkbutton" href="' . getUrl('admin', ['action' => 'admin', 'area' => 'membergroups', 'sa' => 'add', 'postgroup']) . '">' . $txt['membergroups_add_group'] . '</a>',
344
				),
345
			),
346
		);
347
348
		createList($listOptions);
349
	}
350
351
	/**
352
	 * This function handles adding a membergroup and setting some initial properties.
353
	 *
354
	 * What it does:
355
	 *
356
	 * - Called by ?action=admin;area=membergroups;sa=add.
357
	 * - It requires the manage_membergroups permission.
358
	 * - Allows to use a predefined permission profile or copy one from another group.
359
	 * - Redirects to action=admin;area=membergroups;sa=edit;group=x.
360
	 *
361
	 * @event integrate_add_membergroup passed $id_group and $postCountBasedGroup
362
	 * @uses the new_group sub template of ManageMembergroups.
363
	 */
364
	public function action_add()
365
	{
366
		global $context, $txt, $modSettings;
367
368
		require_once(SUBSDIR . '/Membergroups.subs.php');
369
370
		// A form was submitted, we can start adding.
371
		if (!$this->_req->comparePost('group_name', '', 'trim', ''))
372
		{
373
			checkSession();
374
			validateToken('admin-mmg');
375
376
			$postCountBasedGroup = isset($this->_req->post->min_posts) && (!isset($this->_req->post->postgroup_based) || !empty($this->_req->post->postgroup_based));
377
			$group_type = !isset($this->_req->post->group_type) || $this->_req->post->group_type < 0 || $this->_req->post->group_type > 3 || ($this->_req->post->group_type == 1 && !allowedTo('admin_forum')) ? 0 : (int) $this->_req->post->group_type;
378
379
			// @todo Check for members with same name too?
380
381
			// Don't allow copying of a real privileged person!
382
			$permissionsObject = new Permissions();
383
			$illegal_permissions = $permissionsObject->getIllegalPermissions();
384
			$minposts = empty($this->_req->post->min_posts) ? '-1' : (int) $this->_req->post->min_posts;
385
386
			$id_group = createMembergroup($this->_req->post->group_name, $minposts, $group_type);
387
388
			call_integration_hook('integrate_add_membergroup', array($id_group, $postCountBasedGroup));
389
390
			// Update the post groups now, if this is a post group!
391
			if (isset($this->_req->post->min_posts))
392
			{
393
				require_once(SUBSDIR . '/Membergroups.subs.php');
394
				updatePostGroupStats();
395
			}
396
397
			// You cannot set permissions for post groups if they are disabled.
398
			if ($postCountBasedGroup && empty($modSettings['permission_enable_postgroups']))
399
			{
400
				$this->_req->post->perm_type = '';
401
			}
402
403
			if ($this->_req->post->perm_type === 'predefined')
404
			{
405
				// Set default permission level.
406
				require_once(SUBSDIR . '/ManagePermissions.subs.php');
407
				setPermissionLevel($this->_req->post->level, $id_group, null);
408
			}
409
			// Copy or inherit the permissions!
410
			elseif ($this->_req->post->perm_type === 'copy' || $this->_req->post->perm_type === 'inherit')
411
			{
412
				$copy_id = $this->_req->post->perm_type === 'copy' ? (int) $this->_req->post->copyperm : (int) $this->_req->post->inheritperm;
413
414
				// Are you a powerful admin?
415
				if (!allowedTo('admin_forum'))
416
				{
417
					$copy_type = membergroupById($copy_id);
418
419
					// Keep protected groups ... well, protected!
420
					if ($copy_type['group_type'] === 1)
421
					{
422
						throw new Exception('membergroup_does_not_exist');
423
					}
424
				}
425
426
				// Don't allow copying of a real privileged person!
427
				copyPermissions($id_group, $copy_id, $illegal_permissions);
428
				copyBoardPermissions($id_group, $copy_id);
429
430
				// Also get some membergroup information if we're copying and not copying from guests...
431
				if ($copy_id > 0 && $this->_req->post->perm_type === 'copy')
432
				{
433
					updateCopiedGroup($id_group, $copy_id);
434
				}
435
				// If inheriting say so...
436
				elseif ($this->_req->post->perm_type === 'inherit')
437
				{
438
					updateInheritedGroup($id_group, $copy_id);
439
				}
440
			}
441
442
			// Make sure all boards selected are stored in a proper array.
443
			$changed_boards = array();
444
			$accesses = empty($this->_req->post->boardaccess) || !is_array($this->_req->post->boardaccess) ? array() : $this->_req->post->boardaccess;
445
			$changed_boards['allow'] = array();
446
			$changed_boards['deny'] = array();
447
			$changed_boards['ignore'] = array();
448
			foreach ($accesses as $group_id => $action)
449
			{
450
				$changed_boards[$action][] = (int) $group_id;
451
			}
452
453
			foreach (array('allow', 'deny') as $board_action)
454
			{
455
				// Only do this if they have special access requirements.
456
				if (!isset($changed_boards[$board_action]))
457
				{
458
					continue;
459
				}
460
461
				if ($changed_boards[$board_action] === [])
462
				{
463
					continue;
464
				}
465
466
				assignGroupToBoards($id_group, $changed_boards, $board_action);
467
			}
468
469
			// If this is joinable then set it to show group membership in people's profiles.
470
			if (empty($modSettings['show_group_membership']) && $group_type > 1)
471
			{
472
				updateSettings(array('show_group_membership' => 1));
473
			}
474
475
			// Rebuild the group cache.
476
			updateSettings(array(
477
				'settings_updated' => time(),
478
			));
479
480
			// We did it.
481
			logAction('add_group', array('group' => $this->_req->post->group_name), 'admin');
482
483
			// Go change some more settings.
484
			redirectexit('action=admin;area=membergroups;sa=edit;group=' . $id_group);
485
		}
486
487
		// Just show the 'add membergroup' screen.
488
		$context['page_title'] = $txt['membergroups_new_group'];
489
		$context['sub_template'] = 'new_group';
490
		$context['post_group'] = isset($this->_req->query->postgroup);
491
		$context['undefined_group'] = !isset($this->_req->query->postgroup) && !isset($this->_req->query->generalgroup);
492
		$context['allow_protected'] = allowedTo('admin_forum');
493
494
		if (!empty($modSettings['deny_boards_access']))
495
		{
496
			Txt::load('ManagePermissions');
497
		}
498
499
		$context['groups'] = getBasicMembergroupData(array('globalmod'), array(), 'min_posts, id_group != {int:global_mod_group}, group_name');
500
501
		require_once(SUBSDIR . '/Boards.subs.php');
502
		$context += getBoardList();
503
504
		// Include a list of boards per category for easy toggling.
505
		foreach ($context['categories'] as $category)
506
		{
507
			$context['categories'][$category['id']]['child_ids'] = array_keys($category['boards']);
508
		}
509
510
		createToken('admin-mmg');
511
	}
512
513
	/**
514
	 * Deleting a membergroup by URL (not implemented).
515
	 *
516
	 * What it does:
517
	 *
518
	 * - Called by ?action=admin;area=membergroups;sa=delete;group=x;session_var=y.
519
	 * - Requires the manage_membergroups permission.
520
	 * - Redirects to ?action=admin;area=membergroups.
521
	 *
522
	 * @todo look at this
523
	 */
524
	public function action_delete()
525
	{
526
		checkSession('get');
527
528
		require_once(SUBSDIR . '/Membergroups.subs.php');
529
		deleteMembergroups((int) $this->_req->query->group);
530
531
		// Go back to the membergroup index.
532
		redirectexit('action=admin;area=membergroups;');
533
	}
534
535
	/**
536
	 * Editing a membergroup.
537
	 *
538
	 * What it does:
539
	 *
540
	 * - Screen to edit a specific membergroup.
541
	 * - Called by ?action=admin;area=membergroups;sa=edit;group=x.
542
	 * - It requires the manage_membergroups permission.
543
	 * - Also handles the delete button of the edit form.
544
	 * - Redirects to ?action=admin;area=membergroups.
545
	 *
546
	 * @event integrate_save_membergroup, passed $current_group['id_group']
547
	 * @event integrate_view_membergroup
548
	 * @uses the edit_group sub template of ManageMembergroups.
549
	 */
550
	public function action_edit()
551
	{
552
		global $context, $txt, $modSettings;
553
554
		$current_group_id = $this->_req->getQuery('group', 'intval', 0);
555
		$current_group = array();
556
557
		if (!empty($modSettings['deny_boards_access']))
558
		{
559
			Txt::load('ManagePermissions');
560
		}
561
562
		require_once(SUBSDIR . '/Membergroups.subs.php');
563
564
		// Make sure this group is editable.
565
		if (!empty($current_group_id))
566
		{
567
			$current_group = membergroupById($current_group_id);
568
		}
569
570
		// Now, do we have a valid id?
571
		if (!allowedTo('admin_forum') && !empty($current_group_id) && $current_group['group_type'] == 1)
572
		{
573
			throw new Exception('membergroup_does_not_exist', false);
574
		}
575
576
		// The delete this membergroup button was pressed.
577
		if (isset($this->_req->post->delete))
578
		{
579
			checkSession();
580
			validateToken('admin-mmg');
581
582
			if (empty($current_group_id))
583
			{
584
				throw new Exception('membergroup_does_not_exist', false);
585
			}
586
587
			// Let's delete the group
588
			deleteMembergroups($current_group['id_group']);
589
590
			redirectexit('action=admin;area=membergroups;');
591
		}
592
		// A form was submitted with the new membergroup settings.
593
		elseif (isset($this->_req->post->save))
594
		{
595
			// Validate the session.
596
			checkSession();
597
			validateToken('admin-mmg');
598
599
			if (empty($current_group_id))
600
			{
601
				throw new Exception('membergroup_does_not_exist', false);
602
			}
603
604
			// Empty values will be replaced by validator values where they exist
605
			$empty_post = array('max_messages' => null, 'min_posts' => null, 'group_type' => null, 'group_desc' => '',
606
				'group_name' => '', 'group_hidden' => null, 'group_inherit' => null, 'icon_count' => null,
607
				'icon_image' => '', 'online_color' => '', 'boardaccess' => null);
608
609
			$validator = new DataValidator();
610
611
			// Cleanup the inputs! :D
612
			$validator->sanitation_rules(array(
613
				'max_messages' => 'intval',
614
				'min_posts' => 'intval|abs',
615
				'group_type' => 'intval',
616
				'group_desc' => 'trim|\\ElkArte\\Helper\\Util::htmlspecialchars',
617
				'group_name' => 'trim|\\ElkArte\\Helper\\Util::htmlspecialchars',
618
				'group_hidden' => 'intval',
619
				'group_inherit' => 'intval',
620
				'icon_count' => 'intval',
621
				'icon_image' => 'trim|\\ElkArte\\Helper\\Util::htmlspecialchars',
622
				'online_color' => 'trim|valid_color',
623
			));
624
			$validator->input_processing(array(
625
				'boardaccess' => 'array',
626
			));
627
			$validator->validation_rules(array(
628
				'boardaccess' => 'contains[allow,ignore,deny]',
629
			));
630
			$validator->validate($this->_req->post);
631
632
			// Insert the clean data
633
			$our_post = array_replace((array) $this->_req->post, $empty_post, $validator->validation_data());
634
635
			// Can they really inherit from this group?
636
			$inherit_type = array();
637
			if ($our_post['group_inherit'] != -2 && !allowedTo('admin_forum'))
638
			{
639
				$inherit_type = membergroupById($our_post['group_inherit']);
640
			}
641
642
			$min_posts = $our_post['group_type'] == -1 && $our_post['min_posts'] >= 0 && $current_group['id_group'] > 3 ? $our_post['min_posts'] : ($current_group['id_group'] == 4 ? 0 : -1);
643
			$group_inherit = $current_group['id_group'] > 1 && $current_group['id_group'] != 3 && (empty($inherit_type['group_type']) || $inherit_type['group_type'] != 1) ? $our_post['group_inherit'] : -2;
644
645
			//@todo Don't set online_color for the Moderators group?
646
647
			// Do the update of the membergroup settings.
648
			$properties = array(
649
				'max_messages' => $our_post['max_messages'],
650
				'min_posts' => $min_posts,
651
				'group_type' => $our_post['group_type'] < 0 || $our_post['group_type'] > 3 || ($our_post['group_type'] == 1 && !allowedTo('admin_forum')) ? 0 : $our_post['group_type'],
652
				'hidden' => !$our_post['group_hidden'] || $min_posts != -1 || $current_group['id_group'] == 3 ? 0 : $our_post['group_hidden'],
653
				'id_parent' => $group_inherit,
654
				'current_group' => $current_group['id_group'],
655
				'group_name' => $our_post['group_name'],
656
				'online_color' => $our_post['online_color'],
657
				'icons' => $our_post['icon_count'] <= 0 ? '' : min($our_post['icon_count'], 10) . '#' . $our_post['icon_image'],
658
				// /me wonders why admin is *so* special
659
				'description' => $current_group['id_group'] == 1 || $our_post['group_type'] != -1 ? $our_post['group_desc'] : '',
660
			);
661
			updateMembergroupProperties($properties);
662
663
			call_integration_hook('integrate_save_membergroup', array($current_group['id_group']));
664
665
			// Time to update the boards this membergroup has access to.
666
			if ($current_group['id_group'] == 2 || $current_group['id_group'] > 3)
667
			{
668
				$changed_boards = array();
669
				$changed_boards['allow'] = array();
670
				$changed_boards['deny'] = array();
671
				$changed_boards['ignore'] = array();
672
673
				if ($our_post['boardaccess'])
674
				{
675
					foreach ($our_post['boardaccess'] as $group_id => $action)
676
					{
677
						$changed_boards[$action][] = (int) $group_id;
678
					}
679
				}
680
681
				foreach (array('allow', 'deny') as $board_action)
682
				{
683
					// Find all board this group is in, but shouldn't be in.
684
					detachGroupFromBoards($current_group['id_group'], $changed_boards, $board_action);
685
686
					// Add the membergroup to all boards that hadn't been set yet.
687
					if (!isset($changed_boards[$board_action]))
688
					{
689
						continue;
690
					}
691
692
					if (empty($changed_boards[$board_action]))
693
					{
694
						continue;
695
					}
696
697
					assignGroupToBoards($current_group['id_group'], $changed_boards, $board_action);
698
				}
699
			}
700
701
			// Remove everyone from this group!
702
			if ($min_posts != -1)
703
			{
704
				detachDeletedGroupFromMembers($current_group['id_group']);
705
			}
706
			elseif ($current_group['id_group'] != 3)
707
			{
708
				// Making it a hidden group? If so remove everyone with it as primary group (Actually, just make them additional).
709
				if ($our_post['group_hidden'] == 2)
710
				{
711
					setGroupToHidden($current_group['id_group']);
712
				}
713
714
				// Either way, let's check our "show group membership" setting is correct.
715
				validateShowGroupMembership();
716
			}
717
718
			// Do we need to set inherited permissions?
719
			if ($group_inherit !== -2 && $group_inherit !== $this->_req->post->old_inherit)
720
			{
721
				$permissionsObject = new Permissions();
722
				$permissionsObject->updateChild($group_inherit);
723
			}
724
725
			// Lastly, moderators!
726
			$moderator_string = $this->_req->getPost('group_moderators', 'trim', '');
727
			detachGroupModerators($current_group['id_group']);
728
729
			if ((!empty($moderator_string) || !empty($this->_req->post->moderator_list)) && $min_posts == -1 && $current_group['id_group'] != 3)
730
			{
731
				// Get all the usernames from the string
732
				if (!empty($moderator_string))
733
				{
734
					$moderator_string = strtr(preg_replace('~&amp;#(\d{4,5}|[2-9]\d{2,4}|1[2-9]\d);~', '&#$1;', htmlspecialchars($moderator_string, ENT_QUOTES, 'UTF-8')), array('&quot;' => '"'));
735
					preg_match_all('~"([^"]+)"~', $moderator_string, $matches);
736
					$moderators = array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $moderator_string)));
737
					$moderators = array_filter(array_map('trim', $moderators));
738
739
					// Find all the id_member's for the member_name's in the list.
740
					if (!empty($moderators))
741
					{
742
						$group_moderators = getIDMemberFromGroupModerators($moderators);
743
					}
744
				}
745
				else
746
				{
747
					$moderators = array();
748
					foreach ($this->_req->post->moderator_list as $moderator)
749
					{
750
						$moderators[] = (int) $moderator;
751
					}
752
753
					$group_moderators = array();
754
					if (!empty($moderators))
755
					{
756
						require_once(SUBSDIR . '/Members.subs.php');
757
						$members = getBasicMemberData($moderators);
758
						foreach ($members as $member)
759
						{
760
							$group_moderators[] = $member['id_member'];
761
						}
762
					}
763
				}
764
765
				// Found some?
766
				if (!empty($group_moderators))
767
				{
768
					assignGroupModerators($current_group['id_group'], $group_moderators);
769
				}
770
			}
771
772
			// There might have been some post group changes.
773
			require_once(SUBSDIR . '/Membergroups.subs.php');
774
			updatePostGroupStats();
775
776
			// We've definitely changed some group stuff.
777
			updateSettings(array(
778
				'settings_updated' => time(),
779
			));
780
781
			// Log the edit.
782
			logAction('edited_group', array('group' => $our_post['group_name']), 'admin');
783
784
			redirectexit('action=admin;area=membergroups');
785
		}
786
787
		// Fetch the current group information.
788
		$row = membergroupById($current_group['id_group'], true);
789
790
		if (empty($row) || (!allowedTo('admin_forum') && $row['group_type'] === 1))
791
		{
792
			throw new Exception('membergroup_does_not_exist', false);
793
		}
794
795
		$row['icons'] = explode('#', $row['icons']);
796
797
		$context['group'] = array(
798
			'id' => $row['id_group'],
799
			'name' => $row['group_name'],
800
			'description' => htmlspecialchars($row['description'], ENT_COMPAT, 'UTF-8'),
801
			'editable_name' => $row['group_name'],
802
			'color' => $row['online_color'],
803
			'min_posts' => $row['min_posts'],
804
			'max_messages' => $row['max_messages'],
805
			'icon_count' => (int) $row['icons'][0],
806
			'icon_image' => $row['icons'][1] ?? '',
807
			'is_post_group' => $row['min_posts'] !== -1,
808
			'type' => $row['min_posts'] !== -1 ? 0 : $row['group_type'],
809
			'hidden' => $row['min_posts'] === -1 ? $row['hidden'] : 0,
810
			'inherited_from' => $row['id_parent'],
811
			'allow_post_group' => $row['id_group'] === 2 || $row['id_group'] > 4,
812
			'allow_delete' => $row['id_group'] === 2 || $row['id_group'] > 4,
813
			'allow_protected' => allowedTo('admin_forum'),
814
		);
815
816
		// Get any moderators for this group
817
		$context['group']['moderators'] = getGroupModerators($row['id_group']);
818
		$context['group']['moderator_list'] = empty($context['group']['moderators']) ? '' : '&quot;' . implode('&quot;, &quot;', $context['group']['moderators']) . '&quot;';
819
820
		if (!empty($context['group']['moderators']))
821
		{
822
			[$context['group']['last_moderator_id']] = array_slice(array_keys($context['group']['moderators']), -1);
823
		}
824
825
		// Get a list of boards this membergroup is allowed to see.
826
		$context['boards'] = array();
827
		if ($row['id_group'] === 2 || $row['id_group'] > 3)
828
		{
829
			require_once(SUBSDIR . '/Boards.subs.php');
830
			$context += getBoardList(array('override_permissions' => true, 'access' => $row['id_group'], 'not_redirection' => true));
831
832
			// Include a list of boards per category for easy toggling.
833
			foreach ($context['categories'] as $category)
834
			{
835
				$context['categories'][$category['id']]['child_ids'] = array_keys($category['boards']);
836
			}
837
		}
838
839
		// Finally, get all the groups this could be inherited off.
840
		$context['inheritable_groups'] = getInheritableGroups($row['id_group']);
841
842
		call_integration_hook('integrate_view_membergroup');
843
844
		$context['sub_template'] = 'edit_group';
845
		$context['page_title'] = $txt['membergroups_edit_group'];
846
847
		// Use the autosuggest script when needed
848
		if ($context['group']['id'] != 3 && $context['group']['id'] != 4)
849
		{
850
			loadJavascriptFile('suggest.js', array('defer' => true));
851
		}
852
853
		createToken('admin-mmg');
854
	}
855
856
	/**
857
	 * Set some general membergroup settings and permissions.
858
	 *
859
	 * What it does:
860
	 *
861
	 * - Called by ?action=admin;area=membergroups;sa=settings
862
	 * - Requires the admin_forum permission (and manage_permissions for changing permissions)
863
	 * - Redirects to itself.
864
	 *
865
	 * @event integrate_save_membergroup_settings
866
	 * @uses membergroup_settings sub template of ManageMembergroups.
867
	 */
868
	public function action_groupSettings_display()
869
	{
870
		global $context, $txt;
871
872
		$context['sub_template'] = 'show_settings';
873
		$context['page_title'] = $txt['membergroups_settings'];
874
875
		// Instantiate the form
876
		$settingsForm = new SettingsForm(SettingsForm::DB_ADAPTER);
877
		$settingsForm->setConfigVars($this->_settings());
878
879
		if (isset($this->_req->query->save))
880
		{
881
			checkSession();
882
			call_integration_hook('integrate_save_membergroup_settings');
883
884
			// Yeppers, saving this...
885
			$settingsForm->setConfigValues((array) $this->_req->post);
886
			$settingsForm->save();
887
			redirectexit('action=admin;area=membergroups;sa=settings');
888 2
		}
889
890
		// Some simple context.
891
		$context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'membergroups', 'sa' => 'settings']);
892 2
		$context['settings_title'] = $txt['membergroups_settings'];
893
894
		$settingsForm->prepare();
895
	}
896 2
897
	/**
898 2
	 * Return the configuration settings for membergroups management.
899
	 *
900
	 * @event integrate_modify_membergroup_settings
901
	 */
902
	private function _settings()
903
	{
904 2
		// Only one thing here!
905
		$config_vars = array(
906 2
			array('permissions', 'manage_membergroups'),
907
		);
908
909
		// Add new settings with a nice hook, makes them available for admin settings search as well
910
		call_integration_hook('integrate_modify_membergroup_settings', array(&$config_vars));
911
912
		return $config_vars;
913
	}
914
915
	/**
916
	 * Return the form settings for use in admin search
917
	 */
918
	public function settings_search()
919
	{
920
		return $this->_settings();
921
	}
922
}
923