ModerationCenter::action_viewWatchedUsers()   F
last analyzed

Complexity

Conditions 23
Paths 17920

Size

Total Lines 214
Code Lines 123

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 552

Importance

Changes 0
Metric Value
cc 23
eloc 123
nc 17920
nop 0
dl 0
loc 214
ccs 0
cts 69
cp 0
crap 552
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Moderation Center, provides at a glance view of moderation items to the team
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * This file contains code covered by:
11
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
12
 *
13
 * @version 2.0 dev
14
 *
15
 */
16
17
namespace ElkArte\Controller;
18
19
use BBC\ParserWrapper;
20
use ElkArte\AbstractController;
21
use ElkArte\Action;
22
use ElkArte\AdminController\ManageMembers;
23
use ElkArte\AdminController\Modlog;
24
use ElkArte\Cache\Cache;
25
use ElkArte\Exceptions\Exception;
26
use ElkArte\Helper\Util;
27
use ElkArte\Languages\Txt;
28
use ElkArte\Menu\Menu;
29
use ElkArte\MessagesDelete;
30
use ElkArte\User;
31
32
/**
33
 * Provides overview of moderation items to the team
34
 */
35
class ModerationCenter extends AbstractController
36
{
37
	/** @var array Holds function array to pass to callMenu to call the right moderation area */
38
	private $_mod_include_data;
39
40
	/**
41
	 * Entry point for the moderation center.
42
	 *
43
	 * @see AbstractController::action_index
44
	 */
45 6
	public function action_index()
46
	{
47
		// Set up moderation menu.
48 6
		$this->prepareModcenter();
49
50 6
		// And off we go
51 6
		$action = new Action();
52
		$action->initialize(['action' => $this->_mod_include_data]);
53
		$action->dispatch('action');
54
	}
55
56
	/**
57
	 * Prepare menu, make checks, load files, and create moderation menu.
58
	 *
59 8
	 * This can be called from the class, or from outside, to set up moderation menu.
60
	 */
61 8
	public function prepareModcenter()
62
	{
63
		global $txt, $context, $modSettings, $options;
64 8
65
		// Don't run this twice... and don't conflict with the admin bar.
66 2
		if (isset($context['admin_area']))
67
		{
68
			return;
69 6
		}
70 6
71 6
		$context['can_moderate_boards'] = $this->user->mod_cache['bq'] !== '0=1';
0 ignored issues
show
Bug Best Practice introduced by
The property mod_cache does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
72
		$context['can_moderate_groups'] = $this->user->mod_cache['gq'] !== '0=1';
73
		$context['can_moderate_approvals'] = $modSettings['postmod_active'] && !empty($this->user->mod_cache['ap']);
74 6
75
		// Everyone using this area must be allowed here!
76
		if (!$context['can_moderate_boards'] && !$context['can_moderate_groups'] && !$context['can_moderate_approvals'])
77
		{
78
			isAllowedTo('access_mod_center');
79
		}
80 6
81
		// We're gonna want a menu of some kind.
82
		require_once(SUBSDIR . '/Menu.subs.php');
83 6
84 6
		// Load the language, and the template.
85
		Txt::load('ModerationCenter');
86 6
		loadCSSFile('admin.css');
87
88
		if (!empty($options['admin_preferences']))
89
		{
90
			$context['admin_preferences'] = serializeToJson($options['admin_preferences'], static function ($array_form) {
91
				global $context;
92
93
				$context['admin_preferences'] = $array_form;
94
				require_once(SUBSDIR . '/Admin.subs.php');
95
				updateAdminPreferences();
96
			});
97
		}
98 6
		else
99
		{
100
			$context['admin_preferences'] = array();
101 6
		}
102
103
		$context['robot_no_index'] = true;
104 6
105 6
		// Moderation counts for things that this moderator can take care of
106
		require_once(SUBSDIR . '/Moderation.subs.php');
107
		$mod_counts = loadModeratorMenuCounts();
108
109 3
		// This is the menu structure - refer to subs/Menu.subs.php for the details.
110 6
		$moderation_areas = array(
111
			'main' => array(
112
				'title' => $txt['mc_main'],
113 6
				'areas' => array(
114 6
					'index' => array(
115 6
						'label' => $txt['moderation_center'],
116 6
						'controller' => ModerationCenter::class,
117 6
						'function' => 'action_moderationHome',
118
						'class' => 'i-home i-admin',
119
					),
120 6
					'settings' => array(
121 6
						'label' => $txt['mc_settings'],
122 6
						'controller' => ModerationCenter::class,
123 6
						'function' => 'action_moderationSettings',
124 6
						'class' => 'i-switch-on i-admin',
125
					),
126
					'modlogoff' => array(
127 6
						'label' => $txt['mc_logoff'],
128 6
						'controller' => ModerationCenter::class,
129 6
						'function' => 'action_modEndSession',
130 6
						'enabled' => empty($modSettings['securityDisable_moderate']),
131 6
						'class' => 'i-sign-out i-admin',
132 6
					),
133
					'notice' => array(
134
						'controller' => ModerationCenter::class,
135
						'function' => 'action_showNotice',
136
						'select' => 'index',
137
						'class' => 'i-post-text i-admin',
138
					),
139
				),
140
			),
141
			'logs' => array(
142
				'title' => $txt['mc_logs'],
143
				'areas' => array(
144 6
					'modlog' => array(
145
						'label' => $txt['modlog_view'],
146
						'enabled' => featureEnabled('ml') && $context['can_moderate_boards'],
147 6
						'controller' => Modlog::class,
148 6
						'function' => 'action_log',
149 6
						'class' => 'i-comments i-admin',
150 6
					),
151 6
					'warnings' => array(
152 6
						'label' => $txt['mc_warnings'],
153
						'enabled' => featureEnabled('w') && !empty($modSettings['warning_enable']) && $context['can_moderate_boards'],
154
						'controller' => ModerationCenter::class,
155 6
						'function' => 'action_viewWarnings',
156 6
						'class' => 'i-warn i-admin',
157 6
						'subsections' => array(
158 6
							'log' => array($txt['mc_warning_log']),
159 6
							'templates' => array($txt['mc_warning_templates'], 'issue_warning'),
160 6
						),
161
					),
162 6
				),
163 6
			),
164
			'posts' => array(
165
				'title' => $txt['mc_posts'] . (empty($mod_counts['pt_total']) ? '' : ' [' . $mod_counts['pt_total'] . ']'),
166
				'enabled' => $context['can_moderate_boards'] || $context['can_moderate_approvals'],
167
				'areas' => array(
168
					'postmod' => array(
169 6
						'label' => $txt['mc_unapproved_posts'] . (empty($mod_counts['postmod']) ? '' : ' [' . $mod_counts['postmod'] . ']'),
170 6
						'enabled' => $context['can_moderate_approvals'],
171
						'controller' => PostModeration::class,
172
						'function' => 'action_index',
173 6
						'class' => 'i-post-text i-admin',
174 6
						'custom_url' => getUrl('action', ['action' => 'moderate', 'area' => 'postmod']),
175 6
						'subsections' => array(
176 6
							'posts' => array($txt['mc_unapproved_replies']),
177 6
							'topics' => array($txt['mc_unapproved_topics']),
178 6
						),
179 6
					),
180
					'emailmod' => array(
181 6
						'label' => $txt['mc_emailerror'] . (empty($mod_counts['emailmod']) ? '' : ' [' . $mod_counts['emailmod'] . ']'),
182 6
						'enabled' => !empty($modSettings['maillist_enabled']) && allowedTo('approve_emails'),
183
						'function' => 'UnapprovedEmails',
184
						'class' => 'i-envelope-blank i-admin',
185
						'custom_url' => getUrl('action', ['action' => 'moderate', 'area' => 'maillist', 'sa' => 'emaillist']),
186 6
					),
187 6
					'attachmod' => array(
188 6
						'label' => $txt['mc_unapproved_attachments'] . (empty($mod_counts['attachments']) ? '' : ' [' . $mod_counts['attachments'] . ']'),
189 6
						'enabled' => $context['can_moderate_approvals'],
190 6
						'controller' => PostModeration::class,
191 6
						'function' => 'action_index',
192
						'class' => 'i-paperclip i-admin',
193
						'custom_url' => getUrl('action', ['action' => 'moderate', 'area' => 'attachmod', 'sa' => 'attachments']),
194 6
					),
195 6
					'reports' => array(
196 6
						'label' => $txt['mc_reported_posts'] . (empty($mod_counts['reports']) ? '' : ' [' . $mod_counts['reports'] . ']'),
197 6
						'enabled' => $context['can_moderate_boards'],
198 6
						'controller' => ModerationCenter::class,
199 6
						'function' => 'action_reportedPosts',
200 6
						'class' => 'i-modify i-admin',
201
						'subsections' => array(
202
							'open' => array($txt['mc_reportedp_active'] . (empty($mod_counts['reports']) ? '' : ' [' . $mod_counts['reports'] . ']')),
203 6
							'closed' => array($txt['mc_reportedp_closed']),
204 6
						),
205 6
					),
206 6
					'pm_reports' => array(
207 6
						'label' => $txt['mc_reported_pms'] . (empty($mod_counts['pm_reports']) ? '' : ' [' . $mod_counts['pm_reports'] . ']'),
208 6
						'enabled' => $this->user->is_admin,
0 ignored issues
show
Bug Best Practice introduced by
The property is_admin does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
209
						'controller' => ModerationCenter::class,
210 6
						'function' => 'action_reportedPosts',
211 6
						'class' => 'i-alert i-admin',
212
						'subsections' => array(
213
							'open' => array($txt['mc_reportedp_active']),
214
							'closed' => array($txt['mc_reportedp_closed']),
215 6
						),
216 6
					),
217 6
				),
218 6
			),
219
			'groups' => array(
220 6
				'title' => $txt['mc_groups'] . (empty($mod_counts['mg_total']) ? '' : ' [' . $mod_counts['mg_total'] . ']'),
221 6
				'enabled' => $context['can_moderate_groups'],
222
				'areas' => array(
223
					'userwatch' => array(
224
						'label' => $txt['mc_watched_users_title'],
225
						'enabled' => featureEnabled('w') && !empty($modSettings['warning_enable']) && $context['can_moderate_boards'],
226
						'controller' => ModerationCenter::class,
227 6
						'function' => 'action_viewWatchedUsers',
228 6
						'class' => 'i-user i-admin',
229
						'subsections' => array(
230
							'member' => array($txt['mc_watched_users_member']),
231 6
							'post' => array($txt['mc_watched_users_post']),
232 6
						),
233 6
					),
234 6
					'groups' => array(
235 6
						'label' => $txt['mc_group_requests'] . (empty($mod_counts['groupreq']) ? '' : ' [' . $mod_counts['groupreq'] . ']'),
236 6
						'controller' => Groups::class,
237
						'function' => 'action_index',
238 6
						'class' => 'i-users i-admin',
239 6
						'custom_url' => getUrl('action', ['action' => 'moderate', 'area' => 'groups', 'sa' => 'requests']),
240
					),
241
					'members' => array(
242
						'enabled' => allowedTo('moderate_forum'),
243 6
						'label' => $txt['mc_member_requests'] . (empty($mod_counts['memberreq']) ? '' : ' [' . $mod_counts['memberreq'] . ']'),
244 6
						'controller' => ManageMembers::class,
245 6
						'function' => 'action_approve',
246 6
						'class' => 'i-user-plus i-admin',
247 6
						'custom_url' => getUrl('admin', ['action' => 'admin', 'area' => 'viewmembers', 'sa' => 'browse', 'type' => 'approve']),
248 6
					),
249
					'viewgroups' => array(
250
						'label' => $txt['mc_view_groups'],
251 6
						'controller' => Groups::class,
252 6
						'function' => 'action_index',
253 6
						'class' => 'i-view i-admin',
254 6
					),
255 6
				),
256 6
			),
257 6
		);
258
259
		// Make sure the administrator has a valid session...
260 6
		validateSession('moderate');
261 6
262 6
		// I don't know where we're going - I don't know where we've been...
263 6
		$menuOptions = array(
264 6
			'action' => 'moderate',
265
			'hook' => 'moderation',
266
			'disable_url_session_check' => true,
267
		);
268
269
		// Setup the menu
270
		$mod_include_data = (new Menu())
271 6
			->addMenuData($moderation_areas)
272
			->addOptions($menuOptions)
273
			->prepareMenu()
274
			->setContext()
275 6
			->getIncludeData();
276
277
		unset($moderation_areas);
278
279
		// Retain the ID information in case required by a subaction.
280
		$context['moderation_menu_id'] = $context['max_menu_id'];
281 6
		$context['moderation_menu_name'] = 'menu_data_' . $context['moderation_menu_id'];
282 6
283
		$context[$context['moderation_menu_name']]['object']->prepareTabData([
284
			'title' => $txt['moderation_center'],
285 6
			'description' => sprintf($txt['mc_description'], $context['user']['name'], getUrl('action', ['action' => 'moderate', 'area' => 'settings']))
286 6
		]);
287 6
288 6
		// What a pleasant shortcut - even tho we're not *really* on the admin screen who cares...
289 6
		$context['admin_area'] = $mod_include_data['current_area'];
290 6
291
		// Build the link tree.
292
		$context['breadcrumbs'][] = array(
293 6
			'url' => getUrl('action', ['action' => 'moderate']),
294
			'name' => $txt['moderation_center'],
295
		);
296 6
297 6
		if (isset($mod_include_data['current_area']) && $mod_include_data['current_area'] !== 'index')
298 6
		{
299
			$context['breadcrumbs'][] = [
300
				'url' => getUrl('action', ['action' => 'moderate', 'area' => $mod_include_data['current_area']]),
301 6
				'name' => $mod_include_data['label'],
302
			];
303 4
		}
304 4
305 4
		if (!empty($mod_include_data['current_subsection']) && isset($mod_include_data['subsections'][$mod_include_data['current_subsection']]['label'])
306
			&& $mod_include_data['subsections'][$mod_include_data['current_subsection']]['label'] !== $mod_include_data['label'])
307
		{
308
			$context['breadcrumbs'][] = [
309 6
				'url' => getUrl('action', ['action' => 'moderate', 'area' => $mod_include_data['current_area'], 'sa' => $mod_include_data['current_subsection']]),
310 6
				'name' => $mod_include_data['subsections'][$mod_include_data['current_subsection']]['label'],
311
			];
312 4
		}
313 4
314 4
		// Finally, store this, so that if we're called from the class, it can use it.
315
		$this->_mod_include_data = $mod_include_data;
316
	}
317
318
	/**
319 6
	 * This handler presents the home page of the moderation center.
320 6
	 */
321
	public function action_moderationHome()
322
	{
323
		global $txt, $context;
324
325 2
		theme()->getTemplates()->load('ModerationCenter');
326
		loadJavascriptFile('admin.js', array(), 'admin_scripts');
327 2
328
		$context['page_title'] = $txt['moderation_center'];
329 2
		$context['sub_template'] = 'moderation_center';
330 2
331
		// Start off with no blocks
332 2
		$valid_blocks = [];
333 2
334
		// Load what blocks the user actually can see...
335
		$valid_blocks['p'] = 'notes';
336 2
		$valid_blocks['n'] = 'latestNews';
337
338
		if ($context['can_moderate_boards'])
339 2
		{
340 2
			$valid_blocks['a'] = 'actionRequired';
341
			$valid_blocks['r'] = 'reportedPosts';
342 2
			$valid_blocks['w'] = 'watchedUsers';
343
		}
344 2
345 2
		if ($context['can_moderate_groups'])
346 2
		{
347
			$valid_blocks['g'] = 'groupRequests';
348
		}
349 2
350
		if (empty(User::$settings['mod_prefs']))
351 2
		{
352
			$user_blocks = 'n' . ($context['can_moderate_boards'] ? 'wra' : '') . ($context['can_moderate_groups'] ? 'g' : '');
353
		}
354 2
		else
355
		{
356 2
			[, $user_blocks] = explode('|', User::$settings['mod_prefs']);
0 ignored issues
show
Bug introduced by
It seems like ElkArte\User::settings['mod_prefs'] can also be of type null; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

356
			[, $user_blocks] = explode('|', /** @scrutinizer ignore-type */ User::$settings['mod_prefs']);
Loading history...
357
		}
358
359
		$user_blocks = str_split($user_blocks);
360
361
		$context['mod_blocks'] = array();
362
		foreach ($valid_blocks as $k => $block)
363 2
		{
364
			if (in_array($k, $user_blocks))
0 ignored issues
show
Bug introduced by
It seems like $user_blocks can also be of type true; however, parameter $haystack of in_array() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

364
			if (in_array($k, /** @scrutinizer ignore-type */ $user_blocks))
Loading history...
365 2
			{
366 2
				$block = 'block_' . $block;
367
368 2
				if (method_exists($this, $block))
369
				{
370 2
					$context['mod_blocks'][] = $this->{$block}();
371
				}
372 2
			}
373
		}
374 2
	}
375
376
	/**
377
	 * This ends a moderator session, requiring authentication to access the MCP again.
378 2
	 */
379
	public function action_modEndSession()
380
	{
381
		// This is so easy!
382
		unset($_SESSION['moderate_time']);
383
384
		// Clean any moderator tokens as well.
385
		cleanTokens(false, '-mod');
386
387
		redirectexit('action=moderate');
388
	}
389
390
	/**
391
	 * Show a warning notice sent to a user.
392
	 */
393
	public function action_showNotice()
394
	{
395
		global $txt, $context;
396
397
		// What notice have they asked to view
398
		$id_notice = $this->_req->getQuery('nid', 'intval', 0);
399
		$notice = moderatorNotice($id_notice);
400
401
		// legit?
402
		if (empty($notice) || !$context['can_moderate_boards'])
403
		{
404
			throw new Exception('no_access', false);
405
		}
406
407
		[$context['notice_body'], $context['notice_subject']] = $notice;
408
409
		$parser = ParserWrapper::instance();
410
411
		$context['notice_body'] = $parser->parseNotice($context['notice_body']);
412
		$context['page_title'] = $txt['show_notice'];
413
		$context['sub_template'] = 'show_notice';
414
415
		theme()->getLayers()->removeAll();
416
		theme()->getTemplates()->load('ModerationCenter');
417
	}
418
419
	/**
420
	 * Browse all the reported posts.
421
	 *
422
	 * @todo this needs to be given its own file?
423
	 */
424
	public function action_reportedPosts()
425
	{
426
		global $txt, $context;
427
428 4
		theme()->getTemplates()->load('ModerationCenter');
429
		require_once(SUBSDIR . '/Moderation.subs.php');
430 4
431
		// Put the open and closed options into tabs, because we can...
432 4
		$context[$context['moderation_menu_name']]['object']->prepareTabData([
433 4
			'title' => $txt['mc_reported_posts'],
434
			'description' => $txt['mc_reported_posts_desc'],
435
		]);
436 4
437 4
		// Set up the comforting bits...
438 4
		$context['page_title'] = $txt['mc_reported_posts'];
439 4
		$context['sub_template'] = 'reported_posts';
440
441
		// This comes under the umbrella of moderating posts.
442
		if ($this->user->mod_cache['bq'] === '0=1')
0 ignored issues
show
Bug Best Practice introduced by
The property mod_cache does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
443 4
		{
444 4
			isAllowedTo('moderate_forum');
445
		}
446
447 4
		// Are they wanting to view a particular report?
448
		if (!empty($this->_req->query->report))
449
		{
450
			return $this->action_modReport();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->action_modReport() targeting ElkArte\Controller\Moder...ter::action_modReport() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
451
		}
452
453 4
		// This should not be needed...
454
		$show_pms = false;
455 2
		if ($context['admin_area'] === 'pm_reports')
456
		{
457
			$show_pms = true;
458
			isAllowedTo('admin_forum');
459 2
460 2
			// Put the open and closed options into tabs, because we can...
461
			$context[$context['moderation_menu_name']]['object']->prepareTabData([
462
				'title' => $txt['mc_reported_pms'],
463
				'description' => $txt['mc_reported_pms_desc'],
464
			]);
465
			$context['page_title'] = $txt['mc_reported_pms'];
466
		}
467
468
		// Are we viewing open or closed reports?
469
		$context['view_closed'] = $this->_req->getQuery('sa') === 'closed' ? 1 : 0;
470
471
		// Are we doing any work?
472
		if ((isset($this->_req->query->ignore) || isset($this->_req->query->close)) && isset($this->_req->query->rid))
473
		{
474
			checkSession('get');
475 2
			$rid = $this->_req->getQuery('rid', 'intval');
476
477
			// Update the report...
478 2
			if (isset($this->_req->query->ignore))
479
			{
480
				updateReportsStatus($rid, 'ignore', (int) $this->_req->query->ignore);
481
			}
482
			elseif (isset($this->_req->query->close))
483
			{
484
				updateReportsStatus($rid, 'close', (int) $this->_req->query->close);
485
			}
486
487
			// Time to update.
488
			updateSettings(array('last_mod_report_action' => time()));
489
			recountOpenReports(true, $show_pms);
490
		}
491
		elseif (isset($this->_req->post->close, $this->_req->post->close_selected))
492
		{
493
			checkSession('post');
494
495
			// All the ones to update...
496
			$toClose = array_map('intval', $this->_req->post->close);
497 2
			if (!empty($toClose))
498
			{
499
				updateReportsStatus($toClose, 'close', 1);
500
501
				// Time to update.
502
				updateSettings(array('last_mod_report_action' => time()));
503
				recountOpenReports(true, $show_pms);
504
			}
505
		}
506
507
		// How many entries are we viewing?
508
		$context['total_reports'] = totalReports($context['view_closed'], $show_pms);
509
510
		// So, that means we can page index, yes?
511
		$context['page_index'] = constructPageIndex('{scripturl}?action=moderate;area=' . $context['admin_area'] . ($context['view_closed'] ? ';sa=closed' : ''), $this->_req->query->start, $context['total_reports'], 10);
512
		$context['start'] = $this->_req->query->start;
513
514 2
		// By George, that means we in a position to get the reports, golly good.
515
		$context['reports'] = getModReports($context['view_closed'], $context['start'], 10, $show_pms);
516
		$report_ids = array_keys($context['reports']);
517 2
		$report_boards_ids = array();
518 2
		$bbc_parser = ParserWrapper::instance();
519
		foreach ($context['reports'] as $row)
520
		{
521 2
			$context['reports'][$row['id_report']] = array(
522 2
				'board' => $row['id_board'],
523 2
				'id' => $row['id_report'],
524 2
				'topic_href' => getUrl('topic', ['topic' => $row['id_topic'], 'msg' => $row['id_msg'], 'start' => $row['id_msg'], 'hash' => '#msg' . $row['id_msg']]),
525 2
				'report_href' => getUrl('action', ['action' => 'moderate', 'area' => $context['admin_area'], 'report' => $row['id_report']]),
526
				'author' => array(
527 2
					'id' => $row['id_author'],
528 2
					'name' => $row['author_name'],
529 2
					'link' => $row['id_author'] ? '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row['id_author']]) . '">' . $row['author_name'] . '</a>' : $row['author_name'],
530 2
					'href' => getUrl('profile', ['action' => 'profile', 'u' => $row['id_author']]),
531 2
				),
532
				'comments' => array(),
533 2
				'time_started' => standardTime($row['time_started']),
534 2
				'last_updated' => standardTime($row['time_updated']),
535 2
				'subject' => $row['subject'],
536 2
				'body' => $bbc_parser->parseReport($row['body']),
537
				'num_reports' => $row['num_reports'],
538
				'closed' => $row['closed'],
539 2
				'ignore' => $row['ignore_all'],
540 2
				'buttons' => array(
541 2
					'inline_mod_check' => array(
542 2
						'checkbox' => 'always',
543 2
						'enabled' => !$context['view_closed'],
544 2
						'name' => 'close',
545 2
						'value' => $row['id_report'],
546
					),
547
					'details' => array(
548 2
						'url' => getUrl('action', ['action' => 'moderate', 'area' => $context['admin_area'], 'report' => $row['id_report']]),
549 2
						'text' => 'mc_reportedp_details',
550 2
						'icon' => 'post-text',
551
					),
552
					'ignore' => array(
553 2
						'url' => getUrl('action', ['action' => 'moderate', 'area' => $context['admin_area'], 'sa' => ($context['view_closed'] ? 'closed' : ''), 'ignore' => (int) !$row['ignore_all'], 'rid' => $row['id_report'], 'start' => $context['start'], '{session_data}']),
554 2
						'text' => $row['ignore_all'] ? 'mc_reportedp_unignore' : 'mc_reportedp_ignore',
555
						'custom' => $row['ignore_all'] ? '' : 'onclick="return confirm(' . JavaScriptEscape($txt['mc_reportedp_ignore_confirm']) . ');"',
556
						'icon' => 'delete'
557 2
					),
558 2
					'close' => array(
559 2
						'url' => getUrl('action', ['action' => 'moderate', 'area' => $context['admin_area'], 'sa' => ($context['view_closed'] ? 'closed' : ''), 'close' => (int) !$row['closed'], 'rid' => $row['id_report'], 'start' => $context['start'], '{session_data}']),
560
						'text' => $context['view_closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close',
561
						'icon' => $context['view_closed'] ? 'sign-in' : 'close',
562 2
					),
563 2
				),
564
			);
565
			$report_boards_ids[] = $row['id_board'];
566
		}
567 2
568
		// Get the names of boards these topics are in.
569
		if (!empty($report_ids))
570
		{
571 2
			require_once(SUBSDIR . '/Boards.subs.php');
572
			$board_names = getBoardList(array('included_boards' => $report_boards_ids), true);
573 2
574 2
			// Add the board name to the report array
575
			foreach ($context['reports'] as $id_report => $report)
576
			{
577 2
				if (!empty($board_names[$report['board']]))
578
				{
579 2
					$context['reports'][$id_report]['board_name'] = $board_names[$report['board']]['board_name'];
580
				}
581 2
			}
582
		}
583
584
		// Now get all the people who reported it.
585
		if (!empty($report_ids))
586
		{
587 2
			$comments = getReportsUserComments($report_ids);
588
			foreach ($comments as $id_rep => $rows)
589 2
			{
590 2
				foreach ($rows as $row)
591
				{
592 2
					$context['reports'][$id_rep]['comments'][] = array(
593
						'id' => $row['id_comment'],
594 2
						'message' => $row['comment'],
595 2
						'raw_time' => $row['time_sent'],
596 2
						'time' => standardTime($row['time_sent']),
597 2
						'html_time' => htmlTime($row['time_sent']),
598 2
						'timestamp' => forum_time(true, $row['time_sent']),
599 2
						'member' => array(
600 2
							'id' => $row['id_member'],
601
							'name' => empty($row['reporter']) ? $txt['guest'] : $row['reporter'],
602 2
							'link' => $row['id_member'] ? '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row['id_member']]) . '">' . $row['reporter'] . '</a>' : (empty($row['reporter']) ? $txt['guest'] : $row['reporter']),
603 2
							'href' => $row['id_member'] ? getUrl('profile', ['action' => 'profile', 'u' => $row['id_member']]) : '',
604 2
						),
605 2
					);
606
				}
607
			}
608
		}
609
	}
610
611 2
	/**
612
	 * Get details about the moderation report
613
	 *
614
	 * - report is specified in the url param report.
615
	 */
616
	public function action_modReport()
617
	{
618 2
		global $context, $txt;
619
620 2
		// Have to at least give us something
621
		$report = $this->_req->getQuery('report', 'intval', 0);
622
		if (empty($report))
623 2
		{
624 2
			throw new Exception('mc_no_modreport_specified');
625
		}
626
627
		// This should not be needed...
628
		$show_pms = false;
629
		if ($context['admin_area'] === 'pm_reports')
630 2
		{
631 2
			$show_pms = true;
632
			isAllowedTo('admin_forum');
633
		}
634
635
		// Get the report details, need this so we can limit access to a particular board
636
		$row = modReportDetails($report, $show_pms);
637
638 2
		// So did we find anything?
639
		if ($row === false)
640
		{
641 2
			throw new Exception('mc_no_modreport_found');
642
		}
643
644
		// Woohoo we found a report and they can see it!  Bad news is we have more work to do
645
		// If they are adding a comment then... add a comment.
646
		if (isset($this->_req->post->add_comment) && !empty($this->_req->post->mod_comment))
647
		{
648 2
			checkSession();
649
650
			$newComment = trim(Util::htmlspecialchars($this->_req->post->mod_comment));
651
652
			// In it goes.
653
			if (!empty($newComment))
654
			{
655
				addReportComment($report, $newComment);
656
657
				// Redirect to prevent double submission.
658
				redirectexit('action=moderate;area=' . $context['admin_area'] . ';report=' . $report);
659
			}
660
		}
661
662
		$bbc_parser = ParserWrapper::instance();
663
664 2
		$context['report'] = array(
665
			'id' => $row['id_report'],
666 2
			'topic_id' => $row['id_topic'],
667 2
			'board_id' => $row['id_board'],
668 2
			'message_id' => $row['id_msg'],
669 2
			'message_href' => getUrl('action', ['msg' => $row['id_msg']]),
670 2
			'message_link' => '<a href="' . getUrl('action', ['msg' => $row['id_msg']]) . '">' . $row['subject'] . '</a>',
671 2
			'report_href' => getUrl('action', ['action' => 'moderate', 'area' => $context['admin_area'], $context['admin_area'] => $row['id_report']]),
672 2
			'author' => array(
673 2
				'id' => $row['id_author'],
674
				'name' => $row['author_name'],
675 2
				'link' => $row['id_author'] ? '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row['id_author']]) . '">' . $row['author_name'] . '</a>' : $row['author_name'],
676 2
				'href' => getUrl('profile', ['action' => 'profile', 'u' => $row['id_author']]),
677 2
			),
678 2
			'comments' => array(),
679
			'mod_comments' => array(),
680
			'time_started' => standardTime($row['time_started']),
681
			'last_updated' => standardTime($row['time_updated']),
682 2
			'subject' => $row['subject'],
683 2
			'body' => $bbc_parser->parseReport($row['body']),
684 2
			'num_reports' => $row['num_reports'],
685 2
			'closed' => $row['closed'],
686 2
			'ignore' => $row['ignore_all']
687 2
		);
688 2
689
		// So what bad things do the reporters have to say about it?
690
		$comments = getReportsUserComments($context['report']['id']);
691
		foreach ($comments[$context['report']['id']] as $row)
692 2
		{
693 2
			$context['report']['comments'][] = array(
694
				'id' => $row['id_comment'],
695 2
				'message' => strtr($row['comment'], array("\n" => '<br />')),
696 2
				'time' => standardTime($row['time_sent']),
697 2
				'html_time' => htmlTime($row['time_sent']),
698 2
				'timestamp' => forum_time(true, $row['time_sent']),
699 2
				'member' => array(
700 2
					'id' => $row['id_member'],
701
					'name' => empty($row['reporter']) ? $txt['guest'] : $row['reporter'],
702 2
					'link' => $row['id_member'] ? '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row['id_member']]) . '">' . $row['reporter'] . '</a>' : (empty($row['reporter']) ? $txt['guest'] : $row['reporter']),
703 2
					'href' => $row['id_member'] ? getUrl('profile', ['action' => 'profile', 'u' => $row['id_member']]) : '',
704 2
					'ip' => !empty($row['member_ip']) && allowedTo('moderate_forum') ? '<a href="' . getUrl('action', ['action' => 'trackip', 'searchip' => $row['member_ip']]) . '">' . $row['member_ip'] . '</a>' : '',
705 2
				),
706 2
			);
707
		}
708
709
		// Hang about old chap, any comments from moderators on this one?
710
		$mod_comments = getReportModeratorsComments($context['report']['id']);
711
		foreach ($mod_comments as $row)
712 2
		{
713 2
			$context['report']['mod_comments'][] = array(
714
				'id' => $row['id_comment'],
715
				'message' => $bbc_parser->parseReport($row['body']),
716
				'time' => standardTime($row['log_time']),
717
				'html_time' => htmlTime($row['log_time']),
718
				'timestamp' => forum_time(true, $row['log_time']),
719
				'member' => array(
720
					'id' => $row['id_member'],
721
					'name' => $row['moderator'],
722
					'link' => $row['id_member'] ? '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row['id_member']]) . '">' . $row['moderator'] . '</a>' : $row['moderator'],
723
					'href' => getUrl('profile', ['action' => 'profile', 'u' => $row['id_member']]),
724
				),
725
			);
726
		}
727
728
		// What have the other moderators done to this message?
729
		require_once(SUBSDIR . '/Modlog.subs.php');
730
		Txt::load('Modlog');
731 2
732 2
		// This is all the information from the moderation log.
733
		$listOptions = array(
734
			'id' => 'moderation_actions_list',
735
			'title' => $txt['mc_modreport_modactions'],
736 2
			'items_per_page' => 15,
737 2
			'no_items_label' => $txt['modlog_no_entries_found'],
738 2
			'base_href' => getUrl('action', ['action' => 'moderate', 'area' => $context['admin_area'], 'report' => $context['report']['id']]),
739 2
			'default_sort_col' => 'time',
740 2
			'get_items' => array(
741 2
				'function' => 'list_getModLogEntries',
742
				'params' => array(
743 2
					'lm.id_topic = {int:id_topic}',
744
					array('id_topic' => $context['report']['topic_id']),
745 2
					1,
746 2
				),
747 2
			),
748
			'get_count' => array(
749
				'function' => 'list_getModLogEntryCount',
750
				'params' => array(
751 2
					'lm.id_topic = {int:id_topic}',
752
					array('id_topic' => $context['report']['topic_id']),
753 2
					1,
754 2
				),
755 2
			),
756
			// This assumes we are viewing by user.
757
			'columns' => array(
758
				'action' => array(
759
					'header' => array(
760
						'value' => $txt['modlog_action'],
761
					),
762 2
					'data' => array(
763
						'db' => 'action_text',
764
						'class' => 'smalltext',
765
					),
766
					'sort' => array(
767
						'default' => 'lm.action',
768
						'reverse' => 'lm.action DESC',
769
					),
770
				),
771
				'time' => array(
772
					'header' => array(
773
						'value' => $txt['modlog_date'],
774
					),
775 2
					'data' => array(
776
						'db' => 'time',
777
						'class' => 'smalltext',
778
					),
779
					'sort' => array(
780
						'default' => 'lm.log_time',
781
						'reverse' => 'lm.log_time DESC',
782
					),
783
				),
784
				'moderator' => array(
785
					'header' => array(
786
						'value' => $txt['modlog_member'],
787
					),
788 2
					'data' => array(
789
						'db' => 'moderator_link',
790
						'class' => 'smalltext',
791
					),
792
					'sort' => array(
793
						'default' => 'mem.real_name',
794
						'reverse' => 'mem.real_name DESC',
795
					),
796
				),
797
				'position' => array(
798
					'header' => array(
799
						'value' => $txt['modlog_position'],
800
					),
801 2
					'data' => array(
802
						'db' => 'position',
803
						'class' => 'smalltext',
804
					),
805
					'sort' => array(
806
						'default' => 'mg.group_name',
807
						'reverse' => 'mg.group_name DESC',
808
					),
809
				),
810
				'ip' => array(
811
					'header' => array(
812
						'value' => $txt['modlog_ip'],
813
					),
814 2
					'data' => array(
815
						'db' => 'ip',
816
						'class' => 'smalltext',
817
					),
818
					'sort' => array(
819
						'default' => 'lm.ip',
820
						'reverse' => 'lm.ip DESC',
821
					),
822
				),
823
			),
824
		);
825
826
		// Create the watched user list.
827
		createList($listOptions);
828
829 2
		// Make sure to get the correct tab selected.
830
		if ($context['report']['closed'])
831
		{
832 2
			$context[$context['moderation_menu_name']]['current_subsection'] = 'closed';
833
		}
834
835
		// Finally we are done :P
836
		theme()->getTemplates()->load('ModerationCenter');
837
		if ($context['admin_area'] === 'pm_reports')
838 2
		{
839 2
			$context['page_title'] = sprintf($txt['mc_view_pmreport'], $context['report']['author']['name']);
840
			$context['section_title'] = sprintf($txt['mc_view_pmreport'], $context['report']['author']['link']);
841
			$context['section_descripion'] = sprintf($txt['mc_pmreport_summary'], $context['report']['num_reports'], $context['report']['last_updated']);
842
		}
843
		else
844
		{
845
			$context['page_title'] = sprintf($txt['mc_viewmodreport'], $context['report']['subject'], $context['report']['author']['name']);
846
			$context['section_title'] = sprintf($txt['mc_viewmodreport'], $context['report']['message_link'], $context['report']['author']['link']);
847 2
			$context['section_descripion'] = sprintf($txt['mc_modreport_summary'], $context['report']['num_reports'], $context['report']['last_updated']);
848 2
		}
849 2
850
		$context['mod_buttons'] = [
851
			'ignore' => [
852 2
				'url' => getUrl('action', ['action' => 'moderate', 'area' => $context['admin_area'], 'ignore' => (int) !$context['report'] ['ignore'], 'rid' => $context['report'] ['id'], '{session_data}']),
853 2
				'text' => $context['report'] ['ignore'] ? 'mc_reportedp_unignore' : 'mc_reportedp_ignore',
854
				'custom' => $context['report'] ['ignore'] ? '' : 'onclick="return confirm(' . JavaScriptEscape($txt['mc_reportedp_ignore_confirm']) . ');"',
855
				'icon' => 'delete'
856
			],
857
			'close' => [
858
				'url' => getUrl('action', ['action' => 'moderate', 'area' => $context['admin_area'], 'close' => (int) !$context['report'] ['closed'], 'rid' => $context['report'] ['id'], '{session_data}']),
859
				'text' => $context['report'] ['closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close',
860
				'icon' => $context['report'] ['closed'] ? 'sign-in' : 'close',
861
			],
862
		];
863
864
		$context['sub_template'] = 'viewmodreport';
865
	}
866
867
	/**
868
	 * Change moderation preferences.
869
	 */
870
	public function action_moderationSettings()
871
	{
872
		global $context, $txt;
873
874
		// Some useful context stuff.
875
		theme()->getTemplates()->load('ModerationCenter');
876
		$context['page_title'] = $txt['mc_settings'];
877
		$context['sub_template'] = 'moderation_settings';
878
		$context[$context['moderation_menu_name']]['object']->prepareTabData([
879
			'title' => $txt['mc_prefs_title'],
880
			'description' => $txt['mc_prefs_desc']
881
		]);
882
883
		// What blocks can this user see?
884
		$context['homepage_blocks'] = array(
885
			'n' => $txt['mc_prefs_latest_news'],
886
			'p' => $txt['mc_notes'],
887
		);
888
889
		if ($context['can_moderate_groups'])
890
		{
891
			$context['homepage_blocks']['g'] = $txt['mc_group_requests'];
892
		}
893
894
		if ($context['can_moderate_boards'])
895
		{
896
			$context['homepage_blocks']['r'] = $txt['mc_reported_posts'];
897
			$context['homepage_blocks']['w'] = $txt['mc_watched_users'];
898
			$context['homepage_blocks']['a'] = $txt['mc_required'];
899
		}
900
901
		// Does the user have any settings yet?
902
		if (empty(User::$settings['mod_prefs']))
903
		{
904
			$mod_blocks = 'np' . ($context['can_moderate_boards'] ? 'wra' : '') . ($context['can_moderate_groups'] ? 'g' : '');
905
			$pref_binary = 5;
906
			$show_reports = 1;
907
		}
908
		else
909
		{
910
			[$show_reports, $mod_blocks, $pref_binary] = explode('|', User::$settings['mod_prefs']);
0 ignored issues
show
Bug introduced by
It seems like ElkArte\User::settings['mod_prefs'] can also be of type null; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

910
			[$show_reports, $mod_blocks, $pref_binary] = explode('|', /** @scrutinizer ignore-type */ User::$settings['mod_prefs']);
Loading history...
911
		}
912
913
		// Are we saving?
914
		if (isset($this->_req->post->save))
915
		{
916
			checkSession('post');
917
			validateToken('mod-set');
918
919
			/* Current format of mod_prefs is:
920
				x|ABCD|yyy
921
922
				WHERE:
923
					x = Show report count on forum header.
924
					ABCD = Block indexes to show on moderation main page.
925
					yyy = Integer with the following bit status:
926
						- yyy & 1 = Always notify on reports.
927
						- yyy & 2 = Notify on reports for moderators only.
928
						- yyy & 4 = Notify about posts awaiting approval.
929
			*/
930
931
			// Do blocks first!
932
			$mod_blocks = '';
933
			if (!empty($this->_req->post->mod_homepage))
934
			{
935
				foreach ($this->_req->post->mod_homepage as $k => $v)
936
				{
937
					// Make sure they can add this...
938
					if (isset($context['homepage_blocks'][$k]))
939
					{
940
						$mod_blocks .= $k;
941
					}
942
				}
943
			}
944
945
			// Now check other options!
946
			$pref_binary = 0;
947
948
			if ($context['can_moderate_approvals'] && !empty($this->_req->post->mod_notify_approval))
949
			{
950
				$pref_binary |= 4;
951
			}
952
953
			if ($context['can_moderate_boards'])
954
			{
955
				if (!empty($this->_req->post->mod_notify_report))
956
				{
957
					$pref_binary |= ($this->_req->post->mod_notify_report == 2 ? 1 : 2);
958
				}
959
960
				$show_reports = empty($this->_req->post->mod_show_reports) ? 0 : 1;
961
			}
962
963
			// Put it all together.
964
			$mod_prefs = $show_reports . '|' . $mod_blocks . '|' . $pref_binary;
965
			require_once(SUBSDIR . '/Members.subs.php');
966
			updateMemberData($this->user->id, array('mod_prefs' => $mod_prefs));
967
		}
968
969
		// What blocks does the user currently have selected?
970
		$context['mod_settings'] = array(
971
			'show_reports' => $show_reports,
972
			'notify_report' => $pref_binary & 2 ? 1 : ($pref_binary & 1 ? 2 : 0),
973
			'notify_approval' => $pref_binary & 4,
974
			'user_blocks' => str_split($mod_blocks),
975
		);
976
977
		createToken('mod-set');
978
	}
979
980
	/**
981
	 * View watched users and their posts
982
	 */
983
	public function action_viewWatchedUsers()
984
	{
985
		global $modSettings, $context, $txt;
986
987
		// Some important context!
988
		$context['page_title'] = $txt['mc_watched_users_title'];
989
		$context['view_posts'] = isset($this->_req->query->sa) && $this->_req->query->sa === 'post';
990
		$context['start'] = $this->_req->getQuery('start', 'intval', 0);
991
992
		theme()->getTemplates()->load('ModerationCenter');
993
994
		// Get some key settings!
995
		$modSettings['warning_watch'] = empty($modSettings['warning_watch']) ? 1 : $modSettings['warning_watch'];
996
997
		// Put some pretty tabs on cause we're gonna be doing hot stuff here...
998
		$context[$context['moderation_menu_name']]['object']->prepareTabData([
999
			'title' => $txt['mc_watched_users_title'],
1000
			'description' => $txt['mc_watched_users_desc'],
1001
		]);
1002
1003
		// First off - are we deleting?
1004
		if (!empty($this->_req->query->delete) || !empty($this->_req->post->delete))
1005
		{
1006
			checkSession(isset($this->_req->query->delete) ? 'get' : 'post');
1007
1008
			// Clicked on remove or using checkboxes to multi delete
1009
			$toDelete = array();
1010
			if (isset($this->_req->query->delete))
1011
			{
1012
				$toDelete[] = (int) $this->_req->query->delete;
1013
			}
1014
			else
1015
			{
1016
				$toDelete = array_map('intval', $this->_req->post->delete);
1017
			}
1018
1019
			if (!empty($toDelete))
1020
			{
1021
				$remover = new MessagesDelete($modSettings['recycle_enable'], $modSettings['recycle_board']);
1022
1023
				// If they don't have permission we'll let it error - either way no chance of a security slip here!
1024
				foreach ($toDelete as $did)
1025
				{
1026
					$remover->removeMessage($did);
1027
				}
1028
			}
1029
		}
1030
1031
		// Start preparing the list by grabbing relevant permissions.
1032
		if (!$context['view_posts'])
1033
		{
1034
			$approve_query = '';
1035
			$delete_boards = array();
1036
		}
1037
		else
1038
		{
1039
			// Still obey permissions!
1040
			$approve_boards = empty($this->user->mod_cache['ap']) ? boardsAllowedTo('approve_posts') : $this->user->mod_cache['ap'];
0 ignored issues
show
Bug Best Practice introduced by
The property mod_cache does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
1041
			$delete_boards = boardsAllowedTo('delete_any');
1042
1043
			if ($approve_boards == array(0))
1044
			{
1045
				$approve_query = '';
1046
			}
1047
			elseif (!empty($approve_boards))
1048
			{
1049
				$approve_query = ' AND m.id_board IN (' . implode(',', $approve_boards) . ')';
1050
			}
1051
			// Nada, zip, etc...
1052
			else
1053
			{
1054
				$approve_query = ' AND 1=0';
1055
			}
1056
		}
1057
1058
		// This is all the information required for a watched user listing.
1059
		$listOptions = array(
1060
			'id' => 'watch_user_list',
1061
			'title' => $txt['mc_watched_users_title'] . ' - ' . ($context['view_posts'] ? $txt['mc_watched_users_post'] : $txt['mc_watched_users_member']),
1062
			'width' => '100%',
1063
			'items_per_page' => $modSettings['defaultMaxMessages'],
1064
			'no_items_label' => $context['view_posts'] ? $txt['mc_watched_users_no_posts'] : $txt['mc_watched_users_none'],
1065
			'base_href' => getUrl('action', ['action' => 'moderate', 'area' => 'userwatch', 'sa' => ($context['view_posts'] ? 'post' : 'member')]),
1066
			'default_sort_col' => $context['view_posts'] ? '' : 'member',
1067
			'get_items' => array(
1068
				'function' => $context['view_posts']
1069
					? fn($start, $items_per_page, $sort, $approve_query, $delete_boards) => $this->list_getWatchedUserPosts($start, $items_per_page, $sort, $approve_query, $delete_boards)
1070
					: fn($start, $items_per_page, $sort) => $this->list_getWatchedUsers($start, $items_per_page, $sort),
1071
				'params' => array(
1072
					$approve_query,
1073
					$delete_boards,
1074
				),
1075
			),
1076
			'get_count' => array(
1077
				'function' => $context['view_posts']
1078
					? fn($approve_query) => $this->list_getWatchedUserPostsCount($approve_query)
1079
					: fn() => $this->list_getWatchedUserCount(),
1080
				'params' => array(
1081
					$approve_query,
1082
				),
1083
			),
1084
			// This assumes we are viewing by user.
1085
			'columns' => array(
1086
				'member' => array(
1087
					'header' => array(
1088
						'value' => $txt['mc_watched_users_member'],
1089
					),
1090
					'data' => array(
1091
						'sprintf' => array(
1092
							'format' => '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => '%1$d']) . '">%2$s</a>',
1093
							'params' => array(
1094
								'id' => false,
1095
								'name' => false,
1096
							),
1097
						),
1098
					),
1099
					'sort' => array(
1100
						'default' => 'real_name',
1101
						'reverse' => 'real_name DESC',
1102
					),
1103
				),
1104
				'warning' => array(
1105
					'header' => array(
1106
						'value' => $txt['mc_watched_users_warning'],
1107
					),
1108
					'data' => array(
1109
						'function' => static fn($member) => allowedTo('issue_warning') ? '<a href="' . getUrl('action', ['action' => 'profile', 'area' => 'issuewarning', 'u' => $member['id']]) . '">' . $member['warning'] . '%</a>' : $member['warning'] . '%',
1110
					),
1111
					'sort' => array(
1112
						'default' => 'warning',
1113
						'reverse' => 'warning DESC',
1114
					),
1115
				),
1116
				'posts' => array(
1117
					'header' => array(
1118
						'value' => $txt['posts'],
1119
					),
1120
					'data' => array(
1121
						'sprintf' => array(
1122
							'format' => '<a href="' . getUrl('action', ['action' => 'profile', 'u' => '%1$d', 'area' => 'showposts', 'sa' => 'messages']) . '">%2$s</a>',
1123
							'params' => array(
1124
								'id' => false,
1125
								'posts' => false,
1126
							),
1127
						),
1128
					),
1129
					'sort' => array(
1130
						'default' => 'posts',
1131
						'reverse' => 'posts DESC',
1132
					),
1133
				),
1134
				'last_login' => array(
1135
					'header' => array(
1136
						'value' => $txt['mc_watched_users_last_login'],
1137
					),
1138
					'data' => array(
1139
						'db' => 'last_login',
1140
					),
1141
					'sort' => array(
1142
						'default' => 'last_login',
1143
						'reverse' => 'last_login DESC',
1144
					),
1145
				),
1146
				'last_post' => array(
1147
					'header' => array(
1148
						'value' => $txt['mc_watched_users_last_post'],
1149
					),
1150
					'data' => array(
1151
						'function' => static function ($member) {
1152
							if ($member['last_post_id'])
1153
							{
1154
								return '<a href="' . getUrl('action', ['msg' => $member['last_post_id']]) . '">' . $member['last_post'] . '</a>';
1155
							}
1156
1157
							return $member['last_post'];
1158
						},
1159
					),
1160
				),
1161
			),
1162
			'form' => array(
1163
				'href' => getUrl('action', ['action' => 'moderate', 'area' => 'userwatch', 'sa' => 'post']),
1164
				'include_sort' => true,
1165
				'include_start' => true,
1166
				'hidden_fields' => array(
1167
					$context['session_var'] => $context['session_id'],
1168
				),
1169
			),
1170
			'additional_rows' => array(
1171
				$context['view_posts'] ?
1172
					array(
1173
						'position' => 'below_table_data',
1174
						'value' => '
1175
						<input type="submit" name="delete_selected" value="' . $txt['quickmod_delete_selected'] . '" class="right_submit" />',
1176
					) : array(),
1177
			),
1178
		);
1179
1180
		// If this is being viewed by posts we actually change the columns to call a template each time.
1181
		if ($context['view_posts'])
1182
		{
1183
			$listOptions['columns'] = array(
1184
				'posts' => array(
1185
					'data' => array(
1186
						'function' => static fn($post) => template_user_watch_post_callback($post),
1187
					),
1188
				),
1189
			);
1190
		}
1191
1192
		// Create the watched user list.
1193
		createList($listOptions);
1194
1195
		$context['sub_template'] = 'show_list';
1196
		$context['default_list'] = 'watch_user_list';
1197
	}
1198
1199
	/**
1200
	 * Callback for createList().
1201
	 *
1202
	 * @param int $start The item to start with (for pagination purposes)
1203
	 * @param int $items_per_page The number of items to show per page
1204
	 * @param string $sort A string indicating how to sort the results
1205
	 * @param string $approve_query
1206
	 * @param int[] $delete_boards
1207
	 *
1208
	 * @return array
1209
	 * @uses watchedUserPosts()
1210
	 *
1211
	 */
1212
	public function list_getWatchedUserPosts($start, $items_per_page, $sort, $approve_query, $delete_boards)
1213
	{
1214
		// Watched users posts
1215
		return watchedUserPosts($start, $items_per_page, $approve_query, $delete_boards);
1216
	}
1217
1218
	/**
1219
	 * Callback for createList() used in watched users
1220
	 *
1221
	 * @param int $start The item to start with (for pagination purposes)
1222
	 * @param int $items_per_page The number of items to show per page
1223
	 * @param string $sort A string indicating how to sort the results
1224
	 *
1225
	 * @return array
1226
	 * @uses watchedUsers()
1227
	 *
1228
	 */
1229
	public function list_getWatchedUsers($start, $items_per_page, $sort)
1230
	{
1231
		// Find all our watched users
1232
		return watchedUsers($start, $items_per_page, $sort);
1233
	}
1234
1235
	/**
1236
	 * Callback for createList().
1237
	 *
1238
	 * @param string $approve_query
1239
	 *
1240
	 * @return int
1241
	 * @uses watchedUserPostsCount()
1242
	 *
1243
	 */
1244
	public function list_getWatchedUserPostsCount($approve_query)
1245
	{
1246
		global $modSettings;
1247
1248
		return watchedUserPostsCount($approve_query, $modSettings['warning_watch']);
1249
	}
1250
1251
	/**
1252
	 * Callback for createList() for watched users
1253
	 *
1254
	 * - returns count
1255
	 *
1256
	 * @uses watchedUserCount()
1257
	 */
1258
	public function list_getWatchedUserCount()
1259
	{
1260
		global $modSettings;
1261
1262
		return watchedUserCount($modSettings['warning_watch']);
1263
	}
1264
1265
	/**
1266
	 * Simply put, look at the warning log!
1267
	 */
1268
	public function action_viewWarningLog()
1269
	{
1270
		global $modSettings, $context, $txt;
1271
1272
		// Setup context as always.
1273
		$context['page_title'] = $txt['mc_warning_log_title'];
1274
1275
		require_once(SUBSDIR . '/Moderation.subs.php');
1276
		Txt::load('Modlog');
1277
1278
		// If we're coming in from a search, get the variables.
1279
		if (!empty($this->_req->post->params) && empty($this->_req->post->is_search))
1280
		{
1281
			$search_params = base64_decode(strtr($this->_req->post->params, array(' ' => '+')));
1282
			$search_params = @json_decode($search_params);
1283
		}
1284
1285
		// This array houses all the valid search types.
1286
		$searchTypes = array(
1287
			'member' => array('sql' => 'mem.real_name', 'label' => $txt['profile_warning_previous_issued']),
1288
			'recipient' => array('sql' => 'recipient_name', 'label' => $txt['mc_warnings_recipient']),
1289
		);
1290
1291
		// Setup the allowed quick search type
1292
		$context['order'] = isset($this->_req->query->sort) && isset($searchTypes[$this->_req->query->sort]) ? $this->_req->query->sort : 'member';
1293
1294
		if (!isset($search_params['string']) || (!empty($this->_req->post->search) && $search_params['string'] !== $this->_req->post->search))
1295
		{
1296
			$search_params_string = empty($this->_req->post->search) ? '' : $this->_req->post->search;
1297
		}
1298
		else
1299
		{
1300
			$search_params_string = $search_params['string'];
1301
		}
1302
1303
		if (isset($this->_req->post->search_type) || empty($search_params['type']) || !isset($searchTypes[$search_params['type']]))
1304
		{
1305
			$search_params_type = isset($this->_req->post->search_type, $searchTypes[$this->_req->post->search_type]) ? $this->_req->post->search_type : (isset($searchTypes[$context['order']]) ? $context['order'] : 'member');
1306
		}
1307
		else
1308
		{
1309
			$search_params_type = $search_params['type'];
1310
		}
1311
1312
		$search_params_column = $searchTypes[$search_params_type]['sql'];
1313
		$search_params = array(
1314
			'string' => $search_params_string,
1315
			'type' => $search_params_type,
1316
		);
1317
1318
		// Setup the search context.
1319
		$context['search_params'] = empty($search_params['string']) ? '' : base64_encode(json_encode($search_params));
1320
		$context['search'] = array(
1321
			'string' => $search_params['string'],
1322
			'type' => $search_params['type'],
1323
			'label' => $searchTypes[$search_params_type]['label'],
1324
		);
1325
1326
		// This is all the information required for a watched user listing.
1327
		$listOptions = array(
1328
			'id' => 'warning_list',
1329
			'title' => $txt['mc_warning_log_title'],
1330
			'items_per_page' => $modSettings['defaultMaxMessages'],
1331
			'no_items_label' => $txt['mc_warnings_none'],
1332
			'base_href' => getUrl('action', ['action' => 'moderate', 'area' => 'warnings', 'sa' => 'log', '{session_data}']),
1333
			'default_sort_col' => 'time',
1334
			'get_items' => array(
1335
				'function' => fn($start, $items_per_page, $sort, $query_string, $query_params) => $this->list_getWarnings($start, $items_per_page, $sort, $query_string, $query_params),
1336
				'params' => array(
1337
					(empty($search_params['string']) ? '' : ' INSTR({raw:sql_type}, {string:search_string})'),
1338
					array('sql_type' => $search_params_column, 'search_string' => $search_params['string']),
1339
				),
1340
			),
1341
			'get_count' => array(
1342
				'function' => fn($query_string, $query_params) => $this->list_getWarningCount($query_string, $query_params),
1343
				'params' => array(
1344
					(empty($search_params['string']) ? '' : ' INSTR({raw:sql_type}, {string:search_string})'),
1345
					array('sql_type' => $search_params_column, 'search_string' => $search_params['string']),
1346
				),
1347
			),
1348
			// This assumes we are viewing by user.
1349
			'columns' => array(
1350
				'issuer' => array(
1351
					'header' => array(
1352
						'value' => $txt['profile_warning_previous_issued'],
1353
					),
1354
					'data' => array(
1355
						'db' => 'issuer_link',
1356
					),
1357
					'sort' => array(
1358
						'default' => 'member_name_col',
1359
						'reverse' => 'member_name_col DESC',
1360
					),
1361
				),
1362
				'recipient' => array(
1363
					'header' => array(
1364
						'value' => $txt['mc_warnings_recipient'],
1365
					),
1366
					'data' => array(
1367
						'db' => 'recipient_link',
1368
					),
1369
					'sort' => array(
1370
						'default' => 'recipient_name',
1371
						'reverse' => 'recipient_name DESC',
1372
					),
1373
				),
1374
				'time' => array(
1375
					'header' => array(
1376
						'value' => $txt['profile_warning_previous_time'],
1377
					),
1378
					'data' => array(
1379
						'db' => 'time',
1380
					),
1381
					'sort' => array(
1382
						'default' => 'lc.log_time DESC',
1383
						'reverse' => 'lc.log_time',
1384
					),
1385
				),
1386
				'reason' => array(
1387
					'header' => array(
1388
						'value' => $txt['profile_warning_previous_reason'],
1389
					),
1390
					'data' => array(
1391
						'function' => static function ($warning) {
1392
							global $txt;
1393
1394
							$output = '
1395
								<div class="floatleft">
1396
									' . $warning['reason'] . '
1397
								</div>';
1398
1399
							// If a notice was sent, provide a link to it
1400
							if (!empty($warning['id_notice']))
1401
							{
1402
								$output .= '
1403
									<a href="' . getUrl('action', ['action' => 'moderate', 'area' => 'notice', 'nid' => $warning['id_notice']]) . '" onclick="window.open(this.href, \'\', \'scrollbars=yes,resizable=yes,width=480,height=320\');return false;" target="_blank" class="new_win" title="' . $txt['profile_warning_previous_notice'] . '"><i class="icon icon-small i-search" title"' . $txt['profile_warning_previous_notice'] . '"></i></a>';
1404
							}
1405
1406
							return $output;
1407
						},
1408
					),
1409
				),
1410
				'points' => array(
1411
					'header' => array(
1412
						'value' => $txt['profile_warning_previous_level'],
1413
					),
1414
					'data' => array(
1415
						'db' => 'counter',
1416
					),
1417
				),
1418
			),
1419
			'form' => array(
1420
				'href' => getUrl('action', ['action' => 'moderate', 'area' => 'warnings', 'sa' => 'log', 'sort' => $context['order']]),
1421
				'include_sort' => true,
1422
				'include_start' => true,
1423
				'hidden_fields' => array(
1424
					$context['session_var'] => $context['session_id'],
1425
					'params' => $context['search_params']
1426
				),
1427
			),
1428
			'additional_rows' => array(
1429
				array(
1430
					'class' => 'submitbutton',
1431
					'position' => 'below_table_data',
1432
					'value' => '
1433
						' . $txt['modlog_search'] . ' (' . $txt['modlog_by'] . ': ' . $context['search']['label'] . ')
1434
						<input type="text" name="search" size="18" value="' . Util::htmlspecialchars($context['search']['string']) . '" class="input_text" />
1435
						<input type="submit" name="is_search" value="' . $txt['modlog_go'] . '" />',
1436
				),
1437
			),
1438
		);
1439
1440
		// Create the watched user list.
1441
		createList($listOptions);
1442
1443
		$context['sub_template'] = 'show_list';
1444
		$context['default_list'] = 'warning_list';
1445
	}
1446
1447
	/**
1448
	 * Callback for createList()
1449
	 *
1450
	 * - Used to get all issued warnings in the system
1451
	 *
1452
	 * @param int $start The item to start with (for pagination purposes)
1453
	 * @param int $items_per_page The number of items to show per page
1454
	 * @param string $sort A string indicating how to sort the results
1455
	 * @param string $query_string
1456
	 * @param mixed[] $query_params
1457
	 *
1458
	 * @return array
1459
	 * @uses warnings() function in moderation.subs
1460
	 *
1461
	 */
1462
	public function list_getWarnings($start, $items_per_page, $sort, $query_string, $query_params)
1463
	{
1464
		return warnings($start, $items_per_page, $sort, $query_string, $query_params);
1465
	}
1466
1467
	/**
1468
	 * Callback for createList()
1469
	 *
1470
	 * - Get the total count of all current warnings
1471
	 *
1472
	 * @param string $query_string
1473
	 * @param mixed[] $query_params
1474
	 *
1475
	 * @return int
1476
	 * @uses warningCount() function in moderation.subs
1477
	 *
1478
	 */
1479
	public function list_getWarningCount($query_string, $query_params)
1480
	{
1481
		return warningCount($query_string, $query_params);
1482
	}
1483
1484
	/**
1485
	 * View all the custom warning templates.
1486
	 *
1487
	 *  - Shows all the templates in the system
1488
	 *  - Provides for actions to add or delete them
1489
	 */
1490
	public function action_viewWarningTemplates()
1491
	{
1492
		global $modSettings, $context, $txt;
1493
1494
		require_once(SUBSDIR . '/Moderation.subs.php');
1495
		// Submitting a new one?
1496
		if (isset($this->_req->post->add))
1497
		{
1498
			$this->action_modifyWarningTemplate();
1499
			return true;
1500
		}
1501
1502
		// Deleting and existing one
1503
		if (isset($this->_req->post->delete) && !empty($this->_req->post->deltpl))
1504
		{
1505
			checkSession('post');
1506
			validateToken('mod-wt');
1507
			removeWarningTemplate($this->_req->post->deltpl);
1508
		}
1509
1510
		// Setup context as always.
1511
		$context['page_title'] = $txt['mc_warning_templates_title'];
1512
1513
		// This is all the information required for a watched user listing.
1514
		$listOptions = array(
1515
			'id' => 'warning_template_list',
1516
			'title' => $txt['mc_warning_templates_title'],
1517
			'items_per_page' => $modSettings['defaultMaxMessages'],
1518
			'no_items_label' => $txt['mc_warning_templates_none'],
1519
			'base_href' => getUrl('action', ['action' => 'moderate', 'area' => 'warnings', 'sa' => 'templates', '{session_data}']),
1520
			'default_sort_col' => 'title',
1521
			'get_items' => array(
1522
				'function' => fn($start, $items_per_page, $sort, $template_type = 'warntpl') => $this->list_getWarningTemplates($start, $items_per_page, $sort, $template_type),
1523
			),
1524
			'get_count' => array(
1525
				'function' => fn($template_type = 'warntpl') => $this->list_getWarningTemplateCount($template_type),
1526
			),
1527
			'columns' => array(
1528
				'title' => array(
1529
					'header' => array(
1530
						'value' => $txt['mc_warning_templates_name'],
1531
					),
1532
					'data' => array(
1533
						'sprintf' => array(
1534
							'format' => '<a href="' . getUrl('action', ['action' => 'moderate', 'area' => 'warnings', 'sa' => 'templateedit', 'tid' => '%1$d']) . '">%2$s</a>',
1535
							'params' => array(
1536
								'id_comment' => false,
1537
								'title' => false,
1538
								'body' => false,
1539
							),
1540
						),
1541
					),
1542
					'sort' => array(
1543
						'default' => 'template_title',
1544
						'reverse' => 'template_title DESC',
1545
					),
1546
				),
1547
				'creator' => array(
1548
					'header' => array(
1549
						'value' => $txt['mc_warning_templates_creator'],
1550
					),
1551
					'data' => array(
1552
						'db' => 'creator',
1553
					),
1554
					'sort' => array(
1555
						'default' => 'creator_name',
1556
						'reverse' => 'creator_name DESC',
1557
					),
1558
				),
1559
				'time' => array(
1560
					'header' => array(
1561
						'value' => $txt['mc_warning_templates_time'],
1562
					),
1563
					'data' => array(
1564
						'db' => 'time',
1565
					),
1566
					'sort' => array(
1567
						'default' => 'lc.log_time DESC',
1568
						'reverse' => 'lc.log_time',
1569
					),
1570
				),
1571
				'delete' => array(
1572
					'header' => array(
1573
						'value' => '<input type="checkbox" class="input_check" onclick="invertAll(this, this.form);" />',
1574
						'style' => 'width: 4%;text-align: center;',
1575
					),
1576
					'data' => array(
1577
						'function' => static fn($rowData) => '<input type="checkbox" name="deltpl[]" value="' . $rowData['id_comment'] . '" class="input_check" />',
1578
						'class' => 'centertext',
1579
					),
1580
				),
1581
			),
1582
			'form' => array(
1583
				'href' => getUrl('action', ['action' => 'moderate', 'area' => 'warnings', 'sa' => 'templates']),
1584
				'token' => 'mod-wt',
1585
			),
1586
			'additional_rows' => array(
1587
				array(
1588
					'position' => 'below_table_data',
1589
					'value' => '
1590
						<input type="submit" name="delete" value="' . $txt['mc_warning_template_delete'] . '" onclick="return confirm(\'' . $txt['mc_warning_template_delete_confirm'] . '\');" class="right_submit" />
1591
						<input type="submit" name="add" value="' . $txt['mc_warning_template_add'] . '" class="right_submit" />',
1592
				),
1593
			),
1594
		);
1595
1596
		// Create the watched user list.
1597
		createToken('mod-wt');
1598
		createList($listOptions);
1599
1600
		$context['sub_template'] = 'show_list';
1601
		$context['default_list'] = 'warning_template_list';
1602
	}
1603
1604
	/**
1605
	 * Edit a warning template.
1606
	 *
1607
	 * @uses template_warn_template()
1608
	 */
1609
	public function action_modifyWarningTemplate()
1610
	{
1611
		global $context, $txt;
1612
1613
		require_once(SUBSDIR . '/Moderation.subs.php');
1614
		loadJavascriptFile('admin.js', array(), 'admin_scripts');
1615
1616
		$context['id_template'] = $this->_req->getQuery('tid', 'intval', 0);
1617
		$context['is_edit'] = $context['id_template'];
1618
1619
		// Standard template things.
1620
		$context['page_title'] = $context['is_edit'] ? $txt['mc_warning_template_modify'] : $txt['mc_warning_template_add'];
1621
		$context['sub_template'] = 'warn_template';
1622
		$context[$context['moderation_menu_name']]['current_subsection'] = 'templates';
1623
1624
		// Defaults.
1625
		$context['template_data'] = array(
1626
			'title' => '',
1627
			'body' => $txt['mc_warning_template_body_default'],
1628
			'personal' => false,
1629
			'can_edit_personal' => true,
1630
		);
1631
1632
		// If it's an edit load it.
1633
		if ($context['is_edit'])
1634
		{
1635
			modLoadTemplate($context['id_template']);
1636
		}
1637
1638
		// Wait, we are saving?
1639
		if (isset($this->_req->post->save))
1640
		{
1641
			checkSession('post');
1642
			validateToken('mod-wt');
1643
1644
			// To check the BBC is pretty good...
1645
			require_once(SUBSDIR . '/Post.subs.php');
1646
1647
			// Bit of cleaning!
1648
			$template_body = trim($this->_req->post->template_body);
1649
			$template_title = trim($this->_req->post->template_title);
1650
1651
			// Need something in both boxes.
1652
			if (!empty($template_body) && !empty($template_title))
1653
			{
1654
				// Safety first.
1655
				$template_title = Util::htmlspecialchars($template_title);
1656
1657
				// Clean up BBC.
1658
				preparsecode($template_body);
1659
1660
				// But put line breaks back!
1661
				$template_body = strtr($template_body, array('<br />' => "\n"));
1662
1663
				// Is this personal?
1664
				$recipient_id = empty($this->_req->post->make_personal) ? 0 : $this->user->id;
1665
1666
				// If we are this far it's save time.
1667
				if ($context['is_edit'])
1668
				{
1669
					// Simple update...
1670
					modAddUpdateTemplate($recipient_id, $template_title, $template_body, $context['id_template']);
1671
1672
					// If it wasn't visible and now is they've effectively added it.
1673
					if ($context['template_data']['personal'] && !$recipient_id)
1674
					{
1675
						logAction('add_warn_template', array('template' => $template_title));
1676
					}
1677
					// Conversely if they made it personal it's a delete.
1678
					elseif (!$context['template_data']['personal'] && $recipient_id)
1679
					{
1680
						logAction('delete_warn_template', array('template' => $template_title));
1681
					}
1682
					// Otherwise just an edit.
1683
					else
1684
					{
1685
						logAction('modify_warn_template', array('template' => $template_title));
1686
					}
1687
				}
1688
				else
1689
				{
1690
					modAddUpdateTemplate($recipient_id, $template_title, $template_body, $context['id_template'], false);
1691
					logAction('add_warn_template', array('template' => $template_title));
1692
				}
1693
1694
				// Get out of town...
1695
				redirectexit('action=moderate;area=warnings;sa=templates');
1696
			}
1697
			else
1698
			{
1699
				$context['warning_errors'] = array();
1700
				$context['template_data']['title'] = empty($template_title) ? '' : $template_title;
1701
				$context['template_data']['body'] = empty($template_body) ? $txt['mc_warning_template_body_default'] : $template_body;
1702
				$context['template_data']['personal'] = !empty($this->_req->post->make_personal);
1703
1704
				if (empty($template_title))
1705
				{
1706
					$context['warning_errors'][] = $txt['mc_warning_template_error_no_title'];
1707
				}
1708
1709
				if (empty($template_body))
1710
				{
1711
					$context['warning_errors'][] = $txt['mc_warning_template_error_no_body'];
1712
				}
1713
			}
1714
		}
1715
1716
		createToken('mod-wt');
1717
	}
1718
1719
	/**
1720
	 * Callback for createList() to get all the templates of a type from the system
1721
	 *
1722
	 * @param int $start The item to start with (for pagination purposes)
1723
	 * @param int $items_per_page The number of items to show per page
1724
	 * @param string $sort A string indicating how to sort the results
1725
	 * @param string $template_type type of template to load
1726
	 *
1727
	 * @return array
1728
	 * @uses warningTemplates()
1729
	 *
1730
	 */
1731
	public function list_getWarningTemplates($start, $items_per_page, $sort, $template_type = 'warntpl')
1732
	{
1733
		return warningTemplates($start, $items_per_page, $sort, $template_type);
1734
	}
1735
1736
	/**
1737
	 * Callback for createList() to get the number of templates of a type in the system
1738
	 *
1739
	 * @param string $template_type
1740
	 *
1741
	 * @return int
1742
	 * @uses warningTemplateCount()
1743
	 *
1744
	 */
1745
	public function list_getWarningTemplateCount($template_type = 'warntpl')
1746
	{
1747
		return warningTemplateCount($template_type);
1748
	}
1749
1750
	/**
1751
	 * Entry point for viewing warning related stuff.
1752
	 */
1753
	public function action_viewWarnings()
1754
	{
1755
		global $context, $txt;
1756
1757
		// Some of this stuff is overseas, so to speak.
1758
		theme()->getTemplates()->load('ModerationCenter');
1759
		Txt::load('Profile');
1760
1761
		$subActions = array(
1762
			'log' => array($this, 'action_viewWarningLog'),
1763
			'templateedit' => array($this, 'action_modifyWarningTemplate', 'permission' => 'issue_warning'),
1764
			'templates' => array($this, 'action_viewWarningTemplates', 'permission' => 'issue_warning'),
1765
		);
1766
1767
		// Setup the admin tabs.
1768
		$context[$context['moderation_menu_name']]['object']->prepareTabData([
1769
			'title' => $txt['mc_warnings'],
1770
			'description' => $txt['mc_warnings_description'],
1771
		]);
1772
1773
		// Call the right function.
1774
		$action = new Action('moderation_center');
1775
		$subAction = $action->initialize($subActions, 'log');
1776
		$context['sub_action'] = $subAction;
1777
		$action->dispatch($subAction);
1778
	}
1779
1780
	/**
1781
	 * Show a list of all the group requests they can see.
1782
	 * Checks permissions for group moderation.
1783
	 */
1784
	public function block_groupRequests()
1785
	{
1786
		global $context;
1787
1788
		// Make sure they can even moderate someone!
1789
		if ($this->user->mod_cache['gq'] === '0=1')
0 ignored issues
show
Bug Best Practice introduced by
The property mod_cache does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
1790
		{
1791
			return 'group_requests_block';
1792
		}
1793
1794
		$context['group_requests'] = groupRequests();
1795
1796
		return 'group_requests_block';
1797
	}
1798
1799
	/**
1800
	 * Just prepares the time stuff for the latest news.
1801
	 */
1802
	public function block_latestNews()
1803
	{
1804
		global $context;
1805 2
1806
		$context['time_format'] = urlencode($this->user->time_format);
0 ignored issues
show
Bug introduced by
It seems like $this->user->time_format can also be of type null; however, parameter $string of urlencode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

1806
		$context['time_format'] = urlencode(/** @scrutinizer ignore-type */ $this->user->time_format);
Loading history...
Bug Best Practice introduced by
The property time_format does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
1807 2
1808
		// Return the template to use.
1809
		return 'latest_news';
1810 2
	}
1811
1812
	/**
1813
	 * Show a list of the most active watched users.
1814
	 */
1815 2
	public function block_watchedUsers()
1816
	{
1817 2
		global $context;
1818
1819
		$watched_users = basicWatchedUsers();
1820
1821
		$context['watched_users'] = array();
1822
		if (is_array($watched_users) || is_object($watched_users))
1823 2
		{
1824
			foreach ($watched_users as $user)
1825 2
			{
1826
				$context['watched_users'][] = array(
1827 2
					'id' => $user['id_member'],
1828
					'name' => $user['real_name'],
1829
					'link' => '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $user['id_member']]) . '">' . $user['real_name'] . '</a>',
1830 2
					'href' => getUrl('profile', ['action' => 'profile', 'u' => $user['id_member']]),
1831
					'last_login' => empty($user['last_login']) ? '' : standardTime($user['last_login']),
1832
				);
1833
			}
1834
		}
1835
1836 2
		return 'watched_users';
1837
	}
1838 2
1839
	/**
1840 2
	 * Shows a list of items requiring moderation action
1841
	 * Includes post, topic, attachment, group, member and PBE values with links to each
1842 2
	 */
1843 2
	public function block_actionRequired()
1844
	{
1845
		global $context;
1846
1847
		// Get the action totals
1848
		$mod_totals = loadModeratorMenuCounts();
1849
1850
		// This blocks total is only these fields
1851
		$context['mc_required'] = $mod_totals['attachments'] + $mod_totals['emailmod'] + $mod_totals['topics'] + $mod_totals['posts'] + $mod_totals['memberreq'] + $mod_totals['groupreq'] + +$mod_totals['reports'];
1852
		unset($mod_totals['postmod'], $mod_totals['pt_total'], $mod_totals['mg_total'], $mod_totals['grand_total']);
1853
		$context['required'] = $mod_totals;
1854
1855
		// Links to the areas
1856
		$context['links'] = array(
1857 2
			'attachments' => '?action=moderate;area=attachmod;sa=attachments',
1858
			'emailmod' => '?action=admin;area=maillist;sa=emaillist',
1859
			'topics' => '?action=moderate;area=postmod;sa=topics',
1860
			'posts' => '?action=moderate;area=postmod;sa=posts',
1861
			'memberreq' => '?action=admin;area=viewmembers;sa=browse;type=approve',
1862
			'groupreq' => '?action=moderate;area=groups;sa=requests',
1863
			'reports' => '?action=moderate;area=reports;sa=open',
1864 2
			'pm_reports' => '?action=moderate;area=pm_reports;sa=open',
1865
		);
1866 2
1867
		return 'action_required';
1868
	}
1869 2
1870
	/**
1871
	 * Show an area for the moderator to type into.
1872 2
	 */
1873 2
	public function block_notes()
1874 2
	{
1875
		global $context, $txt;
1876
1877 2
		// Are we saving a note?
1878
		if (isset($this->_req->post->makenote) && isset($this->_req->post->new_note))
1879
		{
1880
			checkSession();
1881
1882
			$new_note = Util::htmlspecialchars(trim($this->_req->post->new_note));
1883
1884
			// Make sure they actually entered something.
1885
			if (!empty($new_note) && $new_note !== $txt['mc_click_add_note'])
1886
			{
1887
				// Insert it into the database then!
1888 2
				addModeratorNote($this->user->id, $this->user->name, $new_note);
1889
1890
				// Clear the cache.
1891
				Cache::instance()->remove('moderator_notes');
1892
				Cache::instance()->remove('moderator_notes_total');
1893
			}
1894
1895
			// Redirect otherwise people can resubmit.
1896
			redirectexit('action=moderate');
1897
		}
1898
1899
		// Bye... bye...
1900
		if (isset($this->_req->query->notes, $this->_req->query->delete) && is_numeric($this->_req->query->delete))
1901
		{
1902
			checkSession('get');
1903
1904
			// Just checkin'!
1905
			$id_delete = (int) $this->_req->query->delete;
1906
1907
			// Lets delete it.
1908
			removeModeratorNote($id_delete);
1909
1910
			// Clear the cache.
1911
			Cache::instance()->remove('moderator_notes');
1912
			Cache::instance()->remove('moderator_notes_total');
1913
1914
			redirectexit('action=moderate');
1915
		}
1916
1917
		// How many notes in total?
1918
		$moderator_notes_total = countModeratorNotes();
1919
1920
		// Grab the current notes. We can only use the cache for the first page of notes.
1921
		$offset = isset($this->_req->query->notes) && isset($this->_req->query->start) ? $this->_req->query->start : 0;
1922
		$moderator_notes = moderatorNotes($offset);
1923
1924
		// Lets construct a page index.
1925
		$context['page_index'] = constructPageIndex('{scripturl}?action=moderate;area=index;notes', $this->_req->query->start, $moderator_notes_total, 10);
1926
		$context['start'] = $this->_req->query->start;
1927
1928
		$bbc_parser = ParserWrapper::instance();
1929
1930
		$context['notes'] = array();
1931
		foreach ($moderator_notes as $note)
1932
		{
1933
			$context['notes'][] = array(
1934
				'author' => array(
1935
					'id' => $note['id_member'],
1936
					'link' => $note['id_member'] ? ('<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $note['id_member']]) . '" title="' . $txt['on'] . ' ' . strip_tags(standardTime($note['log_time'])) . '">' . $note['member_name'] . '</a>') : $note['member_name'],
1937
				),
1938
				'time' => standardTime($note['log_time']),
1939
				'html_time' => htmlTime($note['log_time']),
1940
				'timestamp' => forum_time(true, $note['log_time']),
1941
				'text' => $bbc_parser->parseReport($note['body']),
1942
				'delete_href' => getUrl('action', ['action' => 'moderate', 'area' => 'index', 'notes' => '', 'delete=' . $note['id_note'], '{session_data}']),
1943
			);
1944
		}
1945
1946
		return 'notes';
1947
	}
1948
1949
	/**
1950
	 * Show a list of the most recent reported posts.
1951
	 */
1952
	public function block_reportedPosts()
1953
	{
1954
		global $context;
1955
1956
		if ($this->user->mod_cache['bq'] === '0=1')
0 ignored issues
show
Bug Best Practice introduced by
The property mod_cache does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
1957
		{
1958
			return 'reported_posts_block';
1959
		}
1960
1961
		$context['reported_posts'] = array();
1962
1963
		$reported_posts = reportedPosts(false);
1964
		foreach ($reported_posts as $row)
1965
		{
1966
			$context['reported_posts'][] = array(
1967
				'id' => $row['id_report'],
1968
				'topic_href' => getUrl('topic', ['topic' => $row['id_topic'], 'msg' => $row['id_msg'], 'start' => $row['id_msg'], 'hash' => '#msg' . $row['id_msg']]),
1969
				'report_href' => getUrl('action', ['action' => 'moderate', 'area' => $context['admin_area'], 'report' => $row['id_report']]),
1970
				'author' => array(
1971
					'id' => $row['id_author'],
1972
					'name' => $row['author_name'],
1973 2
					'link' => $row['id_author'] ? '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row['id_author']]) . '">' . $row['author_name'] . '</a>' : $row['author_name'],
1974
					'href' => getUrl('profile', ['action' => 'profile', 'u' => $row['id_author']]),
1975 2
				),
1976
				'comments' => array(),
1977 2
				'subject' => $row['subject'],
1978
				'num_reports' => $row['num_reports'],
1979
			);
1980
		}
1981
1982 2
		return 'reported_posts_block';
1983
	}
1984
}
1985