getBasicMembergroupData()   F
last analyzed

Complexity

Conditions 22
Paths > 20000

Size

Total Lines 134
Code Lines 70

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 65.0544

Importance

Changes 0
Metric Value
cc 22
eloc 70
nc 98304
nop 4
dl 0
loc 134
ccs 31
cts 56
cp 0.5536
crap 65.0544
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file contains functions regarding manipulation of and information about 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
use ElkArte\Cache\Cache;
18
use ElkArte\Helper\Util;
19
use ElkArte\Languages\Txt;
20
use ElkArte\User;
21
22
/**
23
 * Delete one of more membergroups.
24
 *
25
 * - Requires the manage_membergroups permission.
26
 * - Returns true on success or false on failure.
27
 * - Has protection against deletion of protected membergroups.
28
 * - Deletes the permissions linked to the membergroup.
29
 * - Takes members out of the deleted membergroups.
30
 *
31
 * @param int[]|int $groups
32
 * @return bool
33
 * @package Membergroups
34
 */
35
function deleteMembergroups($groups)
36
{
37
	global $modSettings;
38
39
	$db = database();
40
41
	// Make sure it's an array.
42
	if (!is_array($groups))
43
	{
44
		$groups = array((int) $groups);
45
	}
46
	else
47
	{
48
		$groups = array_unique($groups);
49
50
		// Make sure all groups are integer.
51
		foreach ($groups as $key => $value)
52
		{
53
			$groups[$key] = (int) $value;
54
		}
55
	}
56
57
	// Some groups are protected (guests, administrators, moderators, newbies).
58
	$protected_groups = array(-1, 0, 1, 3, 4);
59
60
	// There maybe some others as well.
61
	if (!allowedTo('admin_forum'))
62
	{
63
		$db->fetchQuery('
64
			SELECT 
65
				id_group
66
			FROM {db_prefix}membergroups
67
			WHERE group_type = {int:is_protected}',
68
			array(
69
				'is_protected' => 1,
70
			)
71
		)->fetch_callback(
72
			function ($row) use (&$protected_groups) {
73
				$protected_groups[] = $row['id_group'];
74
			}
75
		);
76
	}
77
78
	// Make sure they don't delete protected groups!
79
	$groups = array_diff($groups, array_unique($protected_groups));
80
	if (empty($groups))
81
	{
82
		return false;
83
	}
84
85
	// Log the deletion.
86
	$groups_to_log = membergroupsById($groups, 0);
87
	foreach ($groups_to_log as $key => $row)
88
	{
89
		logAction('delete_group', array('group' => $row['group_name']), 'admin');
90
	}
91
92
	call_integration_hook('integrate_delete_membergroups', array($groups));
93
94
	// Remove the membergroups themselves.
95
	$db->query('', '
96
		DELETE FROM {db_prefix}membergroups
97
		WHERE id_group IN ({array_int:group_list})',
98
		array(
99
			'group_list' => $groups,
100
		)
101
	);
102
103
	// Remove the permissions of the membergroups.
104
	$db->query('', '
105
		DELETE FROM {db_prefix}permissions
106
		WHERE id_group IN ({array_int:group_list})',
107
		array(
108
			'group_list' => $groups,
109
		)
110
	);
111
	$db->query('', '
112
		DELETE FROM {db_prefix}board_permissions
113
		WHERE id_group IN ({array_int:group_list})',
114
		array(
115
			'group_list' => $groups,
116
		)
117
	);
118
	$db->query('', '
119
		DELETE FROM {db_prefix}group_moderators
120
		WHERE id_group IN ({array_int:group_list})',
121
		array(
122
			'group_list' => $groups,
123
		)
124
	);
125
126
	// Delete any outstanding requests.
127
	$db->query('', '
128
		DELETE FROM {db_prefix}log_group_requests
129
		WHERE id_group IN ({array_int:group_list})',
130
		array(
131
			'group_list' => $groups,
132
		)
133
	);
134
135
	// Update the primary groups of members.
136
	$db->query('', '
137
		UPDATE {db_prefix}members
138
		SET id_group = {int:regular_group}
139
		WHERE id_group IN ({array_int:group_list})',
140
		array(
141
			'group_list' => $groups,
142
			'regular_group' => 0,
143
		)
144
	);
145
146
	// Update any inherited groups (Lose inheritance).
147
	$db->query('', '
148
		UPDATE {db_prefix}membergroups
149
		SET id_parent = {int:uninherited}
150
		WHERE id_parent IN ({array_int:group_list})',
151
		array(
152
			'group_list' => $groups,
153
			'uninherited' => -2,
154
		)
155
	);
156
157
	// Update the additional groups of members.
158
	$updates = array();
159
	$db->fetchQuery('
160
		SELECT 
161
			id_member, additional_groups
162
		FROM {db_prefix}members
163
		WHERE FIND_IN_SET({raw:additional_groups_explode}, additional_groups) != 0',
164
		array(
165
			'additional_groups_explode' => implode(', additional_groups) != 0 OR FIND_IN_SET(', $groups),
166
		)
167
	)->fetch_callback(
168
		function ($row) use (&$updates) {
169
			// Update each member information.
170
			$updates[$row['additional_groups']][] = $row['id_member'];
171
		}
172
	);
173
174
	require_once(SUBSDIR . '/Members.subs.php');
175
	foreach ($updates as $additional_groups => $memberArray)
176
	{
177
		updateMemberData($memberArray, array('additional_groups' => implode(',', array_diff(explode(',', $additional_groups), $groups))));
178
	}
179
180
	// No boards can provide access to these membergroups anymore.
181
	$updates = array();
182
	$db->fetchQuery('
183
		SELECT 
184
			id_board, member_groups
185
		FROM {db_prefix}boards
186
		WHERE FIND_IN_SET({raw:member_groups_explode}, member_groups) != 0',
187
		array(
188
			'member_groups_explode' => implode(', member_groups) != 0 OR FIND_IN_SET(', $groups),
189
		)
190
	)->fetch_callback(
191
		function ($row) use (&$updates) {
192
			$updates[$row['member_groups']][] = $row['id_board'];
193
		}
194
	);
195
196
	foreach ($updates as $member_groups => $boardArray)
197
	{
198
		$db->query('', '
199
			UPDATE {db_prefix}boards
200
			SET member_groups = {string:member_groups}
201
			WHERE id_board IN ({array_int:board_lists})',
202
			array(
203
				'board_lists' => $boardArray,
204
				'member_groups' => implode(',', array_diff(explode(',', $member_groups), $groups)),
205
			)
206
		);
207
	}
208
209
	// Recalculate the post groups, as they likely changed.
210
	updatePostGroupStats();
211
212
	// Make a note of the fact that the cache may be wrong.
213
	$settings_update = array('settings_updated' => time());
214
215
	// Have we deleted the spider group?
216
	// @memo we are lucky that the group 1 and 0 cannot be deleted
217
	// $modSettings['spider_group'] is set to 1 (admin) for regular members (that usually is group 0)
218
	if (isset($modSettings['spider_group']) && in_array($modSettings['spider_group'], $groups))
219
	{
220
		$settings_update['spider_group'] = 0;
221
	}
222
223
	updateSettings($settings_update);
224
225
	// It was a success.
226
	return true;
227
}
228
229
/**
230
 * Remove one or more members from one or more membergroups.
231
 *
232
 * - Requires the manage_membergroups permission.
233
 * - Function includes a protection against removing from implicit groups.
234
 * - Non-admins are not able to remove members from the admin group.
235
 *
236
 * @param int[]|int $members
237
 * @param int|null $groups
238
 * @param bool $permissionCheckDone = false
239
 *
240
 * @return bool
241
 * @package Membergroups
242
 */
243
function removeMembersFromGroups($members, $groups = null, $permissionCheckDone = false)
244
{
245
	global $modSettings;
246
247
	$db = database();
248
249
	// You're getting nowhere without this permission, unless of course you are the group's moderator.
250
	if (!$permissionCheckDone)
251
	{
252
		isAllowedTo('manage_membergroups');
253
	}
254
255
	// Assume something will happen.
256
	updateSettings(array('settings_updated' => time()));
257
258
	// Cleaning the input.
259
	if (!is_array($members))
260
	{
261
		$members = array((int) $members);
262
	}
263
	else
264
	{
265
		$members = array_unique($members);
266
267
		// Cast the members to integer.
268
		foreach ($members as $key => $value)
269
		{
270
			$members[$key] = (int) $value;
271
		}
272
	}
273
274
	// Before we get started, let's check we won't leave the admin group empty!
275
	if ($groups === null || $groups == 1 || (is_array($groups) && in_array(1, $groups)))
276
	{
277
		$admins = array();
278
		listMembergroupMembers_Href($admins, 1);
279
280
		// Remove any admins if there are too many.
281
		$non_changing_admins = array_diff(array_keys($admins), $members);
282
283
		if (empty($non_changing_admins))
284
		{
285
			$members = array_diff($members, array_keys($admins));
286
		}
287
	}
288
289
	// Just in case.
290
	if (empty($members))
291
	{
292
		return false;
293
	}
294
295
	// Wanna remove all groups from these members? That's easy.
296
	if ($groups === null)
297
	{
298
		$db->query('', '
299
			UPDATE {db_prefix}members
300
			SET
301
				id_group = {int:regular_member},
302
				additional_groups = {string:blank_string}
303
			WHERE id_member IN ({array_int:member_list})' . (allowedTo('admin_forum') ? '' : '
304
				AND id_group != {int:admin_group}
305
				AND FIND_IN_SET({int:admin_group}, additional_groups) = 0'),
306
			array(
307
				'member_list' => $members,
308
				'regular_member' => 0,
309
				'admin_group' => 1,
310
				'blank_string' => '',
311
			)
312
		);
313
314
		updatePostGroupStats($members);
315
316
		// Log what just happened.
317
		foreach ($members as $member)
318
		{
319
			logAction('removed_all_groups', array('member' => $member), 'admin');
320
		}
321
322
		return true;
323
	}
324
	elseif (!is_array($groups))
0 ignored issues
show
introduced by
The condition is_array($groups) is always false.
Loading history...
325
	{
326
		$groups = array((int) $groups);
327
	}
328
	// Make sure all groups are integer.
329
	else
330
	{
331
		$groups = array_unique(array_map('intval', $groups));
332
	}
333
334
	// Fetch a list of groups members cannot be assigned to explicitly, and the group names of the ones we want.
335
	$implicitGroups = array(-1, 0, 3);
336
	$group_names = array();
337
	$group_details = membergroupsById($groups, 0, true);
338
	foreach ($group_details as $key => $row)
339
	{
340
		if ($row['min_posts'] != -1)
341
		{
342
			$implicitGroups[] = $row['id_group'];
343
		}
344
		else
345
		{
346
			$group_names[$row['id_group']] = $row['group_name'];
347
		}
348
	}
349
350
	// Now get rid of those groups.
351
	$groups = array_diff($groups, $implicitGroups);
352
353
	// Don't forget the protected groups.
354
	if (!allowedTo('admin_forum'))
355
	{
356
		$protected_groups = array(1);
357
		$db->fetchQuery('
358
			SELECT 
359
				id_group
360
			FROM {db_prefix}membergroups
361
			WHERE group_type = {int:is_protected}',
362
			array(
363
				'is_protected' => 1,
364
			)
365
		)->fetch_callback(
366
			function ($row) use (&$protected_groups) {
367
				$protected_groups[] = $row['id_group'];
368
			}
369
		);
370
371
		// If you're not an admin yourself, you can't touch protected groups!
372
		$groups = array_diff($groups, array_unique($protected_groups));
373
	}
374
375
	// Only continue if there are still groups and members left.
376
	if (empty($groups) || empty($members))
377
	{
378
		return false;
379
	}
380
381
	// First, reset those who have this as their primary group - this is the easy one.
382
	$log_inserts = $db->fetchQuery('
383
		SELECT 
384
			id_member, id_group
385
		FROM {db_prefix}members AS members
386
		WHERE id_group IN ({array_int:group_list})
387
			AND id_member IN ({array_int:member_list})',
388
		array(
389
			'group_list' => $groups,
390
			'member_list' => $members,
391
		)
392
	)->fetch_callback(
393
		function ($row) use ($group_names) {
394
			return array('group' => $group_names[$row['id_group']], 'member' => $row['id_member']);
395
		}
396
	);
397
398
	$db->query('', '
399
		UPDATE {db_prefix}members
400
		SET id_group = {int:regular_member}
401
		WHERE id_group IN ({array_int:group_list})
402
			AND id_member IN ({array_int:member_list})',
403
		array(
404
			'group_list' => $groups,
405
			'member_list' => $members,
406
			'regular_member' => 0,
407
		)
408
	);
409
410
	// Those who have it as part of their additional group must be updated the long way... sadly.
411
	$updates = array();
412
	$db->fetchQuery('
413
		SELECT 
414
			id_member, additional_groups
415
		FROM {db_prefix}members
416
		WHERE (FIND_IN_SET({raw:additional_groups_implode}, additional_groups) != 0)
417
			AND id_member IN ({array_int:member_list})
418
		LIMIT ' . count($members),
419
		array(
420
			'member_list' => $members,
421
			'additional_groups_implode' => implode(', additional_groups) != 0 OR FIND_IN_SET(', $groups),
422
		)
423
	)->fetch_callback(
424
		function ($row) use (&$updates, $groups, $group_names) {
425
			// What log entries must we make for this one, eh?
426
			foreach (explode(',', $row['additional_groups']) as $group)
427
			{
428
				if (in_array($group, $groups))
429
				{
430
					$log_inserts[] = array(
431
						'group' => $group_names[$group],
432
						'member' => $row['id_member']
433
					);
434
				}
435
			}
436
437
			$updates[$row['additional_groups']][] = $row['id_member'];
438
		}
439
	);
440
441
	require_once(SUBSDIR . '/Members.subs.php');
442
	foreach ($updates as $additional_groups => $memberArray)
443
	{
444
		updateMemberData($memberArray, array('additional_groups' => implode(',', array_diff(explode(',', $additional_groups), $groups))));
445
	}
446
447
	// Their post groups may have changed now...
448
	updatePostGroupStats($members);
449
450
	// Do the log.
451
	if (!empty($log_inserts) && featureEnabled('ml'))
452
	{
453
		foreach ($log_inserts as $extra)
454
		{
455
			logAction('removed_from_group', $extra, 'admin');
456
		}
457
	}
458
459
	// Mission successful.
460
	return true;
461
}
462
463
/**
464
 * Add one or more members to a membergroup.
465
 *
466
 * - Requires the manage_membergroups permission.
467
 * - Function has protection against adding members to implicit groups.
468
 * - Non-admins cannot add members to the admin group, or protected groups.
469
 *
470
 * @param int|int[] $members
471
 * @param int $group
472
 * @param string $type = 'auto' specifies whether the group is added as primary or as additional group.
473
 * Supported types:
474
 * - only_primary    - Assigns a membergroup as primary membergroup, but only
475
 *                     if a member has not yet a primary membergroup assigned,
476
 *                     unless the member is already part of the membergroup.
477
 * - only_additional - Assigns a membergroup to the additional membergroups,
478
 *                     unless the member is already part of the membergroup.
479
 * - force_primary   - Assigns a membergroup as primary membergroup no matter
480
 *                     what the previous primary membergroup was.
481
 * - auto            - Assigns a membergroup to the primary group if it's still
482
 *                     available. If not, assign it to the additional group.
483
 * @param bool $permissionCheckDone = false if true, it checks permission of the current user to add groups ('manage_membergroups')
484
 * @return bool success or failure
485
 * @package Membergroups
486
 */
487
function addMembersToGroup($members, $group, $type = 'auto', $permissionCheckDone = false)
488
{
489
	$db = database();
490
491
	// Show your licence, but only if it hasn't been done yet.
492
	if (!$permissionCheckDone)
493
	{
494
		isAllowedTo('manage_membergroups');
495
	}
496
497
	// Make sure we don't keep old stuff cached.
498
	updateSettings(array('settings_updated' => time()));
499
500
	$members = !is_array($members) ? array((int) $members) : array_unique(array_map('intval', $members));
501
502
	$group = (int) $group;
503
504
	// Some groups just don't like explicitly having members.
505
	$implicitGroups = array(-1, 0, 3);
506
	$group_names = array();
507
	$group_details = membergroupById($group, true);
508
	if ($group_details['min_posts'] != -1)
509
	{
510
		$implicitGroups[] = $group_details['id_group'];
511
	}
512
	else
513
	{
514
		$group_names[$group_details['id_group']] = $group_details['group_name'];
515
	}
516
517
	// Sorry, you can't join an implicit group.
518
	if (in_array($group, $implicitGroups) || empty($members))
519
	{
520
		return false;
521
	}
522
523
	// Only admins can add admins...
524
	if (!allowedTo('admin_forum') && $group == 1)
525
	{
526
		return false;
527
	}
528
	// ... and assign protected groups!
529
	elseif (!allowedTo('admin_forum') && $group_details['group_type'] == 1)
530
	{
531
		return false;
532
	}
533
534
	// Do the actual updates.
535
	if ($type === 'only_additional')
536
	{
537
		$db->query('', '
538
			UPDATE {db_prefix}members
539
			SET additional_groups = CASE WHEN additional_groups = {string:blank_string} THEN {string:id_group_string} ELSE CONCAT(additional_groups, {string:id_group_string_extend}) END
540
			WHERE id_member IN ({array_int:member_list})
541
				AND id_group != {int:id_group}
542
				AND FIND_IN_SET({int:id_group}, additional_groups) = 0',
543
			array(
544
				'member_list' => $members,
545
				'id_group' => $group,
546
				'id_group_string' => (string) $group,
547
				'id_group_string_extend' => ',' . $group,
548
				'blank_string' => '',
549
			)
550
		);
551
	}
552
	elseif ($type === 'only_primary' || $type === 'force_primary')
553
	{
554
		$db->query('', '
555
			UPDATE {db_prefix}members
556
			SET id_group = {int:id_group}
557
			WHERE id_member IN ({array_int:member_list})' . ($type === 'force_primary' ? '' : '
558
				AND id_group = {int:regular_group}
559
				AND FIND_IN_SET({int:id_group}, additional_groups) = 0'),
560
			array(
561
				'member_list' => $members,
562
				'id_group' => $group,
563
				'regular_group' => 0,
564
			)
565
		);
566
	}
567
	elseif ($type === 'auto')
568
	{
569
		$db->query('', '
570
			UPDATE {db_prefix}members
571
			SET
572
				id_group = CASE WHEN id_group = {int:regular_group} THEN {int:id_group} ELSE id_group END,
573
				additional_groups = CASE WHEN id_group = {int:id_group} THEN additional_groups
574
					WHEN additional_groups = {string:blank_string} THEN {string:id_group_string}
575
					ELSE CONCAT(additional_groups, {string:id_group_string_extend}) END
576
			WHERE id_member IN ({array_int:member_list})
577
				AND id_group != {int:id_group}
578
				AND FIND_IN_SET({int:id_group}, additional_groups) = 0',
579
			array(
580
				'member_list' => $members,
581
				'regular_group' => 0,
582
				'id_group' => $group,
583
				'blank_string' => '',
584
				'id_group_string' => (string) $group,
585
				'id_group_string_extend' => ',' . $group,
586
			)
587
		);
588
	}
589
	// Ack!!?  What happened?
590
	else
591
	{
592
		trigger_error('addMembersToGroup(): Unknown type \'' . $type . '\'', E_USER_WARNING);
593
	}
594
595
	call_integration_hook('integrate_add_members_to_group', array($members, $group_details, &$group_names));
596
597
	// Update their postgroup statistics.
598
	updatePostGroupStats($members);
599
600
	require_once(SOURCEDIR . '/Logging.php');
601
	foreach ($members as $member)
602
	{
603
		logAction('added_to_group', array('group' => $group_names[$group], 'member' => $member), 'admin');
604
	}
605
606
	return true;
607
}
608
609
/**
610
 * Gets the members of a supplied membergroup.
611
 *
612
 * - Returns them as a link for display.
613
 *
614
 * @param int[] $members
615
 * @param int $membergroup
616
 * @param int|null $limit = null
617
 * @return bool
618
 * @package Membergroups
619
 */
620
function listMembergroupMembers_Href(&$members, $membergroup, $limit = null)
621
{
622
	$db = database();
623
624
	$members = array();
625
	$db->fetchQuery('
626
		SELECT 
627
			id_member, real_name
628
		FROM {db_prefix}members
629
		WHERE id_group = {int:id_group} OR FIND_IN_SET({int:id_group}, additional_groups) != 0' . ($limit === null ? '' : '
630
		LIMIT ' . ($limit + 1)),
631
		array(
632
			'id_group' => $membergroup,
633
		)
634
	)->fetch_callback(
635
		function ($row) use (&$members) {
636
			$members[$row['id_member']] = '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row['id_member'], 'name' => $row['real_name']]) . '">' . $row['real_name'] . '</a>';
637
		}
638
	);
639
640
	// If there are more than $limit members, add a 'more' link.
641
	if ($limit !== null && count($members) > $limit)
642
	{
643
		array_pop($members);
644
645
		return true;
646
	}
647
648
	return false;
649
}
650
651
/**
652
 * Retrieve a list of (visible) membergroups used by the cache.
653
 *
654
 * @package Membergroups
655
 */
656
function cache_getMembergroupList()
657
{
658
	$db = database();
659
660
	$groupCache = $db->fetchQuery('
661
		SELECT 
662
			id_group, group_name, online_color
663
		FROM {db_prefix}membergroups
664
		WHERE min_posts = {int:min_posts}
665
			AND hidden = {int:not_hidden}
666
			AND id_group != {int:mod_group}
667
			AND online_color != {string:blank_string}
668
		ORDER BY group_name',
669
		array(
670
			'min_posts' => -1,
671
			'not_hidden' => 0,
672
			'mod_group' => 3,
673
			'blank_string' => '',
674
		)
675
	)->fetch_callback(
676
		function ($row) {
677
			return '<a href="' . getUrl('group', ['action' => 'groups', 'sa' => 'members', 'group' => $row['id_group'], 'name' => $row['group_name']]) . '" ' . ($row['online_color'] ? 'style="color: ' . $row['online_color'] . '"' : '') . '>' . $row['group_name'] . '</a>';
678
		}
679
	);
680
681
	return array(
682
		'data' => $groupCache,
683
		'expires' => time() + 3600,
684
		'refresh_eval' => 'return $GLOBALS[\'modSettings\'][\'settings_updated\'] > ' . time() . ';',
685
	);
686
}
687
688
/**
689
 * Helper function to generate a list of membergroups for display.
690
 *
691
 * @param int $start not used
692
 * @param int $items_per_page not used
693
 * @param string $sort An SQL query indicating how to sort the results
694
 * @param string $membergroup_type Should be 'post_count' for post groups or 'regular' for other groups
695
 * @param int $user_id id of the member making the request
696
 * @param bool $include_hidden If true includes hidden groups if the user has permission
697
 * @param bool $include_all If true includes all groups the user can see
698
 * @param bool $aggregate
699
 * @param bool $count_permissions
700
 * @param int|null $pid - profile id
701
 *
702
 * @return array
703
 * @package Membergroups
704
 *
705
 */
706
function list_getMembergroups($start, $items_per_page, $sort, $membergroup_type, $user_id, $include_hidden, $include_all = false, $aggregate = false, $count_permissions = false, $pid = null)
707
{
708
	global $txt, $context;
709
710
	$db = database();
711
	Txt::load('Admin');
712 2
713
	// Start collecting the data.
714 2
	$groups = array();
715 2
	$group_ids = array();
716
	$parent_groups = array();
717
718 2
	if ($membergroup_type === 'all')
719 2
	{
720 2
		// Determine the number of ungrouped members.
721
		$num_members = countMembersInGroup(0);
722 2
723
		// Fill the context variable with 'Guests' and 'Regular Members'.
724
		$groups = array(
725
			-1 => array(
726
				'id_group' => -1,
727
				'group_name' => $txt['membergroups_guests'],
728
				'group_name_color' => $txt['membergroups_guests'],
729
				'min_posts' => 0,
730
				'desc' => '',
731
				'num_members' => $txt['membergroups_guests_na'],
732
				'icons' => '',
733
				'can_search' => false,
734
				'id_parent' => -2,
735
				'num_permissions' => array(
736
					'allowed' => 0,
737
					'denied' => 0,
738
				)
739
			),
740
			0 => array(
741
				'id_group' => 0,
742
				'group_name' => $txt['membergroups_members'],
743
				'group_name_color' => $txt['membergroups_members'],
744
				'min_posts' => 0,
745
				'desc' => '',
746
				'num_members' => $num_members,
747
				'icons' => '',
748
				'can_search' => true,
749
				'id_parent' => -2,
750
				'num_permissions' => array(
751
					'allowed' => 0,
752
					'denied' => 0,
753
				)
754
			),
755
		);
756
	}
757
758
	$db->fetchQuery('
759
		SELECT 
760
			mg.id_group, mg.group_name, mg.min_posts, mg.description, mg.group_type, mg.online_color,
761
			mg.hidden, mg.id_parent, mg.icons, COALESCE(gm.id_member, 0) AS can_moderate, 0 AS num_members
762 2
		FROM {db_prefix}membergroups AS mg
763
			LEFT JOIN {db_prefix}group_moderators AS gm ON (gm.id_group = mg.id_group AND gm.id_member = {int:current_member})
764
		WHERE mg.min_posts {raw:min_posts}' . ($include_all ? '' : '
765
			AND mg.id_group != {int:mod_group}
766
			AND mg.group_type != {int:is_protected}') . '
767
		ORDER BY {raw:sort}',
768 2
		array(
769
			'current_member' => $user_id,
770 2
			'min_posts' => ($membergroup_type === 'post_count' ? '!= -1' : '= -1'),
771
			'mod_group' => 3,
772
			'is_protected' => 1,
773 2
			'sort' => $sort,
774 2
		)
775 2
	)->fetch_callback(
776 2
		function ($row) use (&$parent_groups, &$groups, &$group_ids, &$include_hidden, $count_permissions, $aggregate) {
777 2
			global $txt;
778
779 2
			// We only list the groups they can see.
780
			if ($row['hidden'] && !$row['can_moderate'] && !$include_hidden)
781 2
			{
782
				return;
783
			}
784 2
785
			if ((int) $row['id_parent'] !== -2)
786
			{
787
				$parent_groups[] = (int) $row['id_parent'];
788
			}
789 2
790
			// If it's inherited, just add it as a child.
791
			if ($aggregate && (int) $row['id_parent'] !== -2)
792
			{
793
				if (isset($groups[$row['id_parent']]))
794
				{
795 2
					$groups[$row['id_parent']]['children'][$row['id_group']] = $row['group_name'];
796
				}
797
798
				return;
799
			}
800
801
			$row['icons'] = explode('#', $row['icons']);
802
			$row['id_group'] = (int) $row['id_group'];
803
			$groups[$row['id_group']] = array(
804
				'id_group' => $row['id_group'],
805 2
				'group_name' => $row['group_name'],
806
				'group_name_color' => empty($row['online_color']) ? $row['group_name'] : '<span style="color: ' . $row['online_color'] . '">' . $row['group_name'] . '</span>',
807 2
				'min_posts' => (int) $row['min_posts'],
808 2
				'desc' => $row['description'],
809 2
				'online_color' => $row['online_color'],
810 2
				'type' => (int) $row['group_type'],
811 2
				'num_members' => (int) $row['num_members'],
812 2
				'moderators' => array(),
813 2
				'icons' => $row['icons'],
814 2
				'can_search' => $row['id_group'] !== 3,
815 2
				'id_parent' => (int) $row['id_parent'],
816
			);
817 2
818 2
			if ($count_permissions)
819 2
			{
820
				$groups[$row['id_group']]['num_permissions'] = array(
821
					'allowed' => $row['id_group'] === 1 ? '(' . $txt['permissions_all'] . ')' : 0,
822 2
					'denied' => $row['id_group'] === 1 ? '(' . $txt['permissions_none'] . ')' : 0,
823
				);
824
			}
825
826
			$include_hidden |= $row['can_moderate'];
827
			$group_ids[] = $row['id_group'];
828
		}
829
	);
830 2
831 2
	// If we found any membergroups, get the amount of members in them.
832 2
	if (!empty($group_ids))
833
	{
834
		if ($membergroup_type === 'post_count')
835
		{
836 2
			$groups_count = membersInGroups($group_ids);
837
		}
838 2
		else
839
		{
840
			$groups_count = membersInGroups(array(), $group_ids, $include_hidden);
841
		}
842
843
		// @todo not sure why += wouldn't = be enough?
844 2
		foreach ($groups_count as $group_id => $num_members)
845
		{
846
			$groups[$group_id]['num_members'] += $num_members;
847
		}
848 2
849
		$db->fetchQuery('
850 2
			SELECT 
851
				mods.id_group, mods.id_member, mem.member_name, mem.real_name
852
			FROM {db_prefix}group_moderators AS mods
853 2
				INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
854
			WHERE mods.id_group IN ({array_int:group_list})',
855
			array(
856
				'group_list' => $group_ids,
857
			)
858
		)->fetch_callback(
859
			function ($row) use (&$groups) {
860 2
				$groups[$row['id_group']]['moderators'][] = '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row['id_member'], 'name' => $row['real_name']]) . '">' . $row['real_name'] . '</a>';
861
			}
862 2
		);
863
	}
864
865 2
	if (!empty($parent_groups))
866
	{
867
		$all_group_names = array(
868
			-1 => $txt['membergroups_guests'],
869 2
			0 => $txt['membergroups_members']
870
		);
871
		$db->fetchQuery('
872
			SELECT 
873
				id_group, group_name
874
			FROM {db_prefix}membergroups
875
			WHERE id_group IN ({array_int:groups})',
876
			array(
877
				'groups' => $parent_groups,
878
			)
879
		)->fetch_callback(
880
			function ($row) use (&$all_group_names) {
881
				$all_group_names[$row['id_group']] = $row['group_name'];
882
			}
883
		);
884
	}
885
	foreach ($groups as $key => $group)
886
	{
887
		if ($group['id_parent'] != -2)
888
		{
889 2
			$groups[$key]['parent_name'] = $all_group_names[$group['id_parent']];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $all_group_names does not seem to be defined for all execution paths leading up to this point.
Loading history...
890
		}
891 2
	}
892
893 1
	// Apply manual sorting if the 'number of members' column is selected.
894
	if (substr($sort, 0, 1) === '1' || strpos($sort, ', 1') !== false)
895
	{
896
		$sort_ascending = strpos($sort, 'DESC') === false;
897
		$sort_array = array();
898 2
899
		foreach ($groups as $group)
900
		{
901
			$sort_array[] = $group['id_group'] != 3 ? (int) $group['num_members'] : -1;
902
		}
903
904
		array_multisort($sort_array, $sort_ascending ? SORT_ASC : SORT_DESC, SORT_REGULAR, $groups);
0 ignored issues
show
Bug introduced by
$sort_ascending ? SORT_ASC : SORT_DESC cannot be passed to array_multisort() as the parameter $rest expects a reference. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

904
		array_multisort($sort_array, /** @scrutinizer ignore-type */ $sort_ascending ? SORT_ASC : SORT_DESC, SORT_REGULAR, $groups);
Loading history...
Bug introduced by
SORT_REGULAR cannot be passed to array_multisort() as the parameter $rest expects a reference. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

904
		array_multisort($sort_array, $sort_ascending ? SORT_ASC : SORT_DESC, /** @scrutinizer ignore-type */ SORT_REGULAR, $groups);
Loading history...
905
	}
906
907
	if ($count_permissions)
908
	{
909
		// pid = profile id
910
		if (empty($pid))
911 2
		{
912
			$groups = countPermissions($groups, $context['hidden_permissions']);
913
914
			// Get the "default" profile permissions too.
915
			$groups = countBoardPermissions($groups, $context['hidden_permissions'], 1);
916
		}
917
		else
918
		{
919
			$groups = countBoardPermissions($groups, null, $pid);
920
		}
921
	}
922
923
	return $groups;
924
}
925
926
/**
927 2
 * Count the number of members in specific groups
928
 *
929
 * @param int[] $postGroups an array of post-based groups id.
930
 * @param int[] $normalGroups = array() an array of normal groups id.
931
 * @param bool $include_hidden if true, includes hidden groups in the count (default false).
932
 * @param bool $include_moderators if true, includes board moderators too (default false).
933
 * @param bool $include_non_active if true, includes non active members (default false).
934
 * @return array
935
 * @package Membergroups
936
 */
937
function membersInGroups($postGroups, $normalGroups = array(), $include_hidden = false, $include_moderators = false, $include_non_active = false)
938
{
939
	$db = database();
940
941
	$groups = array();
942
943
	// If we have post groups, let's count the number of members...
944 2
	if (!empty($postGroups))
945
	{
946 2
		$db->fetchQuery('
947
			SELECT 
948
				id_post_group AS id_group, COUNT(*) AS member_count
949 2
			FROM {db_prefix}members
950
			WHERE id_post_group IN ({array_int:post_group_list})' . ($include_non_active ? '' : '
951
				AND is_activated = {int:active_members}') . '
952
			GROUP BY id_post_group',
953
			array(
954
				'post_group_list' => $postGroups,
955
				'active_members' => 1,
956
			)
957
		)->fetch_callback(
958
			function ($row) use (&$groups) {
959
				$groups[$row['id_group']] = $row['member_count'];
960
			}
961
		);
962
	}
963
964
	if (!empty($normalGroups))
965
	{
966
		// Find people who are members of this group...
967
		$db->fetchQuery('
968
			SELECT 
969 2
				id_group, COUNT(*) AS member_count
970
			FROM {db_prefix}members
971
			WHERE id_group IN ({array_int:normal_group_list})' . ($include_non_active ? '' : '
972 2
				AND is_activated = {int:active_members}') . '
973
			GROUP BY id_group',
974
			array(
975
				'normal_group_list' => $normalGroups,
976 2
				'active_members' => 1,
977 2
			)
978
		)->fetch_callback(
979
			function ($row) use (&$groups) {
980 2
				$groups[$row['id_group']] = $row['member_count'];
981 2
			}
982
		);
983 2
984
		// Only do additional groups if we can moderate...
985 2
		if ($include_hidden)
986 2
		{
987
			// Also do those who have it as an additional membergroup - this ones more yucky...
988
			$db->fetchQuery('
989
				SELECT 
990 2
					mg.id_group, COUNT(*) AS member_count
991
				FROM {db_prefix}membergroups AS mg
992
					INNER JOIN {db_prefix}members AS mem ON (mem.additional_groups != {string:blank_string}
993 2
						AND mem.id_group != mg.id_group
994
						AND FIND_IN_SET(mg.id_group, mem.additional_groups) != 0)
995
				WHERE mg.id_group IN ({array_int:normal_group_list})' . ($include_non_active ? '' : '
996
					AND mem.is_activated = {int:active_members}') . '
997
				GROUP BY mg.id_group',
998
				array(
999
					'normal_group_list' => $normalGroups,
1000 2
					'active_members' => 1,
1001 2
					'blank_string' => '',
1002
				)
1003
			)->fetch_callback(
1004 2
				function ($row) use (&$groups) {
1005 2
					if (isset($groups[$row['id_group']]))
1006 2
					{
1007
						$groups[$row['id_group']] += $row['member_count'];
1008 2
					}
1009
					else
1010
					{
1011
						$groups[$row['id_group']] = $row['member_count'];
1012
					}
1013
				}
1014
			);
1015
		}
1016
	}
1017
1018 2
	if ($include_moderators)
1019
	{
1020
		// Any moderators?
1021
		$request = $db->query('', '
1022
			SELECT 
1023 2
				COUNT(DISTINCT id_member) AS num_distinct_mods
1024
			FROM {db_prefix}moderators
1025
			LIMIT 1',
1026
			array()
1027
		);
1028
		list ($groups[3]) = $request->fetch_row();
1029
		$request->free_result();
1030
	}
1031
1032
	return $groups;
1033
}
1034
1035
/**
1036
 * Returns details of membergroups based on the id
1037 2
 *
1038
 * @param int[]|int $group_ids the IDs of the groups.
1039
 * @param int $limit = 1 the number of results returned (default 1, if null/false/0 returns all).
1040
 * @param bool $detailed = false if true then it returns more fields (default false).
1041
 *     false returns: id_group, group_name, group_type.
1042
 *     true adds to above: description, min_posts, online_color, max_messages, icons, hidden, id_parent.
1043
 * @param bool $assignable = false determine if the group is assignable or not and return that information.
1044
 * @return array
1045
 * @package Membergroups
1046
 */
1047
function membergroupsById($group_ids, $limit = 1, $detailed = false, $assignable = false)
1048
{
1049
	$db = database();
1050
1051
	if (empty($group_ids))
1052
	{
1053
		return [];
1054
	}
1055
1056
	$group_ids = !is_array($group_ids) ? array($group_ids) : $group_ids;
1057
1058
	$groups = array();
1059
	$group_ids = array_map('intval', $group_ids);
1060
1061
	$db->fetchQuery('
1062
		SELECT 
1063
			id_group, group_name, group_type' . (!$detailed ? '' : ',
1064
			description, min_posts, online_color, max_messages, icons, hidden, id_parent') . (!$assignable ? '' : ',
1065
			CASE WHEN min_posts = {int:min_posts} THEN 1 ELSE 0 END AS assignable,
1066
			CASE WHEN min_posts != {int:min_posts} THEN 1 ELSE 0 END AS is_post_group') . '
1067
		FROM {db_prefix}membergroups
1068
		WHERE id_group IN ({array_int:group_ids})' . (empty($limit) ? '' : '
1069
		LIMIT {int:limit}'),
1070
		array(
1071
			'min_posts' => -1,
1072
			'group_ids' => $group_ids,
1073
			'limit' => $limit,
1074
		)
1075
	)->fetch_callback(
1076
		function ($row) use (&$groups, $detailed) {
1077
			$row['id_group'] = (int) $row['id_group'];
1078
			$row['group_type'] = (int) $row['group_type'];
1079
1080
			if ($detailed)
1081
			{
1082
				$row['id_parent'] = (int) $row['id_parent'];
1083
				$row['min_posts'] = (int) $row['min_posts'];
1084
				$row['max_messages'] = (int) $row['max_messages'];
1085
			}
1086
1087
			$groups[$row['id_group']] = $row;
1088
		}
1089
	);
1090
1091
	return $groups;
1092
}
1093
1094
/**
1095
 * Uses membergroupsById to return the group information of a single group
1096
 *
1097
 * @param int $group_id
1098
 * @param bool $detailed
1099
 * @param bool $assignable
1100
 *
1101
 * @return bool|mixed
1102
 * @package Membergroups
1103
 *
1104
 */
1105
function membergroupById($group_id, $detailed = false, $assignable = false)
1106
{
1107
	$groups = membergroupsById(array($group_id), 1, $detailed, $assignable);
1108
1109
	return $groups[$group_id] ?? false;
1110
}
1111
1112
/**
1113
 * Gets basic membergroup data
1114
 *
1115
 * - the $includes and $excludes array is used for granular filtering the output.
1116
 * - We need to exclude groups sometimes because they are special ones.
1117
 * Example: getBasicMembergroupData(array('admin', 'mod', 'globalmod'));
1118
 * $includes parameters:
1119
 * - 'admin' includes the admin: id_group = 1
1120
 * - 'mod' includes the local moderator: id_group = 3
1121
 * - 'globalmod' includes the global moderators: id_group = 2
1122
 * - 'member' includes the ungrouped users from id_group = 0
1123
 * - 'postgroups' includes the post based membergroups
1124
 * - 'protected' includes protected groups
1125
 * - 'all' lists all groups
1126
 * $excludes parameters:
1127
 * - 'newbie' excludes the newbie group id_group 4
1128
 * - 'custom' lists only the system based groups (id 0, 1, 2, 3)
1129
 * - 'membergroups' excludes permission groups, lists the post based membergroups
1130
 * - 'hidden' excludes hidden groups
1131
 *
1132
 * @param string[]|string $includes
1133
 * @param string[] $excludes
1134
 * @param string|null $sort_order
1135
 * @param bool|null $split splits postgroups and membergroups
1136
 * @return array
1137
 * @package Membergroups
1138
 */
1139
function getBasicMembergroupData($includes = array(), $excludes = array(), $sort_order = null, $split = null)
1140
{
1141
	global $txt, $modSettings;
1142
1143
	$db = database();
1144
1145
	// No $includes parameters given? Let's set some default values
1146 2
	if (empty($includes))
1147
	{
1148 2
		$includes = array('globalmod', 'member', 'postgroups');
1149
	}
1150
	elseif (!is_array($includes))
1151 2
	{
1152
		$includes = array($includes);
1153
	}
1154
1155 2
	$groups = array();
1156
1157
	$where = '';
1158
	$sort_order = $sort_order ?? 'min_posts, CASE WHEN id_group < {int:newbie_group} THEN id_group ELSE 4 END, group_name';
1159
1160 2
	// Do we need the post based membergroups?
1161
	$where .= !empty($modSettings['permission_enable_postgroups']) || in_array('postgroups', $includes) ? '' : 'AND min_posts = {int:min_posts}';
0 ignored issues
show
Bug introduced by
It seems like $includes can also be of type string; however, parameter $haystack of in_array() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1161
	$where .= !empty($modSettings['permission_enable_postgroups']) || in_array('postgroups', /** @scrutinizer ignore-type */ $includes) ? '' : 'AND min_posts = {int:min_posts}';
Loading history...
1162 2
	// Include protected groups?
1163 2
	$where .= allowedTo('admin_forum') || in_array('protected', $includes) ? '' : ' AND group_type != {int:is_protected}';
1164
	// Include the global moderators?
1165
	$where .= in_array('globalmod', $includes) ? '' : ' AND id_group != {int:global_mod_group}';
1166 2
	// Include the admins?
1167
	$where .= in_array('admin', $includes) ? '' : ' AND id_group != {int:admin_group}';
1168 2
	// Local Moderators?
1169
	$where .= in_array('mod', $includes) ? '' : ' AND id_group != {int:moderator_group}';
1170 2
	// Ignore the first post based group?
1171
	$where .= !in_array('newbie', $excludes) ? '' : ' AND id_group != {int:newbie_group}';
1172 2
	// Exclude custom groups?
1173
	$where .= !in_array('custom', $excludes) ? '' : ' AND id_group < {int:newbie_group}';
1174 2
	// Exclude hidden?
1175
	$where .= !in_array('hidden', $excludes) ? '' : ' AND hidden != {int:hidden_group}';
1176 2
1177
	// Only the post based membergroups? We can safely overwrite the $where.
1178 2
	if (in_array('membergroups', $excludes))
1179
	{
1180 2
		$where = ' AND min_posts != {int:min_posts}';
1181
	}
1182
1183 2
	// Simply all of them?
1184
	if (in_array('all', $includes))
1185
	{
1186
		$where = '';
1187
	}
1188
1189 2
	$request = $db->query('', '
1190
		SELECT 
1191
			id_group, group_name, min_posts, online_color
1192
		FROM {db_prefix}membergroups
1193
		WHERE 1 = 1
1194 2
			' . $where . '
1195
		ORDER BY ' . $sort_order,
1196
		array(
1197
			'admin_group' => 1,
1198
			'moderator_group' => 3,
1199 2
			'global_mod_group' => 2,
1200 2
			'min_posts' => -1,
1201
			'is_protected' => 1,
1202 2
			'newbie_group' => 4,
1203
			'hidden_group' => 2,
1204
		)
1205
	);
1206
1207
	// Include the default membergroup? the ones with id_member = 0
1208
	if (in_array('member', $includes) && !isset($split))
1209
	{
1210
		$groups[] = array(
1211
			'id' => 0,
1212
			'name' => $txt['membergroups_members']
1213 2
		);
1214
	}
1215 2
1216 2
	if (!empty($split))
1217 2
	{
1218
		if (empty($modSettings['permission_enable_postgroups']))
1219
		{
1220
			$groups['groups'][0] = array(
1221 2
				'id' => 0,
1222
				'name' => $txt['membergroups_members'],
1223
				'can_be_additional' => false,
1224
				'member_count' => 0,
1225
			);
1226
			$groups['membergroups'][0] = array(
1227
				'id' => 0,
1228
				'name' => $txt['membergroups_members'],
1229
				'can_be_additional' => false,
1230
				'member_count' => 0,
1231
			);
1232
		}
1233
		while (($row = $request->fetch_assoc()))
1234
		{
1235
			$groups['groups'][$row['id_group']] = array(
1236
				'id' => $row['id_group'],
1237
				'name' => $row['group_name'],
1238
				'member_count' => 0,
1239
			);
1240
1241
			if ($row['min_posts'] == -1)
1242
			{
1243
				$groups['membergroups'][] = array(
1244
					'id' => $row['id_group'],
1245
					'name' => $row['group_name'],
1246
					'can_be_additional' => true,
1247
				);
1248
			}
1249
			else
1250
			{
1251
				$groups['postgroups'][] = array(
1252
					'id' => $row['id_group'],
1253
					'name' => $row['group_name'],
1254
				);
1255
			}
1256
		}
1257
	}
1258
	else
1259
	{
1260
		while (($row = $request->fetch_assoc()))
1261
		{
1262
			$groups[] = array(
1263
				'id' => $row['id_group'],
1264
				'name' => $row['group_name'],
1265 2
				'online_color' => $row['online_color'],
1266
			);
1267 2
		}
1268 2
	}
1269 2
1270 2
	$request->free_result();
1271
1272
	return $groups;
1273
}
1274
1275 2
/**
1276
 * Retrieve groups and their number of members.
1277 2
 *
1278
 * @param int[] $groupList
1279
 * @return array with ('id', 'name', 'member_count')
1280
 * @package Membergroups
1281
 */
1282
function getGroups($groupList)
1283
{
1284
	global $txt;
1285
1286
	$db = database();
1287
1288
	$groups = array();
1289
	if (in_array(0, $groupList))
1290 2
	{
1291
		$groups[0] = array(
1292 2
			'id' => 0,
1293
			'name' => $txt['announce_regular_members'],
1294 2
			'member_count' => 'n/a',
1295 2
		);
1296
	}
1297 2
1298 2
	// Get all membergroups that have access to the board the announcement was made on.
1299 2
	$db->fetchQuery('
1300 2
		SELECT 
1301
			mg.id_group, mg.group_name, COUNT(mem.id_member) AS num_members
1302
		FROM {db_prefix}membergroups AS mg
1303
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_group = mg.id_group OR FIND_IN_SET(mg.id_group, mem.additional_groups) != 0 OR mg.id_group = mem.id_post_group)
1304
		WHERE mg.id_group IN ({array_int:group_list})
1305 2
		GROUP BY mg.id_group, mg.group_name',
1306
		array(
1307
			'group_list' => $groupList,
1308
		)
1309
	)->fetch_callback(
1310
		function ($row) use (&$groups) {
1311
			$groups[$row['id_group']] = array(
1312
				'id' => $row['id_group'],
1313 2
				'name' => $row['group_name'],
1314
				'member_count' => $row['num_members'],
1315 2
			);
1316
		}
1317 2
	);
1318 2
1319 2
	return $groups;
1320 2
}
1321
1322 2
/**
1323
 * Gets the last assigned group id.
1324
 *
1325 2
 * @return int $id_group
1326
 * @package Membergroups
1327
 */
1328
function getMaxGroupID()
1329
{
1330
	$db = database();
1331
1332
	$request = $db->query('', '
1333
		SELECT 
1334
			MAX(id_group)
1335
		FROM {db_prefix}membergroups',
1336
		array()
1337
	);
1338
	list ($id_group) = $request->fetch_row();
1339
	$request->free_result();
1340
1341
	return $id_group;
1342
}
1343
1344
/**
1345
 * Adds a new group to the membergroups table.
1346
 *
1347
 * @param string $groupname
1348
 * @param int $minposts
1349
 * @param string $type
1350
 * @package Membergroups
1351
 */
1352
function createMembergroup($groupname, $minposts, $type)
1353
{
1354
	$db = database();
1355
1356
	$db->insert('',
1357
		'{db_prefix}membergroups',
1358
		array(
1359
			'description' => 'string', 'group_name' => 'string-80', 'min_posts' => 'int',
1360
			'icons' => 'string', 'online_color' => 'string', 'group_type' => 'int',
1361
		),
1362
		array(
1363
			'', Util::htmlspecialchars($groupname, ENT_QUOTES), $minposts,
1364
			'1#icon.png', '', $type,
1365
		),
1366
		array('id_group')
1367
	);
1368
1369
	return $db->insert_id('{db_prefix}membergroups');
1370
}
1371
1372
/**
1373
 * Copies permissions from a given membergroup.
1374
 *
1375
 * @param int $id_group
1376
 * @param int $copy_from
1377
 * @param string[]|null $illegal_permissions
1378
 * @todo another function with the same name in ManagePermissions.subs.php
1379
 * @package Membergroups
1380
 */
1381
function copyPermissions($id_group, $copy_from, $illegal_permissions)
1382
{
1383
	$db = database();
1384
1385
	$inserts = array();
1386
1387
	$db->fetchQuery('
1388
		SELECT 
1389
			permission, add_deny
1390
		FROM {db_prefix}permissions
1391
		WHERE id_group = {int:copy_from}',
1392
		array(
1393
			'copy_from' => $copy_from,
1394
		)
1395
	)->fetch_callback(
1396
		function ($row) use (&$inserts, $illegal_permissions, $id_group) {
1397
			if (empty($illegal_permissions) || !in_array($row['permission'], $illegal_permissions))
1398
			{
1399
				$inserts[] = array($id_group, $row['permission'], $row['add_deny']);
1400
			}
1401
		}
1402
	);
1403
1404
	if (!empty($inserts))
1405
	{
1406
		$db->insert('insert',
1407
			'{db_prefix}permissions',
1408
			array('id_group' => 'int', 'permission' => 'string', 'add_deny' => 'int'),
1409
			$inserts,
1410
			array('id_group', 'permission')
1411
		);
1412
	}
1413
}
1414
1415
/**
1416
 * Copies the board permissions from a given membergroup.
1417
 *
1418
 * @param int $id_group
1419
 * @param int $copy_from
1420
 * @package Membergroups
1421
 */
1422
function copyBoardPermissions($id_group, $copy_from)
1423
{
1424
	$db = database();
1425
1426
	$inserts = $db->fetchQuery('
1427
		SELECT 
1428
			id_profile, permission, add_deny
1429
		FROM {db_prefix}board_permissions
1430
		WHERE id_group = {int:copy_from}',
1431
		array(
1432
			'copy_from' => $copy_from,
1433
		)
1434
	)->fetch_callback(
1435
		function ($row) use ($id_group) {
1436
			return array($id_group, $row['id_profile'], $row['permission'], $row['add_deny']);
1437
		}
1438
	);
1439
1440
	if (!empty($inserts))
1441
	{
1442
		$db->insert('insert',
1443
			'{db_prefix}board_permissions',
1444
			array('id_group' => 'int', 'id_profile' => 'int', 'permission' => 'string', 'add_deny' => 'int'),
1445
			$inserts,
1446
			array('id_group', 'id_profile', 'permission')
1447
		);
1448
	}
1449
}
1450
1451
/**
1452
 * Updates the properties of a copied membergroup.
1453
 *
1454
 * @param int $id_group
1455
 * @param int $copy_from
1456
 * @package Membergroups
1457
 */
1458
function updateCopiedGroup($id_group, $copy_from)
1459
{
1460
	$db = database();
1461
1462
	require_once(SUBSDIR . '/Membergroups.subs.php');
1463
	$group_info = membergroupById($copy_from, true);
1464
1465
	// update the new membergroup
1466
	$db->query('', '
1467
		UPDATE {db_prefix}membergroups
1468
		SET
1469
			online_color = {string:online_color},
1470
			max_messages = {int:max_messages},
1471
			icons = {string:icons}
1472
			WHERE id_group = {int:current_group}',
1473
		array(
1474
			'max_messages' => $group_info['max_messages'],
1475
			'current_group' => $id_group,
1476
			'online_color' => $group_info['online_color'],
1477
			'icons' => $group_info['icons'],
1478
		)
1479
	);
1480
}
1481
1482
/**
1483
 * Updates the properties of a inherited membergroup.
1484
 *
1485
 * @param int $id_group
1486
 * @param int $copy_id
1487
 * @package Membergroups
1488
 */
1489
function updateInheritedGroup($id_group, $copy_id)
1490
{
1491
	$db = database();
1492
1493
	$db->query('', '
1494
		UPDATE {db_prefix}membergroups
1495
		SET id_parent = {int:copy_from}
1496
		WHERE id_group = {int:current_group}',
1497
		array(
1498
			'copy_from' => $copy_id,
1499
			'current_group' => $id_group,
1500
		)
1501
	);
1502
}
1503
1504
/**
1505
 * This function updates the membergroup with the given information.
1506
 *
1507
 * - It's passed an associative array $properties, with 'current_group' holding
1508
 * the group to update. The rest of the keys are details to update it with.
1509
 *
1510
 * @param mixed[] $properties
1511
 * @package Membergroups
1512
 */
1513
function updateMembergroupProperties($properties)
1514
{
1515
	$db = database();
1516
1517
	$known_properties = array(
1518
		'max_messages' => array('type' => 'int'),
1519
		'min_posts' => array('type' => 'int'),
1520
		'group_type' => array('type' => 'int'),
1521
		'hidden' => array('type' => 'int'),
1522
		'id_parent' => array('type' => 'int'),
1523
		'group_name' => array('type' => 'string'),
1524
		'online_color' => array('type' => 'string'),
1525
		'icons' => array('type' => 'string'),
1526
		'description' => array('type' => 'string'),
1527
	);
1528
1529
	$values = array('current_group' => $properties['current_group']);
1530
	$updates = array();
1531
	foreach ($properties as $name => $value)
1532
	{
1533
		if (isset($known_properties[$name]))
1534
		{
1535
			$updates[] = $name . '={' . $known_properties[$name]['type'] . ':subs_' . $name . '}';
1536
			switch ($known_properties[$name]['type'])
1537
			{
1538
				case 'string':
1539
					$values['subs_' . $name] = Util::htmlspecialchars((string) $value);
1540
					break;
1541
				default:
1542
					$values['subs_' . $name] = (int) $value;
1543
			}
1544
		}
1545
	}
1546
1547
	if (empty($values))
1548
	{
1549
		return;
1550
	}
1551
1552
	$db->query('', '
1553
		UPDATE {db_prefix}membergroups
1554
		SET ' . implode(', ', $updates) . '
1555
		WHERE id_group = {int:current_group}',
1556
		$values
1557
	);
1558
}
1559
1560
/**
1561
 * Detaches a membergroup from the boards listed in $boards.
1562
 *
1563
 * @param int $id_group
1564
 * @param mixed[] $boards
1565
 * @param string $access_list ('allow', 'deny')
1566
 * @package Membergroups
1567
 */
1568
function detachGroupFromBoards($id_group, $boards, $access_list)
1569
{
1570
	$db = database();
1571
1572
	// Find all boards in whose access list this group is in, but shouldn't be.
1573
	$db->fetchQuery('
1574
		SELECT 
1575
			id_board, {raw:column}
1576
		FROM {db_prefix}boards
1577
		WHERE FIND_IN_SET({string:current_group}, {raw:column}) != 0' . (empty($boards[$access_list]) ? '' : '
1578
			AND id_board NOT IN ({array_int:board_access_list})'),
1579
		array(
1580
			'current_group' => $id_group,
1581
			'board_access_list' => $boards[$access_list],
1582
			'column' => $access_list === 'allow' ? 'member_groups' : 'deny_member_groups',
1583
		)
1584
	)->fetch_callback(
1585
		function ($row) use ($id_group, $access_list, $db) {
1586
			$db->query('', '
1587
				UPDATE {db_prefix}boards
1588
				SET {raw:column} = {string:member_group_access}
1589
				WHERE id_board = {int:current_board}',
1590
				array(
1591
					'current_board' => $row['id_board'],
1592
					'member_group_access' => implode(',', array_diff(explode(',', $row['member_groups']), array($id_group))),
1593
					'column' => $access_list === 'allow' ? 'member_groups' : 'deny_member_groups',
1594
				)
1595
			);
1596
		}
1597
	);
1598
}
1599
1600
/**
1601
 * Assigns the given group $id_group to the boards specified, for
1602
 * the 'allow' or 'deny' list.
1603
 *
1604
 * @param int $id_group
1605
 * @param mixed[] $boards
1606
 * @param string $access_list ('allow', 'deny')
1607
 * @package Membergroups
1608
 */
1609
function assignGroupToBoards($id_group, $boards, $access_list)
1610
{
1611
	$db = database();
1612
1613
	$db->query('', '
1614
		UPDATE {db_prefix}boards
1615
		SET {raw:column} = CASE WHEN {raw:column} = {string:blank_string} THEN {string:group_id_string} ELSE CONCAT({raw:column}, {string:comma_group}) END
1616
		WHERE id_board IN ({array_int:board_list})
1617
			AND FIND_IN_SET({int:current_group}, {raw:column}) = 0',
1618
		array(
1619
			'board_list' => $boards[$access_list],
1620
			'blank_string' => '',
1621
			'current_group' => $id_group,
1622
			'group_id_string' => (string) $id_group,
1623
			'comma_group' => ',' . $id_group,
1624
			'column' => $access_list === 'allow' ? 'member_groups' : 'deny_member_groups',
1625
		)
1626
	);
1627
}
1628
1629
/**
1630
 * Membergroup was deleted? We need to detach that group from our members, too...
1631
 *
1632
 * @param int $id_group
1633
 * @package Membergroups
1634
 */
1635
function detachDeletedGroupFromMembers($id_group)
1636
{
1637
	$db = database();
1638
1639
	$updates = array();
1640
1641
	$db->query('', '
1642
		UPDATE {db_prefix}members
1643
		SET id_group = {int:regular_member}
1644
		WHERE id_group = {int:current_group}',
1645
		array(
1646
			'regular_member' => 0,
1647
			'current_group' => $id_group,
1648
		)
1649
	);
1650
1651
	$db->fetchQuery('
1652
		SELECT 
1653
			id_member, additional_groups
1654
		FROM {db_prefix}members
1655
		WHERE FIND_IN_SET({string:current_group}, additional_groups) != 0',
1656
		array(
1657
			'current_group' => $id_group,
1658
		)
1659
	)->fetch_callback(
1660
		function ($row) use (&$updates) {
1661
			$updates[$row['additional_groups']][] = $row['id_member'];
1662
		}
1663
	);
1664
1665
	require_once(SUBSDIR . '/Members.subs.php');
1666
	foreach ($updates as $additional_groups => $memberArray)
1667
	{
1668
		updateMemberData($memberArray, array('additional_groups' => implode(',', array_diff(explode(',', $additional_groups), array($id_group)))));
1669
	}
1670
1671
}
1672
1673
/**
1674
 * Make the given group hidden. Hidden groups are stored in the additional_groups.
1675
 *
1676
 * @param int $id_group
1677
 * @package Membergroups
1678
 */
1679
function setGroupToHidden($id_group)
1680
{
1681
	$db = database();
1682
1683
	$updates = array();
1684
1685
	$db->fetchQuery('
1686
		SELECT 
1687
			id_member, additional_groups
1688
		FROM {db_prefix}members
1689
		WHERE id_group = {int:current_group}
1690
			AND FIND_IN_SET({int:current_group}, additional_groups) = 0',
1691
		array(
1692
			'current_group' => $id_group,
1693
		)
1694
	)->fetch_callback(
1695
		function ($row) use (&$updates) {
1696
			$updates[$row['additional_groups']][] = $row['id_member'];
1697
		}
1698
	);
1699
1700
	require_once(SUBSDIR . '/Members.subs.php');
1701
	foreach ($updates as $additional_groups => $memberArray)
1702
	{
1703
		updateMemberData($memberArray, array('additional_groups' => implode(',', array_merge(explode(',', $additional_groups), array($id_group)))));
1704
	}
1705
1706
	$db->query('', '
1707
		UPDATE {db_prefix}members
1708
		SET id_group = {int:regular_member}
1709
		WHERE id_group = {int:current_group}',
1710
		array(
1711
			'regular_member' => 0,
1712
			'current_group' => $id_group,
1713
		)
1714
	);
1715
}
1716
1717
/**
1718
 * Make sure the setting to display membergroup key on the board index is valid.
1719
 * It updates the setting if necessary.
1720
 *
1721
 * @package Membergroups
1722
 */
1723
function validateShowGroupMembership()
1724
{
1725
	global $modSettings;
1726
1727
	$db = database();
1728
1729
	$request = $db->query('', '
1730
		SELECT
1731
		 	COUNT(*)
1732
		FROM {db_prefix}membergroups
1733
		WHERE group_type > {int:non_joinable}',
1734
		array(
1735
			'non_joinable' => 1,
1736
		)
1737
	);
1738
	list ($have_joinable) = $request->fetch_row();
1739
	$request->free_result();
1740
1741
	// Do we need to update the setting?
1742
	if ((empty($modSettings['show_group_membership']) && $have_joinable) || (!empty($modSettings['show_group_membership']) && !$have_joinable))
1743
	{
1744
		updateSettings(array('show_group_membership' => $have_joinable ? 1 : 0));
1745
	}
1746
}
1747
1748
/**
1749
 * Detaches group moderators from a deleted group.
1750
 *
1751
 * @param int $id_group
1752
 * @package Membergroups
1753
 */
1754
function detachGroupModerators($id_group)
1755
{
1756
	$db = database();
1757
1758
	$db->query('', '
1759
		DELETE FROM {db_prefix}group_moderators
1760
		WHERE id_group = {int:current_group}',
1761
		array(
1762
			'current_group' => $id_group,
1763
		)
1764
	);
1765
}
1766
1767
/**
1768
 * Get the id_member from the membergroup moderators.
1769
 *
1770
 * @param string[] $moderators
1771
 *
1772
 * @return int[]
1773
 * @package Membergroups
1774
 */
1775
function getIDMemberFromGroupModerators($moderators)
1776
{
1777
	$db = database();
1778
1779
	return $db->fetchQuery('
1780
		SELECT 
1781
			id_member
1782
		FROM {db_prefix}members
1783
		WHERE member_name IN ({array_string:moderators}) OR real_name IN ({array_string:moderators})
1784
		LIMIT ' . count($moderators),
1785
		array(
1786
			'moderators' => $moderators,
1787
		)
1788
	)->fetch_callback(
1789
		function ($row) {
1790
			return $row['id_member'];
1791
		}
1792
	);
1793
}
1794
1795
/**
1796
 * Assign members to the membergroup moderators.
1797
 *
1798
 * @param int $id_group
1799
 * @param int[] $group_moderators
1800
 * @package Membergroups
1801
 */
1802
function assignGroupModerators($id_group, $group_moderators)
1803
{
1804
	$db = database();
1805
1806
	$mod_insert = array();
1807
	foreach ($group_moderators as $moderator)
1808
	{
1809
		$mod_insert[] = array($id_group, $moderator);
1810
	}
1811
1812
	$db->insert('insert',
1813
		'{db_prefix}group_moderators',
1814
		array('id_group' => 'int', 'id_member' => 'int'),
1815
		$mod_insert,
1816
		array('id_group', 'id_member')
1817
	);
1818
}
1819
1820
/**
1821
 * List moderators from a given membergroup.
1822
 *
1823
 * @param int $id_group
1824
 * @return array moderators as array(id => name)
1825
 * @package Membergroups
1826
 */
1827
function getGroupModerators($id_group)
1828
{
1829
	$db = database();
1830
1831
	$moderators = array();
1832
1833
	$db->fetchQuery('
1834
		SELECT 
1835
			mem.id_member, mem.real_name
1836
		FROM {db_prefix}group_moderators AS mods
1837
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
1838
		WHERE mods.id_group = {int:current_group}',
1839
		array(
1840
			'current_group' => $id_group,
1841
		)
1842
	)->fetch_callback(
1843
		function ($row) use (&$moderators) {
1844
			$moderators[$row['id_member']] = $row['real_name'];
1845
		}
1846
	);
1847
1848
	return $moderators;
1849
}
1850
1851
/**
1852
 * Lists all groups which inherit permission profiles from the given group.
1853
 *
1854
 * - If no group is specified it will list any group that can be used
1855
 *
1856
 * @param int|bool $id_group
1857
 * @return array
1858
 * @package Membergroups
1859
 */
1860
function getInheritableGroups($id_group = false)
1861
{
1862
	global $modSettings;
1863
1864
	$db = database();
1865
1866
	$inheritable_groups = array();
1867
1868
	$db->fetchQuery('
1869
		SELECT 
1870
			id_group, group_name
1871
		FROM {db_prefix}membergroups
1872
		WHERE id_parent = {int:not_inherited}' . ($id_group === false ? '' : '
1873
			AND id_group != {int:current_group}') .
1874
		(empty($modSettings['permission_enable_postgroups']) ? '
1875
			AND min_posts = {int:min_posts}' : '') . (allowedTo('admin_forum') ? '' : '
1876
			AND group_type != {int:is_protected}') . '
1877
			AND id_group NOT IN (1, 3)',
1878
		array(
1879
			'current_group' => $id_group,
1880
			'min_posts' => -1,
1881
			'not_inherited' => -2,
1882
			'is_protected' => 1,
1883
		)
1884
	)->fetch_callback(
1885
		function ($row) use (&$inheritable_groups) {
1886
			$inheritable_groups[$row['id_group']] = $row['group_name'];
1887
		}
1888
	);
1889
1890
	return $inheritable_groups;
1891
}
1892
1893
/**
1894
 * List all membergroups and prepares them to assign permissions to..
1895
 *
1896
 * @return array
1897
 * @package Membergroups
1898
 */
1899
function prepareMembergroupPermissions()
1900
{
1901
	global $modSettings, $txt;
1902
1903
	$db = database();
1904
1905
	// Start this with the guests/members.
1906
	$profile_groups = array(
1907
		-1 => array(
1908
			'id' => -1,
1909
			'name' => $txt['membergroups_guests'],
1910
			'color' => '',
1911
			'new_topic' => 'disallow',
1912
			'replies_own' => 'disallow',
1913
			'replies_any' => 'disallow',
1914
			'attachment' => 'disallow',
1915
			'children' => array(),
1916
		),
1917
		0 => array(
1918
			'id' => 0,
1919
			'name' => $txt['membergroups_members'],
1920
			'color' => '',
1921
			'new_topic' => 'disallow',
1922
			'replies_own' => 'disallow',
1923
			'replies_any' => 'disallow',
1924
			'attachment' => 'disallow',
1925
			'children' => array(),
1926
		),
1927
	);
1928
1929
	$db->fetchQuery('
1930
		SELECT 
1931
			id_group, group_name, online_color, id_parent
1932
		FROM {db_prefix}membergroups
1933
		WHERE id_group != {int:admin_group}
1934
			' . (empty($modSettings['permission_enable_postgroups']) ? ' AND min_posts = {int:min_posts}' : '') . '
1935
		ORDER BY id_parent ASC',
1936
		array(
1937
			'admin_group' => 1,
1938
			'min_posts' => -1,
1939
		)
1940
	)->fetch_callback(
1941
		function ($row) use (&$profile_groups) {
1942
			if ($row['id_parent'] == -2)
1943
			{
1944
				$profile_groups[$row['id_group']] = array(
1945
					'id' => $row['id_group'],
1946
					'name' => $row['group_name'],
1947
					'color' => $row['online_color'],
1948
					'new_topic' => 'disallow',
1949
					'replies_own' => 'disallow',
1950
					'replies_any' => 'disallow',
1951
					'attachment' => 'disallow',
1952
					'children' => array(),
1953
				);
1954
			}
1955
			elseif (isset($profile_groups[$row['id_parent']]))
1956
			{
1957
				$profile_groups[$row['id_parent']]['children'][] = $row['group_name'];
1958
			}
1959
		}
1960
	);
1961
1962
	return $profile_groups;
1963
}
1964
1965
/**
1966
 * Returns the groups that a user could see.
1967
 *
1968
 * - Ask and it will give you.
1969
 *
1970
 * @param int $id_member the id of a member
1971
 * @param bool $show_hidden true if hidden groups (that the user can moderate) should be loaded (default false)
1972
 * @param int $min_posts minimum number of posts for the group (-1 for non-post based groups)
1973
 *
1974
 * @return array
1975
 * @package Membergroups
1976
 */
1977
function loadGroups($id_member, $show_hidden = false, $min_posts = -1)
1978
{
1979
	$db = database();
1980
1981
	$groups = array();
1982
	$db->fetchQuery('
1983
		SELECT 
1984
			mg.id_group, mg.group_name, COALESCE(gm.id_member, 0) AS can_moderate, mg.hidden
1985
		FROM {db_prefix}membergroups AS mg
1986
			LEFT JOIN {db_prefix}group_moderators AS gm ON (gm.id_group = mg.id_group AND gm.id_member = {int:current_member})
1987
		WHERE mg.min_posts = {int:min_posts}
1988
			AND mg.id_group != {int:moderator_group}' . ($show_hidden ? '' : '
1989
			AND mg.hidden = {int:not_hidden}') . '
1990
		ORDER BY mg.group_name',
1991
		array(
1992
			'current_member' => $id_member,
1993
			'min_posts' => $min_posts,
1994
			'moderator_group' => 3,
1995
			'not_hidden' => 0,
1996
		)
1997
	)->fetch_callback(
1998
		function ($row) use (&$groups, $show_hidden) {
1999
			// Hide hidden groups!
2000
			if ($show_hidden && $row['hidden'] && !$row['can_moderate'])
2001
			{
2002
				return;
2003
			}
2004
2005
			$groups[$row['id_group']] = $row['group_name'];
2006
		}
2007
	);
2008
2009
	return $groups;
2010
}
2011
2012
/**
2013
 * Returns the groups that the current user can see.
2014
 *
2015
 * - uses User::$info and allowedTo().
2016
 * - does not include post count based groups
2017
 *
2018
 * @return array
2019
 * @package Membergroups
2020
 */
2021
function accessibleGroups()
2022
{
2023
	$db = database();
2024
2025
	$groups = array();
2026
	$db->fetchQuery('
2027
		SELECT 
2028
			mg.id_group, mg.group_name, COALESCE(gm.id_member, 0) AS can_moderate, mg.hidden
2029
		FROM {db_prefix}membergroups AS mg
2030
			LEFT JOIN {db_prefix}group_moderators AS gm ON (gm.id_group = mg.id_group AND gm.id_member = {int:current_member})
2031
		WHERE mg.min_posts = {int:min_posts}
2032
			AND mg.id_group != {int:moderator_group}',
2033
		array(
2034
			'current_member' => User::$info->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...
2035
			'min_posts' => -1,
2036
			'moderator_group' => 3,
2037
		)
2038
	)->fetch_callback(
2039
		function ($row) use (&$groups) {
2040
			// Hide hidden groups!
2041
			if ($row['hidden'] && !$row['can_moderate'] && !allowedTo('manage_membergroups'))
2042
			{
2043
				return;
2044
			}
2045
2046
			$groups[$row['id_group']] = $row['group_name'];
2047
		}
2048
	);
2049
2050
	asort($groups);
2051
2052
	return $groups;
2053
}
2054
2055
/**
2056
 * Finds the number of group requests in the system
2057
 *
2058
 * - Callback function for createList().
2059
 *
2060
 * @param string $where
2061
 * @param string[] $where_parameters
2062
 * @return int the count of group requests
2063
 * @package Membergroups
2064
 */
2065
function list_getGroupRequestCount($where, $where_parameters)
2066
{
2067
	$db = database();
2068
2069
	$request = $db->query('', '
2070
		SELECT 
2071
			COUNT(*)
2072
		FROM {db_prefix}log_group_requests AS lgr
2073
		WHERE ' . $where,
2074
		array_merge($where_parameters, array())
2075
	);
2076
	list ($totalRequests) = $request->fetch_row();
2077
	$request->free_result();
2078
2079
	return $totalRequests;
2080
}
2081
2082
/**
2083
 * Find the details of pending group requests
2084
 *
2085
 * - Callback function for createList()
2086
 *
2087
 * @param int $start The item to start with (for pagination purposes)
2088
 * @param int $items_per_page The number of items to show per page
2089
 * @param string $sort A string indicating how to sort the results
2090
 * @param string $where
2091
 * @param string[] $where_parameters
2092
 * @return mixed[] an array of group requests
2093
 * Each group request has:
2094
 *   'id'
2095
 *   'member_link'
2096
 *   'group_link'
2097
 *   'reason'
2098
 *   'time_submitted'
2099
 * @package Membergroups
2100
 */
2101
function list_getGroupRequests($start, $items_per_page, $sort, $where, $where_parameters)
2102
{
2103
	$db = database();
2104
2105
	return $db->fetchQuery('
2106
		SELECT 
2107
			lgr.id_request, lgr.id_member, lgr.id_group, lgr.time_applied, lgr.reason,
2108
			mem.member_name, mg.group_name, mg.online_color, mem.real_name
2109
		FROM {db_prefix}log_group_requests AS lgr
2110
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = lgr.id_member)
2111
			INNER JOIN {db_prefix}membergroups AS mg ON (mg.id_group = lgr.id_group)
2112
		WHERE ' . $where . '
2113
		ORDER BY {raw:sort}
2114
		LIMIT ' . $items_per_page . '  OFFSET ' . $start,
2115
		array_merge($where_parameters, array(
2116
			'sort' => $sort,
2117
		))
2118
	)->fetch_callback(
2119
		function ($row) {
2120
			return array(
2121
				'id' => $row['id_request'],
2122
				'member_link' => '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row['id_member'], 'name' => $row['real_name']]) . '">' . $row['real_name'] . '</a>',
2123
				'group_link' => '<span style="color: ' . $row['online_color'] . '">' . $row['group_name'] . '</span>',
2124
				'reason' => censor($row['reason']),
2125
				'time_submitted' => standardTime($row['time_applied']),
2126
			);
2127
		}
2128
	);
2129
}
2130
2131
/**
2132
 * Deletes old group requests.
2133
 *
2134
 * @param int[] $groups
2135
 * @package Membergroups
2136
 */
2137
function deleteGroupRequests($groups)
2138
{
2139
	$db = database();
2140
2141
	// Remove the evidence...
2142
	$db->query('', '
2143
		DELETE FROM {db_prefix}log_group_requests
2144
		WHERE id_request IN ({array_int:request_list})',
2145
		array(
2146
			'request_list' => $groups,
2147
		)
2148
	);
2149
}
2150
2151
/**
2152
 * This function updates those members who match post-based
2153
 * membergroups in the database (restricted by parameter $members).
2154
 *
2155
 * @param int[]|null $members = null The members to update, null if all
2156
 * @param string[]|null $parameter2 = null
2157
 * @package Membergroups
2158
 */
2159
function updatePostGroupStats($members = null, $parameter2 = null)
2160
{
2161
	$db = database();
2162
2163
	// Parameter two is the updated columns: we should check to see if we base groups off any of these.
2164
	if ($parameter2 !== null && !in_array('posts', $parameter2))
2165
	{
2166
		return;
2167
	}
2168
2169
	$postgroups = Cache::instance()->get('updatePostGroupStats', 360);
2170
	if ($postgroups === null || $members === null)
2171
	{
2172
		// Fetch the postgroups!
2173
		$postgroups = array();
2174
		$db->fetchQuery('
2175
			SELECT 
2176
			 	id_group, min_posts
2177
			FROM {db_prefix}membergroups
2178
			WHERE min_posts != {int:min_posts}',
2179
			array(
2180
				'min_posts' => -1,
2181
			)
2182
		)->fetch_callback(
2183
			function ($row) use (&$postgroups) {
2184
				$postgroups[$row['id_group']] = $row['min_posts'];
2185
			}
2186
		);
2187
2188
		// Sort them this way because if it's done with MySQL it causes a filesort :(.
2189
		arsort($postgroups);
2190 28
2191
		Cache::instance()->put('updatePostGroupStats', $postgroups, 360);
2192
	}
2193 28
2194
	// Oh great, they've screwed their post groups.
2195 14
	if (empty($postgroups))
2196
	{
2197
		return;
2198 16
	}
2199 16
2200
	// Set all membergroups from most posts to the least posts.
2201
	$conditions = '';
2202 16
	$lastMin = 0;
2203 16
	foreach ($postgroups as $id => $min_posts)
2204
	{
2205
		$conditions .= '
2206
				WHEN posts >= ' . $min_posts . (!empty($lastMin) ? ' AND posts <= ' . $lastMin : '') . ' THEN ' . $id;
2207
		$lastMin = $min_posts;
2208
	}
2209 16
2210
	// A big fat CASE WHEN... END is faster than a zillion UPDATE's ;).
2211 16
	$db->query('', '
2212
		UPDATE {db_prefix}members
2213 16
		SET id_post_group = CASE ' . $conditions . '
2214 16
				ELSE 0
2215
			END' . ($members !== null ? '
2216
		WHERE id_member IN ({array_int:members})' : ''),
2217
		array(
2218 16
			'members' => is_array($members) ? $members : array($members),
2219
		)
2220 16
	);
2221
}
2222
2223
/**
2224 16
 * Get the ids of the groups that are unassignable
2225
 *
2226
 * @param bool $ignore_protected To ignore protected groups
2227
 * @return int[]
2228
 */
2229
function getUnassignableGroups($ignore_protected)
2230 16
{
2231 16
	$db = database();
2232 16
2233
	return $db->fetchQuery('
2234
		SELECT 
2235 16
			id_group
2236 16
		FROM {db_prefix}membergroups
2237
		WHERE min_posts != {int:min_posts}' . ($ignore_protected ? '' : '
2238
			OR group_type = {int:is_protected}'),
2239
		array(
2240 16
			'min_posts' => -1,
2241
			'is_protected' => 1,
2242 16
		)
2243
	)->fetch_callback(
2244 16
		function ($row) {
2245 16
			return $row['id_group'];
2246
		},
2247 16
		array(-1, 3)
2248
	);
2249
}
2250 16
2251
/**
2252
 * Returns a list of groups that a member can be assigned to
2253
 *
2254
 * @return array
2255
 */
2256
function getGroupsList()
2257
{
2258
	global $txt;
2259
2260
	Txt::load('Profile');
2261 5
2262
	$db = database();
2263 5
	$member_groups = array(
2264
		0 => array(
2265
			'id' => 0,
2266
			'name' => $txt['no_primary_membergroup'],
2267 5
			'is_primary' => false,
2268
			'can_be_additional' => false,
2269
			'can_be_primary' => true,
2270 5
		)
2271
	);
2272
2273 5
	// Load membergroups, but only those groups the user can assign.
2274
	$db->fetchQuery('
2275 5
		SELECT 
2276 5
			group_name, id_group, hidden
2277 5
		FROM {db_prefix}membergroups
2278
		WHERE id_group != {int:moderator_group}
2279
			AND min_posts = {int:min_posts}' . (allowedTo('admin_forum') ? '' : '
2280
			AND group_type != {int:is_protected}') . '
2281
		ORDER BY min_posts, CASE WHEN id_group < {int:newbie_group} THEN id_group ELSE 4 END, group_name',
2282
		array(
2283
			'moderator_group' => 3,
2284
			'min_posts' => -1,
2285
			'is_protected' => 1,
2286
			'newbie_group' => 4,
2287
		)
2288
	)->fetch_callback(
2289 2
		function ($row) use (&$member_groups) {
2290
			// We should skip the administrator group if they don't have the admin_forum permission!
2291 2
			if ($row['id_group'] == 1 && !allowedTo('admin_forum'))
2292
			{
2293 2
				return;
2294
			}
2295 1
2296 2
			$member_groups[$row['id_group']] = array(
2297 2
				'id' => $row['id_group'],
2298
				'name' => $row['group_name'],
2299
				'hidden' => $row['hidden'],
2300
				'is_primary' => false,
2301
				'can_be_primary' => $row['hidden'] != 2,
2302
			);
2303
		}
2304
	);
2305 2
2306
	return $member_groups;
2307
}
2308