loadModeratorMenuCounts()   F
last analyzed

Complexity

Conditions 27
Paths 3492

Size

Total Lines 134
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 83.1598

Importance

Changes 0
Metric Value
cc 27
eloc 60
nc 3492
nop 1
dl 0
loc 134
ccs 27
cts 47
cp 0.5745
crap 83.1598
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
 * Moderation helper functions.
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 BBC\ParserWrapper;
18
use ElkArte\Cache\Cache;
19
use ElkArte\Helper\Util;
20
use ElkArte\User;
21
22
/**
23
 * How many open reports do we have?
24
 *  - if flush is true will clear the moderator menu count
25
 *  - returns the number of open reports
26
 *  - sets $context['open_mod_reports'] for template use
27
 *
28
 * @param bool $flush = true if moderator menu count will be cleared
29
 * @param bool $count_pms Default false, if false returns the number of message
30
 *                           reports, if true sets $context['open_pm_reports'] and
31
 *                           returns the both number of open PM and message reports
32
 *
33
 * @return array
34
 */
35
function recountOpenReports($flush = true, $count_pms = false)
36
{
37
	global $context;
38 2
39
	$db = database();
40 2
41
	$open_reports = [
42
		'msg' => 0,
43 2
		'pm' => 0,
44
	];
45
	$db->fetchQuery('
46 2
		SELECT 
47
			type, COUNT(*) as num_reports
48
		FROM {db_prefix}log_reported
49
		WHERE ' . User::$info->mod_cache['bq'] . '
0 ignored issues
show
Bug Best Practice introduced by
The property mod_cache does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
50 2
			AND type IN ({array_string:rep_type})
51
			AND closed = {int:not_closed}
52
			AND ignore_all = {int:not_ignored}
53
		GROUP BY type',
54
		[
55
			'not_closed' => 0,
56 2
			'not_ignored' => 0,
57
			'rep_type' => ['pm', 'msg'],
58
		]
59
	)->fetch_callback(
60 2
		function ($row) use (&$open_reports) {
61
			$open_reports[$row['type']] = $row['num_reports'];
62 2
		}
63 2
	);
64
65
	if ($count_pms !== true)
66 2
	{
67
		$open_reports['pm'] = 0;
68
	}
69
70
	$_SESSION['rc'] = [
71 2
		'id' => 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...
72 2
		'time' => time(),
73 2
		'reports' => $open_reports['msg'],
74 2
		'pm_reports' => $open_reports['pm'],
75 2
	];
76
77
	// Safety net, even though this (and the above)  should not be done here at all.
78
	$context['open_mod_reports'] = $open_reports['msg'];
79 2
	$context['open_pm_reports'] = $open_reports['pm'];
80 2
81
	if ($flush)
82 2
	{
83
		Cache::instance()->remove('num_menu_errors');
84
	}
85
86
	return $open_reports;
87 2
}
88
89
/**
90
 * How many unapproved posts and topics do we have?
91
 *  - Sets $context['total_unapproved_topics']
92
 *  - Sets $context['total_unapproved_posts']
93
 *  - approve_query is set to list of boards they can see
94
 *
95
 * @param string|null $approve_query
96
 * @return array of values
97
 */
98
function recountUnapprovedPosts($approve_query = null)
99
{
100
	global $context;
101
102
	$db = database();
103
104
	if ($approve_query === null)
105
	{
106
		return ['posts' => 0, 'topics' => 0];
107
	}
108
109
	// Any unapproved posts?
110
	$request = $db->query('', '
111
		SELECT 
112
			COUNT(*)
113
		FROM {db_prefix}messages AS m
114
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic AND t.id_first_msg != m.id_msg)
115
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
116
		WHERE m.approved = {int:not_approved}
117
			AND {query_see_board}
118
			' . $approve_query,
119
		[
120
			'not_approved' => 0,
121
		]
122
	);
123
	list ($unapproved_posts) = $request->fetch_row();
124
	$request->free_result();
125
126
	// What about topics?
127
	$request = $db->query('', '
128
		SELECT 
129
			COUNT(m.id_topic)
130
		FROM {db_prefix}topics AS m
131
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
132
		WHERE m.approved = {int:not_approved}
133
			AND {query_see_board}
134
			' . $approve_query,
135
		[
136
			'not_approved' => 0,
137
		]
138
	);
139
	list ($unapproved_topics) = $request->fetch_row();
140
	$request->free_result();
141
142
	$context['total_unapproved_topics'] = $unapproved_topics;
143
	$context['total_unapproved_posts'] = $unapproved_posts;
144
145
	return ['posts' => $unapproved_posts, 'topics' => $unapproved_topics];
146
}
147
148
/**
149
 * How many failed emails (that they can see) do we have?
150
 *
151
 * @param string|null $approve_query
152
 *
153
 * @return int
154
 */
155
function recountFailedEmails($approve_query = null)
156
{
157
	global $context;
158
159
	$db = database();
160
161
	if ($approve_query === null)
162
	{
163
		return 0;
164
	}
165
166
	$request = $db->query('', '
167
		SELECT 
168
			COUNT(*)
169
		FROM {db_prefix}postby_emails_error AS m
170
			LEFT JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
171
		WHERE {query_see_board}
172
			' . $approve_query . '
173
			OR m.id_board = -1',
174
		[]
175
	);
176
	list ($failed_emails) = $request->fetch_row();
177
	$request->free_result();
178
179
	$context['failed_emails'] = $failed_emails;
180
181
	return $failed_emails;
182
}
183
184
/**
185
 * How many entries are we viewing?
186
 *
187
 * @param int $status
188
 * @param bool $show_pms
189
 *
190
 * @return int
191
 */
192
function totalReports($status = 0, $show_pms = false)
193
{
194
	$db = database();
195
196
	$request = $db->fetchQuery('
197
		SELECT 
198 2
			COUNT(*)
199
		FROM {db_prefix}log_reported AS lr
200 2
		WHERE lr.closed = {int:view_closed}
201
			AND lr.type IN ({array_string:type})
202
			AND ' . (User::$info->mod_cache['bq'] == '1=1' || User::$info->mod_cache['bq'] == '0=1' ? User::$info->mod_cache['bq'] : 'lr.' . User::$info->mod_cache['bq']),
0 ignored issues
show
Bug Best Practice introduced by
The property mod_cache does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
203
		[
204
			'view_closed' => $status,
205
			'type' => $show_pms ? ['pm'] : ['msg'],
206 2
		]
207
	);
208 2
	list ($total_reports) = $request->fetch_row();
209 2
	$request->free_result();
210
211
	return $total_reports;
212 2
}
213 2
214
/**
215 2
 * Changes a property of all the reports passed (and the user can see)
216
 *
217
 * @param int[]|int $reports_id an array of report IDs
218
 * @param string $property the property to update ('close' or 'ignore')
219
 * @param int $status the status of the property (mainly: 0 or 1)
220
 *
221
 * @return int
222
 */
223
function updateReportsStatus($reports_id, $property = 'close', $status = 0)
224
{
225
	if (empty($reports_id))
226
	{
227
		return 0;
228
	}
229
230
	$db = database();
231
232
	$reports_id = is_array($reports_id) ? $reports_id : [$reports_id];
233
234
	$request = $db->query('', '
235
		UPDATE {db_prefix}log_reported
236
		SET 
237
			' . ($property == 'close' ? 'closed' : 'ignore_all') . '= {int:status}
238
		WHERE id_report IN ({array_int:report_list})
239
			AND ' . User::$info->mod_cache['bq'],
0 ignored issues
show
Bug Best Practice introduced by
The property mod_cache does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
240
		[
241
			'report_list' => $reports_id,
242
			'status' => $status,
243
		]
244
	);
245
246
	return $request->affected_rows();
247
}
248
249
/**
250
 * Loads the number of items awaiting moderation attention
251
 *  - Only loads the value a given permission level can see
252
 *  - If supplied a board number will load the values only for that board
253
 *  - Unapproved posts
254
 *  - Unapproved topics
255
 *  - Unapproved attachments
256
 *  - Failed emails
257
 *  - Reported posts
258
 *  - Members awaiting approval (activation, deletion, group requests)
259
 *
260
 * @param int|null $brd
261
 *
262
 * @return mixed
263
 */
264
function loadModeratorMenuCounts($brd = null)
265
{
266
	global $modSettings;
267
268
	static $menu_errors = [];
269
270
	// Work out what boards they can work in!
271
	$approve_boards = !empty(User::$info->mod_cache['ap']) ? User::$info->mod_cache['ap'] : boardsAllowedTo('approve_posts');
0 ignored issues
show
Bug Best Practice introduced by
The property mod_cache does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
272 6
273
	// Supplied a specific board to check?
274 6
	if (!empty($brd))
275
	{
276
		$filter_board = [(int) $brd];
277 6
		$approve_boards = $approve_boards == [0] ? $filter_board : array_intersect($approve_boards, $filter_board);
278
	}
279
280 6
	// Work out the query
281
	if ($approve_boards == [0])
282
	{
283
		$approve_query = '';
284
	}
285
	elseif (!empty($approve_boards))
286
	{
287 6
		$approve_query = ' AND m.id_board IN (' . implode(',', $approve_boards) . ')';
288
	}
289 6
	else
290
	{
291
		$approve_query = ' AND 1=0';
292
	}
293
294
	// Set up the cache key for this permissions level
295
	$cache_key = md5(User::$info->query_see_board . $approve_query . User::$info->mod_cache['bq'] . User::$info->mod_cache['gq'] . User::$info->mod_cache['mq'] . (int) allowedTo('approve_emails') . '_' . (int) allowedTo('moderate_forum'));
0 ignored issues
show
Bug Best Practice introduced by
The property query_see_board does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
296
297
	if (isset($menu_errors[$cache_key]))
298
	{
299
		return $menu_errors[$cache_key];
300
	}
301 6
302
	// If its been cached, guess what, that's right use it!
303 6
	$temp = Cache::instance()->get('num_menu_errors', 900);
304
305 6
	if ($temp === null || !isset($temp[$cache_key]))
306
	{
307
		// Starting out with nothing is a good start
308
		$menu_errors[$cache_key] = [
309 2
			'memberreq' => 0,
310
			'groupreq' => 0,
311 2
			'attachments' => 0,
312
			'reports' => 0,
313
			'emailmod' => 0,
314 2
			'postmod' => 0,
315
			'topics' => 0,
316
			'posts' => 0,
317
			'pm_reports' => 0,
318
		];
319
320
		if ($modSettings['postmod_active'] && !empty($approve_boards))
321
		{
322
			$totals = recountUnapprovedPosts($approve_query);
323
			$menu_errors[$cache_key]['posts'] = $totals['posts'];
324
			$menu_errors[$cache_key]['topics'] = $totals['topics'];
325
326 2
			// Totals for the menu item unapproved posts and topics
327
			$menu_errors[$cache_key]['postmod'] = $menu_errors[$cache_key]['topics'] + $menu_errors[$cache_key]['posts'];
328
		}
329
330
		// Attachments
331
		if ($modSettings['postmod_active'] && !empty($approve_boards))
332
		{
333
			require_once(SUBSDIR . '/ManageAttachments.subs.php');
334
			$menu_errors[$cache_key]['attachments'] = list_getNumUnapprovedAttachments($approve_query);
335
		}
336
337 2
		// Reported posts (and PMs?)
338
		if (!empty(User::$info->mod_cache) && User::$info->mod_cache['bq'] != '0=1')
339
		{
340
			$reports = recountOpenReports(false, allowedTo('admin_forum'));
341
			$menu_errors[$cache_key]['reports'] = $reports['msg'];
342
343
			// Reported PMs
344 2
			if (!empty($reports['pm']))
345
			{
346 2
				$menu_errors[$cache_key]['pm_reports'] = $reports['pm'];
347 2
			}
348
		}
349
350 2
		// Email failures that require attention
351
		if (!empty($modSettings['maillist_enabled']) && allowedTo('approve_emails'))
352
		{
353
			$menu_errors[$cache_key]['emailmod'] = recountFailedEmails($approve_query);
354
		}
355
356
		// Group requests
357 2
		if (!empty(User::$info->mod_cache) && User::$info->mod_cache['gq'] != '0=1')
358
		{
359
			$menu_errors[$cache_key]['groupreq'] = count(groupRequests());
360
		}
361
362
		// Member requests
363 2
		if (allowedTo('moderate_forum') && ((!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2) || !empty($modSettings['approveAccountDeletion'])))
364
		{
365 2
			require_once(SUBSDIR . '/Members.subs.php');
366
			$awaiting_activation = 0;
367
			$activation_numbers = countInactiveMembers();
368
369 2
			// 5 = COPPA, 4 = Awaiting Deletion, 3 = Awaiting Approval
370
			foreach ($activation_numbers as $activation_type => $total_members)
371
			{
372
				if (in_array($activation_type, [3, 4, 5]))
373
				{
374
					$awaiting_activation += $total_members;
375
				}
376
			}
377
			$menu_errors[$cache_key]['memberreq'] = $awaiting_activation;
378
		}
379
380
		// Grand Totals for the top most menus
381
		$menu_errors[$cache_key]['pt_total'] = $menu_errors[$cache_key]['emailmod'] + $menu_errors[$cache_key]['postmod'] + $menu_errors[$cache_key]['reports'] + $menu_errors[$cache_key]['attachments'] + $menu_errors[$cache_key]['pm_reports'];
382
		$menu_errors[$cache_key]['mg_total'] = $menu_errors[$cache_key]['memberreq'] + $menu_errors[$cache_key]['groupreq'];
383
		$menu_errors[$cache_key]['grand_total'] = $menu_errors[$cache_key]['pt_total'] + $menu_errors[$cache_key]['mg_total'];
384
385
		// Add this key in to the array, technically this resets the cache time for all keys
386
		// done this way as the entire thing needs to go null once *any* moderation action is taken
387 2
		$menu_errors = is_array($temp) ? array_merge($temp, $menu_errors) : $menu_errors;
388 2
389 2
		// Store it away for a while, not like this should change that often
390
		Cache::instance()->put('num_menu_errors', $menu_errors, 900);
391
	}
392
	else
393 2
	{
394
		$menu_errors = $temp ?? [];
395
	}
396 2
397
	return $menu_errors[$cache_key];
398
}
399
400
/**
401
 * Log a warning notice.
402
 *
403 2
 * @param string $subject
404
 * @param string $body
405
 * @return int
406
 */
407
function logWarningNotice($subject, $body)
408
{
409
	$db = database();
410
411
	// Log warning notice.
412
	$db->insert('',
413
		'{db_prefix}log_member_notices',
414
		[
415
			'subject' => 'string-255', 'body' => 'string-65534',
416
		],
417
		[
418
			Util::htmlspecialchars($subject), Util::htmlspecialchars($body),
419
		],
420
		['id_notice']
421
	);
422
423
	return (int) $db->insert_id('{db_prefix}log_member_notices');
424
}
425
426
/**
427
 * Logs the warning being sent to the user so other moderators can see
428
 *
429
 * @param int $memberID
430
 * @param string $real_name
431
 * @param int $id_notice
432
 * @param int $level_change
433
 * @param string $warn_reason
434
 */
435
function logWarning($memberID, $real_name, $id_notice, $level_change, $warn_reason)
436
{
437
	$db = database();
438
439
	$db->insert('',
440
		'{db_prefix}log_comments',
441
		[
442
			'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'id_recipient' => 'int', 'recipient_name' => 'string-255',
443
			'log_time' => 'int', 'id_notice' => 'int', 'counter' => 'int', 'body' => 'string-65534',
444
		],
445
		[
446
			User::$info->id, User::$info->name, 'warning', $memberID, $real_name,
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...
Bug Best Practice introduced by
The property name does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
447
			time(), $id_notice, $level_change, $warn_reason,
448
		],
449
		['id_comment']
450
	);
451
}
452
453
/**
454
 * Removes a custom moderation center template from log_comments
455
 *  - Logs the template removal action for each warning affected
456
 *  - Removes the details for all warnings that used the template being removed
457
 *
458
 * @param int $id_tpl id of the template to remove
459
 * @param string $template_type type of template, defaults to warntpl
460
 */
461
function removeWarningTemplate($id_tpl, $template_type = 'warntpl')
462
{
463
	$db = database();
464
465
	// Log the actions.
466
	$db->fetchQuery('
467
		SELECT 
468
			recipient_name
469
		FROM {db_prefix}log_comments
470
		WHERE id_comment IN ({array_int:delete_ids})
471
			AND comment_type = {string:tpltype}
472
			AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})',
473
		[
474
			'delete_ids' => $id_tpl,
475
			'tpltype' => $template_type,
476
			'generic' => 0,
477
			'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...
478
		]
479
	)->fetch_callback(
480
		function ($row) {
481
			logAction('delete_warn_template', ['template' => $row['recipient_name']]);
482
		}
483
	);
484
485
	// Do the deletes.
486
	$db->query('', '
487
		DELETE FROM {db_prefix}log_comments
488
		WHERE id_comment IN ({array_int:delete_ids})
489
			AND comment_type = {string:tpltype}
490
			AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})',
491
		[
492
			'delete_ids' => $id_tpl,
493
			'tpltype' => $template_type,
494
			'generic' => 0,
495
			'current_member' => User::$info->id,
496
		]
497
	);
498
}
499
500
/**
501
 * Returns all the templates of a type from the system.
502
 * (used by createList() callbacks)
503
 *
504
 * @param int $start The item to start with (for pagination purposes)
505
 * @param int $items_per_page The number of items to show per page
506
 * @param string $sort A string indicating how to sort the results
507
 * @param string $template_type type of template to load
508
 *
509
 * @return array
510
 */
511
function warningTemplates($start, $items_per_page, $sort, $template_type = 'warntpl')
512
{
513
	$db = database();
514
515
	$templates = [];
516
	$db->query('', '
517
		SELECT 
518
			lc.id_comment, COALESCE(mem.id_member, 0) AS id_member,
519
			COALESCE(mem.real_name, lc.member_name) AS creator_name, recipient_name AS template_title,
520
			lc.log_time, lc.body
521
		FROM {db_prefix}log_comments AS lc
522
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
523
		WHERE lc.comment_type = {string:tpltype}
524
			AND (id_recipient = {string:generic} OR id_recipient = {int:current_member})
525
		ORDER BY ' . $sort . '
526
		LIMIT ' . $items_per_page . '  OFFSET ' . $start,
527
		[
528
			'tpltype' => $template_type,
529
			'generic' => 0,
530
			'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...
531
		]
532
	)->fetch_callback(
533
		function ($row) use (&$templates) {
534
			global $scripturl;
535
536
			$templates[] = [
537
				'id_comment' => $row['id_comment'],
538
				'creator' => $row['id_member'] ? ('<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['creator_name'] . '</a>') : $row['creator_name'],
539
				'time' => standardTime($row['log_time']),
540
				'html_time' => htmlTime($row['log_time']),
541
				'timestamp' => forum_time(true, $row['log_time']),
542
				'title' => $row['template_title'],
543
				'body' => Util::htmlspecialchars($row['body']),
544
			];
545
		}
546
	);
547
548
	return $templates;
549
}
550
551
/**
552
 * Get the number of templates of a type in the system
553
 *  - Loads the public and users private templates
554
 *  - Loads warning templates by default
555
 *  (used by createList() callbacks)
556
 *
557
 * @param string $template_type
558
 *
559
 * @return int
560
 */
561
function warningTemplateCount($template_type = 'warntpl')
562
{
563
	$db = database();
564
565
	$request = $db->query('', '
566
		SELECT 
567
			COUNT(*)
568
		FROM {db_prefix}log_comments
569
		WHERE comment_type = {string:tpltype}
570
			AND (id_recipient = {string:generic} OR id_recipient = {int:current_member})',
571
		[
572
			'tpltype' => $template_type,
573
			'generic' => 0,
574
			'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...
575
		]
576
	);
577
	list ($totalWarns) = $request->fetch_row();
578
	$request->free_result();
579
580
	return $totalWarns;
581
}
582
583
/**
584
 * Get all issued warnings in the system given the specified query parameters
585
 *
586
 * Callback for createList() in \ElkArte\Controller\ModerationCenter::action_viewWarningLog().
587
 *
588
 * @param int $start The item to start with (for pagination purposes)
589
 * @param int $items_per_page The number of items to show per page
590
 * @param string $sort A string indicating how to sort the results
591
 * @param string|null $query_string
592
 * @param array $query_params
593
 *
594
 * @return array
595
 */
596
function warnings($start, $items_per_page, $sort, $query_string = '', $query_params = [])
597
{
598
	$db = database();
599
600
	$warnings = [];
601
	$db->fetchQuery('
602
		SELECT 
603
			COALESCE(mem.id_member, 0) AS id_member, COALESCE(mem.real_name, lc.member_name) AS member_name_col,
604
			COALESCE(mem2.id_member, 0) AS id_recipient, COALESCE(mem2.real_name, lc.recipient_name) AS recipient_name,
605
			lc.log_time, lc.body, lc.id_notice, lc.counter
606
		FROM {db_prefix}log_comments AS lc
607
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
608
			LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = lc.id_recipient)
609
		WHERE lc.comment_type = {string:warning}' . (!empty($query_string) ? '
610
			AND ' . $query_string : '') . '
611
		ORDER BY ' . $sort . '
612
		LIMIT ' . $items_per_page . '  OFFSET ' . $start,
613
		array_merge($query_params, [
614
			'warning' => 'warning',
615
		])
616
	)->fetch_callback(
617
		function ($row) use (&$warnings) {
618
			global $scripturl;
619
620
			$warnings[] = [
621
				'issuer_link' => $row['id_member'] ? ('<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['member_name_col'] . '</a>') : $row['member_name_col'],
622
				'recipient_link' => $row['id_recipient'] ? ('<a href="' . $scripturl . '?action=profile;u=' . $row['id_recipient'] . '">' . $row['recipient_name'] . '</a>') : $row['recipient_name'],
623
				'time' => standardTime($row['log_time']),
624
				'html_time' => htmlTime($row['log_time']),
625
				'timestamp' => forum_time(true, $row['log_time']),
626
				'reason' => $row['body'],
627
				'counter' => $row['counter'] > 0 ? '+' . $row['counter'] : $row['counter'],
628
				'id_notice' => $row['id_notice'],
629
			];
630
		}
631
	);
632
633
	return $warnings;
634
}
635
636
/**
637
 * Get the count of all current warnings.
638
 *
639
 * Callback for createList() in \ElkArte\Controller\ModerationCenter::action_viewWarningLog().
640
 *
641
 * @param string|null $query_string
642
 * @param array $query_params
643
 *
644
 * @return int
645
 */
646
function warningCount($query_string = '', $query_params = [])
647
{
648
	$db = database();
649
650
	$request = $db->query('', '
651
		SELECT 
652
			COUNT(*)
653
		FROM {db_prefix}log_comments AS lc
654
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
655
			LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = lc.id_recipient)
656
		WHERE comment_type = {string:warning}' . (!empty($query_string) ? '
657
			AND ' . $query_string : ''),
658
		array_merge($query_params, [
659
			'warning' => 'warning',
660
		])
661
	);
662
	list ($totalWarns) = $request->fetch_row();
663
	$request->free_result();
664
665
	return $totalWarns;
666
}
667
668
/**
669
 * Loads a moderation template in to context for use in editing a template
670
 *
671
 * @param int $id_template
672
 * @param string $template_type
673
 */
674
function modLoadTemplate($id_template, $template_type = 'warntpl')
675
{
676
	$db = database();
677
678
	$db->fetchQuery('
679
		SELECT 
680
			id_member, id_recipient, recipient_name AS template_title, body
681
		FROM {db_prefix}log_comments
682
		WHERE id_comment = {int:id}
683
			AND comment_type = {string:tpltype}
684
			AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})',
685
		[
686
			'id' => $id_template,
687
			'tpltype' => $template_type,
688
			'generic' => 0,
689
			'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...
690
		]
691
	)->fetch_callback(
692
		function ($row) {
693
			global $context;
694
695
			$context['template_data'] = [
696
				'title' => $row['template_title'],
697
				'body' => Util::htmlspecialchars($row['body']),
698
				'personal' => $row['id_recipient'],
699
				'can_edit_personal' => $row['id_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...
700
			];
701
		}
702
	);
703
}
704
705
/**
706
 * Updates an existing template or adds in a new one to the log comments table
707
 *
708
 * @param int $recipient_id
709
 * @param string $template_title
710
 * @param string $template_body
711
 * @param int $id_template
712
 * @param bool $edit true to update, false to insert a new row
713
 * @param string $type
714
 */
715
function modAddUpdateTemplate($recipient_id, $template_title, $template_body, $id_template, $edit = true, $type = 'warntpl')
716
{
717
	$db = database();
718
719
	if ($edit)
720
	{
721
		// Simple update...
722
		$db->query('', '
723
			UPDATE {db_prefix}log_comments
724
			SET 
725
				id_recipient = {int:personal}, recipient_name = {string:title}, body = {string:body}
726
			WHERE id_comment = {int:id}
727
				AND comment_type = {string:comment_type}
728
				AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})' .
729
			($recipient_id ? ' AND id_member = {int:current_member}' : ''),
730
			[
731
				'personal' => $recipient_id,
732
				'title' => $template_title,
733
				'body' => $template_body,
734
				'id' => $id_template,
735
				'comment_type' => $type,
736
				'generic' => 0,
737
				'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...
738
			]
739
		);
740
	}
741
	// Or inserting a new row
742
	else
743
	{
744
		$db->insert('',
745
			'{db_prefix}log_comments',
746
			[
747
				'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'id_recipient' => 'int',
748
				'recipient_name' => 'string-255', 'body' => 'string-65535', 'log_time' => 'int',
749
			],
750
			[
751
				User::$info->id, User::$info->name, $type, $recipient_id,
0 ignored issues
show
Bug Best Practice introduced by
The property name does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
752
				$template_title, $template_body, time(),
753
			],
754
			['id_comment']
755
		);
756
	}
757
}
758
759
/**
760
 * Retrieves detailed information about a specific report.
761
 *
762
 * @param int $id_report The ID of the report to retrieve details for.
763
 * @param bool $show_pms (optional) Whether to include PM reports. Defaults to false.
764
 *
765
 * @return array|false An associative array containing the details of the report,
766
 * or false if the report was not found.
767
 */
768
function modReportDetails($id_report, $show_pms = false)
769
{
770
	$db = database();
771
772
	$request = $db->query('', '
773
		SELECT 
774
			lr.id_report, lr.id_msg, lr.id_topic, lr.id_board, lr.id_member, lr.subject, lr.body,
775
			lr.time_started, lr.time_updated, lr.num_reports, lr.closed, lr.ignore_all,
776
			COALESCE(mem.real_name, lr.membername) AS author_name, COALESCE(mem.id_member, 0) AS id_author
777
		FROM {db_prefix}log_reported AS lr
778
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
779
		WHERE lr.id_report = {int:id_report}
780
			AND lr.type IN ({array_string:rep_type})
781
			AND ' . (User::$info->mod_cache['bq'] === '1=1' || User::$info->mod_cache['bq'] === '0=1' ? User::$info->mod_cache['bq'] : 'lr.' . User::$info->mod_cache['bq']) . '
0 ignored issues
show
Bug Best Practice introduced by
The property mod_cache does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
782
		LIMIT 1',
783
		[
784
			'id_report' => $id_report,
785
			'rep_type' => $show_pms ? ['pm'] : ['msg'],
786 2
		]
787
	);
788 2
	// So did we find anything?
789
	if (!$request->num_rows())
790
	{
791
		$row = false;
792
	}
793
	else
794
	{
795
		$row = $request->fetch_assoc();
796
	}
797 2
	$request->free_result();
798
799
	return $row;
800 2
}
801 2
802
/**
803
 * Get the details for a bunch of open/closed reports
804
 *
805 2
 * @param int $status 0 => show open reports, 1 => closed reports
806
 * @param int $start starting point
807
 * @param int $limit the number of reports
808
 * @param bool $show_pms
809
 *
810
 * @return array
811 2
 * @todo move to createList?
812
 */
813 2
function getModReports($status = 0, $start = 0, $limit = 10, $show_pms = false)
814
{
815 2
	$db = database();
816
817
	$reports = [];
818
	$db->fetchQuery('
819
			SELECT 
820
				lr.id_report, lr.id_msg, lr.id_topic, lr.id_board, lr.id_member, lr.subject, lr.body,
821
				lr.time_started, lr.time_updated, lr.num_reports, lr.closed, lr.ignore_all,
822
				COALESCE(mem.real_name, lr.membername) AS author_name, COALESCE(mem.id_member, 0) AS id_author
823
			FROM {db_prefix}log_reported AS lr
824
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
825
			WHERE lr.closed = {int:view_closed}
826
				AND lr.type IN ({array_string:rep_type})
827
				AND ' . (User::$info->mod_cache['bq'] == '1=1' || User::$info->mod_cache['bq'] == '0=1' ? User::$info->mod_cache['bq'] : 'lr.' . User::$info->mod_cache['bq']) . '
0 ignored issues
show
Bug Best Practice introduced by
The property mod_cache does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
828
			ORDER BY lr.time_updated DESC
829
			LIMIT {int:limit} OFFSET {int:start} ',
830
		[
831
			'view_closed' => $status,
832 2
			'start' => $start,
833
			'limit' => $limit,
834 2
			'rep_type' => $show_pms ? ['pm'] : ['msg'],
835 2
		]
836
	)->fetch_callback(
837
		function ($row) use (&$reports) {
838
			$reports[$row['id_report']] = $row;
839
		}
840
	);
841
842
	return $reports;
843
}
844 2
845
/**
846
 * Grabs all the comments made by the reporters to a set of reports
847
 *
848 2
 * @param int[] $id_reports an array of report ids
849 2
 *
850 2
 * @return array
851 2
 */
852
function getReportsUserComments($id_reports)
853 2
{
854
	$db = database();
855 2
856 2
	$id_reports = is_array($id_reports) ? $id_reports : [$id_reports];
0 ignored issues
show
introduced by
The condition is_array($id_reports) is always true.
Loading history...
857
	$comments = [];
858
	$db->fetchQuery('
859 2
		SELECT 
860
			lrc.id_comment, lrc.id_report, lrc.time_sent, lrc.comment, lrc.member_ip,
861
			COALESCE(mem.id_member, 0) AS id_member, COALESCE(mem.real_name, lrc.membername) AS reporter
862
		FROM {db_prefix}log_reported_comments AS lrc
863
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lrc.id_member)
864
		WHERE lrc.id_report IN ({array_int:report_list})',
865
		[
866
			'report_list' => $id_reports,
867
		]
868
	)->fetch_callback(
869
		function ($row) use (&$comments) {
870
			$comments[$row['id_report']][] = $row;
871
		}
872 4
	);
873
874 4
	return $comments;
875 4
}
876 4
877
/**
878
 * Retrieve all the comments made by the moderators to a certain report
879
 *
880
 * @param int $id_report the id of a report
881
 *
882
 * @return array
883
 */
884 4
function getReportModeratorsComments($id_report)
885
{
886 4
	$db = database();
887
888 4
	$comments = [];
889 4
	$db->fetchQuery('
890
		SELECT 
891
			lc.id_comment, lc.id_notice, lc.log_time, lc.body,
892 4
			COALESCE(mem.id_member, 0) AS id_member, COALESCE(mem.real_name, lc.member_name) AS moderator
893
		FROM {db_prefix}log_comments AS lc
894
		LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
895
		WHERE lc.id_notice = {int:id_report}
896
			AND lc.comment_type = {string:reportc}',
897
		[
898
			'id_report' => $id_report,
899
			'reportc' => 'reportc',
900
		]
901
	)->fetch_callback(
902
		function ($row) use (&$comments) {
903
			$comments[] = $row;
904
		}
905 2
	);
906
907 2
	return $comments;
908 2
}
909
910
/**
911
 * This is a helper function: approve everything unapproved.
912
 * Used from moderation panel.
913
 */
914
function approveAllUnapproved()
915
{
916
	$db = database();
917 2
918 2
	// Start with messages and topics.
919
	$msgs = [];
920 2
	$db->fetchQuery('
921
		SELECT 
922
			id_msg
923 2
		FROM {db_prefix}messages
924
		WHERE approved = {int:not_approved}',
925
		[
926 2
			'not_approved' => 0,
927
		]
928
	)->fetch_callback(
929
		function ($row) use (&$msgs) {
930
			$msgs[] = $row['id_msg'];
931
		}
932
	);
933
934
	if (!empty($msgs))
935
	{
936
		require_once(SUBSDIR . '/Post.subs.php');
937
		approvePosts($msgs);
938
		Cache::instance()->remove('num_menu_errors');
939
	}
940
941
	// Now do attachments
942
	$attaches = [];
943
	$db->fetchQuery('
944
		SELECT 
945
			id_attach
946
		FROM {db_prefix}attachments
947
		WHERE approved = {int:not_approved}',
948
		[
949
			'not_approved' => 0,
950
		]
951
	)->fetch_callback(
952
		function ($row) use (&$attaches) {
953
			$attaches[] = $row['id_attach'];
954
		}
955
	);
956
957
	if (!empty($attaches))
958
	{
959
		require_once(SUBSDIR . '/ManageAttachments.subs.php');
960
		approveAttachments($attaches);
961
		Cache::instance()->remove('num_menu_errors');
962
	}
963
}
964
965
/**
966
 * Returns the number of watched users in the system.
967
 * (used by createList() callbacks).
968
 *
969
 * @param int $warning_watch
970
 * @return int
971
 */
972
function watchedUserCount($warning_watch = 0)
973
{
974
	$db = database();
975
976
	// @todo $approve_query is not used
977
978
	$request = $db->query('', '
979
		SELECT 
980
			COUNT(*)
981
		FROM {db_prefix}members
982
		WHERE warning >= {int:warning_watch}',
983
		[
984
			'warning_watch' => $warning_watch,
985
		]
986
	);
987
	list ($totalMembers) = $request->fetch_row();
988
	$request->free_result();
989
990
	return $totalMembers;
991
}
992
993
/**
994
 * Retrieved the watched users in the system.
995
 * (used by createList() callbacks).
996
 *
997
 * @param int $start The item to start with (for pagination purposes)
998
 * @param int $items_per_page The number of items to show per page
999
 * @param string $sort A string indicating how to sort the results
1000
 *
1001
 * @return array
1002
 */
1003
function watchedUsers($start, $items_per_page, $sort)
1004
{
1005
	global $modSettings;
1006
1007
	$db = database();
1008
1009
	$watched_users = [];
1010
	$members = [];
1011
	$db->query('', '
1012
		SELECT 
1013
			id_member, real_name, last_login, posts, warning
1014
		FROM {db_prefix}members
1015
		WHERE warning >= {int:warning_watch}
1016
		ORDER BY {raw:sort}
1017
		LIMIT ' . $items_per_page . '  OFFSET ' . $start,
1018
		[
1019
			'warning_watch' => $modSettings['warning_watch'],
1020
			'sort' => $sort,
1021
		]
1022
	)->fetch_callback(
1023
		function ($row) use (&$watched_users, &$members) {
1024
			global $txt;
1025
1026
			$watched_users[$row['id_member']] = [
1027
				'id' => $row['id_member'],
1028
				'name' => $row['real_name'],
1029
				'last_login' => $row['last_login'] ? standardTime($row['last_login']) : $txt['never'],
1030
				'last_post' => $txt['not_applicable'],
1031
				'last_post_id' => 0,
1032
				'warning' => $row['warning'],
1033
				'posts' => $row['posts'],
1034
			];
1035
1036
			$members[] = $row['id_member'];
1037
		}
1038
	);
1039
1040
	if (!empty($members))
1041
	{
1042
		// First get the latest messages from these users.
1043
		$latest_posts = [];
1044
		$db->fetchQuery('
1045
			SELECT 
1046
				m.id_member, MAX(m.id_msg) AS last_post_id
1047
			FROM {db_prefix}messages AS m' . (User::$info->query_see_board == '1=1' ? '' : '
0 ignored issues
show
Bug Best Practice introduced by
The property query_see_board does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
1048
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})') . '
1049
			WHERE m.id_member IN ({array_int:member_list})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
1050
				AND m.approved = {int:is_approved}') . '
1051
			GROUP BY m.id_member',
1052
			[
1053
				'member_list' => $members,
1054
				'is_approved' => 1,
1055
			]
1056
		)->fetch_callback(
1057
			function ($row) use (&$latest_posts) {
1058
				$latest_posts[$row['id_member']] = $row['last_post_id'];
1059
			}
1060
		);
1061
1062
		if (!empty($latest_posts))
1063
		{
1064
			// Now get the time those messages were posted.
1065
			$db->fetchQuery('
1066
				SELECT 
1067
					id_member, poster_time
1068
				FROM {db_prefix}messages
1069
				WHERE id_msg IN ({array_int:message_list})',
1070
				[
1071
					'message_list' => $latest_posts,
1072
				]
1073
			)->fetch_callback(
1074
				function ($row) use (&$watched_users, $latest_posts) {
1075
					$watched_users[$row['id_member']]['last_post'] = standardTime($row['poster_time']);
1076
					$watched_users[$row['id_member']]['last_post_id'] = $latest_posts[$row['id_member']];
1077
				}
1078
			);
1079
		}
1080
1081
		$db->fetchQuery('
1082
			SELECT 
1083
				MAX(m.poster_time) AS last_post, MAX(m.id_msg) AS last_post_id, m.id_member
1084
			FROM {db_prefix}messages AS m' . (User::$info->query_see_board == '1=1' ? '' : '
1085
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})') . '
1086
			WHERE m.id_member IN ({array_int:member_list})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
1087
				AND m.approved = {int:is_approved}') . '
1088
			GROUP BY m.id_member',
1089
			[
1090
				'member_list' => $members,
1091
				'is_approved' => 1,
1092
			]
1093
		)->fetch_callback(
1094
			function ($row) use (&$watched_users) {
1095
				$watched_users[$row['id_member']]['last_post'] = standardTime($row['last_post']);
1096
				$watched_users[$row['id_member']]['last_post_id'] = $row['last_post_id'];
1097
			}
1098
		);
1099
	}
1100
1101
	return $watched_users;
1102
}
1103
1104
/**
1105
 * Count of posts of watched users.
1106
 * (used by createList() callbacks)
1107
 *
1108
 * @param string $approve_query
1109
 * @param int $warning_watch
1110
 * @return int
1111
 */
1112
function watchedUserPostsCount($approve_query, $warning_watch)
1113
{
1114
	global $modSettings;
1115
1116
	$db = database();
1117
1118
	// @todo $approve_query is not used in the function
1119
1120
	$request = $db->query('', '
1121
		SELECT 
1122
			COUNT(*)
1123
		FROM {db_prefix}messages AS m
1124
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1125
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1126
		WHERE mem.warning >= {int:warning_watch}
1127
			AND {query_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
1128
			AND b.id_board != {int:recycle}' : '') .
1129
		$approve_query,
1130
		[
1131
			'warning_watch' => $warning_watch,
1132
			'recycle' => $modSettings['recycle_board'],
1133
		]
1134
	);
1135
	list ($totalMemberPosts) = $request->fetch_row();
1136
	$request->free_result();
1137
1138
	return $totalMemberPosts;
1139
}
1140
1141
/**
1142
 * Retrieve the posts of watched users.
1143
 * (used by createList() callbacks).
1144
 *
1145
 * @param int $start The item to start with (for pagination purposes)
1146
 * @param int $items_per_page The number of items to show per page
1147
 * @param string $approve_query
1148
 * @param int[] $delete_boards
1149
 *
1150
 * @return array
1151
 */
1152
function watchedUserPosts($start, $items_per_page, $approve_query, $delete_boards)
1153
{
1154
	global $modSettings;
1155
1156
	$db = database();
1157
1158
	$member_posts = [];
1159
	$bbc_parser = ParserWrapper::instance();
1160
	$db->fetchQuery('
1161
		SELECT 
1162
			m.id_msg, m.id_topic, m.id_board, m.id_member, m.subject, m.body, m.poster_time,
1163
			m.approved, mem.real_name, m.smileys_enabled
1164
		FROM {db_prefix}messages AS m
1165
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1166
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1167
		WHERE mem.warning >= {int:warning_watch}
1168
			AND {query_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
1169
			AND b.id_board != {int:recycle}' : '') .
1170
		$approve_query . '
1171
		ORDER BY m.id_msg DESC
1172
		LIMIT ' . $items_per_page . '  OFFSET ' . $start,
1173
		[
1174
			'warning_watch' => $modSettings['warning_watch'],
1175
			'recycle' => $modSettings['recycle_board'],
1176
		]
1177
	)->fetch_callback(
1178
		function ($row) use (&$member_posts, $bbc_parser, $start, $delete_boards) {
1179
			global $scripturl;
1180
1181
			$row['subject'] = censor($row['subject']);
1182
			$row['body'] = censor($row['body']);
1183
1184
			$member_posts[$row['id_msg']] = [
1185
				'id' => $row['id_msg'],
1186
				'id_topic' => $row['id_topic'],
1187
				'author_link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>',
1188
				'subject' => $row['subject'],
1189
				'body' => $bbc_parser->parseMessage($row['body'], $row['smileys_enabled']),
1190
				'poster_time' => standardTime($row['poster_time']),
1191
				'approved' => $row['approved'],
1192
				'can_delete' => $delete_boards == [0] || in_array($row['id_board'], $delete_boards),
1193
				'counter' => ++$start,
1194
			];
1195
		}
1196
	);
1197
1198
	return $member_posts;
1199
}
1200
1201
/**
1202
 * Show a list of all the group requests they can see.
1203
 * Checks permissions for group moderation.
1204
 */
1205
function groupRequests()
1206
{
1207
	$db = database();
1208
1209
	$group_requests = [];
1210
	$i = 0;
1211
1212
	// Make sure they can even moderate someone!
1213
	if (User::$info->mod_cache['gq'] == '0=1')
0 ignored issues
show
Bug Best Practice introduced by
The property mod_cache does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
1214
	{
1215
		return [];
1216
	}
1217
1218
	// What requests are outstanding?
1219
	$db->fetchQuery('
1220
		SELECT 
1221
			lgr.id_request, lgr.id_member, lgr.id_group, lgr.time_applied, mem.member_name, mg.group_name, mem.real_name
1222
		FROM {db_prefix}log_group_requests AS lgr
1223
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = lgr.id_member)
1224
			INNER JOIN {db_prefix}membergroups AS mg ON (mg.id_group = lgr.id_group)
1225
		WHERE ' . (User::$info->mod_cache['gq'] == '1=1' || User::$info->mod_cache['gq'] == '0=1' ? User::$info->mod_cache['gq'] : 'lgr.' . User::$info->mod_cache['gq']) . '
1226
		ORDER BY lgr.id_request DESC
1227
		LIMIT 10',
1228
		[]
1229
	)->fetch_callback(
1230 2
		function ($row) use (&$group_requests, &$i) {
1231
			global $scripturl;
1232 2
1233 2
			$group_requests[] = [
1234
				'id' => $row['id_request'],
1235
				'request_href' => $scripturl . '?action=groups;sa=requests;gid=' . $row['id_group'],
1236 2
				'member' => [
1237
					'id' => $row['id_member'],
1238
					'name' => $row['real_name'],
1239
					'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>',
1240
					'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
1241
				],
1242 2
				'group' => [
1243
					'id' => $row['id_group'],
1244
					'name' => $row['group_name'],
1245
				],
1246
				'time_submitted' => standardTime($row['time_applied']),
1247
			];
1248 2
1249
			$i++;
1250
		}
1251 2
	);
1252 2
1253
	return $group_requests;
1254
}
1255
1256
/**
1257
 * Returns an array of basic info about the most active watched users.
1258
 */
1259
function basicWatchedUsers()
1260
{
1261
	global $modSettings;
1262
1263
	$db = database();
1264
1265
	$watched_users = [];
1266
	if (!Cache::instance()->getVar($watched_users, 'recent_user_watches', 240))
1267
	{
1268
		$modSettings['warning_watch'] = empty($modSettings['warning_watch']) ? 1 : $modSettings['warning_watch'];
1269
		$db->fetchQuery('
1270
			SELECT
1271
				id_member, real_name, last_login
1272
			FROM {db_prefix}members
1273
			WHERE warning >= {int:warning_watch}
1274 2
			ORDER BY last_login DESC
1275
			LIMIT 10',
1276
			[
1277 2
				'warning_watch' => $modSettings['warning_watch'],
1278
			]
1279
		)->fetch_callback(
1280
			function ($row) use (&$watched_users) {
1281
				$watched_users[] = $row;
1282
			}
1283
		);
1284
1285 2
		Cache::instance()->put('recent_user_watches', $watched_users, 240);
1286
	}
1287 2
1288
	return $watched_users;
1289 2
}
1290 2
1291
/**
1292 2
 * Returns the most recent reported posts as array
1293 2
 *
1294
 * @param bool $show_pms
1295
 *
1296
 * @return array
1297
 */
1298
function reportedPosts($show_pms = false)
1299
{
1300
	$db = database();
1301 2
1302
	// Got the info already?
1303 2
	$cachekey = md5(serialize(User::$info->mod_cache['bq']));
0 ignored issues
show
Bug Best Practice introduced by
The property mod_cache does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
1304
1305
	$reported_posts = [];
1306 2
	if (!Cache::instance()->getVar($reported_posts, 'reported_posts_' . $cachekey, 90))
1307
	{
1308
		$reported_posts = [];
1309 2
		// By George, that means we in a position to get the reports, jolly good.
1310
		$db->fetchQuery('
1311
			SELECT
1312 2
				lr.id_report, lr.id_msg, lr.id_topic, lr.id_board, lr.id_member, lr.subject,
1313
				lr.num_reports, COALESCE(mem.real_name, lr.membername) AS author_name,
1314
				COALESCE(mem.id_member, 0) AS id_author
1315
			FROM {db_prefix}log_reported AS lr
1316
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
1317
			WHERE ' . (User::$info->mod_cache['bq'] == '1=1' || User::$info->mod_cache['bq'] == '0=1' ? User::$info->mod_cache['bq'] : 'lr.' . User::$info->mod_cache['bq']) . '
1318
				AND lr.closed = {int:not_closed}
1319
				AND lr.type IN ({array_string:rep_type})
1320
				AND lr.ignore_all = {int:not_ignored}
1321
			ORDER BY lr.time_updated DESC
1322
			LIMIT 10',
1323
			[
1324
				'not_closed' => 0,
1325 2
				'not_ignored' => 0,
1326
				'rep_type' => $show_pms ? ['pm'] : ['msg'],
1327
			]
1328 2
		)->fetch_callback(
1329
			function ($row) use (&$reported_posts) {
1330 2
				$reported_posts[] = $row;
1331 2
			}
1332
		);
1333 2
1334
		// Cache it.
1335 2
		Cache::instance()->put('reported_posts_' . $cachekey, $reported_posts, 90);
1336
	}
1337
1338
	return $reported_posts;
1339
}
1340
1341
/**
1342 2
 * Remove a moderator note.
1343
 *
1344
 * @param int $id_note
1345
 */
1346
function removeModeratorNote($id_note)
1347
{
1348
	$db = database();
1349 2
1350 2
	// Lets delete it.
1351 2
	$db->query('', '
1352
		DELETE FROM {db_prefix}log_comments
1353 2
		WHERE id_comment = {int:note}
1354
			AND comment_type = {string:type}',
1355 2
		[
1356 2
			'note' => $id_note,
1357
			'type' => 'modnote',
1358
		]
1359
	);
1360 2
}
1361
1362
/**
1363 2
 * Get the number of moderator notes stored on the site.
1364
 *
1365
 * @return int
1366
 */
1367
function countModeratorNotes()
1368
{
1369
	$db = database();
1370
1371
	$moderator_notes_total = 0;
1372
	if (!Cache::instance()->getVar($moderator_notes_total, 'moderator_notes_total', 240))
1373
	{
1374
		$request = $db->query('', '
1375
			SELECT
1376
				COUNT(*)
1377
			FROM {db_prefix}log_comments AS lc
1378
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
1379
			WHERE lc.comment_type = {string:modnote}',
1380
			[
1381
				'modnote' => 'modnote',
1382
			]
1383
		);
1384
		list ($moderator_notes_total) = $request->fetch_row();
1385
		$request->free_result();
1386
1387
		Cache::instance()->put('moderator_notes_total', $moderator_notes_total, 240);
1388
	}
1389
1390
	return $moderator_notes_total;
1391
}
1392
1393
/**
1394
 * Adds a moderation note to the moderation center "shoutbox"
1395
 *
1396
 * @param int $id_poster who is posting the add
1397
 * @param string $poster_name a name to show
1398
 * @param string $contents what they are posting
1399
 */
1400
function addModeratorNote($id_poster, $poster_name, $contents)
1401
{
1402
	$db = database();
1403
1404
	// Insert it into the database
1405
	$db->insert('',
1406
		'{db_prefix}log_comments',
1407
		[
1408
			'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'recipient_name' => 'string',
1409
			'body' => 'string', 'log_time' => 'int',
1410
		],
1411
		[
1412
			$id_poster, $poster_name, 'modnote', '', $contents, time(),
1413
		],
1414
		['id_comment']
1415
	);
1416
}
1417
1418
/**
1419
 * Add a moderation comment to an actual moderation report
1420
 *
1421
 * @param int $report
1422
 * @param string $newComment
1423
 */
1424
function addReportComment($report, $newComment)
1425
{
1426
	$db = database();
1427
1428
	// Insert it into the database
1429
	$db->insert('',
1430
		'{db_prefix}log_comments',
1431
		[
1432
			'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'recipient_name' => 'string',
1433
			'id_notice' => 'int', 'body' => 'string', 'log_time' => 'int',
1434
		],
1435
		[
1436
			User::$info->id, User::$info->name, 'reportc', '',
0 ignored issues
show
Bug Best Practice introduced by
The property name does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
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...
1437
			$report, $newComment, time(),
1438
		],
1439
		['id_comment']
1440
	);
1441
}
1442
1443
/**
1444
 * Get the list of current notes in the moderation center "shoutbox"
1445
 *
1446
 * @param int $offset
1447
 *
1448
 * @return array
1449
 */
1450
function moderatorNotes($offset)
1451
{
1452
	$db = database();
1453
1454
	// Grab the current notes.
1455
	// We can only use the cache for the first page of notes.
1456
	if ($offset != 0 || !Cache::instance()->getVar($moderator_notes, 'moderator_notes', 240))
1457
	{
1458
		$moderator_notes = [];
1459
		$db->fetchQuery('
1460
			SELECT 
1461
				COALESCE(mem.id_member, 0) AS id_member, COALESCE(mem.real_name, lc.member_name) AS member_name,
1462
				lc.log_time, lc.body, lc.id_comment AS id_note
1463
			FROM {db_prefix}log_comments AS lc
1464
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
1465
			WHERE lc.comment_type = {string:modnote}
1466
			ORDER BY id_comment DESC
1467
			LIMIT 10 OFFSET {int:offset} ',
1468
			[
1469
				'modnote' => 'modnote',
1470
				'offset' => $offset,
1471
			]
1472
		)->fetch_callback(
1473
			function ($row) use (&$moderator_notes) {
1474
				$moderator_notes[] = $row;
1475
			}
1476
		);
1477
1478
		if ($offset == 0)
1479
		{
1480
			Cache::instance()->put('moderator_notes', $moderator_notes, 240);
1481
		}
1482
	}
1483
1484
	return $moderator_notes;
1485
}
1486
1487
/**
1488
 * Gets a warning notice by id that was sent to a user.
1489
 *
1490
 * @param int $id_notice
1491
 *
1492
 * @return array
1493
 */
1494
function moderatorNotice($id_notice)
1495
{
1496
	$db = database();
1497
1498
	// Get the body and subject of this notice
1499
	$request = $db->query('', '
1500
		SELECT 
1501
			body, subject
1502
		FROM {db_prefix}log_member_notices
1503
		WHERE id_notice = {int:id_notice}',
1504
		[
1505
			'id_notice' => $id_notice,
1506
		]
1507
	);
1508
	if ($request->num_rows() === 0)
1509
	{
1510
		return [];
1511
	}
1512
	list ($notice_body, $notice_subject) = $request->fetch_row();
1513
	$request->free_result();
1514
1515
	// Make it look nice
1516
	$bbc_parser = ParserWrapper::instance();
1517
	$notice_body = $bbc_parser->parseNotice($notice_body);
1518
1519
	return [$notice_body, $notice_subject];
1520
}
1521
1522
/**
1523
 * Make sure the "current user" (uses User::$info) cannot go outside of the limit for the day.
1524
 *
1525
 * @param int $member The member we are going to issue the warning to
1526
 *
1527
 * @return int
1528
 */
1529
function warningDailyLimit($member)
1530
{
1531
	$db = database();
1532
1533
	$request = $db->query('', '
1534
		SELECT 
1535
			SUM(counter)
1536
		FROM {db_prefix}log_comments
1537
		WHERE id_recipient = {int:selected_member}
1538
			AND id_member = {int:current_member}
1539
			AND comment_type = {string:warning}
1540
			AND log_time > {int:day_time_period}',
1541
		[
1542
			'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...
1543
			'selected_member' => $member,
1544
			'day_time_period' => time() - 86400,
1545
			'warning' => 'warning',
1546
		]
1547
	);
1548
	list ($current_applied) = $request->fetch_row();
1549
	$request->free_result();
1550
1551
	return $current_applied;
1552
}
1553
1554
/**
1555
 * Make sure the "current user" (uses User::$info) cannot go outside of the limit for the day.
1556
 *
1557
 * @param string $approve_query additional condition for the query
1558
 * @param string $current_view defined whether return the topics (first
1559
 *                messages) or the messages. If set to 'topics' it returns
1560
 *                the topics, otherwise the messages
1561
 * @param array $boards_allowed array of arrays, it must contain three
1562
 *                 indexes:
1563
 *                  - delete_own_boards
1564
 *                  - delete_any_boards
1565
 *                  - delete_own_replies
1566
 *                 each of which must be an array of boards the user is allowed
1567
 *                 to perform a certain action (return of boardsAllowedTo)
1568
 * @param int $start start of the query LIMIT
1569
 * @param int $limit number of elements to return (default 10)
1570
 *
1571
 * @return array
1572
 */
1573
function getUnapprovedPosts($approve_query, $current_view, $boards_allowed, $start, $limit = 10)
1574
{
1575
	$db = database();
1576
1577
	$unapproved_items = [];
1578
	$bbc_parser = ParserWrapper::instance();
1579
	$i = 1;
1580
	$db->fetchQuery('
1581
		SELECT 
1582
			m.id_msg, m.id_topic, m.id_board, m.subject, m.body, m.id_member,
1583
			COALESCE(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.smileys_enabled,
1584
			t.id_member_started, t.id_first_msg, b.name AS board_name, c.id_cat, c.name AS cat_name
1585
		FROM {db_prefix}messages AS m
1586
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
1587
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1588
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1589
			LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
1590
		WHERE m.approved = {int:not_approved}
1591
			AND t.id_first_msg ' . ($current_view == 'topics' ? '=' : '!=') . ' m.id_msg
1592
			AND {query_see_board}
1593
			' . $approve_query . '
1594
		LIMIT {int:limit} OFFSET {int:start} ',
1595
		[
1596
			'start' => $start,
1597
			'limit' => $limit,
1598
			'not_approved' => 0,
1599
		]
1600
	)->fetch_callback(
1601
		function ($row) use (&$unapproved_items, $bbc_parser, &$i, $boards_allowed) {
1602
			global $context, $scripturl, $modSettings;
1603
1604
			// Can delete is complicated, let's solve it first... is it their own post?
1605
			if ($row['id_member'] == User::$info->id && ($boards_allowed['delete_own_boards'] == [0] || in_array($row['id_board'], $boards_allowed['delete_own_boards'])))
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...
1606
			{
1607
				$can_delete = true;
1608
			}
1609
			// Is it a reply to their own topic?
1610
			elseif ($row['id_member'] == $row['id_member_started'] && $row['id_msg'] != $row['id_first_msg'] && ($boards_allowed['delete_own_replies'] == [0] || in_array($row['id_board'], $boards_allowed['delete_own_replies'])))
1611
			{
1612
				$can_delete = true;
1613
			}
1614
			// Someone else's?
1615
			elseif ($row['id_member'] != User::$info->id && ($boards_allowed['delete_any_boards'] == [0] || in_array($row['id_board'], $boards_allowed['delete_any_boards'])))
1616
			{
1617
				$can_delete = true;
1618
			}
1619
			else
1620
			{
1621
				$can_delete = false;
1622
			}
1623
1624
			$unapproved_items[] = [
1625
				'id' => $row['id_msg'],
1626
				'counter' => $context['start'] + $i,
1627
				'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1628
				'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'] . '">' . $row['subject'] . '</a>',
1629
				'subject' => $row['subject'],
1630
				'body' => $bbc_parser->parseMessage($row['body'], $row['smileys_enabled']),
1631
				'time' => standardTime($row['poster_time']),
1632
				'html_time' => htmlTime($row['poster_time']),
1633
				'timestamp' => forum_time(true, $row['poster_time']),
1634
				'poster' => [
1635
					'id' => $row['id_member'],
1636
					'name' => $row['poster_name'],
1637
					'link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>' : $row['poster_name'],
1638
					'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
1639
				],
1640
				'topic' => [
1641
					'id' => $row['id_topic'],
1642
				],
1643
				'board' => [
1644
					'id' => $row['id_board'],
1645
					'name' => $row['board_name'],
1646
					'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['board_name'] . '</a>',
1647
				],
1648
				'category' => [
1649
					'id' => $row['id_cat'],
1650
					'name' => $row['cat_name'],
1651
					'link' => '<a href="' . getUrl('action', $modSettings['default_forum_action']) . '#c' . $row['id_cat'] . '">' . $row['cat_name'] . '</a>',
1652
				],
1653
				'can_delete' => $can_delete,
1654
			];
1655
1656
			$i++;
1657
		}
1658
	);
1659
1660
	return $unapproved_items;
1661
}
1662