Completed
Push — release-2.1 ( 121660...f19596 )
by Mathias
09:09
created

Sources/ModerationCenter.php (20 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
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
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
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
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
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
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
	// Are they wanting to view a particular report?
694
	if (!empty($_REQUEST['report']))
695
		return MemberReport();
696
697
	// Set up the comforting bits...
698
	$context['page_title'] = $txt['mc_reported_members'];
699
	$context['sub_template'] = 'reported_members';
700
701
	// Are we viewing open or closed reports?
702
	$context['view_closed'] = isset($_GET['sa']) && $_GET['sa'] == 'closed' ? 1 : 0;
703
704
	// Are we doing any work?
705
	if ((isset($_GET['ignore']) || isset($_GET['close'])) && isset($_GET['rid']))
706
	{
707
		checkSession('get');
708
		$_GET['rid'] = (int) $_GET['rid'];
709
710
		// Update the report...
711
		$smcFunc['db_query']('', '
712
			UPDATE {db_prefix}log_reported
713
			SET ' . (isset($_GET['ignore']) ? 'ignore_all = {int:ignore_all}' : 'closed = {int:closed}') . '
714
			WHERE id_report = {int:id_report}',
715
			array(
716
				'ignore_all' => isset($_GET['ignore']) ? (int) $_GET['ignore'] : 0,
717
				'closed' => isset($_GET['close']) ? (int) $_GET['close'] : 0,
718
				'id_report' => $_GET['rid'],
719
			)
720
		);
721
722
		// Get the board, topic and message for this report
723
		$request = $smcFunc['db_query']('', '
724
			SELECT id_member, membername
725
			FROM {db_prefix}log_reported
726
			WHERE id_report = {int:id_report}',
727
			array(
728
				'id_report' => $_GET['rid'],
729
			)
730
		);
731
732
		// Set up the data for the log...
733
		$extra = array('report' => $_GET['rid']);
734
		list($extra['member'], $extra['membername']) = $smcFunc['db_fetch_row']($request);
735
		$smcFunc['db_free_result']($request);
736
737
		// Stick this in string format for consistency
738
		$extra['member'] = (string) $extra['member'];
739
740
		// Tell the user about it.
741
		$context['report_member_action'] = isset($_GET['ignore']) ? (!empty($_GET['ignore']) ? 'ignore' : 'unignore') : (!empty($_GET['close']) ? 'close' : 'open');
742
743
		// Log this action
744
		logAction($context['report_member_action'] . '_user_report', $extra);
745
746
		// Time to update.
747
		updateSettings(array('last_mod_report_action' => time()));
748
		recountOpenReports('members');
749
	}
750
	elseif (isset($_POST['close']) && isset($_POST['close_selected']))
751
	{
752
		checkSession();
753
754
		// All the ones to update...
755
		$toClose = array();
756
		foreach ($_POST['close'] as $rid)
757
			$toClose[] = (int) $rid;
758
759
		if (!empty($toClose))
760
		{
761
			// Get the data for each of these reports
762
			$request = $smcFunc['db_query']('', '
763
				SELECT id_report, id_member, membername
764
				FROM {db_prefix}log_reported
765
				WHERE id_report IN ({array_int:report_list})',
766
				array(
767
					'report_list' => $toClose,
768
				)
769
			);
770
771
			$logs = array();
772
			while ($reports = $smcFunc['db_fetch_assoc']($request))
773
			{
774
				$logs[] = array(
775
					'action' => 'close_user_report',
776
					'log_type' => 'moderate',
777
					'extra' => array(
778
						'report' => $reports['id_report'],
779
						'membername' => $reports['membername'],
780
						'member' => (string) $reports['id_member'],
781
					),
782
				);
783
			}
784
785
			$smcFunc['db_free_result']($request);
786
787
			// Log the closing of all the reports
788
			logActions($logs);
789
790
			$smcFunc['db_query']('', '
791
				UPDATE {db_prefix}log_reported
792
				SET closed = {int:is_closed}
793
				WHERE id_report IN ({array_int:report_list})',
794
				array(
795
					'report_list' => $toClose,
796
					'is_closed' => 1,
797
				)
798
			);
799
800
			// Time to update.
801
			updateSettings(array('last_mod_report_action' => time()));
802
			recountOpenReports('members');
803
		}
804
805
		// Go on and tell the result.
806
		$context['report_member_action'] = 'close_all';
807
	}
808
809
	// How many entries are we viewing?
810
	$request = $smcFunc['db_query']('', '
811
		SELECT COUNT(*)
812
		FROM {db_prefix}log_reported AS lr
813
		WHERE lr.closed = {int:view_closed}
814
			AND lr.id_board = {int:not_a_reported_post}',
815
		array(
816
			'view_closed' => $context['view_closed'],
817
			'not_a_reported_post' => 0,
818
		)
819
	);
820
	list ($context['total_reports']) = $smcFunc['db_fetch_row']($request);
821
	$smcFunc['db_free_result']($request);
822
823
	// So, that means we can page index, yes?
824
	$context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=reportedmembers' . ($context['view_closed'] ? ';sa=closed' : ''), $_GET['start'], $context['total_reports'], 10);
825
	$context['start'] = $_GET['start'];
826
827
	// By George, that means we in a position to get the reports, golly good.
828
	$request = $smcFunc['db_query']('', '
829
		SELECT lr.id_report, lr.id_member, lr.time_started, lr.time_updated, lr.num_reports, lr.closed, lr.ignore_all,
830
			COALESCE(mem.real_name, lr.membername) AS user_name, COALESCE(mem.id_member, 0) AS id_user
831
		FROM {db_prefix}log_reported AS lr
832
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
833
		WHERE lr.closed = {int:view_closed}
834
			AND lr.id_board = {int:not_a_reported_post}
835
		ORDER BY lr.time_updated DESC
836
		LIMIT {int:limit}, {int:max}',
837
		array(
838
			'view_closed' => $context['view_closed'],
839
			'not_a_reported_post' => 0,
840
			'limit' => $context['start'],
841
			'max' => 10,
842
		)
843
	);
844
	$context['reports'] = array();
845
	$report_ids = array();
846
	for ($i = 0; $row = $smcFunc['db_fetch_assoc']($request); $i++)
847
	{
848
		$report_ids[] = $row['id_report'];
849
		$context['reports'][$row['id_report']] = array(
850
			'id' => $row['id_report'],
851
			'report_href' => $scripturl . '?action=moderate;area=reportedmembers;report=' . $row['id_report'],
852
			'user' => array(
853
				'id' => $row['id_user'],
854
				'name' => $row['user_name'],
855
				'link' => $row['id_user'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_user'] . '">' . $row['user_name'] . '</a>' : $row['user_name'],
856
				'href' => $scripturl . '?action=profile;u=' . $row['id_user'],
857
			),
858
			'comments' => array(),
859
			'time_started' => timeformat($row['time_started']),
860
			'last_updated' => timeformat($row['time_updated']),
861
			'num_reports' => $row['num_reports'],
862
			'closed' => $row['closed'],
863
			'ignore' => $row['ignore_all']
864
		);
865
	}
866
	$smcFunc['db_free_result']($request);
867
868
	// Now get all the people who reported it.
869 View Code Duplication
	if (!empty($report_ids))
0 ignored issues
show
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...
870
	{
871
		$request = $smcFunc['db_query']('', '
872
			SELECT lrc.id_comment, lrc.id_report, lrc.time_sent, lrc.comment,
873
				COALESCE(mem.id_member, 0) AS id_member, COALESCE(mem.real_name, lrc.membername) AS reporter
874
			FROM {db_prefix}log_reported_comments AS lrc
875
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lrc.id_member)
876
			WHERE lrc.id_report IN ({array_int:report_list})',
877
			array(
878
				'report_list' => $report_ids,
879
			)
880
		);
881
		while ($row = $smcFunc['db_fetch_assoc']($request))
882
		{
883
			$context['reports'][$row['id_report']]['comments'][] = array(
884
				'id' => $row['id_comment'],
885
				'message' => $row['comment'],
886
				'time' => timeformat($row['time_sent']),
887
				'member' => array(
888
					'id' => $row['id_member'],
889
					'name' => empty($row['reporter']) ? $txt['guest'] : $row['reporter'],
890
					'link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['reporter'] . '</a>' : (empty($row['reporter']) ? $txt['guest'] : $row['reporter']),
891
					'href' => $row['id_member'] ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
892
				),
893
			);
894
		}
895
		$smcFunc['db_free_result']($request);
896
	}
897
898
	$context['report_manage_bans'] = allowedTo('manage_bans');
899
}
900
901
/**
902
 * Act as an entrace for all group related activity.
903
 * @todo As for most things in this file, this needs to be moved somewhere appropriate?
904
 */
905
function ModerateGroups()
906
{
907
	global $context, $user_info;
908
909
	// You need to be allowed to moderate groups...
910
	if ($user_info['mod_cache']['gq'] == '0=1')
911
		isAllowedTo('manage_membergroups');
912
913
	// Load the group templates.
914
	loadTemplate('ModerationCenter');
915
916
	// Setup the subactions...
917
	$subActions = array(
918
		'requests' => 'GroupRequests',
919
		'view' => 'ViewGroups',
920
	);
921
922
	if (!isset($_GET['sa']) || !isset($subActions[$_GET['sa']]))
923
		$_GET['sa'] = 'view';
924
	$context['sub_action'] = $_GET['sa'];
925
926
	// Call the relevant function.
927
	call_helper($subActions[$context['sub_action']]);
928
}
929
930
/**
931
 * Show a notice sent to a user.
932
 */
933
function ShowNotice()
934
{
935
	global $smcFunc, $txt, $context;
936
937
	$context['page_title'] = $txt['show_notice'];
938
	$context['sub_template'] = 'show_notice';
939
	$context['template_layers'] = array();
940
941
	loadTemplate('ModerationCenter');
942
943
	// @todo Assumes nothing needs permission more than accessing moderation center!
944
	$id_notice = (int) $_GET['nid'];
945
	$request = $smcFunc['db_query']('', '
946
		SELECT body, subject
947
		FROM {db_prefix}log_member_notices
948
		WHERE id_notice = {int:id_notice}',
949
		array(
950
			'id_notice' => $id_notice,
951
		)
952
	);
953
	if ($smcFunc['db_num_rows']($request) == 0)
954
		fatal_lang_error('no_access', false);
955
	list ($context['notice_body'], $context['notice_subject']) = $smcFunc['db_fetch_row']($request);
956
	$smcFunc['db_free_result']($request);
957
958
	$context['notice_body'] = parse_bbc($context['notice_body'], false);
959
}
960
961
/**
962
 * View watched users.
963
 */
964
function ViewWatchedUsers()
965
{
966
	global $modSettings, $context, $txt, $scripturl, $sourcedir;
967
968
	// Some important context!
969
	$context['page_title'] = $txt['mc_watched_users_title'];
970
	$context['view_posts'] = isset($_GET['sa']) && $_GET['sa'] == 'post';
971
	$context['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0;
972
973
	loadTemplate('ModerationCenter');
974
975
	// Get some key settings!
976
	$modSettings['warning_watch'] = empty($modSettings['warning_watch']) ? 1 : $modSettings['warning_watch'];
977
978
	// Put some pretty tabs on cause we're gonna be doing hot stuff here...
979
	$context[$context['moderation_menu_name']]['tab_data'] = array(
980
		'title' => $txt['mc_watched_users_title'],
981
		'help' => '',
982
		'description' => $txt['mc_watched_users_desc'],
983
	);
984
985
	// First off - are we deleting?
986
	if (!empty($_REQUEST['delete']))
987
	{
988
		checkSession(!is_array($_REQUEST['delete']) ? 'get' : 'post');
989
990
		$toDelete = array();
991
		if (!is_array($_REQUEST['delete']))
992
			$toDelete[] = (int) $_REQUEST['delete'];
993
		else
994
			foreach ($_REQUEST['delete'] as $did)
995
				$toDelete[] = (int) $did;
996
997
		if (!empty($toDelete))
998
		{
999
			require_once($sourcedir . '/RemoveTopic.php');
1000
			// If they don't have permission we'll let it error - either way no chance of a security slip here!
1001
			foreach ($toDelete as $did)
1002
				removeMessage($did);
1003
		}
1004
	}
1005
1006
	// Start preparing the list by grabbing relevant permissions.
1007
	if (!$context['view_posts'])
1008
	{
1009
		$approve_query = '';
1010
		$delete_boards = array();
1011
	}
1012
	else
1013
	{
1014
		// Still obey permissions!
1015
		$approve_boards = boardsAllowedTo('approve_posts');
1016
		$delete_boards = boardsAllowedTo('delete_any');
1017
1018 View Code Duplication
		if ($approve_boards == array(0))
0 ignored issues
show
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...
1019
			$approve_query = '';
1020
		elseif (!empty($approve_boards))
1021
			$approve_query = ' AND m.id_board IN (' . implode(',', $approve_boards) . ')';
1022
		// Nada, zip, etc...
1023
		else
1024
			$approve_query = ' AND 1=0';
1025
	}
1026
1027
	require_once($sourcedir . '/Subs-List.php');
1028
1029
	// This is all the information required for a watched user listing.
1030
	$listOptions = array(
1031
		'id' => 'watch_user_list',
1032
		'title' => $txt['mc_watched_users_title'] . ' - ' . ($context['view_posts'] ? $txt['mc_watched_users_post'] : $txt['mc_watched_users_member']),
1033
		'width' => '100%',
1034
		'items_per_page' => $modSettings['defaultMaxListItems'],
1035
		'no_items_label' => $context['view_posts'] ? $txt['mc_watched_users_no_posts'] : $txt['mc_watched_users_none'],
1036
		'base_href' => $scripturl . '?action=moderate;area=userwatch;sa=' . ($context['view_posts'] ? 'post' : 'member'),
1037
		'default_sort_col' => $context['view_posts'] ? '' : 'member',
1038
		'get_items' => array(
1039
			'function' => $context['view_posts'] ? 'list_getWatchedUserPosts' : 'list_getWatchedUsers',
1040
			'params' => array(
1041
				$approve_query,
1042
				$delete_boards,
1043
			),
1044
		),
1045
		'get_count' => array(
1046
			'function' => $context['view_posts'] ? 'list_getWatchedUserPostsCount' : 'list_getWatchedUserCount',
1047
			'params' => array(
1048
				$approve_query,
1049
			),
1050
		),
1051
		// This assumes we are viewing by user.
1052
		'columns' => array(
1053
			'member' => array(
1054
				'header' => array(
1055
					'value' => $txt['mc_watched_users_member'],
1056
				),
1057
				'data' => array(
1058
					'sprintf' => array(
1059
						'format' => '<a href="' . $scripturl . '?action=profile;u=%1$d">%2$s</a>',
1060
						'params' => array(
1061
							'id' => false,
1062
							'name' => false,
1063
						),
1064
					),
1065
				),
1066
				'sort' => array(
1067
					'default' => 'real_name',
1068
					'reverse' => 'real_name DESC',
1069
				),
1070
			),
1071
			'warning' => array(
1072
				'header' => array(
1073
					'value' => $txt['mc_watched_users_warning'],
1074
				),
1075
				'data' => array(
1076
					'function' => function($member) use ($scripturl)
1077
					{
1078
						return allowedTo('issue_warning') ? '<a href="' . $scripturl . '?action=profile;area=issuewarning;u=' . $member['id'] . '">' . $member['warning'] . '%</a>' : $member['warning'] . '%';
1079
					},
1080
				),
1081
				'sort' => array(
1082
					'default' => 'warning',
1083
					'reverse' => 'warning DESC',
1084
				),
1085
			),
1086
			'posts' => array(
1087
				'header' => array(
1088
					'value' => $txt['posts'],
1089
				),
1090
				'data' => array(
1091
					'sprintf' => array(
1092
						'format' => '<a href="' . $scripturl . '?action=profile;u=%1$d;area=showposts;sa=messages">%2$s</a>',
1093
						'params' => array(
1094
							'id' => false,
1095
							'posts' => false,
1096
						),
1097
					),
1098
				),
1099
				'sort' => array(
1100
					'default' => 'posts',
1101
					'reverse' => 'posts DESC',
1102
				),
1103
			),
1104
			'last_login' => array(
1105
				'header' => array(
1106
					'value' => $txt['mc_watched_users_last_login'],
1107
				),
1108
				'data' => array(
1109
					'db' => 'last_login',
1110
				),
1111
				'sort' => array(
1112
					'default' => 'last_login',
1113
					'reverse' => 'last_login DESC',
1114
				),
1115
			),
1116
			'last_post' => array(
1117
				'header' => array(
1118
					'value' => $txt['mc_watched_users_last_post'],
1119
				),
1120
				'data' => array(
1121
					'function' => function($member) use ($scripturl)
1122
					{
1123
						if ($member['last_post_id'])
1124
							return '<a href="' . $scripturl . '?msg=' . $member['last_post_id'] . '">' . $member['last_post'] . '</a>';
1125
						else
1126
							return $member['last_post'];
1127
					},
1128
				),
1129
			),
1130
		),
1131
		'form' => array(
1132
			'href' => $scripturl . '?action=moderate;area=userwatch;sa=post',
1133
			'include_sort' => true,
1134
			'include_start' => true,
1135
			'hidden_fields' => array(
1136
				$context['session_var'] => $context['session_id'],
1137
			),
1138
		),
1139
		'additional_rows' => array(
1140
			$context['view_posts'] ?
1141
			array(
1142
				'position' => 'bottom_of_list',
1143
				'value' => '
1144
					<input type="submit" name="delete_selected" value="' . $txt['quickmod_delete_selected'] . '" class="button_submit">',
1145
				'class' => 'floatright',
1146
			) : array(),
1147
		),
1148
	);
1149
1150
	// If this is being viewed by posts we actually change the columns to call a template each time.
1151
	if ($context['view_posts'])
1152
	{
1153
		$listOptions['columns'] = array(
1154
			'posts' => array(
1155
				'data' => array(
1156
					'function' => function($post)
1157
					{
1158
						return template_user_watch_post_callback($post);
1159
					},
1160
				),
1161
			),
1162
		);
1163
	}
1164
1165
	// Create the watched user list.
1166
	createList($listOptions);
1167
1168
	$context['sub_template'] = 'show_list';
1169
	$context['default_list'] = 'watch_user_list';
1170
}
1171
1172
/**
1173
 * Callback for createList().
1174
 * @param string $approve_query Not used here
1175
 * @return int The number of users on the watch list
1176
 */
1177 View Code Duplication
function list_getWatchedUserCount($approve_query)
0 ignored issues
show
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...
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...
1178
{
1179
	global $smcFunc, $modSettings;
1180
1181
	$request = $smcFunc['db_query']('', '
1182
		SELECT COUNT(*)
1183
		FROM {db_prefix}members
1184
		WHERE warning >= {int:warning_watch}',
1185
		array(
1186
			'warning_watch' => $modSettings['warning_watch'],
1187
		)
1188
	);
1189
	list ($totalMembers) = $smcFunc['db_fetch_row']($request);
1190
	$smcFunc['db_free_result']($request);
1191
1192
	return $totalMembers;
1193
}
1194
1195
/**
1196
 * Callback for createList().
1197
 *
1198
 * @param int $start The item to start with (for pagination purposes)
1199
 * @param int $items_per_page The number of items to show per page
1200
 * @param string $sort A string indicating how to sort things
1201
 * @param string $approve_query A query for approving things. Not used here.
1202
 * @param string $dummy Not used here.
1203
 */
1204
function list_getWatchedUsers($start, $items_per_page, $sort, $approve_query, $dummy)
0 ignored issues
show
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...
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...
1205
{
1206
	global $smcFunc, $txt, $modSettings, $user_info;
1207
1208
	$request = $smcFunc['db_query']('', '
1209
		SELECT id_member, real_name, last_login, posts, warning
1210
		FROM {db_prefix}members
1211
		WHERE warning >= {int:warning_watch}
1212
		ORDER BY {raw:sort}
1213
		LIMIT {int:start}, {int:max}',
1214
		array(
1215
			'warning_watch' => $modSettings['warning_watch'],
1216
			'sort' => $sort,
1217
			'start' => $start,
1218
			'max' => $items_per_page,
1219
		)
1220
	);
1221
	$watched_users = array();
1222
	$members = array();
1223
	while ($row = $smcFunc['db_fetch_assoc']($request))
1224
	{
1225
		$watched_users[$row['id_member']] = array(
1226
			'id' => $row['id_member'],
1227
			'name' => $row['real_name'],
1228
			'last_login' => $row['last_login'] ? timeformat($row['last_login']) : $txt['never'],
1229
			'last_post' => $txt['not_applicable'],
1230
			'last_post_id' => 0,
1231
			'warning' => $row['warning'],
1232
			'posts' => $row['posts'],
1233
		);
1234
		$members[] = $row['id_member'];
1235
	}
1236
	$smcFunc['db_free_result']($request);
1237
1238
	if (!empty($members))
1239
	{
1240
		// First get the latest messages from these users.
1241
		$request = $smcFunc['db_query']('', '
1242
			SELECT m.id_member, MAX(m.id_msg) AS last_post_id
1243
			FROM {db_prefix}messages AS m' . ($user_info['query_see_board'] == '1=1' ? '' : '
1244
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})') . '
1245
			WHERE m.id_member IN ({array_int:member_list})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
1246
				AND m.approved = {int:is_approved}') . '
1247
			GROUP BY m.id_member',
1248
			array(
1249
				'member_list' => $members,
1250
				'is_approved' => 1,
1251
			)
1252
		);
1253
		$latest_posts = array();
1254
		while ($row = $smcFunc['db_fetch_assoc']($request))
1255
			$latest_posts[$row['id_member']] = $row['last_post_id'];
1256
1257
		if (!empty($latest_posts))
1258
		{
1259
			// Now get the time those messages were posted.
1260
			$request = $smcFunc['db_query']('', '
1261
				SELECT id_member, poster_time
1262
				FROM {db_prefix}messages
1263
				WHERE id_msg IN ({array_int:message_list})',
1264
				array(
1265
					'message_list' => $latest_posts,
1266
				)
1267
			);
1268
			while ($row = $smcFunc['db_fetch_assoc']($request))
1269
			{
1270
				$watched_users[$row['id_member']]['last_post'] = timeformat($row['poster_time']);
1271
				$watched_users[$row['id_member']]['last_post_id'] = $latest_posts[$row['id_member']];
1272
			}
1273
1274
			$smcFunc['db_free_result']($request);
1275
		}
1276
1277
		$request = $smcFunc['db_query']('', '
1278
			SELECT MAX(m.poster_time) AS last_post, MAX(m.id_msg) AS last_post_id, m.id_member
1279
			FROM {db_prefix}messages AS m' . ($user_info['query_see_board'] == '1=1' ? '' : '
1280
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})') . '
1281
			WHERE m.id_member IN ({array_int:member_list})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
1282
				AND m.approved = {int:is_approved}') . '
1283
			GROUP BY m.id_member',
1284
			array(
1285
				'member_list' => $members,
1286
				'is_approved' => 1,
1287
			)
1288
		);
1289
		while ($row = $smcFunc['db_fetch_assoc']($request))
1290
		{
1291
			$watched_users[$row['id_member']]['last_post'] = timeformat($row['last_post']);
1292
			$watched_users[$row['id_member']]['last_post_id'] = $row['last_post_id'];
1293
		}
1294
		$smcFunc['db_free_result']($request);
1295
	}
1296
1297
	return $watched_users;
1298
}
1299
1300
/**
1301
 * Callback for createList().
1302
 *
1303
 * @param string $approve_query A query to pull only approved items
1304
 * @return int The total number of posts by watched users
1305
 */
1306 View Code Duplication
function list_getWatchedUserPostsCount($approve_query)
0 ignored issues
show
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...
1307
{
1308
	global $smcFunc, $modSettings;
1309
1310
	$request = $smcFunc['db_query']('', '
1311
		SELECT COUNT(*)
1312
			FROM {db_prefix}messages AS m
1313
				INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1314
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1315
			WHERE mem.warning >= {int:warning_watch}
1316
				AND {query_see_board}
1317
				' . $approve_query,
1318
		array(
1319
			'warning_watch' => $modSettings['warning_watch'],
1320
		)
1321
	);
1322
	list ($totalMemberPosts) = $smcFunc['db_fetch_row']($request);
1323
	$smcFunc['db_free_result']($request);
1324
1325
	return $totalMemberPosts;
1326
}
1327
1328
/**
1329
 * Callback for createList().
1330
 *
1331
 * @param int $start The item to start with (for pagination purposes)
1332
 * @param int $items_per_page The number of items to show per page
1333
 * @param string $sort A string indicating how to sort the results (not used here)
1334
 * @param string $approve_query A query to only pull approved items
1335
 * @param int[] $delete_boards An array containing the IDs of boards we can delete posts in
1336
 * @return array An array of info about posts by watched users
1337
 */
1338
function list_getWatchedUserPosts($start, $items_per_page, $sort, $approve_query, $delete_boards)
0 ignored issues
show
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...
1339
{
1340
	global $smcFunc, $scripturl, $modSettings;
1341
1342
	$request = $smcFunc['db_query']('', '
1343
		SELECT m.id_msg, m.id_topic, m.id_board, m.id_member, m.subject, m.body, m.poster_time,
1344
			m.approved, mem.real_name, m.smileys_enabled
1345
		FROM {db_prefix}messages AS m
1346
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1347
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1348
		WHERE mem.warning >= {int:warning_watch}
1349
			AND {query_see_board}
1350
			' . $approve_query . '
1351
		ORDER BY m.id_msg DESC
1352
		LIMIT {int:start}, {int:max}',
1353
		array(
1354
			'warning_watch' => $modSettings['warning_watch'],
1355
			'start' => $start,
1356
			'max' => $items_per_page,
1357
		)
1358
	);
1359
	$member_posts = array();
1360
	while ($row = $smcFunc['db_fetch_assoc']($request))
1361
	{
1362
		$row['subject'] = censorText($row['subject']);
1363
		$row['body'] = censorText($row['body']);
1364
1365
		$member_posts[$row['id_msg']] = array(
1366
			'id' => $row['id_msg'],
1367
			'id_topic' => $row['id_topic'],
1368
			'author_link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>',
1369
			'subject' => $row['subject'],
1370
			'body' => parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']),
1371
			'poster_time' => timeformat($row['poster_time']),
1372
			'approved' => $row['approved'],
1373
			'can_delete' => $delete_boards == array(0) || in_array($row['id_board'], $delete_boards),
1374
		);
1375
	}
1376
	$smcFunc['db_free_result']($request);
1377
1378
	return $member_posts;
1379
}
1380
1381
/**
1382
 * Entry point for viewing warning related stuff.
1383
 */
1384
function ViewWarnings()
1385
{
1386
	global $context, $txt;
1387
1388
	$subActions = array(
1389
		'log' => array('ViewWarningLog'),
1390
		'templateedit' => array('ModifyWarningTemplate', 'issue_warning'),
1391
		'templates' => array('ViewWarningTemplates', 'issue_warning'),
1392
	);
1393
1394
	call_integration_hook('integrate_warning_log_actions', array(&$subActions));
1395
1396
	$_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) && (empty($subActions[$_REQUEST['sa']][1]) || allowedTo($subActions[$_REQUEST['sa']])) ? $_REQUEST['sa'] : 'log';
1397
1398
	// Some of this stuff is overseas, so to speak.
1399
	loadTemplate('ModerationCenter');
1400
	loadLanguage('Profile');
1401
1402
	// Setup the admin tabs.
1403
	$context[$context['moderation_menu_name']]['tab_data'] = array(
1404
		'title' => $txt['mc_warnings'],
1405
		'description' => $txt['mc_warnings_description'],
1406
	);
1407
1408
	// Call the right function.
1409
	call_helper($subActions[$_REQUEST['sa']][0]);
1410
}
1411
1412
/**
1413
 * Simply put, look at the warning log!
1414
 */
1415
function ViewWarningLog()
1416
{
1417
	global $smcFunc, $modSettings, $context, $txt, $scripturl, $sourcedir;
1418
1419
	// Setup context as always.
1420
	$context['page_title'] = $txt['mc_warning_log_title'];
1421
1422
	loadLanguage('Modlog');
1423
1424
	// If we're coming from a search, get the variables.
1425 View Code Duplication
	if (!empty($_REQUEST['params']) && empty($_REQUEST['is_search']))
0 ignored issues
show
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...
1426
	{
1427
		$search_params = base64_decode(strtr($_REQUEST['params'], array(' ' => '+')));
1428
		$search_params = $smcFunc['json_decode']($search_params, true);
1429
	}
1430
1431
	// This array houses all the valid search types.
1432
	$searchTypes = array(
1433
		'member' => array('sql' => 'member_name_col', 'label' => $txt['profile_warning_previous_issued']),
1434
		'recipient' => array('sql' => 'recipient_name', 'label' => $txt['mc_warnings_recipient']),
1435
	);
1436
1437
	// Do the column stuff!
1438
	$sort_types = array(
1439
		'member' => 'member_name_col',
1440
		'recipient' => 'recipient_name',
1441
	);
1442
1443
	// Setup the direction stuff...
1444
	$context['order'] = isset($_REQUEST['sort']) && isset($sort_types[$_REQUEST['sort']]) ? $_REQUEST['sort'] : 'member';
1445
1446
	if (!isset($search_params['string']) || (!empty($_REQUEST['search']) && $search_params['string'] != $_REQUEST['search']))
1447
		$search_params_string = empty($_REQUEST['search']) ? '' : $_REQUEST['search'];
1448
	else
1449
		$search_params_string = $search_params['string'];
1450
1451 View Code Duplication
	if (isset($_REQUEST['search_type']) || empty($search_params['type']) || !isset($searchTypes[$search_params['type']]))
0 ignored issues
show
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...
1452
		$search_params_type = isset($_REQUEST['search_type']) && isset($searchTypes[$_REQUEST['search_type']]) ? $_REQUEST['search_type'] : (isset($searchTypes[$context['order']]) ? $context['order'] : 'member');
1453
	else
1454
		$search_params_type = $search_params['type'];
1455
1456
	$search_params = array(
1457
		'string' => $search_params_string,
1458
		'type' => $search_params_type,
1459
	);
1460
1461
	$context['url_start'] = '?action=moderate;area=warnings;sa=log;sort=' . $context['order'];
1462
1463
	// Setup the search context.
1464
	$context['search_params'] = empty($search_params['string']) ? '' : base64_encode($smcFunc['json_encode']($search_params));
1465
	$context['search'] = array(
1466
		'string' => $search_params['string'],
1467
		'type' => $search_params['type'],
1468
		'label' => $searchTypes[$search_params_type]['label'],
1469
	);
1470
1471
	require_once($sourcedir . '/Subs-List.php');
1472
1473
	// This is all the information required for a watched user listing.
1474
	$listOptions = array(
1475
		'id' => 'warning_list',
1476
		'title' => $txt['mc_warning_log_title'],
1477
		'items_per_page' => $modSettings['defaultMaxListItems'],
1478
		'no_items_label' => $txt['mc_warnings_none'],
1479
		'base_href' => $scripturl . '?action=moderate;area=warnings;sa=log;' . $context['session_var'] . '=' . $context['session_id'],
1480
		'default_sort_col' => 'time',
1481
		'get_items' => array(
1482
			'function' => 'list_getWarnings',
1483
		),
1484
		'get_count' => array(
1485
			'function' => 'list_getWarningCount',
1486
		),
1487
		// This assumes we are viewing by user.
1488
		'columns' => array(
1489
			'issuer' => array(
1490
				'header' => array(
1491
					'value' => $txt['profile_warning_previous_issued'],
1492
				),
1493
				'data' => array(
1494
					'db' => 'issuer_link',
1495
				),
1496
				'sort' => array(
1497
					'default' => 'member_name_col',
1498
					'reverse' => 'member_name_col DESC',
1499
				),
1500
			),
1501
			'recipient' => array(
1502
				'header' => array(
1503
					'value' => $txt['mc_warnings_recipient'],
1504
				),
1505
				'data' => array(
1506
					'db' => 'recipient_link',
1507
				),
1508
				'sort' => array(
1509
					'default' => 'recipient_name',
1510
					'reverse' => 'recipient_name DESC',
1511
				),
1512
			),
1513
			'time' => array(
1514
				'header' => array(
1515
					'value' => $txt['profile_warning_previous_time'],
1516
				),
1517
				'data' => array(
1518
					'db' => 'time',
1519
				),
1520
				'sort' => array(
1521
					'default' => 'lc.log_time DESC',
1522
					'reverse' => 'lc.log_time',
1523
				),
1524
			),
1525
			'reason' => array(
1526
				'header' => array(
1527
					'value' => $txt['profile_warning_previous_reason'],
1528
				),
1529
				'data' => array(
1530 View Code Duplication
					'function' => function($rowData) use ($scripturl, $txt)
0 ignored issues
show
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...
1531
					{
1532
						$output = '
1533
							<div class="floatleft">
1534
								' . $rowData['reason'] . '
1535
							</div>';
1536
1537
						if (!empty($rowData['id_notice']))
1538
							$output .= '
1539
								&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>';
1540
						return $output;
1541
					},
1542
				),
1543
			),
1544
			'points' => array(
1545
				'header' => array(
1546
					'value' => $txt['profile_warning_previous_level'],
1547
				),
1548
				'data' => array(
1549
					'db' => 'counter',
1550
				),
1551
			),
1552
		),
1553
		'form' => array(
1554
			'href' => $scripturl . $context['url_start'],
1555
			'include_sort' => true,
1556
			'include_start' => true,
1557
			'hidden_fields' => array(
1558
				$context['session_var'] => $context['session_id'],
1559
				'params' => false
1560
			),
1561
		),
1562
		'additional_rows' => array(
1563
			array(
1564
				'position' => 'below_table_data',
1565
				'value' => '
1566
					' . $txt['modlog_search'] . ':
1567
					<input type="text" name="search" size="18" value="' . $smcFunc['htmlspecialchars']($context['search']['string']) . '" class="input_text">
1568
					<input type="submit" name="is_search" value="' . $txt['modlog_go'] . '" class="button_submit">',
1569
				'class' => 'floatright',
1570
			),
1571
		),
1572
	);
1573
1574
	// Create the watched user list.
1575
	createList($listOptions);
1576
1577
	$context['sub_template'] = 'show_list';
1578
	$context['default_list'] = 'warning_list';
1579
}
1580
1581
/**
1582
 * Callback for createList().
1583
 * @return int The total number of warnings that have been issued
1584
 */
1585
function list_getWarningCount()
1586
{
1587
	global $smcFunc;
1588
1589
	$request = $smcFunc['db_query']('', '
1590
		SELECT COUNT(*)
1591
		FROM {db_prefix}log_comments
1592
		WHERE comment_type = {string:warning}',
1593
		array(
1594
			'warning' => 'warning',
1595
		)
1596
	);
1597
	list ($totalWarns) = $smcFunc['db_fetch_row']($request);
1598
	$smcFunc['db_free_result']($request);
1599
1600
	return $totalWarns;
1601
}
1602
1603
/**
1604
 * Callback for createList().
1605
 *
1606
 * @param int $start The item to start with (for pagination purposes)
1607
 * @param int $items_per_page The number of items to show per page
1608
 * @param string $sort A string indicating how to sort the results
1609
 * @return array An array of data about warning log entries
1610
 */
1611
function list_getWarnings($start, $items_per_page, $sort)
1612
{
1613
	global $smcFunc, $scripturl;
1614
1615
	$request = $smcFunc['db_query']('', '
1616
		SELECT COALESCE(mem.id_member, 0) AS id_member, COALESCE(mem.real_name, lc.member_name) AS member_name_col,
1617
			COALESCE(mem2.id_member, 0) AS id_recipient, COALESCE(mem2.real_name, lc.recipient_name) AS recipient_name,
1618
			lc.log_time, lc.body, lc.id_notice, lc.counter
1619
		FROM {db_prefix}log_comments AS lc
1620
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
1621
			LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = lc.id_recipient)
1622
		WHERE lc.comment_type = {string:warning}
1623
		ORDER BY {raw:sort}
1624
		LIMIT {int:start}, {int:max}',
1625
		array(
1626
			'warning' => 'warning',
1627
			'start' => $start,
1628
			'max' => $items_per_page,
1629
			'sort' => $sort,
1630
		)
1631
	);
1632
	$warnings = array();
1633
	while ($row = $smcFunc['db_fetch_assoc']($request))
1634
	{
1635
		$warnings[] = array(
1636
			'issuer_link' => $row['id_member'] ? ('<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['member_name_col'] . '</a>') : $row['member_name_col'],
1637
			'recipient_link' => $row['id_recipient'] ? ('<a href="' . $scripturl . '?action=profile;u=' . $row['id_recipient'] . '">' . $row['recipient_name'] . '</a>') : $row['recipient_name'],
1638
			'time' => timeformat($row['log_time']),
1639
			'reason' => $row['body'],
1640
			'counter' => $row['counter'] > 0 ? '+' . $row['counter'] : $row['counter'],
1641
			'id_notice' => $row['id_notice'],
1642
		);
1643
	}
1644
	$smcFunc['db_free_result']($request);
1645
1646
	return $warnings;
1647
}
1648
1649
/**
1650
 * Load all the warning templates.
1651
 */
1652
function ViewWarningTemplates()
1653
{
1654
	global $smcFunc, $modSettings, $context, $txt, $scripturl, $sourcedir, $user_info;
1655
1656
	// Submitting a new one?
1657
	if (isset($_POST['add']))
1658
		return ModifyWarningTemplate();
1659
	elseif (isset($_POST['delete']) && !empty($_POST['deltpl']))
1660
	{
1661
		checkSession();
1662
		validateToken('mod-wt');
1663
1664
		// Log the actions.
1665
		$request = $smcFunc['db_query']('', '
1666
			SELECT recipient_name
1667
			FROM {db_prefix}log_comments
1668
			WHERE id_comment IN ({array_int:delete_ids})
1669
				AND comment_type = {string:warntpl}
1670
				AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})',
1671
			array(
1672
				'delete_ids' => $_POST['deltpl'],
1673
				'warntpl' => 'warntpl',
1674
				'generic' => 0,
1675
				'current_member' => $user_info['id'],
1676
			)
1677
		);
1678
		while ($row = $smcFunc['db_fetch_assoc']($request))
1679
			logAction('delete_warn_template', array('template' => $row['recipient_name']));
1680
		$smcFunc['db_free_result']($request);
1681
1682
		// Do the deletes.
1683
		$smcFunc['db_query']('', '
1684
			DELETE FROM {db_prefix}log_comments
1685
			WHERE id_comment IN ({array_int:delete_ids})
1686
				AND comment_type = {string:warntpl}
1687
				AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})',
1688
			array(
1689
				'delete_ids' => $_POST['deltpl'],
1690
				'warntpl' => 'warntpl',
1691
				'generic' => 0,
1692
				'current_member' => $user_info['id'],
1693
			)
1694
		);
1695
	}
1696
1697
	// Setup context as always.
1698
	$context['page_title'] = $txt['mc_warning_templates_title'];
1699
1700
	require_once($sourcedir . '/Subs-List.php');
1701
1702
	// This is all the information required for a watched user listing.
1703
	$listOptions = array(
1704
		'id' => 'warning_template_list',
1705
		'title' => $txt['mc_warning_templates_title'],
1706
		'items_per_page' => $modSettings['defaultMaxListItems'],
1707
		'no_items_label' => $txt['mc_warning_templates_none'],
1708
		'base_href' => $scripturl . '?action=moderate;area=warnings;sa=templates;' . $context['session_var'] . '=' . $context['session_id'],
1709
		'default_sort_col' => 'title',
1710
		'get_items' => array(
1711
			'function' => 'list_getWarningTemplates',
1712
		),
1713
		'get_count' => array(
1714
			'function' => 'list_getWarningTemplateCount',
1715
		),
1716
		// This assumes we are viewing by user.
1717
		'columns' => array(
1718
			'title' => array(
1719
				'header' => array(
1720
					'value' => $txt['mc_warning_templates_name'],
1721
				),
1722
				'data' => array(
1723
					'sprintf' => array(
1724
						'format' => '<a href="' . $scripturl . '?action=moderate;area=warnings;sa=templateedit;tid=%1$d">%2$s</a>',
1725
						'params' => array(
1726
							'id_comment' => false,
1727
							'title' => false,
1728
							'body' => false,
1729
						),
1730
					),
1731
				),
1732
				'sort' => array(
1733
					'default' => 'template_title',
1734
					'reverse' => 'template_title DESC',
1735
				),
1736
			),
1737
			'creator' => array(
1738
				'header' => array(
1739
					'value' => $txt['mc_warning_templates_creator'],
1740
				),
1741
				'data' => array(
1742
					'db' => 'creator',
1743
				),
1744
				'sort' => array(
1745
					'default' => 'creator_name',
1746
					'reverse' => 'creator_name DESC',
1747
				),
1748
			),
1749
			'time' => array(
1750
				'header' => array(
1751
					'value' => $txt['mc_warning_templates_time'],
1752
				),
1753
				'data' => array(
1754
					'db' => 'time',
1755
				),
1756
				'sort' => array(
1757
					'default' => 'lc.log_time DESC',
1758
					'reverse' => 'lc.log_time',
1759
				),
1760
			),
1761
			'delete' => array(
1762
				'header' => array(
1763
					'value' => '<input type="checkbox" class="input_check" onclick="invertAll(this, this.form);">',
1764
					'style' => 'width: 4%;',
1765
					'class' => 'centercol',
1766
				),
1767
				'data' => array(
1768
					'function' => function($rowData)
1769
					{
1770
						return '<input type="checkbox" name="deltpl[]" value="' . $rowData['id_comment'] . '" class="input_check">';
1771
					},
1772
					'class' => 'centercol',
1773
				),
1774
			),
1775
		),
1776
		'form' => array(
1777
			'href' => $scripturl . '?action=moderate;area=warnings;sa=templates',
1778
			'token' => 'mod-wt',
1779
		),
1780
		'additional_rows' => array(
1781
			array(
1782
				'position' => 'bottom_of_list',
1783
				'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">',
1784
			),
1785
			array(
1786
				'position' => 'bottom_of_list',
1787
				'value' => '<input type="submit" name="add" value="' . $txt['mc_warning_template_add'] . '" class="button_submit">',
1788
			),
1789
		),
1790
	);
1791
1792
	// Create the watched user list.
1793
	createToken('mod-wt');
1794
	createList($listOptions);
1795
1796
	$context['sub_template'] = 'show_list';
1797
	$context['default_list'] = 'warning_template_list';
1798
}
1799
1800
/**
1801
  * Callback for createList().
1802
  * @return int The total number of warning templates
1803
  */
1804 View Code Duplication
function list_getWarningTemplateCount()
0 ignored issues
show
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...
1805
{
1806
	global $smcFunc, $user_info;
1807
1808
	$request = $smcFunc['db_query']('', '
1809
		SELECT COUNT(*)
1810
		FROM {db_prefix}log_comments
1811
		WHERE comment_type = {string:warntpl}
1812
			AND (id_recipient = {string:generic} OR id_recipient = {int:current_member})',
1813
		array(
1814
			'warntpl' => 'warntpl',
1815
			'generic' => 0,
1816
			'current_member' => $user_info['id'],
1817
		)
1818
	);
1819
	list ($totalWarns) = $smcFunc['db_fetch_row']($request);
1820
	$smcFunc['db_free_result']($request);
1821
1822
	return $totalWarns;
1823
}
1824
1825
/**
1826
 * Callback for createList().
1827
 *
1828
 * @param int $start The item to start with (for pagination purposes)
1829
 * @param int $items_per_page The number of items to show per page
1830
 * @param string $sort A string indicating how to sort the results
1831
 * @return array An arrray of info about the available warning templates
1832
 */
1833
function list_getWarningTemplates($start, $items_per_page, $sort)
1834
{
1835
	global $smcFunc, $scripturl, $user_info;
1836
1837
	$request = $smcFunc['db_query']('', '
1838
		SELECT lc.id_comment, COALESCE(mem.id_member, 0) AS id_member,
1839
			COALESCE(mem.real_name, lc.member_name) AS creator_name, recipient_name AS template_title,
1840
			lc.log_time, lc.body
1841
		FROM {db_prefix}log_comments AS lc
1842
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
1843
		WHERE lc.comment_type = {string:warntpl}
1844
			AND (id_recipient = {string:generic} OR id_recipient = {int:current_member})
1845
		ORDER BY ' . $sort . '
1846
		LIMIT ' . $start . ', ' . $items_per_page,
1847
		array(
1848
			'warntpl' => 'warntpl',
1849
			'generic' => 0,
1850
			'current_member' => $user_info['id'],
1851
		)
1852
	);
1853
	$templates = array();
1854
	while ($row = $smcFunc['db_fetch_assoc']($request))
1855
	{
1856
		$templates[] = array(
1857
			'id_comment' => $row['id_comment'],
1858
			'creator' => $row['id_member'] ? ('<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['creator_name'] . '</a>') : $row['creator_name'],
1859
			'time' => timeformat($row['log_time']),
1860
			'title' => $row['template_title'],
1861
			'body' => $smcFunc['htmlspecialchars']($row['body']),
1862
		);
1863
	}
1864
	$smcFunc['db_free_result']($request);
1865
1866
	return $templates;
1867
}
1868
1869
/**
1870
 * Edit a warning template.
1871
 */
1872
function ModifyWarningTemplate()
1873
{
1874
	global $smcFunc, $context, $txt, $user_info, $sourcedir;
1875
1876
	$context['id_template'] = isset($_REQUEST['tid']) ? (int) $_REQUEST['tid'] : 0;
1877
	$context['is_edit'] = $context['id_template'];
1878
1879
	// Standard template things.
1880
	$context['page_title'] = $context['is_edit'] ? $txt['mc_warning_template_modify'] : $txt['mc_warning_template_add'];
1881
	$context['sub_template'] = 'warn_template';
1882
	$context[$context['moderation_menu_name']]['current_subsection'] = 'templates';
1883
1884
	// Defaults.
1885
	$context['template_data'] = array(
1886
		'title' => '',
1887
		'body' => $txt['mc_warning_template_body_default'],
1888
		'personal' => false,
1889
		'can_edit_personal' => true,
1890
	);
1891
1892
	// If it's an edit load it.
1893
	if ($context['is_edit'])
1894
	{
1895
		$request = $smcFunc['db_query']('', '
1896
			SELECT id_member, id_recipient, recipient_name AS template_title, body
1897
			FROM {db_prefix}log_comments
1898
			WHERE id_comment = {int:id}
1899
				AND comment_type = {string:warntpl}
1900
				AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})',
1901
			array(
1902
				'id' => $context['id_template'],
1903
				'warntpl' => 'warntpl',
1904
				'generic' => 0,
1905
				'current_member' => $user_info['id'],
1906
			)
1907
		);
1908
		while ($row = $smcFunc['db_fetch_assoc']($request))
1909
		{
1910
			$context['template_data'] = array(
1911
				'title' => $row['template_title'],
1912
				'body' => $smcFunc['htmlspecialchars']($row['body']),
1913
				'personal' => $row['id_recipient'],
1914
				'can_edit_personal' => $row['id_member'] == $user_info['id'],
1915
			);
1916
		}
1917
		$smcFunc['db_free_result']($request);
1918
	}
1919
1920
	// Wait, we are saving?
1921
	if (isset($_POST['save']))
1922
	{
1923
		checkSession();
1924
		validateToken('mod-wt');
1925
1926
		// To check the BBC is pretty good...
1927
		require_once($sourcedir . '/Subs-Post.php');
1928
1929
		// Bit of cleaning!
1930
		$_POST['template_body'] = trim($_POST['template_body']);
1931
		$_POST['template_title'] = trim($_POST['template_title']);
1932
1933
		// Need something in both boxes.
1934
		if (!empty($_POST['template_body']) && !empty($_POST['template_title']))
1935
		{
1936
			// Safety first.
1937
			$_POST['template_title'] = $smcFunc['htmlspecialchars']($_POST['template_title']);
1938
1939
			// Clean up BBC.
1940
			preparsecode($_POST['template_body']);
1941
			// But put line breaks back!
1942
			$_POST['template_body'] = strtr($_POST['template_body'], array('<br>' => "\n"));
1943
1944
			// Is this personal?
1945
			$recipient_id = !empty($_POST['make_personal']) ? $user_info['id'] : 0;
1946
1947
			// If we are this far it's save time.
1948
			if ($context['is_edit'])
1949
			{
1950
				// Simple update...
1951
				$smcFunc['db_query']('', '
1952
					UPDATE {db_prefix}log_comments
1953
					SET id_recipient = {int:personal}, recipient_name = {string:title}, body = {string:body}
1954
					WHERE id_comment = {int:id}
1955
						AND comment_type = {string:warntpl}
1956
						AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})'.
1957
						($recipient_id ? ' AND id_member = {int:current_member}' : ''),
1958
					array(
1959
						'personal' => $recipient_id,
1960
						'title' => $_POST['template_title'],
1961
						'body' => $_POST['template_body'],
1962
						'id' => $context['id_template'],
1963
						'warntpl' => 'warntpl',
1964
						'generic' => 0,
1965
						'current_member' => $user_info['id'],
1966
					)
1967
				);
1968
1969
				// If it wasn't visible and now is they've effectively added it.
1970
				if ($context['template_data']['personal'] && !$recipient_id)
1971
					logAction('add_warn_template', array('template' => $_POST['template_title']));
1972
				// Conversely if they made it personal it's a delete.
1973
				elseif (!$context['template_data']['personal'] && $recipient_id)
1974
					logAction('delete_warn_template', array('template' => $_POST['template_title']));
1975
				// Otherwise just an edit.
1976
				else
1977
					logAction('modify_warn_template', array('template' => $_POST['template_title']));
1978
			}
1979
			else
1980
			{
1981
				$smcFunc['db_insert']('',
1982
					'{db_prefix}log_comments',
1983
					array(
1984
						'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'id_recipient' => 'int',
1985
						'recipient_name' => 'string-255', 'body' => 'string-65535', 'log_time' => 'int',
1986
					),
1987
					array(
1988
						$user_info['id'], $user_info['name'], 'warntpl', $recipient_id,
1989
						$_POST['template_title'], $_POST['template_body'], time(),
1990
					),
1991
					array('id_comment')
1992
				);
1993
1994
				logAction('add_warn_template', array('template' => $_POST['template_title']));
1995
			}
1996
1997
			// Get out of town...
1998
			redirectexit('action=moderate;area=warnings;sa=templates');
1999
		}
2000
		else
2001
		{
2002
			$context['warning_errors'] = array();
2003
			$context['template_data']['title'] = !empty($_POST['template_title']) ? $_POST['template_title'] : '';
2004
			$context['template_data']['body'] = !empty($_POST['template_body']) ? $_POST['template_body'] : $txt['mc_warning_template_body_default'];
2005
			$context['template_data']['personal'] = !empty($_POST['make_personal']);
2006
			if (empty($_POST['template_title']))
2007
				$context['warning_errors'][] = $txt['mc_warning_template_error_no_title'];
2008
			if (empty($_POST['template_body']))
2009
				$context['warning_errors'][] = $txt['mc_warning_template_error_no_body'];
2010
		}
2011
	}
2012
2013
	createToken('mod-wt');
2014
}
2015
2016
/**
2017
 * Change moderation preferences.
2018
 */
2019
function ModerationSettings()
2020
{
2021
	global $context, $txt, $user_info;
2022
2023
	// Some useful context stuff.
2024
	loadTemplate('ModerationCenter');
2025
	$context['page_title'] = $txt['mc_settings'];
2026
	$context['sub_template'] = 'moderation_settings';
2027
	$context[$context['moderation_menu_name']]['tab_data'] = array(
2028
		'title' => $txt['mc_prefs_title'],
2029
		'help' => '',
2030
		'description' => $txt['mc_prefs_desc']
2031
	);
2032
2033
	$pref_binary = 5;
2034
2035
	// Are we saving?
2036
	if (isset($_POST['save']))
2037
	{
2038
		checkSession();
2039
		validateToken('mod-set');
2040
2041
		/* Current format of mod_prefs is:
2042
			x|ABCD|yyy
2043
2044
			WHERE:
2045
				x = Show report count on forum header.
2046
				ABCD = Block indexes to show on moderation main page.
2047
				yyy = Integer with the following bit status:
2048
					- yyy & 4 = Notify about posts awaiting approval.
2049
		*/
2050
2051
		// Now check other options!
2052
		$pref_binary = 0;
2053
2054
		if ($context['can_moderate_approvals'] && !empty($_POST['mod_notify_approval']))
2055
			$pref_binary |= 4;
2056
2057
		// Put it all together.
2058
		$mod_prefs = '0||' . $pref_binary;
2059
		updateMemberData($user_info['id'], array('mod_prefs' => $mod_prefs));
2060
	}
2061
2062
	// What blocks does the user currently have selected?
2063
	$context['mod_settings'] = array(
2064
		'notify_approval' => $pref_binary & 4,
2065
	);
2066
2067
	createToken('mod-set');
2068
}
2069
2070
/**
2071
 * This ends a moderator session, requiring authentication to access the MCP again.
2072
 */
2073
function ModEndSession()
2074
{
2075
	// This is so easy!
2076
	unset($_SESSION['moderate_time']);
2077
2078
	// Clean any moderator tokens as well.
2079 View Code Duplication
	foreach ($_SESSION['token'] as $key => $token)
0 ignored issues
show
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...
2080
		if (strpos($key, '-mod') !== false)
2081
			unset($_SESSION['token'][$key]);
2082
2083
	redirectexit();
2084
}
2085
2086
?>
0 ignored issues
show
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...