Completed
Push — release-2.1 ( 6f6d35...abeae7 )
by Mathias
08:46
created

ModerationCenter.php ➔ ModBlockGroupRequests()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 45
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 24
nc 3
nop 0
dl 0
loc 45
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Moderation Center.
5
 *
6
 * Simple Machines Forum (SMF)
7
 *
8
 * @package SMF
9
 * @author Simple Machines http://www.simplemachines.org
10
 * @copyright 2017 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 4
14
 */
15
16
if (!defined('SMF'))
17
	die('No direct access...');
18
19
/**
20
 * Entry point for the moderation center.
21
 *
22
 * @param bool $dont_call If true, doesn't call the function for the appropriate mod area
23
 */
24
function ModerationMain($dont_call = false)
25
{
26
	global $smcFunc, $txt, $context, $scripturl, $modSettings, $user_info, $sourcedir, $options;
27
28
	// Don't run this twice... and don't conflict with the admin bar.
29
	if (isset($context['admin_area']))
30
		return;
31
32
	$context['can_moderate_boards'] = $user_info['mod_cache']['bq'] != '0=1';
33
	$context['can_moderate_groups'] = $user_info['mod_cache']['gq'] != '0=1';
34
	$context['can_moderate_approvals'] = $modSettings['postmod_active'] && !empty($user_info['mod_cache']['ap']);
35
	$context['can_moderate_users'] = allowedTo('moderate_forum');
36
37
	// Everyone using this area must be allowed here!
38
	if (!$context['can_moderate_boards'] && !$context['can_moderate_groups'] && !$context['can_moderate_approvals'] && !$context['can_moderate_users'])
39
		isAllowedTo('access_mod_center');
40
41
	// We're gonna want a menu of some kind.
42
	require_once($sourcedir . '/Subs-Menu.php');
43
44
	// Load the language, and the template.
45
	loadLanguage('ModerationCenter');
46
	loadTemplate(false, 'admin');
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
47
48
	$context['admin_preferences'] = !empty($options['admin_preferences']) ? $smcFunc['json_decode']($options['admin_preferences'], true) : array();
49
	$context['robot_no_index'] = true;
50
51
	// This is the menu structure - refer to Subs-Menu.php for the details.
52
	$moderation_areas = array(
53
		'main' => array(
54
			'title' => $txt['mc_main'],
55
			'areas' => array(
56
				'index' => array(
57
					'label' => $txt['moderation_center'],
58
					'function' => 'ModerationHome',
59
					'icon' => 'administration',
60
				),
61
				'settings' => array(
62
					'label' => $txt['mc_settings'],
63
					'function' => 'ModerationSettings',
64
					'icon' => 'features',
65
				),
66
				'modlogoff' => array(
67
					'label' => $txt['mc_logoff'],
68
					'function' => 'ModEndSession',
69
					'enabled' => empty($modSettings['securityDisable_moderate']),
70
					'icon' => 'exit',
71
				),
72
				'notice' => array(
73
					'file' => 'ModerationCenter.php',
74
					'function' => 'ShowNotice',
75
					'select' => 'index'
76
				),
77
			),
78
		),
79
		'logs' => array(
80
			'title' => $txt['mc_logs'],
81
			'areas' => array(
82
				'modlog' => array(
83
					'label' => $txt['modlog_view'],
84
					'enabled' => !empty($modSettings['modlog_enabled']) && $context['can_moderate_boards'],
85
					'file' => 'Modlog.php',
86
					'function' => 'ViewModlog',
87
					'icon' => 'logs',
88
				),
89
				'warnings' => array(
90
					'label' => $txt['mc_warnings'],
91
					'enabled' => $modSettings['warning_settings'][0] == 1 && $context['can_moderate_boards'],
92
					'function' => 'ViewWarnings',
93
					'icon' => 'warning',
94
					'subsections' => array(
95
						'log' => array($txt['mc_warning_log']),
96
						'templates' => array($txt['mc_warning_templates'], 'issue_warning'),
97
					),
98
				),
99
			),
100
		),
101
		'posts' => array(
102
			'title' => $txt['mc_posts'],
103
			'enabled' => $context['can_moderate_boards'] || $context['can_moderate_approvals'],
104
			'areas' => array(
105
				'postmod' => array(
106
					'label' => $txt['mc_unapproved_posts'],
107
					'enabled' => $context['can_moderate_approvals'],
108
					'file' => 'PostModeration.php',
109
					'function' => 'PostModerationMain',
110
					'icon' => 'posts',
111
					'custom_url' => $scripturl . '?action=moderate;area=postmod',
112
					'subsections' => array(
113
						'posts' => array($txt['mc_unapproved_replies']),
114
						'topics' => array($txt['mc_unapproved_topics']),
115
					),
116
				),
117
				'attachmod' => array(
118
					'label' => $txt['mc_unapproved_attachments'],
119
					'enabled' => $context['can_moderate_approvals'],
120
					'file' => 'PostModeration.php',
121
					'function' => 'PostModerationMain',
122
					'icon' => 'post_moderation_attach',
123
					'custom_url' => $scripturl . '?action=moderate;area=attachmod;sa=attachments',
124
				),
125
				'reportedposts' => array(
126
					'label' => $txt['mc_reported_posts'],
127
					'enabled' => $context['can_moderate_boards'],
128
					'file' => 'ReportedContent.php',
129
					'function' => 'ReportedContent',
130
					'icon' => 'reports',
131
					'subsections' => array(
132
						'show' => array($txt['mc_reportedp_active']),
133
						'closed' => array($txt['mc_reportedp_closed']),
134
					),
135
				),
136
			),
137
		),
138
		'groups' => array(
139
			'title' => $txt['mc_groups'],
140
			'enabled' => $context['can_moderate_groups'],
141
			'areas' => array(
142
				'groups' => array(
143
					'label' => $txt['mc_group_requests'],
144
					'file' => 'Groups.php',
145
					'function' => 'Groups',
146
					'icon' => 'members_request',
147
					'custom_url' => $scripturl . '?action=moderate;area=groups;sa=requests',
148
				),
149
				'viewgroups' => array(
150
					'label' => $txt['mc_view_groups'],
151
					'file' => 'Groups.php',
152
					'function' => 'Groups',
153
					'icon' => 'membergroups',
154
				),
155
			),
156
		),
157
		'members' => array(
158
			'title' => $txt['mc_members'],
159
			'enabled' => $context['can_moderate_users'] || ($modSettings['warning_settings'][0] == 1 && $context['can_moderate_boards']),
160
			'areas' => array(
161
				'userwatch' => array(
162
					'label' => $txt['mc_watched_users_title'],
163
					'enabled' => $modSettings['warning_settings'][0] == 1 && $context['can_moderate_boards'],
164
					'function' => 'ViewWatchedUsers',
165
					'icon' => 'members_watched',
166
					'subsections' => array(
167
						'member' => array($txt['mc_watched_users_member']),
168
						'post' => array($txt['mc_watched_users_post']),
169
					),
170
				),
171
				'reportedmembers' => array(
172
					'label' => $txt['mc_reported_members_title'],
173
					'enabled' => $context['can_moderate_users'],
174
					'file' => 'ReportedContent.php',
175
					'function' => 'ReportedContent',
176
					'icon' => 'members_watched',
177
					'subsections' => array(
178
						'open' => array($txt['mc_reportedp_active']),
179
						'closed' => array($txt['mc_reportedp_closed']),
180
					),
181
				),
182
			),
183
		)
184
	);
185
186
	// Make sure the administrator has a valid session...
187
	validateSession('moderate');
188
189
	// I don't know where we're going - I don't know where we've been...
190
	$menuOptions = array(
191
		'action' => 'moderate',
192
		'disable_url_session_check' => true,
193
	);
194
	$mod_include_data = createMenu($moderation_areas, $menuOptions);
195
	unset($moderation_areas);
196
197
	// We got something - didn't we? DIDN'T WE!
198
	if ($mod_include_data == false)
199
		fatal_lang_error('no_access', false);
200
201
	// Retain the ID information in case required by a subaction.
202
	$context['moderation_menu_id'] = $context['max_menu_id'];
203
	$context['moderation_menu_name'] = 'menu_data_' . $context['moderation_menu_id'];
204
205
	// @todo: html in here is not good
206
	$context[$context['moderation_menu_name']]['tab_data'] = array(
207
		'title' => $txt['moderation_center'],
208
		'help' => '',
209
		'description' => '
210
			<strong>' . $txt['hello_guest'] . ' ' . $context['user']['name'] . '!</strong>
211
			<br><br>
212
			' . $txt['mc_description']);
213
214
	// What a pleasant shortcut - even tho we're not *really* on the admin screen who cares...
215
	$context['admin_area'] = $mod_include_data['current_area'];
216
217
	// Build the link tree.
218
	$context['linktree'][] = array(
219
		'url' => $scripturl . '?action=moderate',
220
		'name' => $txt['moderation_center'],
221
	);
222
	if (isset($mod_include_data['current_area']) && $mod_include_data['current_area'] != 'index')
223
		$context['linktree'][] = array(
224
			'url' => $scripturl . '?action=moderate;area=' . $mod_include_data['current_area'],
225
			'name' => $mod_include_data['label'],
226
		);
227
	if (!empty($mod_include_data['current_subsection']) && $mod_include_data['subsections'][$mod_include_data['current_subsection']][0] != $mod_include_data['label'])
228
		$context['linktree'][] = array(
229
			'url' => $scripturl . '?action=moderate;area=' . $mod_include_data['current_area'] . ';sa=' . $mod_include_data['current_subsection'],
230
			'name' => $mod_include_data['subsections'][$mod_include_data['current_subsection']][0],
231
		);
232
233
	// Now - finally - the bit before the encore - the main performance of course!
234
	if (!$dont_call)
235
	{
236
		if (isset($mod_include_data['file']))
237
			require_once($sourcedir . '/' . $mod_include_data['file']);
238
239
		call_helper($mod_include_data['function']);
240
	}
241
}
242
243
/**
244
 * This function basically is the home page of the moderation center.
245
 */
246
function ModerationHome()
247
{
248
	global $smcFunc, $txt, $context, $options;
249
250
	loadTemplate('ModerationCenter');
251
	loadJavaScriptFile('admin.js', array(), 'smf_admin');
252
253
	$context['page_title'] = $txt['moderation_center'];
254
	$context['sub_template'] = 'moderation_center';
255
256
	// Handle moderators notes.
257
	ModBlockNotes();
258
259
	// Load what blocks the user actually can see...
260
	$valid_blocks = array();
261
262
	if ($context['can_moderate_groups'])
263
		$valid_blocks['g'] = 'GroupRequests';
264
	if ($context['can_moderate_boards'])
265
	{
266
		$valid_blocks['r'] = 'ReportedPosts';
267
		$valid_blocks['w'] = 'WatchedUsers';
268
	}
269
	if ($context['can_moderate_users'])
270
	{
271
		// This falls under the category of moderating users as well...
272
		if (!$context['can_moderate_boards'])
273
			$valid_blocks['w'] = 'WatchedUsers';
274
275
		$valid_blocks['rm'] = 'ReportedMembers';
276
	}
277
278
	call_integration_hook('integrate_mod_centre_blocks', array(&$valid_blocks));
279
280
	$context['mod_blocks'] = array();
281
	foreach ($valid_blocks as $k => $block)
282
	{
283
		$block = 'ModBlock' . $block;
284
		if (function_exists($block))
285
			$context['mod_blocks'][] = $block();
286
	}
287
288
	$context['admin_prefs'] = !empty($options['admin_preferences']) ? $smcFunc['json_decode']($options['admin_preferences'], true) : array();
289
}
290
291
/**
292
 * Show a list of the most active watched users.
293
 */
294
function ModBlockWatchedUsers()
295
{
296
	global $context, $smcFunc, $scripturl, $modSettings;
297
298
	if (($watched_users = cache_get_data('recent_user_watches', 240)) === null)
299
	{
300
		$modSettings['warning_watch'] = empty($modSettings['warning_watch']) ? 1 : $modSettings['warning_watch'];
301
		$request = $smcFunc['db_query']('', '
302
			SELECT id_member, real_name, last_login
303
			FROM {db_prefix}members
304
			WHERE warning >= {int:warning_watch}
305
			ORDER BY last_login DESC
306
			LIMIT 10',
307
			array(
308
				'warning_watch' => $modSettings['warning_watch'],
309
			)
310
		);
311
		$watched_users = array();
312
		while ($row = $smcFunc['db_fetch_assoc']($request))
313
			$watched_users[] = $row;
314
		$smcFunc['db_free_result']($request);
315
316
		cache_put_data('recent_user_watches', $watched_users, 240);
317
	}
318
319
	$context['watched_users'] = array();
320
	foreach ($watched_users as $user)
0 ignored issues
show
Bug introduced by
The expression $watched_users of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
321
	{
322
		$context['watched_users'][] = array(
323
			'id' => $user['id_member'],
324
			'name' => $user['real_name'],
325
			'link' => '<a href="' . $scripturl . '?action=profile;u=' . $user['id_member'] . '">' . $user['real_name'] . '</a>',
326
			'href' => $scripturl . '?action=profile;u=' . $user['id_member'],
327
			'last_login' => !empty($user['last_login']) ? timeformat($user['last_login']) : '',
328
		);
329
	}
330
331
	return 'watched_users';
332
}
333
334
/**
335
 * Show an area for the moderator to type into.
336
 */
337
function ModBlockNotes()
338
{
339
	global $context, $smcFunc, $scripturl, $user_info;
340
341
	// Set a nice and informative message.
342
	$context['report_post_action'] = !empty($_SESSION['rc_confirmation']) ? $_SESSION['rc_confirmation'] : array();
343
	unset($_SESSION['rc_confirmation']);
344
345
	// Are we saving a note?
346
	if (isset($_GET['modnote']) && isset($_POST['makenote']) && isset($_POST['new_note']))
347
	{
348
		checkSession();
349
		validateToken('mod-modnote-add');
350
351
		$_POST['new_note'] = $smcFunc['htmlspecialchars'](trim($_POST['new_note']));
352
		// Make sure they actually entered something.
353
		if (!empty($_POST['new_note']))
354
		{
355
			// Insert it into the database then!
356
			$smcFunc['db_insert']('',
357
				'{db_prefix}log_comments',
358
				array(
359
					'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'recipient_name' => 'string',
360
					'body' => 'string', 'log_time' => 'int',
361
				),
362
				array(
363
					$user_info['id'], $user_info['name'], 'modnote', '', $_POST['new_note'], time(),
364
				),
365
				array('id_comment')
366
			);
367
368
			// Clear the cache.
369
			cache_put_data('moderator_notes', null, 240);
370
			cache_put_data('moderator_notes_total', null, 240);
371
		}
372
373
		// Everything went better than expected!
374
		$_SESSION['rc_confirmation'] = 'message_saved';
375
376
		// Redirect otherwise people can resubmit.
377
		redirectexit('action=moderate');
378
	}
379
380
	// Bye... bye...
381
	if (isset($_GET['notes']) && isset($_GET['delete']) && is_numeric($_GET['delete']))
382
	{
383
		checkSession('get');
384
		validateToken('mod-modnote-del', 'get');
385
386
		// No sneaky stuff now!
387
		if (!allowedTo('admin_forum'))
388
		{
389
			// Is this your note?
390
			$get_owner = $smcFunc['db_query']('', '
391
				SELECT id_member
392
				FROM {db_prefix}log_comments
393
				WHERE id_comment = {int:note}
394
					AND comment_type = {literal:modnote}
395
					AND id_member = {int:user}',
396
				array(
397
					'note' => $_GET['delete'],
398
					'user' => $user_info['id'],
399
				)
400
			);
401
402
			$note_owner = $smcFunc['db_num_rows']($get_owner);
403
			$smcFunc['db_free_result']($get_owner);
404
405
			if (empty($note_owner))
406
				fatal_lang_error('mc_notes_delete_own', false);
407
		}
408
409
		// Lets delete it.
410
		$smcFunc['db_query']('', '
411
			DELETE FROM {db_prefix}log_comments
412
			WHERE id_comment = {int:note}
413
				AND comment_type = {literal:modnote}',
414
			array(
415
				'note' => $_GET['delete'],
416
			)
417
		);
418
419
		// Clear the cache.
420
		cache_put_data('moderator_notes', null, 240);
421
		cache_put_data('moderator_notes_total', null, 240);
422
423
		// Tell them the message was deleted.
424
		$_SESSION['rc_confirmation'] = 'message_deleted';
425
426
		redirectexit('action=moderate');
427
	}
428
429
	// How many notes in total?
430 View Code Duplication
	if (($moderator_notes_total = cache_get_data('moderator_notes_total', 240)) === null)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
431
	{
432
		$request = $smcFunc['db_query']('', '
433
			SELECT COUNT(*)
434
			FROM {db_prefix}log_comments AS lc
435
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
436
			WHERE lc.comment_type = {literal:modnote}',
437
			array(
438
			)
439
		);
440
		list ($moderator_notes_total) = $smcFunc['db_fetch_row']($request);
441
		$smcFunc['db_free_result']($request);
442
443
		cache_put_data('moderator_notes_total', $moderator_notes_total, 240);
444
	}
445
446
	// Grab the current notes. We can only use the cache for the first page of notes.
447
	$offset = isset($_GET['notes']) && isset($_GET['start']) ? $_GET['start'] : 0;
448
	if ($offset != 0 || ($moderator_notes = cache_get_data('moderator_notes', 240)) === null)
449
	{
450
		$request = $smcFunc['db_query']('', '
451
			SELECT COALESCE(mem.id_member, 0) AS id_member, COALESCE(mem.real_name, lc.member_name) AS member_name,
452
				lc.log_time, lc.body, lc.id_comment AS id_note
453
			FROM {db_prefix}log_comments AS lc
454
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
455
			WHERE lc.comment_type = {literal:modnote}
456
			ORDER BY id_comment DESC
457
			LIMIT {int:offset}, 10',
458
			array(
459
				'offset' => $offset,
460
			)
461
		);
462
		$moderator_notes = array();
463
		while ($row = $smcFunc['db_fetch_assoc']($request))
464
			$moderator_notes[] = $row;
465
		$smcFunc['db_free_result']($request);
466
467
		if ($offset == 0)
468
			cache_put_data('moderator_notes', $moderator_notes, 240);
469
	}
470
471
	// Lets construct a page index.
472
	$context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=index;notes', $_GET['start'], $moderator_notes_total, 10);
473
	$context['start'] = $_GET['start'];
474
475
	$context['notes'] = array();
476
	foreach ($moderator_notes as $note)
0 ignored issues
show
Bug introduced by
The expression $moderator_notes of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
477
	{
478
		$context['notes'][] = array(
479
			'author' => array(
480
				'id' => $note['id_member'],
481
				'link' => $note['id_member'] ? ('<a href="' . $scripturl . '?action=profile;u=' . $note['id_member'] . '">' . $note['member_name'] . '</a>') : $note['member_name'],
482
			),
483
			'time' => timeformat($note['log_time']),
484
			'text' => parse_bbc($note['body']),
485
			'delete_href' => $scripturl . '?action=moderate;area=index;notes;delete=' . $note['id_note'] . ';' . $context['session_var'] . '=' . $context['session_id'],
486
			'can_delete' => allowedTo('admin_forum') || $note['id_member'] == $user_info['id'],
487
		);
488
	}
489
490
	// Couple tokens for add/delete modnotes
491
	createToken('mod-modnote-add');
492
	createToken('mod-modnote-del', 'get');
493
494
	return 'notes';
495
}
496
497
/**
498
 * Show a list of the most recent reported posts.
499
 */
500
function ModBlockReportedPosts()
501
{
502
	global $context, $user_info, $scripturl, $smcFunc;
503
504
	// Got the info already?
505
	$cachekey = md5($smcFunc['json_encode']($user_info['mod_cache']['bq']));
506
	$context['reported_posts'] = array();
507
	if ($user_info['mod_cache']['bq'] == '0=1')
508
		return 'reported_posts_block';
509
510
	if (($reported_posts = cache_get_data('reported_posts_' . $cachekey, 90)) === null)
511
	{
512
		// By George, that means we in a position to get the reports, jolly good.
513
		$request = $smcFunc['db_query']('', '
514
			SELECT lr.id_report, lr.id_msg, lr.id_topic, lr.id_board, lr.id_member, lr.subject,
515
				lr.num_reports, COALESCE(mem.real_name, lr.membername) AS author_name,
516
				COALESCE(mem.id_member, 0) AS id_author
517
			FROM {db_prefix}log_reported AS lr
518
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
519
			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']) . '
520
				AND lr.id_board != {int:not_a_reported_post}
521
				AND lr.closed = {int:not_closed}
522
				AND lr.ignore_all = {int:not_ignored}
523
			ORDER BY lr.time_updated DESC
524
			LIMIT 10',
525
			array(
526
				'not_a_reported_post' => 0,
527
				'not_closed' => 0,
528
				'not_ignored' => 0,
529
			)
530
		);
531
		$reported_posts = array();
532
		while ($row = $smcFunc['db_fetch_assoc']($request))
533
			$reported_posts[] = $row;
534
		$smcFunc['db_free_result']($request);
535
536
		// Cache it.
537
		cache_put_data('reported_posts_' . $cachekey, $reported_posts, 90);
538
	}
539
540
	$context['reported_posts'] = array();
541
	foreach ($reported_posts as $i => $row)
0 ignored issues
show
Bug introduced by
The expression $reported_posts of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
542
	{
543
		$context['reported_posts'][] = array(
544
			'id' => $row['id_report'],
545
			'topic_href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
546
			'report_href' => $scripturl . '?action=moderate;area=reportedposts;sa=details;rid=' . $row['id_report'],
547
			'report_link' => '<a href="' . $scripturl . '?action=moderate;area=reportedposts;sa=details;rid=' . $row['id_report'] . '">' . $row['subject'] . '</a>',
548
			'author' => array(
549
				'id' => $row['id_author'],
550
				'name' => $row['author_name'],
551
				'link' => $row['id_author'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_author'] . '">' . $row['author_name'] . '</a>' : $row['author_name'],
552
				'href' => $scripturl . '?action=profile;u=' . $row['id_author'],
553
			),
554
			'subject' => $row['subject'],
555
			'num_reports' => $row['num_reports'],
556
		);
557
	}
558
559
	return 'reported_posts_block';
560
}
561
562
/**
563
 * Show a list of all the group requests they can see.
564
 */
565
function ModBlockGroupRequests()
566
{
567
	global $context, $user_info, $scripturl, $smcFunc;
568
569
	$context['group_requests'] = array();
570
	// Make sure they can even moderate someone!
571
	if ($user_info['mod_cache']['gq'] == '0=1')
572
		return 'group_requests_block';
573
574
	// What requests are outstanding?
575
	$request = $smcFunc['db_query']('', '
576
		SELECT lgr.id_request, lgr.id_member, lgr.id_group, lgr.time_applied, mem.member_name, mg.group_name, mem.real_name
577
		FROM {db_prefix}log_group_requests AS lgr
578
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = lgr.id_member)
579
			INNER JOIN {db_prefix}membergroups AS mg ON (mg.id_group = lgr.id_group)
580
		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']) . '
581
			AND lgr.status = {int:status_open}
582
		ORDER BY lgr.id_request DESC
583
		LIMIT 10',
584
		array(
585
			'status_open' => 0,
586
		)
587
	);
588
	for ($i = 0; $row = $smcFunc['db_fetch_assoc']($request); $i++)
589
	{
590
		$context['group_requests'][] = array(
591
			'id' => $row['id_request'],
592
			'request_href' => $scripturl . '?action=groups;sa=requests;gid=' . $row['id_group'],
593
			'member' => array(
594
				'id' => $row['id_member'],
595
				'name' => $row['real_name'],
596
				'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>',
597
				'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
598
			),
599
			'group' => array(
600
				'id' => $row['id_group'],
601
				'name' => $row['group_name'],
602
			),
603
			'time_submitted' => timeformat($row['time_applied']),
604
		);
605
	}
606
	$smcFunc['db_free_result']($request);
607
608
	return 'group_requests_block';
609
}
610
611
/**
612
 * Show a list of the most recent reported posts.
613
 */
614
function ModBlockReportedMembers()
615
{
616
	global $context, $scripturl, $smcFunc;
617
618
	// Got the info already?
619
	$cachekey = md5($smcFunc['json_encode']((int) allowedTo('moderate_forum')));
620
	$context['reported_users'] = array();
621
	if (!allowedTo('moderate_forum'))
622
		return 'reported_users_block';
623
624
	if (($reported_users = cache_get_data('reported_users_' . $cachekey, 90)) === null)
625
	{
626
		// By George, that means we in a position to get the reports, jolly good.
627
		$request = $smcFunc['db_query']('', '
628
			SELECT lr.id_report, lr.id_member,
629
				lr.num_reports, COALESCE(mem.real_name, lr.membername) AS user_name,
630
				COALESCE(mem.id_member, 0) AS id_user
631
			FROM {db_prefix}log_reported AS lr
632
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
633
			WHERE lr.id_board = {int:not_a_reported_post}
634
				AND lr.closed = {int:not_closed}
635
				AND lr.ignore_all = {int:not_ignored}
636
			ORDER BY lr.time_updated DESC
637
			LIMIT 10',
638
			array(
639
				'not_a_reported_post' => 0,
640
				'not_closed' => 0,
641
				'not_ignored' => 0,
642
			)
643
		);
644
		$reported_users = array();
645
		while ($row = $smcFunc['db_fetch_assoc']($request))
646
			$reported_users[] = $row;
647
		$smcFunc['db_free_result']($request);
648
649
		// Cache it.
650
		cache_put_data('reported_users_' . $cachekey, $reported_users, 90);
651
	}
652
653
	$context['reported_users'] = array();
654
	foreach ($reported_users as $i => $row)
0 ignored issues
show
Bug introduced by
The expression $reported_users of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
655
	{
656
		$context['reported_users'][] = array(
657
			'id' => $row['id_report'],
658
			'report_href' => $scripturl . '?action=moderate;area=reportedmembers;report=' . $row['id_report'],
659
			'user' => array(
660
				'id' => $row['id_user'],
661
				'name' => $row['user_name'],
662
				'link' => $row['id_user'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_user'] . '">' . $row['user_name'] . '</a>' : $row['user_name'],
663
				'href' => $scripturl . '?action=profile;u=' . $row['id_user'],
664
			),
665
			'num_reports' => $row['num_reports'],
666
		);
667
	}
668
669
	return 'reported_users_block';
670
}
671
672
/**
673
 * Browse all the reported users...
674
 */
675
function ReportedMembers()
676
{
677
	global $txt, $context, $scripturl, $smcFunc;
678
679
	loadTemplate('ModerationCenter');
680
681
	// Set an empty var for the server response.
682
	$context['report_member_action'] = '';
683
684
	// Put the open and closed options into tabs, because we can...
685
	$context[$context['moderation_menu_name']]['tab_data'] = array(
686
		'title' => $txt['mc_reported_members'],
687
		'help' => '',
688
		'description' => $txt['mc_reported_members_desc'],
689
	);
690
691
	isAllowedTo('moderate_forum');
692
693
	// Set up the comforting bits...
694
	$context['page_title'] = $txt['mc_reported_members'];
695
	$context['sub_template'] = 'reported_members';
696
697
	// Are we viewing open or closed reports?
698
	$context['view_closed'] = isset($_GET['sa']) && $_GET['sa'] == 'closed' ? 1 : 0;
699
700
	// Are we doing any work?
701
	if ((isset($_GET['ignore']) || isset($_GET['close'])) && isset($_GET['rid']))
702
	{
703
		checkSession('get');
704
		$_GET['rid'] = (int) $_GET['rid'];
705
706
		// Update the report...
707
		$smcFunc['db_query']('', '
708
			UPDATE {db_prefix}log_reported
709
			SET ' . (isset($_GET['ignore']) ? 'ignore_all = {int:ignore_all}' : 'closed = {int:closed}') . '
710
			WHERE id_report = {int:id_report}',
711
			array(
712
				'ignore_all' => isset($_GET['ignore']) ? (int) $_GET['ignore'] : 0,
713
				'closed' => isset($_GET['close']) ? (int) $_GET['close'] : 0,
714
				'id_report' => $_GET['rid'],
715
			)
716
		);
717
718
		// Get the board, topic and message for this report
719
		$request = $smcFunc['db_query']('', '
720
			SELECT id_member, membername
721
			FROM {db_prefix}log_reported
722
			WHERE id_report = {int:id_report}',
723
			array(
724
				'id_report' => $_GET['rid'],
725
			)
726
		);
727
728
		// Set up the data for the log...
729
		$extra = array('report' => $_GET['rid']);
730
		list($extra['member'], $extra['membername']) = $smcFunc['db_fetch_row']($request);
731
		$smcFunc['db_free_result']($request);
732
733
		// Stick this in string format for consistency
734
		$extra['member'] = (string) $extra['member'];
735
736
		// Tell the user about it.
737
		$context['report_member_action'] = isset($_GET['ignore']) ? (!empty($_GET['ignore']) ? 'ignore' : 'unignore') : (!empty($_GET['close']) ? 'close' : 'open');
738
739
		// Log this action
740
		logAction($context['report_member_action'] . '_user_report', $extra);
741
742
		// Time to update.
743
		updateSettings(array('last_mod_report_action' => time()));
744
		recountOpenReports('members');
745
	}
746
	elseif (isset($_POST['close']) && isset($_POST['close_selected']))
747
	{
748
		checkSession();
749
750
		// All the ones to update...
751
		$toClose = array();
752
		foreach ($_POST['close'] as $rid)
753
			$toClose[] = (int) $rid;
754
755
		if (!empty($toClose))
756
		{
757
			// Get the data for each of these reports
758
			$request = $smcFunc['db_query']('', '
759
				SELECT id_report, id_member, membername
760
				FROM {db_prefix}log_reported
761
				WHERE id_report IN ({array_int:report_list})',
762
				array(
763
					'report_list' => $toClose,
764
				)
765
			);
766
767
			$logs = array();
768
			while ($reports = $smcFunc['db_fetch_assoc']($request))
769
			{
770
				$logs[] = array(
771
					'action' => 'close_user_report',
772
					'log_type' => 'moderate',
773
					'extra' => array(
774
						'report' => $reports['id_report'],
775
						'membername' => $reports['membername'],
776
						'member' => (string) $reports['id_member'],
777
					),
778
				);
779
			}
780
781
			$smcFunc['db_free_result']($request);
782
783
			// Log the closing of all the reports
784
			logActions($logs);
785
786
			$smcFunc['db_query']('', '
787
				UPDATE {db_prefix}log_reported
788
				SET closed = {int:is_closed}
789
				WHERE id_report IN ({array_int:report_list})',
790
				array(
791
					'report_list' => $toClose,
792
					'is_closed' => 1,
793
				)
794
			);
795
796
			// Time to update.
797
			updateSettings(array('last_mod_report_action' => time()));
798
			recountOpenReports('members');
799
		}
800
801
		// Go on and tell the result.
802
		$context['report_member_action'] = 'close_all';
803
	}
804
805
	// How many entries are we viewing?
806
	$request = $smcFunc['db_query']('', '
807
		SELECT COUNT(*)
808
		FROM {db_prefix}log_reported AS lr
809
		WHERE lr.closed = {int:view_closed}
810
			AND lr.id_board = {int:not_a_reported_post}',
811
		array(
812
			'view_closed' => $context['view_closed'],
813
			'not_a_reported_post' => 0,
814
		)
815
	);
816
	list ($context['total_reports']) = $smcFunc['db_fetch_row']($request);
817
	$smcFunc['db_free_result']($request);
818
819
	// So, that means we can page index, yes?
820
	$context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=reportedmembers' . ($context['view_closed'] ? ';sa=closed' : ''), $_GET['start'], $context['total_reports'], 10);
821
	$context['start'] = $_GET['start'];
822
823
	// By George, that means we in a position to get the reports, golly good.
824
	$request = $smcFunc['db_query']('', '
825
		SELECT lr.id_report, lr.id_member, lr.time_started, lr.time_updated, lr.num_reports, lr.closed, lr.ignore_all,
826
			COALESCE(mem.real_name, lr.membername) AS user_name, COALESCE(mem.id_member, 0) AS id_user
827
		FROM {db_prefix}log_reported AS lr
828
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
829
		WHERE lr.closed = {int:view_closed}
830
			AND lr.id_board = {int:not_a_reported_post}
831
		ORDER BY lr.time_updated DESC
832
		LIMIT {int:limit}, {int:max}',
833
		array(
834
			'view_closed' => $context['view_closed'],
835
			'not_a_reported_post' => 0,
836
			'limit' => $context['start'],
837
			'max' => 10,
838
		)
839
	);
840
	$context['reports'] = array();
841
	$report_ids = array();
842
	for ($i = 0; $row = $smcFunc['db_fetch_assoc']($request); $i++)
843
	{
844
		$report_ids[] = $row['id_report'];
845
		$context['reports'][$row['id_report']] = array(
846
			'id' => $row['id_report'],
847
			'report_href' => $scripturl . '?action=moderate;area=reportedmembers;report=' . $row['id_report'],
848
			'user' => array(
849
				'id' => $row['id_user'],
850
				'name' => $row['user_name'],
851
				'link' => $row['id_user'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_user'] . '">' . $row['user_name'] . '</a>' : $row['user_name'],
852
				'href' => $scripturl . '?action=profile;u=' . $row['id_user'],
853
			),
854
			'comments' => array(),
855
			'time_started' => timeformat($row['time_started']),
856
			'last_updated' => timeformat($row['time_updated']),
857
			'num_reports' => $row['num_reports'],
858
			'closed' => $row['closed'],
859
			'ignore' => $row['ignore_all']
860
		);
861
	}
862
	$smcFunc['db_free_result']($request);
863
864
	// Now get all the people who reported it.
865 View Code Duplication
	if (!empty($report_ids))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
866
	{
867
		$request = $smcFunc['db_query']('', '
868
			SELECT lrc.id_comment, lrc.id_report, lrc.time_sent, lrc.comment,
869
				COALESCE(mem.id_member, 0) AS id_member, COALESCE(mem.real_name, lrc.membername) AS reporter
870
			FROM {db_prefix}log_reported_comments AS lrc
871
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lrc.id_member)
872
			WHERE lrc.id_report IN ({array_int:report_list})',
873
			array(
874
				'report_list' => $report_ids,
875
			)
876
		);
877
		while ($row = $smcFunc['db_fetch_assoc']($request))
878
		{
879
			$context['reports'][$row['id_report']]['comments'][] = array(
880
				'id' => $row['id_comment'],
881
				'message' => $row['comment'],
882
				'time' => timeformat($row['time_sent']),
883
				'member' => array(
884
					'id' => $row['id_member'],
885
					'name' => empty($row['reporter']) ? $txt['guest'] : $row['reporter'],
886
					'link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['reporter'] . '</a>' : (empty($row['reporter']) ? $txt['guest'] : $row['reporter']),
887
					'href' => $row['id_member'] ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
888
				),
889
			);
890
		}
891
		$smcFunc['db_free_result']($request);
892
	}
893
894
	$context['report_manage_bans'] = allowedTo('manage_bans');
895
}
896
897
/**
898
 * Act as an entrace for all group related activity.
899
 * @todo As for most things in this file, this needs to be moved somewhere appropriate?
900
 */
901
function ModerateGroups()
902
{
903
	global $context, $user_info;
904
905
	// You need to be allowed to moderate groups...
906
	if ($user_info['mod_cache']['gq'] == '0=1')
907
		isAllowedTo('manage_membergroups');
908
909
	// Load the group templates.
910
	loadTemplate('ModerationCenter');
911
912
	// Setup the subactions...
913
	$subActions = array(
914
		'requests' => 'GroupRequests',
915
		'view' => 'ViewGroups',
916
	);
917
918
	if (!isset($_GET['sa']) || !isset($subActions[$_GET['sa']]))
919
		$_GET['sa'] = 'view';
920
	$context['sub_action'] = $_GET['sa'];
921
922
	// Call the relevant function.
923
	call_helper($subActions[$context['sub_action']]);
924
}
925
926
/**
927
 * Show a notice sent to a user.
928
 */
929
function ShowNotice()
930
{
931
	global $smcFunc, $txt, $context;
932
933
	$context['page_title'] = $txt['show_notice'];
934
	$context['sub_template'] = 'show_notice';
935
	$context['template_layers'] = array();
936
937
	loadTemplate('ModerationCenter');
938
939
	// @todo Assumes nothing needs permission more than accessing moderation center!
940
	$id_notice = (int) $_GET['nid'];
941
	$request = $smcFunc['db_query']('', '
942
		SELECT body, subject
943
		FROM {db_prefix}log_member_notices
944
		WHERE id_notice = {int:id_notice}',
945
		array(
946
			'id_notice' => $id_notice,
947
		)
948
	);
949
	if ($smcFunc['db_num_rows']($request) == 0)
950
		fatal_lang_error('no_access', false);
951
	list ($context['notice_body'], $context['notice_subject']) = $smcFunc['db_fetch_row']($request);
952
	$smcFunc['db_free_result']($request);
953
954
	$context['notice_body'] = parse_bbc($context['notice_body'], false);
955
}
956
957
/**
958
 * View watched users.
959
 */
960
function ViewWatchedUsers()
961
{
962
	global $modSettings, $context, $txt, $scripturl, $sourcedir;
963
964
	// Some important context!
965
	$context['page_title'] = $txt['mc_watched_users_title'];
966
	$context['view_posts'] = isset($_GET['sa']) && $_GET['sa'] == 'post';
967
	$context['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0;
968
969
	loadTemplate('ModerationCenter');
970
971
	// Get some key settings!
972
	$modSettings['warning_watch'] = empty($modSettings['warning_watch']) ? 1 : $modSettings['warning_watch'];
973
974
	// Put some pretty tabs on cause we're gonna be doing hot stuff here...
975
	$context[$context['moderation_menu_name']]['tab_data'] = array(
976
		'title' => $txt['mc_watched_users_title'],
977
		'help' => '',
978
		'description' => $txt['mc_watched_users_desc'],
979
	);
980
981
	// First off - are we deleting?
982
	if (!empty($_REQUEST['delete']))
983
	{
984
		checkSession(!is_array($_REQUEST['delete']) ? 'get' : 'post');
985
986
		$toDelete = array();
987
		if (!is_array($_REQUEST['delete']))
988
			$toDelete[] = (int) $_REQUEST['delete'];
989
		else
990
			foreach ($_REQUEST['delete'] as $did)
991
				$toDelete[] = (int) $did;
992
993
		if (!empty($toDelete))
994
		{
995
			require_once($sourcedir . '/RemoveTopic.php');
996
			// If they don't have permission we'll let it error - either way no chance of a security slip here!
997
			foreach ($toDelete as $did)
998
				removeMessage($did);
999
		}
1000
	}
1001
1002
	// Start preparing the list by grabbing relevant permissions.
1003
	if (!$context['view_posts'])
1004
	{
1005
		$approve_query = '';
1006
		$delete_boards = array();
1007
	}
1008
	else
1009
	{
1010
		// Still obey permissions!
1011
		$approve_boards = boardsAllowedTo('approve_posts');
1012
		$delete_boards = boardsAllowedTo('delete_any');
1013
1014 View Code Duplication
		if ($approve_boards == array(0))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1015
			$approve_query = '';
1016
		elseif (!empty($approve_boards))
1017
			$approve_query = ' AND m.id_board IN (' . implode(',', $approve_boards) . ')';
1018
		// Nada, zip, etc...
1019
		else
1020
			$approve_query = ' AND 1=0';
1021
	}
1022
1023
	require_once($sourcedir . '/Subs-List.php');
1024
1025
	// This is all the information required for a watched user listing.
1026
	$listOptions = array(
1027
		'id' => 'watch_user_list',
1028
		'title' => $txt['mc_watched_users_title'] . ' - ' . ($context['view_posts'] ? $txt['mc_watched_users_post'] : $txt['mc_watched_users_member']),
1029
		'width' => '100%',
1030
		'items_per_page' => $modSettings['defaultMaxListItems'],
1031
		'no_items_label' => $context['view_posts'] ? $txt['mc_watched_users_no_posts'] : $txt['mc_watched_users_none'],
1032
		'base_href' => $scripturl . '?action=moderate;area=userwatch;sa=' . ($context['view_posts'] ? 'post' : 'member'),
1033
		'default_sort_col' => $context['view_posts'] ? '' : 'member',
1034
		'get_items' => array(
1035
			'function' => $context['view_posts'] ? 'list_getWatchedUserPosts' : 'list_getWatchedUsers',
1036
			'params' => array(
1037
				$approve_query,
1038
				$delete_boards,
1039
			),
1040
		),
1041
		'get_count' => array(
1042
			'function' => $context['view_posts'] ? 'list_getWatchedUserPostsCount' : 'list_getWatchedUserCount',
1043
			'params' => array(
1044
				$approve_query,
1045
			),
1046
		),
1047
		// This assumes we are viewing by user.
1048
		'columns' => array(
1049
			'member' => array(
1050
				'header' => array(
1051
					'value' => $txt['mc_watched_users_member'],
1052
				),
1053
				'data' => array(
1054
					'sprintf' => array(
1055
						'format' => '<a href="' . $scripturl . '?action=profile;u=%1$d">%2$s</a>',
1056
						'params' => array(
1057
							'id' => false,
1058
							'name' => false,
1059
						),
1060
					),
1061
				),
1062
				'sort' => array(
1063
					'default' => 'real_name',
1064
					'reverse' => 'real_name DESC',
1065
				),
1066
			),
1067
			'warning' => array(
1068
				'header' => array(
1069
					'value' => $txt['mc_watched_users_warning'],
1070
				),
1071
				'data' => array(
1072
					'function' => function($member) use ($scripturl)
1073
					{
1074
						return allowedTo('issue_warning') ? '<a href="' . $scripturl . '?action=profile;area=issuewarning;u=' . $member['id'] . '">' . $member['warning'] . '%</a>' : $member['warning'] . '%';
1075
					},
1076
				),
1077
				'sort' => array(
1078
					'default' => 'warning',
1079
					'reverse' => 'warning DESC',
1080
				),
1081
			),
1082
			'posts' => array(
1083
				'header' => array(
1084
					'value' => $txt['posts'],
1085
				),
1086
				'data' => array(
1087
					'sprintf' => array(
1088
						'format' => '<a href="' . $scripturl . '?action=profile;u=%1$d;area=showposts;sa=messages">%2$s</a>',
1089
						'params' => array(
1090
							'id' => false,
1091
							'posts' => false,
1092
						),
1093
					),
1094
				),
1095
				'sort' => array(
1096
					'default' => 'posts',
1097
					'reverse' => 'posts DESC',
1098
				),
1099
			),
1100
			'last_login' => array(
1101
				'header' => array(
1102
					'value' => $txt['mc_watched_users_last_login'],
1103
				),
1104
				'data' => array(
1105
					'db' => 'last_login',
1106
				),
1107
				'sort' => array(
1108
					'default' => 'last_login',
1109
					'reverse' => 'last_login DESC',
1110
				),
1111
			),
1112
			'last_post' => array(
1113
				'header' => array(
1114
					'value' => $txt['mc_watched_users_last_post'],
1115
				),
1116
				'data' => array(
1117
					'function' => function($member) use ($scripturl)
1118
					{
1119
						if ($member['last_post_id'])
1120
							return '<a href="' . $scripturl . '?msg=' . $member['last_post_id'] . '">' . $member['last_post'] . '</a>';
1121
						else
1122
							return $member['last_post'];
1123
					},
1124
				),
1125
			),
1126
		),
1127
		'form' => array(
1128
			'href' => $scripturl . '?action=moderate;area=userwatch;sa=post',
1129
			'include_sort' => true,
1130
			'include_start' => true,
1131
			'hidden_fields' => array(
1132
				$context['session_var'] => $context['session_id'],
1133
			),
1134
		),
1135
		'additional_rows' => array(
1136
			$context['view_posts'] ?
1137
			array(
1138
				'position' => 'bottom_of_list',
1139
				'value' => '
1140
					<input type="submit" name="delete_selected" value="' . $txt['quickmod_delete_selected'] . '" class="button_submit">',
1141
				'class' => 'floatright',
1142
			) : array(),
1143
		),
1144
	);
1145
1146
	// If this is being viewed by posts we actually change the columns to call a template each time.
1147
	if ($context['view_posts'])
1148
	{
1149
		$listOptions['columns'] = array(
1150
			'posts' => array(
1151
				'data' => array(
1152
					'function' => function($post)
1153
					{
1154
						return template_user_watch_post_callback($post);
1155
					},
1156
				),
1157
			),
1158
		);
1159
	}
1160
1161
	// Create the watched user list.
1162
	createList($listOptions);
1163
1164
	$context['sub_template'] = 'show_list';
1165
	$context['default_list'] = 'watch_user_list';
1166
}
1167
1168
/**
1169
 * Callback for createList().
1170
 * @param string $approve_query Not used here
1171
 * @return int The number of users on the watch list
1172
 */
1173 View Code Duplication
function list_getWatchedUserCount($approve_query)
0 ignored issues
show
Unused Code introduced by
The parameter $approve_query is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1174
{
1175
	global $smcFunc, $modSettings;
1176
1177
	$request = $smcFunc['db_query']('', '
1178
		SELECT COUNT(*)
1179
		FROM {db_prefix}members
1180
		WHERE warning >= {int:warning_watch}',
1181
		array(
1182
			'warning_watch' => $modSettings['warning_watch'],
1183
		)
1184
	);
1185
	list ($totalMembers) = $smcFunc['db_fetch_row']($request);
1186
	$smcFunc['db_free_result']($request);
1187
1188
	return $totalMembers;
1189
}
1190
1191
/**
1192
 * Callback for createList().
1193
 *
1194
 * @param int $start The item to start with (for pagination purposes)
1195
 * @param int $items_per_page The number of items to show per page
1196
 * @param string $sort A string indicating how to sort things
1197
 * @param string $approve_query A query for approving things. Not used here.
1198
 * @param string $dummy Not used here.
1199
 */
1200
function list_getWatchedUsers($start, $items_per_page, $sort, $approve_query, $dummy)
0 ignored issues
show
Unused Code introduced by
The parameter $approve_query is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $dummy is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1201
{
1202
	global $smcFunc, $txt, $modSettings, $user_info;
1203
1204
	$request = $smcFunc['db_query']('', '
1205
		SELECT id_member, real_name, last_login, posts, warning
1206
		FROM {db_prefix}members
1207
		WHERE warning >= {int:warning_watch}
1208
		ORDER BY {raw:sort}
1209
		LIMIT {int:start}, {int:max}',
1210
		array(
1211
			'warning_watch' => $modSettings['warning_watch'],
1212
			'sort' => $sort,
1213
			'start' => $start,
1214
			'max' => $items_per_page,
1215
		)
1216
	);
1217
	$watched_users = array();
1218
	$members = array();
1219
	while ($row = $smcFunc['db_fetch_assoc']($request))
1220
	{
1221
		$watched_users[$row['id_member']] = array(
1222
			'id' => $row['id_member'],
1223
			'name' => $row['real_name'],
1224
			'last_login' => $row['last_login'] ? timeformat($row['last_login']) : $txt['never'],
1225
			'last_post' => $txt['not_applicable'],
1226
			'last_post_id' => 0,
1227
			'warning' => $row['warning'],
1228
			'posts' => $row['posts'],
1229
		);
1230
		$members[] = $row['id_member'];
1231
	}
1232
	$smcFunc['db_free_result']($request);
1233
1234
	if (!empty($members))
1235
	{
1236
		// First get the latest messages from these users.
1237
		$request = $smcFunc['db_query']('', '
1238
			SELECT m.id_member, MAX(m.id_msg) AS last_post_id
1239
			FROM {db_prefix}messages AS m' . ($user_info['query_see_board'] == '1=1' ? '' : '
1240
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})') . '
1241
			WHERE m.id_member IN ({array_int:member_list})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
1242
				AND m.approved = {int:is_approved}') . '
1243
			GROUP BY m.id_member',
1244
			array(
1245
				'member_list' => $members,
1246
				'is_approved' => 1,
1247
			)
1248
		);
1249
		$latest_posts = array();
1250
		while ($row = $smcFunc['db_fetch_assoc']($request))
1251
			$latest_posts[$row['id_member']] = $row['last_post_id'];
1252
1253
		if (!empty($latest_posts))
1254
		{
1255
			// Now get the time those messages were posted.
1256
			$request = $smcFunc['db_query']('', '
1257
				SELECT id_member, poster_time
1258
				FROM {db_prefix}messages
1259
				WHERE id_msg IN ({array_int:message_list})',
1260
				array(
1261
					'message_list' => $latest_posts,
1262
				)
1263
			);
1264
			while ($row = $smcFunc['db_fetch_assoc']($request))
1265
			{
1266
				$watched_users[$row['id_member']]['last_post'] = timeformat($row['poster_time']);
1267
				$watched_users[$row['id_member']]['last_post_id'] = $latest_posts[$row['id_member']];
1268
			}
1269
1270
			$smcFunc['db_free_result']($request);
1271
		}
1272
1273
		$request = $smcFunc['db_query']('', '
1274
			SELECT MAX(m.poster_time) AS last_post, MAX(m.id_msg) AS last_post_id, m.id_member
1275
			FROM {db_prefix}messages AS m' . ($user_info['query_see_board'] == '1=1' ? '' : '
1276
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})') . '
1277
			WHERE m.id_member IN ({array_int:member_list})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
1278
				AND m.approved = {int:is_approved}') . '
1279
			GROUP BY m.id_member',
1280
			array(
1281
				'member_list' => $members,
1282
				'is_approved' => 1,
1283
			)
1284
		);
1285
		while ($row = $smcFunc['db_fetch_assoc']($request))
1286
		{
1287
			$watched_users[$row['id_member']]['last_post'] = timeformat($row['last_post']);
1288
			$watched_users[$row['id_member']]['last_post_id'] = $row['last_post_id'];
1289
		}
1290
		$smcFunc['db_free_result']($request);
1291
	}
1292
1293
	return $watched_users;
1294
}
1295
1296
/**
1297
 * Callback for createList().
1298
 *
1299
 * @param string $approve_query A query to pull only approved items
1300
 * @return int The total number of posts by watched users
1301
 */
1302 View Code Duplication
function list_getWatchedUserPostsCount($approve_query)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1303
{
1304
	global $smcFunc, $modSettings;
1305
1306
	$request = $smcFunc['db_query']('', '
1307
		SELECT COUNT(*)
1308
			FROM {db_prefix}messages AS m
1309
				INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1310
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1311
			WHERE mem.warning >= {int:warning_watch}
1312
				AND {query_see_board}
1313
				' . $approve_query,
1314
		array(
1315
			'warning_watch' => $modSettings['warning_watch'],
1316
		)
1317
	);
1318
	list ($totalMemberPosts) = $smcFunc['db_fetch_row']($request);
1319
	$smcFunc['db_free_result']($request);
1320
1321
	return $totalMemberPosts;
1322
}
1323
1324
/**
1325
 * Callback for createList().
1326
 *
1327
 * @param int $start The item to start with (for pagination purposes)
1328
 * @param int $items_per_page The number of items to show per page
1329
 * @param string $sort A string indicating how to sort the results (not used here)
1330
 * @param string $approve_query A query to only pull approved items
1331
 * @param int[] $delete_boards An array containing the IDs of boards we can delete posts in
1332
 * @return array An array of info about posts by watched users
1333
 */
1334
function list_getWatchedUserPosts($start, $items_per_page, $sort, $approve_query, $delete_boards)
0 ignored issues
show
Unused Code introduced by
The parameter $sort is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1335
{
1336
	global $smcFunc, $scripturl, $modSettings;
1337
1338
	$request = $smcFunc['db_query']('', '
1339
		SELECT m.id_msg, m.id_topic, m.id_board, m.id_member, m.subject, m.body, m.poster_time,
1340
			m.approved, mem.real_name, m.smileys_enabled
1341
		FROM {db_prefix}messages AS m
1342
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1343
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1344
		WHERE mem.warning >= {int:warning_watch}
1345
			AND {query_see_board}
1346
			' . $approve_query . '
1347
		ORDER BY m.id_msg DESC
1348
		LIMIT {int:start}, {int:max}',
1349
		array(
1350
			'warning_watch' => $modSettings['warning_watch'],
1351
			'start' => $start,
1352
			'max' => $items_per_page,
1353
		)
1354
	);
1355
	$member_posts = array();
1356
	while ($row = $smcFunc['db_fetch_assoc']($request))
1357
	{
1358
		$row['subject'] = censorText($row['subject']);
1359
		$row['body'] = censorText($row['body']);
1360
1361
		$member_posts[$row['id_msg']] = array(
1362
			'id' => $row['id_msg'],
1363
			'id_topic' => $row['id_topic'],
1364
			'author_link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>',
1365
			'subject' => $row['subject'],
1366
			'body' => parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']),
1367
			'poster_time' => timeformat($row['poster_time']),
1368
			'approved' => $row['approved'],
1369
			'can_delete' => $delete_boards == array(0) || in_array($row['id_board'], $delete_boards),
1370
		);
1371
	}
1372
	$smcFunc['db_free_result']($request);
1373
1374
	return $member_posts;
1375
}
1376
1377
/**
1378
 * Entry point for viewing warning related stuff.
1379
 */
1380
function ViewWarnings()
1381
{
1382
	global $context, $txt;
1383
1384
	$subActions = array(
1385
		'log' => array('ViewWarningLog'),
1386
		'templateedit' => array('ModifyWarningTemplate', 'issue_warning'),
1387
		'templates' => array('ViewWarningTemplates', 'issue_warning'),
1388
	);
1389
1390
	call_integration_hook('integrate_warning_log_actions', array(&$subActions));
1391
1392
	$_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) && (empty($subActions[$_REQUEST['sa']][1]) || allowedTo($subActions[$_REQUEST['sa']])) ? $_REQUEST['sa'] : 'log';
1393
1394
	// Some of this stuff is overseas, so to speak.
1395
	loadTemplate('ModerationCenter');
1396
	loadLanguage('Profile');
1397
1398
	// Setup the admin tabs.
1399
	$context[$context['moderation_menu_name']]['tab_data'] = array(
1400
		'title' => $txt['mc_warnings'],
1401
		'description' => $txt['mc_warnings_description'],
1402
	);
1403
1404
	// Call the right function.
1405
	call_helper($subActions[$_REQUEST['sa']][0]);
1406
}
1407
1408
/**
1409
 * Simply put, look at the warning log!
1410
 */
1411
function ViewWarningLog()
1412
{
1413
	global $smcFunc, $modSettings, $context, $txt, $scripturl, $sourcedir;
1414
1415
	// Setup context as always.
1416
	$context['page_title'] = $txt['mc_warning_log_title'];
1417
1418
	loadLanguage('Modlog');
1419
1420
	// If we're coming from a search, get the variables.
1421 View Code Duplication
	if (!empty($_REQUEST['params']) && empty($_REQUEST['is_search']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1422
	{
1423
		$search_params = base64_decode(strtr($_REQUEST['params'], array(' ' => '+')));
1424
		$search_params = $smcFunc['json_decode']($search_params, true);
1425
	}
1426
1427
	// This array houses all the valid search types.
1428
	$searchTypes = array(
1429
		'member' => array('sql' => 'member_name_col', 'label' => $txt['profile_warning_previous_issued']),
1430
		'recipient' => array('sql' => 'recipient_name', 'label' => $txt['mc_warnings_recipient']),
1431
	);
1432
1433
	// Do the column stuff!
1434
	$sort_types = array(
1435
		'member' => 'member_name_col',
1436
		'recipient' => 'recipient_name',
1437
	);
1438
1439
	// Setup the direction stuff...
1440
	$context['order'] = isset($_REQUEST['sort']) && isset($sort_types[$_REQUEST['sort']]) ? $_REQUEST['sort'] : 'member';
1441
1442
	if (!isset($search_params['string']) || (!empty($_REQUEST['search']) && $search_params['string'] != $_REQUEST['search']))
1443
		$search_params_string = empty($_REQUEST['search']) ? '' : $_REQUEST['search'];
1444
	else
1445
		$search_params_string = $search_params['string'];
1446
1447 View Code Duplication
	if (isset($_REQUEST['search_type']) || empty($search_params['type']) || !isset($searchTypes[$search_params['type']]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1448
		$search_params_type = isset($_REQUEST['search_type']) && isset($searchTypes[$_REQUEST['search_type']]) ? $_REQUEST['search_type'] : (isset($searchTypes[$context['order']]) ? $context['order'] : 'member');
1449
	else
1450
		$search_params_type = $search_params['type'];
1451
1452
	$search_params = array(
1453
		'string' => $search_params_string,
1454
		'type' => $search_params_type,
1455
	);
1456
1457
	$context['url_start'] = '?action=moderate;area=warnings;sa=log;sort=' . $context['order'];
1458
1459
	// Setup the search context.
1460
	$context['search_params'] = empty($search_params['string']) ? '' : base64_encode($smcFunc['json_encode']($search_params));
1461
	$context['search'] = array(
1462
		'string' => $search_params['string'],
1463
		'type' => $search_params['type'],
1464
		'label' => $searchTypes[$search_params_type]['label'],
1465
	);
1466
1467
	require_once($sourcedir . '/Subs-List.php');
1468
1469
	// This is all the information required for a watched user listing.
1470
	$listOptions = array(
1471
		'id' => 'warning_list',
1472
		'title' => $txt['mc_warning_log_title'],
1473
		'items_per_page' => $modSettings['defaultMaxListItems'],
1474
		'no_items_label' => $txt['mc_warnings_none'],
1475
		'base_href' => $scripturl . '?action=moderate;area=warnings;sa=log;' . $context['session_var'] . '=' . $context['session_id'],
1476
		'default_sort_col' => 'time',
1477
		'get_items' => array(
1478
			'function' => 'list_getWarnings',
1479
		),
1480
		'get_count' => array(
1481
			'function' => 'list_getWarningCount',
1482
		),
1483
		// This assumes we are viewing by user.
1484
		'columns' => array(
1485
			'issuer' => array(
1486
				'header' => array(
1487
					'value' => $txt['profile_warning_previous_issued'],
1488
				),
1489
				'data' => array(
1490
					'db' => 'issuer_link',
1491
				),
1492
				'sort' => array(
1493
					'default' => 'member_name_col',
1494
					'reverse' => 'member_name_col DESC',
1495
				),
1496
			),
1497
			'recipient' => array(
1498
				'header' => array(
1499
					'value' => $txt['mc_warnings_recipient'],
1500
				),
1501
				'data' => array(
1502
					'db' => 'recipient_link',
1503
				),
1504
				'sort' => array(
1505
					'default' => 'recipient_name',
1506
					'reverse' => 'recipient_name DESC',
1507
				),
1508
			),
1509
			'time' => array(
1510
				'header' => array(
1511
					'value' => $txt['profile_warning_previous_time'],
1512
				),
1513
				'data' => array(
1514
					'db' => 'time',
1515
				),
1516
				'sort' => array(
1517
					'default' => 'lc.log_time DESC',
1518
					'reverse' => 'lc.log_time',
1519
				),
1520
			),
1521
			'reason' => array(
1522
				'header' => array(
1523
					'value' => $txt['profile_warning_previous_reason'],
1524
				),
1525
				'data' => array(
1526 View Code Duplication
					'function' => function($rowData) use ($scripturl, $txt)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1527
					{
1528
						$output = '
1529
							<div class="floatleft">
1530
								' . $rowData['reason'] . '
1531
							</div>';
1532
1533
						if (!empty($rowData['id_notice']))
1534
							$output .= '
1535
								&nbsp;<a href="' . $scripturl . '?action=moderate;area=notice;nid=' . $rowData['id_notice'] . '" onclick="window.open(this.href, \'\', \'scrollbars=yes,resizable=yes,width=400,height=250\');return false;" target="_blank" class="new_win" title="' . $txt['profile_warning_previous_notice'] . '"><span class="generic_icons filter centericon"></span></a>';
1536
						return $output;
1537
					},
1538
				),
1539
			),
1540
			'points' => array(
1541
				'header' => array(
1542
					'value' => $txt['profile_warning_previous_level'],
1543
				),
1544
				'data' => array(
1545
					'db' => 'counter',
1546
				),
1547
			),
1548
		),
1549
		'form' => array(
1550
			'href' => $scripturl . $context['url_start'],
1551
			'include_sort' => true,
1552
			'include_start' => true,
1553
			'hidden_fields' => array(
1554
				$context['session_var'] => $context['session_id'],
1555
				'params' => false
1556
			),
1557
		),
1558
		'additional_rows' => array(
1559
			array(
1560
				'position' => 'below_table_data',
1561
				'value' => '
1562
					' . $txt['modlog_search'] . ':
1563
					<input type="text" name="search" size="18" value="' . $smcFunc['htmlspecialchars']($context['search']['string']) . '">
1564
					<input type="submit" name="is_search" value="' . $txt['modlog_go'] . '" class="button_submit">',
1565
				'class' => 'floatright',
1566
			),
1567
		),
1568
	);
1569
1570
	// Create the watched user list.
1571
	createList($listOptions);
1572
1573
	$context['sub_template'] = 'show_list';
1574
	$context['default_list'] = 'warning_list';
1575
}
1576
1577
/**
1578
 * Callback for createList().
1579
 * @return int The total number of warnings that have been issued
1580
 */
1581
function list_getWarningCount()
1582
{
1583
	global $smcFunc;
1584
1585
	$request = $smcFunc['db_query']('', '
1586
		SELECT COUNT(*)
1587
		FROM {db_prefix}log_comments
1588
		WHERE comment_type = {string:warning}',
1589
		array(
1590
			'warning' => 'warning',
1591
		)
1592
	);
1593
	list ($totalWarns) = $smcFunc['db_fetch_row']($request);
1594
	$smcFunc['db_free_result']($request);
1595
1596
	return $totalWarns;
1597
}
1598
1599
/**
1600
 * Callback for createList().
1601
 *
1602
 * @param int $start The item to start with (for pagination purposes)
1603
 * @param int $items_per_page The number of items to show per page
1604
 * @param string $sort A string indicating how to sort the results
1605
 * @return array An array of data about warning log entries
1606
 */
1607
function list_getWarnings($start, $items_per_page, $sort)
1608
{
1609
	global $smcFunc, $scripturl;
1610
1611
	$request = $smcFunc['db_query']('', '
1612
		SELECT COALESCE(mem.id_member, 0) AS id_member, COALESCE(mem.real_name, lc.member_name) AS member_name_col,
1613
			COALESCE(mem2.id_member, 0) AS id_recipient, COALESCE(mem2.real_name, lc.recipient_name) AS recipient_name,
1614
			lc.log_time, lc.body, lc.id_notice, lc.counter
1615
		FROM {db_prefix}log_comments AS lc
1616
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
1617
			LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = lc.id_recipient)
1618
		WHERE lc.comment_type = {string:warning}
1619
		ORDER BY {raw:sort}
1620
		LIMIT {int:start}, {int:max}',
1621
		array(
1622
			'warning' => 'warning',
1623
			'start' => $start,
1624
			'max' => $items_per_page,
1625
			'sort' => $sort,
1626
		)
1627
	);
1628
	$warnings = array();
1629
	while ($row = $smcFunc['db_fetch_assoc']($request))
1630
	{
1631
		$warnings[] = array(
1632
			'issuer_link' => $row['id_member'] ? ('<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['member_name_col'] . '</a>') : $row['member_name_col'],
1633
			'recipient_link' => $row['id_recipient'] ? ('<a href="' . $scripturl . '?action=profile;u=' . $row['id_recipient'] . '">' . $row['recipient_name'] . '</a>') : $row['recipient_name'],
1634
			'time' => timeformat($row['log_time']),
1635
			'reason' => $row['body'],
1636
			'counter' => $row['counter'] > 0 ? '+' . $row['counter'] : $row['counter'],
1637
			'id_notice' => $row['id_notice'],
1638
		);
1639
	}
1640
	$smcFunc['db_free_result']($request);
1641
1642
	return $warnings;
1643
}
1644
1645
/**
1646
 * Load all the warning templates.
1647
 */
1648
function ViewWarningTemplates()
1649
{
1650
	global $smcFunc, $modSettings, $context, $txt, $scripturl, $sourcedir, $user_info;
1651
1652
	// Submitting a new one?
1653
	if (isset($_POST['add']))
1654
		return ModifyWarningTemplate();
1655
	elseif (isset($_POST['delete']) && !empty($_POST['deltpl']))
1656
	{
1657
		checkSession();
1658
		validateToken('mod-wt');
1659
1660
		// Log the actions.
1661
		$request = $smcFunc['db_query']('', '
1662
			SELECT recipient_name
1663
			FROM {db_prefix}log_comments
1664
			WHERE id_comment IN ({array_int:delete_ids})
1665
				AND comment_type = {string:warntpl}
1666
				AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})',
1667
			array(
1668
				'delete_ids' => $_POST['deltpl'],
1669
				'warntpl' => 'warntpl',
1670
				'generic' => 0,
1671
				'current_member' => $user_info['id'],
1672
			)
1673
		);
1674
		while ($row = $smcFunc['db_fetch_assoc']($request))
1675
			logAction('delete_warn_template', array('template' => $row['recipient_name']));
1676
		$smcFunc['db_free_result']($request);
1677
1678
		// Do the deletes.
1679
		$smcFunc['db_query']('', '
1680
			DELETE FROM {db_prefix}log_comments
1681
			WHERE id_comment IN ({array_int:delete_ids})
1682
				AND comment_type = {string:warntpl}
1683
				AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})',
1684
			array(
1685
				'delete_ids' => $_POST['deltpl'],
1686
				'warntpl' => 'warntpl',
1687
				'generic' => 0,
1688
				'current_member' => $user_info['id'],
1689
			)
1690
		);
1691
	}
1692
1693
	// Setup context as always.
1694
	$context['page_title'] = $txt['mc_warning_templates_title'];
1695
1696
	require_once($sourcedir . '/Subs-List.php');
1697
1698
	// This is all the information required for a watched user listing.
1699
	$listOptions = array(
1700
		'id' => 'warning_template_list',
1701
		'title' => $txt['mc_warning_templates_title'],
1702
		'items_per_page' => $modSettings['defaultMaxListItems'],
1703
		'no_items_label' => $txt['mc_warning_templates_none'],
1704
		'base_href' => $scripturl . '?action=moderate;area=warnings;sa=templates;' . $context['session_var'] . '=' . $context['session_id'],
1705
		'default_sort_col' => 'title',
1706
		'get_items' => array(
1707
			'function' => 'list_getWarningTemplates',
1708
		),
1709
		'get_count' => array(
1710
			'function' => 'list_getWarningTemplateCount',
1711
		),
1712
		// This assumes we are viewing by user.
1713
		'columns' => array(
1714
			'title' => array(
1715
				'header' => array(
1716
					'value' => $txt['mc_warning_templates_name'],
1717
				),
1718
				'data' => array(
1719
					'sprintf' => array(
1720
						'format' => '<a href="' . $scripturl . '?action=moderate;area=warnings;sa=templateedit;tid=%1$d">%2$s</a>',
1721
						'params' => array(
1722
							'id_comment' => false,
1723
							'title' => false,
1724
							'body' => false,
1725
						),
1726
					),
1727
				),
1728
				'sort' => array(
1729
					'default' => 'template_title',
1730
					'reverse' => 'template_title DESC',
1731
				),
1732
			),
1733
			'creator' => array(
1734
				'header' => array(
1735
					'value' => $txt['mc_warning_templates_creator'],
1736
				),
1737
				'data' => array(
1738
					'db' => 'creator',
1739
				),
1740
				'sort' => array(
1741
					'default' => 'creator_name',
1742
					'reverse' => 'creator_name DESC',
1743
				),
1744
			),
1745
			'time' => array(
1746
				'header' => array(
1747
					'value' => $txt['mc_warning_templates_time'],
1748
				),
1749
				'data' => array(
1750
					'db' => 'time',
1751
				),
1752
				'sort' => array(
1753
					'default' => 'lc.log_time DESC',
1754
					'reverse' => 'lc.log_time',
1755
				),
1756
			),
1757
			'delete' => array(
1758
				'header' => array(
1759
					'value' => '<input type="checkbox" onclick="invertAll(this, this.form);">',
1760
					'style' => 'width: 4%;',
1761
					'class' => 'centercol',
1762
				),
1763
				'data' => array(
1764
					'function' => function($rowData)
1765
					{
1766
						return '<input type="checkbox" name="deltpl[]" value="' . $rowData['id_comment'] . '">';
1767
					},
1768
					'class' => 'centercol',
1769
				),
1770
			),
1771
		),
1772
		'form' => array(
1773
			'href' => $scripturl . '?action=moderate;area=warnings;sa=templates',
1774
			'token' => 'mod-wt',
1775
		),
1776
		'additional_rows' => array(
1777
			array(
1778
				'position' => 'bottom_of_list',
1779
				'value' => '&nbsp;<input type="submit" name="delete" value="' . $txt['mc_warning_template_delete'] . '" data-confirm="' . $txt['mc_warning_template_delete_confirm'] . '" class="button_submit you_sure">',
1780
			),
1781
			array(
1782
				'position' => 'bottom_of_list',
1783
				'value' => '<input type="submit" name="add" value="' . $txt['mc_warning_template_add'] . '" class="button_submit">',
1784
			),
1785
		),
1786
	);
1787
1788
	// Create the watched user list.
1789
	createToken('mod-wt');
1790
	createList($listOptions);
1791
1792
	$context['sub_template'] = 'show_list';
1793
	$context['default_list'] = 'warning_template_list';
1794
}
1795
1796
/**
1797
  * Callback for createList().
1798
  * @return int The total number of warning templates
1799
  */
1800 View Code Duplication
function list_getWarningTemplateCount()
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1801
{
1802
	global $smcFunc, $user_info;
1803
1804
	$request = $smcFunc['db_query']('', '
1805
		SELECT COUNT(*)
1806
		FROM {db_prefix}log_comments
1807
		WHERE comment_type = {string:warntpl}
1808
			AND (id_recipient = {string:generic} OR id_recipient = {int:current_member})',
1809
		array(
1810
			'warntpl' => 'warntpl',
1811
			'generic' => 0,
1812
			'current_member' => $user_info['id'],
1813
		)
1814
	);
1815
	list ($totalWarns) = $smcFunc['db_fetch_row']($request);
1816
	$smcFunc['db_free_result']($request);
1817
1818
	return $totalWarns;
1819
}
1820
1821
/**
1822
 * Callback for createList().
1823
 *
1824
 * @param int $start The item to start with (for pagination purposes)
1825
 * @param int $items_per_page The number of items to show per page
1826
 * @param string $sort A string indicating how to sort the results
1827
 * @return array An arrray of info about the available warning templates
1828
 */
1829
function list_getWarningTemplates($start, $items_per_page, $sort)
1830
{
1831
	global $smcFunc, $scripturl, $user_info;
1832
1833
	$request = $smcFunc['db_query']('', '
1834
		SELECT lc.id_comment, COALESCE(mem.id_member, 0) AS id_member,
1835
			COALESCE(mem.real_name, lc.member_name) AS creator_name, recipient_name AS template_title,
1836
			lc.log_time, lc.body
1837
		FROM {db_prefix}log_comments AS lc
1838
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
1839
		WHERE lc.comment_type = {string:warntpl}
1840
			AND (id_recipient = {string:generic} OR id_recipient = {int:current_member})
1841
		ORDER BY ' . $sort . '
1842
		LIMIT ' . $start . ', ' . $items_per_page,
1843
		array(
1844
			'warntpl' => 'warntpl',
1845
			'generic' => 0,
1846
			'current_member' => $user_info['id'],
1847
		)
1848
	);
1849
	$templates = array();
1850
	while ($row = $smcFunc['db_fetch_assoc']($request))
1851
	{
1852
		$templates[] = array(
1853
			'id_comment' => $row['id_comment'],
1854
			'creator' => $row['id_member'] ? ('<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['creator_name'] . '</a>') : $row['creator_name'],
1855
			'time' => timeformat($row['log_time']),
1856
			'title' => $row['template_title'],
1857
			'body' => $smcFunc['htmlspecialchars']($row['body']),
1858
		);
1859
	}
1860
	$smcFunc['db_free_result']($request);
1861
1862
	return $templates;
1863
}
1864
1865
/**
1866
 * Edit a warning template.
1867
 */
1868
function ModifyWarningTemplate()
1869
{
1870
	global $smcFunc, $context, $txt, $user_info, $sourcedir;
1871
1872
	$context['id_template'] = isset($_REQUEST['tid']) ? (int) $_REQUEST['tid'] : 0;
1873
	$context['is_edit'] = $context['id_template'];
1874
1875
	// Standard template things.
1876
	$context['page_title'] = $context['is_edit'] ? $txt['mc_warning_template_modify'] : $txt['mc_warning_template_add'];
1877
	$context['sub_template'] = 'warn_template';
1878
	$context[$context['moderation_menu_name']]['current_subsection'] = 'templates';
1879
1880
	// Defaults.
1881
	$context['template_data'] = array(
1882
		'title' => '',
1883
		'body' => $txt['mc_warning_template_body_default'],
1884
		'personal' => false,
1885
		'can_edit_personal' => true,
1886
	);
1887
1888
	// If it's an edit load it.
1889
	if ($context['is_edit'])
1890
	{
1891
		$request = $smcFunc['db_query']('', '
1892
			SELECT id_member, id_recipient, recipient_name AS template_title, body
1893
			FROM {db_prefix}log_comments
1894
			WHERE id_comment = {int:id}
1895
				AND comment_type = {string:warntpl}
1896
				AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})',
1897
			array(
1898
				'id' => $context['id_template'],
1899
				'warntpl' => 'warntpl',
1900
				'generic' => 0,
1901
				'current_member' => $user_info['id'],
1902
			)
1903
		);
1904
		while ($row = $smcFunc['db_fetch_assoc']($request))
1905
		{
1906
			$context['template_data'] = array(
1907
				'title' => $row['template_title'],
1908
				'body' => $smcFunc['htmlspecialchars']($row['body']),
1909
				'personal' => $row['id_recipient'],
1910
				'can_edit_personal' => $row['id_member'] == $user_info['id'],
1911
			);
1912
		}
1913
		$smcFunc['db_free_result']($request);
1914
	}
1915
1916
	// Wait, we are saving?
1917
	if (isset($_POST['save']))
1918
	{
1919
		checkSession();
1920
		validateToken('mod-wt');
1921
1922
		// To check the BBC is pretty good...
1923
		require_once($sourcedir . '/Subs-Post.php');
1924
1925
		// Bit of cleaning!
1926
		$_POST['template_body'] = trim($_POST['template_body']);
1927
		$_POST['template_title'] = trim($_POST['template_title']);
1928
1929
		// Need something in both boxes.
1930
		if (!empty($_POST['template_body']) && !empty($_POST['template_title']))
1931
		{
1932
			// Safety first.
1933
			$_POST['template_title'] = $smcFunc['htmlspecialchars']($_POST['template_title']);
1934
1935
			// Clean up BBC.
1936
			preparsecode($_POST['template_body']);
1937
			// But put line breaks back!
1938
			$_POST['template_body'] = strtr($_POST['template_body'], array('<br>' => "\n"));
1939
1940
			// Is this personal?
1941
			$recipient_id = !empty($_POST['make_personal']) ? $user_info['id'] : 0;
1942
1943
			// If we are this far it's save time.
1944
			if ($context['is_edit'])
1945
			{
1946
				// Simple update...
1947
				$smcFunc['db_query']('', '
1948
					UPDATE {db_prefix}log_comments
1949
					SET id_recipient = {int:personal}, recipient_name = {string:title}, body = {string:body}
1950
					WHERE id_comment = {int:id}
1951
						AND comment_type = {string:warntpl}
1952
						AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})'.
1953
						($recipient_id ? ' AND id_member = {int:current_member}' : ''),
1954
					array(
1955
						'personal' => $recipient_id,
1956
						'title' => $_POST['template_title'],
1957
						'body' => $_POST['template_body'],
1958
						'id' => $context['id_template'],
1959
						'warntpl' => 'warntpl',
1960
						'generic' => 0,
1961
						'current_member' => $user_info['id'],
1962
					)
1963
				);
1964
1965
				// If it wasn't visible and now is they've effectively added it.
1966
				if ($context['template_data']['personal'] && !$recipient_id)
1967
					logAction('add_warn_template', array('template' => $_POST['template_title']));
1968
				// Conversely if they made it personal it's a delete.
1969
				elseif (!$context['template_data']['personal'] && $recipient_id)
1970
					logAction('delete_warn_template', array('template' => $_POST['template_title']));
1971
				// Otherwise just an edit.
1972
				else
1973
					logAction('modify_warn_template', array('template' => $_POST['template_title']));
1974
			}
1975
			else
1976
			{
1977
				$smcFunc['db_insert']('',
1978
					'{db_prefix}log_comments',
1979
					array(
1980
						'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'id_recipient' => 'int',
1981
						'recipient_name' => 'string-255', 'body' => 'string-65535', 'log_time' => 'int',
1982
					),
1983
					array(
1984
						$user_info['id'], $user_info['name'], 'warntpl', $recipient_id,
1985
						$_POST['template_title'], $_POST['template_body'], time(),
1986
					),
1987
					array('id_comment')
1988
				);
1989
1990
				logAction('add_warn_template', array('template' => $_POST['template_title']));
1991
			}
1992
1993
			// Get out of town...
1994
			redirectexit('action=moderate;area=warnings;sa=templates');
1995
		}
1996
		else
1997
		{
1998
			$context['warning_errors'] = array();
1999
			$context['template_data']['title'] = !empty($_POST['template_title']) ? $_POST['template_title'] : '';
2000
			$context['template_data']['body'] = !empty($_POST['template_body']) ? $_POST['template_body'] : $txt['mc_warning_template_body_default'];
2001
			$context['template_data']['personal'] = !empty($_POST['make_personal']);
2002
			if (empty($_POST['template_title']))
2003
				$context['warning_errors'][] = $txt['mc_warning_template_error_no_title'];
2004
			if (empty($_POST['template_body']))
2005
				$context['warning_errors'][] = $txt['mc_warning_template_error_no_body'];
2006
		}
2007
	}
2008
2009
	createToken('mod-wt');
2010
}
2011
2012
/**
2013
 * Change moderation preferences.
2014
 */
2015
function ModerationSettings()
2016
{
2017
	global $context, $txt, $user_info;
2018
2019
	// Some useful context stuff.
2020
	loadTemplate('ModerationCenter');
2021
	$context['page_title'] = $txt['mc_settings'];
2022
	$context['sub_template'] = 'moderation_settings';
2023
	$context[$context['moderation_menu_name']]['tab_data'] = array(
2024
		'title' => $txt['mc_prefs_title'],
2025
		'help' => '',
2026
		'description' => $txt['mc_prefs_desc']
2027
	);
2028
2029
	$pref_binary = 5;
2030
2031
	// Are we saving?
2032
	if (isset($_POST['save']))
2033
	{
2034
		checkSession();
2035
		validateToken('mod-set');
2036
2037
		/* Current format of mod_prefs is:
2038
			x|ABCD|yyy
2039
2040
			WHERE:
2041
				x = Show report count on forum header.
2042
				ABCD = Block indexes to show on moderation main page.
2043
				yyy = Integer with the following bit status:
2044
					- yyy & 4 = Notify about posts awaiting approval.
2045
		*/
2046
2047
		// Now check other options!
2048
		$pref_binary = 0;
2049
2050
		if ($context['can_moderate_approvals'] && !empty($_POST['mod_notify_approval']))
2051
			$pref_binary |= 4;
2052
2053
		// Put it all together.
2054
		$mod_prefs = '0||' . $pref_binary;
2055
		updateMemberData($user_info['id'], array('mod_prefs' => $mod_prefs));
2056
	}
2057
2058
	// What blocks does the user currently have selected?
2059
	$context['mod_settings'] = array(
2060
		'notify_approval' => $pref_binary & 4,
2061
	);
2062
2063
	createToken('mod-set');
2064
}
2065
2066
/**
2067
 * This ends a moderator session, requiring authentication to access the MCP again.
2068
 */
2069
function ModEndSession()
2070
{
2071
	// This is so easy!
2072
	unset($_SESSION['moderate_time']);
2073
2074
	// Clean any moderator tokens as well.
2075 View Code Duplication
	foreach ($_SESSION['token'] as $key => $token)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2076
		if (strpos($key, '-mod') !== false)
2077
			unset($_SESSION['token'][$key]);
2078
2079
	redirectexit();
2080
}
2081
2082
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...