ModerationCenter::list_getWarningTemplates()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

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

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