MessageIndex::action_messageindex()   B
last analyzed

Complexity

Conditions 9
Paths 24

Size

Total Lines 65
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 11.42

Importance

Changes 3
Bugs 0 Features 1
Metric Value
cc 9
eloc 23
c 3
b 0
f 1
nc 24
nop 0
dl 0
loc 65
ccs 20
cts 29
cp 0.6897
crap 11.42
rs 8.0555

How to fix   Long Method   

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
 * This file is what shows the listing of topics in a board.
5
 * It's just one or two functions, but don't underestimate it ;).
6
 *
7
 * @package   ElkArte Forum
8
 * @copyright ElkArte Forum contributors
9
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
10
 *
11
 * This file contains code covered by:
12
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
13
 *
14
 * @version 2.0 Beta 1
15
 *
16
 */
17
18
namespace ElkArte\Controller;
19
20
use BBC\ParserWrapper;
21
use ElkArte\AbstractController;
22
use ElkArte\BoardsList;
23
use ElkArte\EventManager;
24
use ElkArte\FrontpageInterface;
25
use ElkArte\Helper\DataValidator;
26
use ElkArte\MembersList;
27
use ElkArte\Themes\TemplateLayers;
28
use ElkArte\TopicUtil;
29
use ElkArte\User;
30
31
/**
32
 * The all powerful messageindex, shows all the topics on a given board
33
 */
34
class MessageIndex extends AbstractController implements FrontpageInterface
35
{
36
	/** @var string The db column wer are going to sort */
37
	public $sort_column = '';
38
39
	/** @var array Know sort methods to db column */
40
	public $sort_methods = [];
41
42
	/** @var bool Sort direction asc or desc */
43
	public $ascending = '';
44
45
	/** @var bool if we are marking as read */
46
	public $is_marked_notify;
47
48
	/** @var string Chosen sort method from the request */
49
	public $sort_by;
50
51
	/** @var int Basically the page start */
52
	public $sort_start;
53
54
	/**
55
	 * {@inheritDoc}
56
	 */
57
	public static function frontPageHook(&$default_action)
58
	{
59
		add_integration_function('integrate_menu_buttons', '\\ElkArte\\Controller\\MessageIndex::addForumButton', '', false);
60
		add_integration_function('integrate_current_action', '\\ElkArte\\Controller\\MessageIndex::fixCurrentAction', '', false);
61
62
		$default_action = [
63
			'controller' => MessageIndex::class,
64
			'function' => 'action_messageindex_fp'
65
		];
66
	}
67
68
	/**
69
	 * {@inheritDoc}
70
	 */
71
	public static function frontPageOptions()
72
	{
73
		parent::frontPageOptions();
74
75
		theme()->addInlineJavascript('
76
			document.getElementById("front_page").addEventListener("change", function() {
77
			    let base = document.getElementById("message_index_frontpage").parentNode;
78
			
79
			    if (this.value.endsWith("MessageIndex")) 
80
			    {
81
			        base.fadeIn();
82
			        base.previousElementSibling.fadeIn();
83
			    }
84
			    else 
85
			    {
86
			        base.fadeOut();
87
			        base.previousElementSibling.fadeOut();
88
			    }
89
			});
90
			
91
			// Trigger change event
92
			let event = new Event("change");
93
			document.getElementById("front_page").dispatchEvent(event);', true);
94
95
		return [['select', 'message_index_frontpage', self::_getBoardsList()]];
96
	}
97
98
	/**
99
	 * Return the board listing for use in this class
100
	 *
101
	 * @return string[] list of boards with key = id and value = cat + name
102
	 * @uses getBoardList()
103
	 */
104
	protected static function _getBoardsList(): array
105
	{
106
		// Load the boards list.
107
		require_once(SUBSDIR . '/Boards.subs.php');
108
		$boards_list = getBoardList(['override_permissions' => true, 'not_redirection' => true], true);
109
110
		$boards = [];
111
		foreach ($boards_list as $board)
112
		{
113
			$boards[$board['id_board']] = $board['cat_name'] . ' - ' . $board['board_name'];
114
		}
115 2
116
		return $boards;
117
	}
118 2
119 2
	/**
120
	 * {@inheritDoc}
121
	 */
122
	public static function validateFrontPageOptions($post)
123
	{
124
		parent::validateFrontPageOptions($post);
125
		$boards = self::_getBoardsList();
126 2
127
		if (empty($post->message_index_frontpage) || !isset($boards[$post->message_index_frontpage]))
128 2
		{
129 2
			$post->front_page = null;
130
131
			return false;
132 2
		}
133
134
		return true;
135 2
	}
136
137
	/**
138
	 * Dispatches forward to message index handler.
139
	 *
140
	 * @see AbstractController::action_index
141 2
	 */
142 2
	public function action_index()
143
	{
144 2
		// Forward to the message index, it's not like we know much more :P
145
		$this->action_messageindex();
146 2
	}
147 2
148 2
	/**
149 2
	 * Show the list of topics in this board, along with any sub-boards.
150
	 *
151
	 * @uses template_topic_listing() sub template of the MessageIndex template
152 2
	 */
153
	public function action_messageindex(): void
154
	{
155 2
		global $txt, $context, $board_info;
156 2
157 2
		// Check for the redirection board, and if found, head off
158
		if ($board_info['redirect'])
159
		{
160 2
			$this->handleRedirectBoard();
161 2
		}
162
163
		// Load any necessary resources
164 2
		$this->loadSupportingResources();
165
166 2
		// Initialize $context
167
		$this->initializeContext();
168
169
		// Build a list of unapproved posts, if applicable
170 2
		if ($this->currentUserCanApprovePosts() && $this->hasUnapprovedPosts())
171
		{
172
			$context['unapproved_posts_message'] = $this->buildUnapprovedPostsMessage();
173
		}
174
175
		// Make sure the starting place makes sense and construct the page index
176 2
		$this->setPageNavigation();
177
178
		// Prepare profile links to those who can moderate on this board
179
		$this->setBoardModeratorLinks();
180
181
		// Mark current and parent boards as seen.
182
		$this->markCurrentAndParentBoardsAsSeen();
183
184 2
		// Load basic information about the boards children, aka sub boards
185
		$this->prepareSubBoardsForDisplay();
186
187 2
		// Who else is taking a look?
188 2
		$this->prepareWhoViewing();
189
190
		// Setup topic sort icons/options for template use
191 2
		$this->setSortIcons();
192
193
		// Load the topic listing, accounting for sort, start page, etc.
194
		$this->loadBoardTopics();
195
196 2
		// What quick moderation options are available?
197
		$this->quickModeration();
198 2
199
		// Set template details/layers
200
		$template_layers = theme()->getLayers();
201
		if (!empty($context['boards']) && $this->sort_start === 0)
202
		{
203
			$template_layers->add('display_child_boards');
204
		}
205
206
		// If there are children, but no topics and no ability to post topics...
207 2
		$context['no_topic_listing'] = !empty($context['boards']) && empty($context['topics']) && !$context['can_post_new'];
208 2
209
		$template_layers->add('topic_listing');
210
211 2
		theme()->addJavascriptVar(['notification_board_notice' => $this->is_marked_notify ? $txt['notification_disable_board'] : $txt['notification_enable_board']], true);
212
213
		// Is Quick Topic available?
214 2
		$this->quickTopic();
215 2
216
		// Finally, action buttons like start a new topic, notify, mark read ...
217
		$this->buildBoardButtons();
218 2
	}
219 2
220 2
	/**
221
	 * Handles redirection for a board. Increments the number of posts in the board
222
	 * and redirects to the specified board URL.
223 2
	 */
224
	private function handleRedirectBoard(): void
225
	{
226
		global $board, $board_info;
227
228
		// If this is a redirection board, head off.
229
		require_once(SUBSDIR . '/Boards.subs.php');
230 2
231 2
		incrementBoard($board, 'num_posts');
232 2
		redirectexit($board_info['redirect']);
233
	}
234
235
	/**
236
	 * Initializes the context by setting various variables for the template.
237
	 */
238
	private function initializeContext(): void
239
	{
240
		global $txt, $context, $board_info, $modSettings;
241 2
242
		$description = ParserWrapper::instance()->parseBoard($board_info['description']);
243
244 2
		$context += [
245
			'name' => $board_info['name'],
246
			'sub_template' => 'topic_listing',
247 2
			'description' => $description,
248
			'robot_no_index' => $this->setRobotNoIndex(),
249
			// 'Print' the header and board info.
250
			'page_title' => strip_tags($board_info['name']),
251
			'page_description' => strip_tags($description),
252
			// Set the variables up for the template.
253
			'can_mark_notify' => $this->currentUserCanMarkNotify(),
254 2
			'can_post_new' => $this->currentUserCanPostNew(),
255
			'can_post_poll' => $this->currentUserCanPostPoll(),
256
			'can_moderate_forum' => $this->currentUserCanModerate(),
257
			'can_approve_posts' => $this->currentUserCanApprovePosts(),
258 2
			'jump_to' => [
259
				'label' => addslashes(un_htmlspecialchars($txt['jump_to'])),
260
				'board_name' => htmlspecialchars(strtr(strip_tags($board_info['name']), ['&amp;' => '&']), ENT_COMPAT, 'UTF-8'),
261 2
				'child_level' => $board_info['child_level'],
262
			],
263
			'message_index_preview' => !empty($modSettings['message_index_preview'])
264
		];
265
	}
266
267
	/**
268
	 * Sets if this is a page that we do, or do not, want bots to index
269
	 *
270
	 * @return bool
271
	 */
272
	public function setRobotNoIndex(): bool
273 2
	{
274
		global $modSettings;
275
276
		foreach ($this->_req->query as $k => $v)
277
		{
278
			// Don't index a sort result etc.
279 2
			if (!in_array($k, ['board', 'start', session_name()], true))
280
			{
281
				return true;
282
			}
283
		}
284
285
		$start = $this->_req->getQuery('start', 'intval', 0);
286
		return $start !== 0 && ($start % $modSettings['defaultMaxMessages'] !== 0);
287 2
	}
288
289
	/**
290 2
	 * Checks whether the current user has permission to mark notifications
291 2
	 *
292 2
	 * @return bool True if the current user can mark notifications, false otherwise
293 2
	 */
294 2
	private function currentUserCanMarkNotify(): bool
295
	{
296
		return allowedTo('mark_notify') && $this->user->is_guest === false;
0 ignored issues
show
Bug Best Practice introduced by
The property is_guest does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
297
	}
298 2
299 2
	/**
300 2
	 * Checks if the current user is allowed to post new topics
301
	 *
302 2
	 * @return bool Returns true if the current user is allowed to post new topics, otherwise false.
303
	 */
304 2
	private function currentUserCanPostNew(): bool
305 2
	{
306
		global $modSettings;
307
308 2
		return allowedTo('post_new') || ($modSettings['postmod_active'] && allowedTo('post_unapproved_topics'));
309
	}
310
311
	/**
312
	 * Checks if the current user can post a poll
313
	 *
314
	 * @return bool Returns true if the current user can post a poll, false otherwise
315 2
	 */
316
	private function currentUserCanPostPoll(): bool
317 2
	{
318 2
		global $modSettings;
319
320
		return !empty($modSettings['pollMode']) && allowedTo('poll_post') && $this->currentUserCanPostNew();
321
	}
322
323
	/**
324
	 * Checks if the current user is allowed to moderate the forum
325
	 *
326
	 * @return bool Returns true if the current user is allowed to moderate the forum, false otherwise
327 2
	 */
328
	private function currentUserCanModerate(): bool
329 2
	{
330 2
		return allowedTo('moderate_forum');
331
	}
332
333 2
	/**
334
	 * Checks if the current user has the permission to approve posts
335
	 *
336 2
	 * @return bool True if the current user can approve posts, false otherwise
337
	 */
338 1
	private function currentUserCanApprovePosts(): bool
339
	{
340 2
		return allowedTo('approve_posts');
341 2
	}
342 2
343 2
	/**
344 2
	 * Check if the current user can restore a topic
345
	 *
346 2
	 * @return bool True if they can restore a topic
347
	 */
348
	private function currentUserCanRestore(): bool
349 2
	{
350 2
		global $modSettings, $board;
351 2
352
		return allowedTo('move_any') && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board;
353
	}
354
355
	/**
356 2
	 * Loads supporting resources for the MessageIndex page.
357 2
	 */
358
	private function loadSupportingResources(): void
359
	{
360
		global $modSettings, $txt;
361
362
		// Fairly often, we'll work with boards. Current board, sub-boards.
363
		require_once(SUBSDIR . '/Boards.subs.php');
364
		require_once(SUBSDIR . '/MessageIndex.subs.php');
365
366 2
		theme()->getTemplates()->load('MessageIndex');
367
		loadJavascriptFile('topic.js');
368
369 2
		if (!empty($modSettings['message_index_preview']))
370
		{
371
			loadJavascriptFile('elk_toolTips.js', ['defer' => true]);
372
		}
373 2
374 2
		theme()->addJavascriptVar([
375 2
			'txt_mark_as_read_confirm' => $txt['mark_these_as_read_confirm']
376 2
		], true);
377 2
	}
378
379
	/**
380
	 * Checks if the current board has unapproved posts or topics.
381 2
	 *
382
	 * @return bool Returns true if the board has unapproved posts or topics, otherwise false.
383 2
	 */
384
	private function hasUnapprovedPosts(): bool
385 2
	{
386
		global $board_info;
387
388 2
		return $board_info['unapproved_topics'] || $board_info['unapproved_posts'];
389
	}
390
391 2
	/**
392
	 * Builds the message/links for the number of unapproved posts and topics in the current board.
393
	 *
394
	 * @return string The message containing the number of unapproved topics and posts.
395
	 */
396 2
	private function buildUnapprovedPostsMessage(): string
397
	{
398 2
		global $txt, $board_info, $board;
399
400 2
		$unApprovedTopics = $board_info['unapproved_topics'] ? '<a href="' . getUrl('action', ['action' => 'moderate', 'area' => 'postmod', 'sa' => 'topics', 'brd' => $board]) . '">' . $board_info['unapproved_topics'] . '</a>' : 0;
401 2
		$unApprovedPosts = $board_info['unapproved_posts'] ? '<a href="' . getUrl('action', ['action' => 'moderate', 'area' => 'postmod', 'sa' => 'posts', 'brd' => $board]) . '">' . ($board_info['unapproved_posts'] - $board_info['unapproved_topics']) . '</a>' : 0;
402
403
		return sprintf($txt['there_are_unapproved_topics'], $unApprovedTopics, $unApprovedPosts, getUrl('action', ['action' => 'moderate', 'area' => 'postmod', 'sa' => ($board_info['unapproved_topics'] ? 'topics' : 'posts'), 'brd' => $board]));
404
	}
405
406
	/**
407
	 * Sets up the page navigation for the board view.
408 2
	 */
409 2
	private function setPageNavigation(): void
410 2
	{
411 2
		global $board, $modSettings, $context, $options, $board_info, $url_format;
412
413
		// How many topics do we have in total?
414
		$board_info['total_topics'] = $this->currentUserCanApprovePosts()
415 2
			? $board_info['num_topics'] + $board_info['unapproved_topics']
416
			: $board_info['num_topics'] + $board_info['unapproved_user_topics'];
417
418
		$all = $this->_req->isSet('all');
419
		$start = $this->_req->getQuery('start', 'intval', 0);
420
421
		// View all the topics, or just a few?
422
		$context['topics_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['topics_per_page']) ? $options['topics_per_page'] : $modSettings['defaultMaxTopics'];
423
		$context['messages_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
424
		$per_page = $all && !empty($modSettings['enableAllMessages']) ? $board_info['total_topics'] : $context['topics_per_page'];
425
426
		// Make sure the starting place makes sense based on the chosen URL style and construct the page index.
427
		if ($url_format === 'queryless')
428
		{
429
			$context['page_index'] = constructPageIndex('{scripturl}?board,' . $board . '.%1$d.html' . $this->buildSortingString(), $start, $board_info['total_topics'], $per_page, true);
430
		}
431
		else
432
		{
433
			$base_url = getUrl('board', ['board' => $board_info['id'], 'name' => $board_info['name']]);
434
			$base_url = str_replace('%', '%%', $base_url);
435
			$context['page_index'] = constructPageIndex($base_url . '.%1$d' . $this->buildSortingString(), $start, $board_info['total_topics'], $per_page, true);
436
		}
437
438
		// Set a canonical URL for this page.
439
		$context['canonical_url'] = getUrl('board', ['board' => $board, 'start' => $start, 'name' => $board_info['name']]);
440
441
		$context['links'] += [
442
			'prev' => $start >= $context['topics_per_page'] ? getUrl('board', ['board' => $board, 'start' => $start - $context['topics_per_page'], 'name' => $board_info['name']]) : '',
443
			'next' => $start + $context['topics_per_page'] < $board_info['total_topics'] ? getUrl('board', ['board' => $board, 'start' => $start + $context['topics_per_page'], 'name' => $board_info['name']]) : '',
444
		];
445
446
		if ($all && !empty($modSettings['enableAllMessages']) && $per_page > $modSettings['enableAllMessages'])
447
		{
448
			$per_page = $modSettings['enableAllMessages'];
449
			$start = 0;
450
		}
451
452
		$this->sort_start = $start;
453
		$context['start'] = $start;
454
		$context['per_page'] = $per_page;
455
	}
456
457
	/**
458
	 * Builds the sorting string for the message index page.
459 2
	 *
460
	 * @return string The sorting string with the chosen sort method and direction
461
	 */
462
	private function buildSortingString(): string
463
	{
464
		global $context, $txt;
465 2
466
		// Known sort methods.
467
		$this->sort_methods = messageIndexSort();
468
		$default_sort_method = 'last_post';
469
470
		// Requested a sorting method?
471 2
		$chosen_sort = $this->_req->getQuery('sort', 'trim', $default_sort_method);
472 2
473
		// We only know these.
474 2
		if (!isset($this->sort_methods[$chosen_sort]))
475
		{
476
			$chosen_sort = $default_sort_method;
477 2
		}
478 2
479 2
		$sort_string = ';sort=' . $chosen_sort . ($this->_req->isSet('desc') ? ';desc' : '');
480
		$this->sort_by = $chosen_sort;
481
		$this->ascending = $this->_req->isSet('asc');
482 2
		$this->sort_column = $this->sort_methods[$this->sort_by];
483 2
484 2
		$context['sort_by'] = $this->sort_by;
485
		$context['sort_direction'] = $this->ascending ? 'up' : 'down';
486
		$context['sort_title'] = $this->ascending ? $txt['sort_desc'] : $txt['sort_asc'];
487 2
488
		return $sort_string;
489 2
	}
490 2
491 2
	/**
492
	 * Load board moderator links into context for displaying on the template.
493 2
	 */
494 2
	private function setBoardModeratorLinks(): void
495
	{
496
		global $board_info, $context, $txt;
497
498
		// Build a list of the board's moderators.
499 2
		$context['moderators'] = &$board_info['moderators'];
500 2
		$context['link_moderators'] = [];
501
502
		if (!empty($board_info['moderators']))
503
		{
504
			foreach ($board_info['moderators'] as $mod)
505
			{
506
				$context['link_moderators'][] = '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $mod['id'], 'name' => $mod['name']]) . '" title="' . $txt['board_moderator'] . '">' . $mod['name'] . '</a>';
507
			}
508
		}
509
	}
510
511
	/**
512
	 * Mark the current board and its parent boards as seen for the current user
513
	 */
514
	public function markCurrentAndParentBoardsAsSeen(): void
515
	{
516
		global $board_info, $board;
517
518
		if ($this->user->is_guest)
0 ignored issues
show
Bug Best Practice introduced by
The property is_guest does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
519
		{
520
			$this->is_marked_notify = false;
521
			return;
522
		}
523
524
		// We can't know they read it if we allow prefetches.
525
		stop_prefetching();
526
527
		// Mark the board as read, and its parents.
528
		if (!empty($board_info['parent_boards']))
529
		{
530
			$board_list = array_keys($board_info['parent_boards']);
531
			$board_list[] = $board;
532
		}
533
		else
534
		{
535
			$board_list = [$board];
536
		}
537
538
		// Mark boards as read. Boards alone, no need for topics.
539
		markBoardsRead($board_list);
540
541
		// Clear topicseen cache
542
		if (!empty($board_info['parent_boards']))
543
		{
544
			// We've seen all these boards now!
545
			foreach ($board_info['parent_boards'] as $k => $dummy)
546
			{
547
				if (isset($_SESSION['topicseen_cache'][$k]))
548
				{
549
					unset($_SESSION['topicseen_cache'][$k]);
550
				}
551
			}
552
		}
553
554
		if (isset($_SESSION['topicseen_cache'][$board]))
555
		{
556
			unset($_SESSION['topicseen_cache'][$board]);
557
		}
558
559
		// From now on, they've seen it. So we reset notifications.
560
		$this->is_marked_notify = resetSentBoardNotification($this->user->id, $board);
561
	}
562
563
	/**
564
	 * Prepare and load sub-boards for display.
565
	 */
566
	private function prepareSubBoardsForDisplay(): void
567
	{
568
		global $board_info, $modSettings, $context;
569
570
		// Prepare sub-boards for display.
571
		$boardIndexOptions = [
572
			'include_categories' => false,
573
			'base_level' => $board_info['child_level'] + 1,
574
			'parent_id' => $board_info['id'],
575
			'set_latest_post' => false,
576
			'countChildPosts' => !empty($modSettings['countChildPosts']),
577
		];
578
579
		$boardList = new BoardsList($boardIndexOptions);
580
		$context['boards'] = $boardList->getBoards();
581
	}
582
583
	/**
584
	 * Prepares and loads into context the information about who is currently viewing the board
585
	 */
586
	private function prepareWhoViewing(): void
587
	{
588
		global $settings, $board;
589
590
		// Nosey, nosey - who's viewing this board?
591
		if (!empty($settings['display_who_viewing']))
592
		{
593
			require_once(SUBSDIR . '/Who.subs.php');
594
			formatViewers($board, 'board');
595
		}
596
	}
597
598
	/**
599
	 * Sets the sort icons for the topics headers in the context.
600
	 */
601
	private function setSortIcons(): void
602
	{
603
		global $context, $board, $board_info, $txt;
604
605
		// Trick
606
		$txt['starter'] = $txt['started_by'];
607
608
		// todo: Need to move this to theme.
609
		foreach ($this->sort_methods as $key => $val)
610
		{
611
			$sortIcon = match ($key)
612
			{
613
				'subject', 'starter', 'last_poster' => 'alpha',
614
				default => 'numeric',
615
			};
616
617
			$context['topics_headers'][$key] = [
618
				'url' => getUrl('board', ['board' => $board, 'start' => $this->sort_start, 'sort' => $key, 'name' => $board_info['name'], $this->sort_by === $key && $this->ascending ? 'desc' : 'asc']),
619
				'sort_dir_img' => $this->sort_by === $key ? '<i class="icon icon-small i-sort-' . $sortIcon . '-' . $context['sort_direction'] . '" title="' . $context['sort_title'] . '"><s>' . $context['sort_title'] . '</s></i>' : '',
620
			];
621
		}
622
	}
623
624
	/**
625
	 * Loads board topics into the context
626
	 */
627
	private function loadBoardTopics(): void
628
	{
629
		global $board, $modSettings, $context, $settings, $board_info;
630
631
		// Calculate the fastest way to get the topics.
632
		$start = $this->_req->getQuery('start', 'intval', 0);
633
		$per_page = $context['per_page'];
634
		$fake_ascending = false;
635
		if ($start > ($board_info['total_topics'] - 1) / 2)
636
		{
637
			// Rolled off the end?
638
			$start = ($start >= $board_info['total_topics']) ? $board_info['total_topics'] - $per_page : $start;
639
			$this->ascending = !$this->ascending;
640
			$fake_ascending = true;
641
			$per_page = $board_info['total_topics'] < $start + $per_page + 1 ? $board_info['total_topics'] - $start : $per_page;
642
			$start = $board_info['total_topics'] < $start + $per_page + 1 ? 0 : $board_info['total_topics'] - $start - $per_page;
643
		}
644
645
		$context['topics'] = [];
646
647
		// Set up the query options
648
		$indexOptions = [
649
			'only_approved' => $modSettings['postmod_active'] && !allowedTo('approve_posts'),
650
			'previews' => empty($modSettings['message_index_preview']) ? 0 : (empty($modSettings['preview_characters']) ? -1 : $modSettings['preview_characters']),
651
			'include_avatars' => $settings['avatars_on_indexes'],
652
			'ascending' => $this->ascending,
653
			'fake_ascending' => $fake_ascending
654
		];
655
656
		// Allow integration to modify / add to the $indexOptions
657
		call_integration_hook('integrate_messageindex_topics', [&$this->sort_column, &$indexOptions]);
658
659
		$topics_info = messageIndexTopics($board, $this->user->id, $start, $per_page, $this->sort_by, $this->sort_column, $indexOptions);
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
660
661
		$context['topics'] = TopicUtil::prepareContext($topics_info, false, empty($modSettings['preview_characters']) ? 128 : $modSettings['preview_characters']);
662
663
		// Allow addons to add to the $context['topics']
664
		call_integration_hook('integrate_messageindex_listing', [$topics_info]);
665
666
		// Fix the sequence of topics if they were retrieved in the wrong order. (for speed reasons...)
667
		if ($fake_ascending)
668
		{
669
			$context['topics'] = array_reverse($context['topics'], true);
670
		}
671
672
		$topic_ids = array_keys($context['topics']);
673
674
		if (!empty($modSettings['enableParticipation']) && $this->user->is_guest === false && !empty($topic_ids))
0 ignored issues
show
Bug Best Practice introduced by
The property is_guest does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
675
		{
676
			$topics_participated_in = topicsParticipation($this->user->id, $topic_ids);
677
			foreach ($topics_participated_in as $participated)
678
			{
679
				$context['topics'][$participated['id_topic']]['is_posted_in'] = true;
680
				$context['topics'][$participated['id_topic']]['class'] = 'my_' . $context['topics'][$participated['id_topic']]['class'];
681
			}
682
		}
683
684
		// Trigger a topic loaded event
685
		$this->_events->trigger('topicinfo', ['callbacks' => &$context['topics']]);
686
	}
687
688
	/**
689
	 * Determines which quick moderation actions are available for this user.
690
	 * Loads which actions are available, on a per-topic basis, into $context.
691
	 */
692
	private function quickModeration(): void
693
	{
694
		global $modSettings, $context, $options, $board_info;
695
696
		// Is Quick Moderation active/needed?
697
		if (!empty($options['display_quick_mod']) && !empty($context['topics']))
698
		{
699
			$context += [
700
				'can_markread' => $context['user']['is_logged'],
701
				'can_lock' => allowedTo('lock_any'),
702
				'can_sticky' => allowedTo('make_sticky'),
703
				'can_move' => allowedTo('move_any'),
704
				'can_remove' => allowedTo('remove_any'),
705
				'can_merge' => allowedTo('merge_any'),
706
			];
707
708
			// Ignore approving own topics as it's unlikely to come up...
709
			$context['can_approve'] = $modSettings['postmod_active'] && allowedTo('approve_posts') && !empty($board_info['unapproved_topics']);
710
711
			// Can we restore topics?
712
			$context['can_restore'] = $this->currentUserCanRestore();
713
714
			// Set permissions for all the topics.
715
			foreach ($context['topics'] as $t => $topic)
716
			{
717
				$started = (int) $topic['first_post']['member']['id'] === $this->user->id;
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
718
				$context['topics'][$t]['quick_mod'] = [
719
					'lock' => allowedTo('lock_any') || ($started && allowedTo('lock_own')),
720
					'sticky' => allowedTo('make_sticky'),
721
					'move' => allowedTo('move_any') || ($started && allowedTo('move_own')),
722
					'modify' => allowedTo('modify_any') || ($started && allowedTo('modify_own')),
723
					'remove' => allowedTo('remove_any') || ($started && allowedTo('remove_own')),
724
					'approve' => $context['can_approve'] && $topic['unapproved_posts']
725
				];
726
				$context['can_lock'] |= ($started && allowedTo('lock_own'));
727
				$context['can_move'] |= ($started && allowedTo('move_own'));
728
				$context['can_remove'] |= ($started && allowedTo('remove_own'));
729
			}
730
731
			// Can we even use quick moderation on this batch?
732
			$context['can_quick_mod'] = $context['user']['is_logged'] || $context['can_approve'] || $context['can_remove'] || $context['can_lock'] || $context['can_sticky'] || $context['can_move'] || $context['can_merge'] || $context['can_restore'];
733
			if (!empty($context['can_quick_mod']))
734
			{
735
				$this->buildQuickModerationButtons();
736
				$context['qmod_actions'] = ['approve', 'remove', 'lock', 'sticky', 'move', 'merge', 'restore', 'markread'];
737
				call_integration_hook('integrate_quick_mod_actions');
738
			}
739
		}
740
	}
741
742
	/**
743
	 * Loads into $context the moderation button array for template use.
744
	 * Call integrate_message_index_mod_buttons hook
745
	 */
746
	public function buildQuickModerationButtons(): void
747
	{
748
		global $context;
749
750
		$context['can_show'] = false;
751
		$quickMod = array_column($context['topics'], 'quick_mod', 'id');
752
		$context['show_qm_message_checkbox'] = array_column($context['topics'], 'id');
753
754
		// Build valid topic id's by action
755
		$keys = array_keys($quickMod);
756
		foreach (['move', 'lock', 'remove', 'approve'] as $area)
757
		{
758
			// e.g., get topic ids where this quick_mod action xxx value is valid
759
			$temp = array_combine($keys, array_column($quickMod, $area));
760
			$context['allow_qm']['can_' . $area] = array_keys($temp, true);
761
			${'show_' . $area} = !empty($context['allow_qm']['can_' . $area]);
762
		}
763
764
		// Build the mod button array with buttons that are valid for, at least some, of the messages
765
		$context['mod_buttons'] = [
766
			'move' => [
767
				'test' => $show_move ? 'can_move' : 'can_show',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $show_move seems to be never defined.
Loading history...
768
				'text' => 'move_topic',
769
				'id' => 'move',
770
				'lang' => true,
771
				'url' => 'javascript:void(0);',
772
			],
773
			'remove' => [
774
				'test' => $show_remove ? 'can_remove' : 'can_show',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $show_remove seems to be never defined.
Loading history...
775
				'text' => 'remove_topic',
776
				'id' => 'remove',
777
				'lang' => true,
778
				'url' => 'javascript:void(0);',
779
			],
780
			'lock' => [
781
				'test' => $show_lock ? 'can_lock' : 'can_show',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $show_lock seems to be never defined.
Loading history...
782
				'text' => 'set_lock',
783
				'id' => 'lock',
784
				'lang' => true,
785
				'url' => 'javascript:void(0);',
786
			],
787
			'approve' => [
788
				'test' => $show_approve ? 'can_approve' : 'can_show',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $show_approve seems to be never defined.
Loading history...
789
				'text' => 'approve',
790
				'id' => 'approve',
791
				'lang' => true,
792
				'url' => 'javascript:void(0);',
793
			],
794
			'sticky' => [
795
				'test' => 'can_sticky',
796
				'text' => 'set_sticky',
797
				'id' => 'sticky',
798
				'lang' => true,
799
				'url' => 'javascript:void(0);',
800
			],
801
			'merge' => [
802
				'test' => 'can_merge',
803
				'text' => 'merge',
804
				'id' => 'merge',
805
				'lang' => true,
806
				'url' => 'javascript:void(0);',
807
			],
808
			'markread' => [
809
				'test' => 'can_markread',
810
				'text' => 'mark_read_short',
811
				'id' => 'markread',
812
				'lang' => true,
813
				'url' => 'javascript:void(0);',
814
			],
815
		];
816
817
		// Restore a topic, maybe even some doxing!
818
		if ($context['can_restore'])
819
		{
820
			$context['mod_buttons']['restore'] = [
821
				'text' => 'restore_topic',
822
				'lang' => true,
823
				'url' => 'javascript:void(0);',
824
			];
825
		}
826
827
		// Allow adding new buttons easily.
828
		call_integration_hook('integrate_message_index_quickmod_buttons');
829
830
		$context['mod_buttons'] = array_reverse($context['mod_buttons']);
831
	}
832
833
	/**
834
	 * Similar to Quick Reply, this is Quick Topic.
835
	 * Allows a way to start a new topic from the boards message index.
836
	 */
837
	private function quickTopic(): void
838
	{
839
		global $txt, $modSettings, $context, $options;
840
841
		// Quick topic enabled?
842
		if ($context['can_post_new'] && !empty($options['display_quick_reply']))
843
		{
844
			$this->prepareQuickTopic();
845
846
			checkSubmitOnce('register');
847
848
			$context['becomes_approved'] = true;
849
			if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics'))
850
			{
851
				$context['becomes_approved'] = false;
852
			}
853
			else
854
			{
855
				isAllowedTo('post_new');
856
			}
857
858
			require_once(SUBSDIR . '/Editor.subs.php');
859
			// Create the editor for the QT area
860
			$editorOptions = [
861
				'id' => 'message',
862
				'value' => '',
863
				'labels' => [
864
					'post_button' => $txt['post'],
865
				],
866
				'height' => '200px',
867
				'width' => '100%',
868
				'smiley_container' => 'smileyBox_message',
869
				'bbc_container' => 'bbcBox_message',
870
				// We submit/switch to the full post page for the preview
871
				'preview_type' => 1,
872
				'buttons' => [
873
					'more' => [
874
						'type' => 'submit',
875
						'name' => 'more_options',
876
						'value' => $txt['post_options'],
877
						'options' => ''
878
					]
879
				],
880
			];
881
882
			// Trigger the prepare_context event for modules that have tied in to it
883
			$this->_events->trigger('prepare_context', ['editorOptions' => &$editorOptions, 'use_quick_reply' => !empty($options['display_quick_reply'])]);
884
885
			create_control_richedit($editorOptions);
886
887
			theme()->getTemplates()->load('GenericMessages');
888
		}
889
	}
890
891
	/**
892
	 * If Quick Topic is on, we need to load user information into $context so the poster sidebar renders
893
	 */
894
	private function prepareQuickTopic(): void
895
	{
896
		global $options, $context, $modSettings;
897
898
		if (empty($options['hide_poster_area']) && $options['display_quick_reply'])
899
		{
900
			if ($this->user->is_guest)
0 ignored issues
show
Bug Best Practice introduced by
The property is_guest does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
901
			{
902
				MembersList::loadGuest();
903
			}
904
			else
905
			{
906
				MembersList::load(User::$info->id);
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
907
			}
908
909
			$thisUser = MembersList::get(User::$info->id);
910
			$thisUser->loadContext();
911
912
			$context['thisMember'] = [
913
				'id' => 'new',
914
				'is_message_author' => true,
915
				'member' => $thisUser->toArray()['data']
916
			];
917
			$context['can_issue_warning'] = allowedTo('issue_warning') && featureEnabled('w') && !empty($modSettings['warning_enable']);
918
			$context['can_send_pm'] = allowedTo('pm_send');
919
		}
920
	}
921
922
	/**
923
	 * Build the board buttons for the message index page.
924
	 */
925
	private function buildBoardButtons(): void
926
	{
927
		global $context, $settings, $board;
928
929
		// Build the message index button array.
930
		$context['normal_buttons'] = [
931
			'new_topic' => [
932
				'test' => 'can_post_new',
933
				'text' => 'new_topic',
934
				'lang' => true,
935
				'url' => getUrl('action', ['action' => 'post', 'board' => $board . '.0']),
936
				'active' => true],
937
			'notify' => [
938
				'test' => 'can_mark_notify',
939
				'text' => $this->is_marked_notify ? 'unnotify' : 'notify',
940
				'lang' => true, 'custom' => 'onclick="return notifyboardButton(this);"',
941
				'url' => getUrl('action', ['action' => 'notifyboard', 'sa' => ($this->is_marked_notify ? 'off' : 'on'), 'board' => $board . '.' . $this->sort_start, '{session_data}'])],
942
		];
943
944
		// They can only mark read if they are logged in, and it's enabled!
945
		if ($this->user->is_guest === false && $settings['show_mark_read'])
0 ignored issues
show
Bug Best Practice introduced by
The property is_guest does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
946
		{
947
			$context['normal_buttons']['markread'] = [
948
				'text' => 'mark_read_short',
949
				'lang' => true,
950
				'url' => getUrl('action', ['action' => 'markasread', 'sa' => 'board', 'board' => $board . '.0', '{session_data}']),
951
				'custom' => 'onclick="return markboardreadButton(this);"'
952
			];
953
		}
954
955
		// Allow adding new buttons easily.
956
		call_integration_hook('integrate_messageindex_buttons');
957
958
		// Trigger a post load event with quick access to normal buttons
959
		$this->_events->trigger('post_load', ['callbacks' => &$context['normal_buttons']]);
960
	}
961
962
	/**
963
	 * Show the list of topics in this board, along with any sub-boards.
964
	 *
965
	 * @uses template_topic_listing() sub template of the MessageIndex template
966
	 */
967
	public function action_messageindex_fp(): void
968
	{
969
		global $modSettings, $board;
970
971
		$board = $modSettings['message_index_frontpage'];
972
		loadBoard();
973
974
		$this->action_messageindex();
975
	}
976
977
	/**
978
	 * Allows for moderation from the message index.
979
	 *
980
	 * @todo refactor this...
981
	 */
982
	public function action_quickmod(): ?bool
983
	{
984
		global $board, $modSettings, $context;
985
986
		// Check the session = get or post.
987
		checkSession('request');
988
989
		// Cleanup
990
		$validator = new DataValidator();
991
		$validator->sanitation_rules([
992
			'topics' => 'intval',
993
			'qaction' => 'trim',
994
			'move_to' => 'intval',
995
			'redirect_topic' => 'intval',
996
			'redirect_expires' => 'intval',
997
		]);
998
		$validator->input_processing(['topics' => 'array']);
999
		$validator->validate($this->_req->post);
1000
1001
		$selected_topics = $validator->topics;
1002
		$selected_qaction = $validator->qaction;
1003
1004
		// Let's go straight to the restore area.
1005
		if ($selected_qaction === 'restore' && !empty($selected_topics))
1006
		{
1007
			redirectexit('action=restoretopic;topics=' . implode(',', $selected_topics) . ';' . $context['session_var'] . '=' . $context['session_id']);
1008
		}
1009
1010
		if (isset($_SESSION['topicseen_cache']))
1011
		{
1012
			$_SESSION['topicseen_cache'] = [];
1013
		}
1014
1015
		// Remember the last board they moved things to.
1016
		if (!empty($validator->move_to))
1017
		{
1018
			$_SESSION['move_to_topic'] = [
1019
				'move_to' => $validator->move_to,
1020
				// And remember the last expiry period too.
1021
				'redirect_topic' => $validator->redirect_topic,
1022
				'redirect_expires' => $validator->redirect_expires,
1023
			];
1024
		}
1025
1026
		// This is going to be needed to send off the notifications and for updateLastMessages().
1027
		require_once(SUBSDIR . '/Post.subs.php');
1028
		require_once(SUBSDIR . '/Notification.subs.php');
1029
		require_once(SUBSDIR . '/Topic.subs.php');
1030
1031
		// Only a few possible actions.
1032
		$actions = [];
1033
1034
		// Permissions on this board
1035
		if (!empty($board))
1036
		{
1037
			$boards_can = [
1038
				'make_sticky' => allowedTo('make_sticky') ? [$board] : [],
1039
				'move_any' => allowedTo('move_any') ? [$board] : [],
1040
				'move_own' => allowedTo('move_own') ? [$board] : [],
1041
				'remove_any' => allowedTo('remove_any') ? [$board] : [],
1042
				'remove_own' => allowedTo('remove_own') ? [$board] : [],
1043
				'lock_any' => allowedTo('lock_any') ? [$board] : [],
1044
				'lock_own' => allowedTo('lock_own') ? [$board] : [],
1045
				'merge_any' => allowedTo('merge_any') ? [$board] : [],
1046
				'approve_posts' => allowedTo('approve_posts') ? [$board] : [],
1047
			];
1048
1049
			$start = $this->_req->getQuery('start', 'intval', 0);
1050
			$redirect_url = 'board=' . $board . '.' . $start;
1051
		}
1052
		else
1053
		{
1054
			$boards_can = boardsAllowedTo(['make_sticky', 'move_any', 'move_own', 'remove_any', 'remove_own', 'lock_any', 'lock_own', 'merge_any', 'approve_posts'], true, false);
1055
			$redirect_url = $this->_req->getPost('redirect_url', 'trim|strval', $_SESSION['old_url'] ?? getUrlQuery('action', $modSettings['default_forum_action']));
1056
		}
1057
1058
		// Just what actions can they do?, approve, move, remove, lock, sticky, lock, merge, mark read?
1059
		$possibleActions = $this->setPossibleQmActions($boards_can);
1060
1061
		// Two methods:
1062
		// $_REQUEST['actions'] (id_topic => action), and
1063
		// $_REQUEST['topics'] and $this->_req->post->qaction.
1064
		// (if action is 'move', $_REQUEST['move_to'] or $_REQUEST['move_tos'][$topic] is used.)
1065
		if (!empty($selected_topics))
1066
		{
1067
			// If the action isn't valid, just quit now.
1068
			if (empty($selected_qaction) || !in_array($selected_qaction, $possibleActions, true))
1069
			{
1070
				redirectexit($redirect_url);
1071
			}
1072
1073
			// Merge requires all topics as one parameter and can be done at once.
1074
			if ($selected_qaction === 'merge')
1075
			{
1076
				// Merge requires at least two topics.
1077
				if (count($selected_topics) < 2)
1078
				{
1079
					redirectexit($redirect_url);
1080
				}
1081
1082
				$controller = new MergeTopics(new EventManager());
1083
				$controller->setUser(User::$info);
1084
				$controller->pre_dispatch();
1085
1086
				return $controller->action_mergeExecute($selected_topics);
1087
			}
1088
1089
			// Just convert to the other method to make it easier.
1090
			foreach ($selected_topics as $topic)
1091
			{
1092
				$actions[$topic] = $selected_qaction;
1093
			}
1094
		}
1095
		else
1096
		{
1097
			$actions = $this->_req->getRequest('actions');
1098
		}
1099
1100
		// Weird... how'd you get here?
1101
		if (empty($actions))
1102
		{
1103
			redirectexit($redirect_url);
1104
		}
1105
1106
		// Validate each action.
1107
		$all_actions = [];
1108
		$action = '';
1109
		foreach ($actions as $topic => $action)
1110
		{
1111
			if (in_array($action, $possibleActions, true))
1112
			{
1113
				$all_actions[(int) $topic] = $action;
1114
			}
1115
		}
1116
1117
		$stickyCache = [];
1118
		$moveCache = [0 => [], 1 => []];
1119
		$removeCache = [];
1120
		$lockCache = [];
1121
		$markCache = [];
1122
		$approveCache = [];
1123
1124
		if (!empty($all_actions))
1125
		{
1126
			// Find all topics...
1127
			$topics_info = topicsDetails(array_keys($all_actions));
1128
1129
			foreach ($topics_info as $row)
1130
			{
1131
				if (!empty($board) && ($row['id_board'] != $board || ($modSettings['postmod_active'] && !$row['approved'] && !allowedTo('approve_posts'))))
1132
				{
1133
					continue;
1134
				}
1135
1136
				// Don't allow them to act on unapproved posts they can't see...
1137
				if ($modSettings['postmod_active'] && !$row['approved'] && !in_array(0, $boards_can['approve_posts']) && !in_array($row['id_board'], $boards_can['approve_posts']))
1138
				{
1139
					continue;
1140
				}
1141
1142
				// Goodness, this is fun.  We need to validate the action.
1143
				if ($all_actions[$row['id_topic']] === 'sticky' && !$this->canMakeSticky($boards_can, $row))
1144
				{
1145
					continue;
1146
				}
1147
1148
				if ($all_actions[$row['id_topic']] === 'move' && !$this->canMove($boards_can, $row))
1149
				{
1150
					continue;
1151
				}
1152
1153
				if ($all_actions[$row['id_topic']] === 'remove' && !$this->canRemove($boards_can, $row))
1154
				{
1155
					continue;
1156
				}
1157
1158
				if ($all_actions[$row['id_topic']] === 'lock' && !$this->canLock($boards_can, $row))
1159
				{
1160
					continue;
1161
				}
1162
1163
				// Separate the actions.
1164
				switch ($action)
1165
				{
1166
					case 'markread':
1167
						$markCache[] = $row['id_topic'];
1168
						break;
1169
					case 'sticky':
1170
						$stickyCache[] = $row['id_topic'];
1171
						break;
1172
					case 'move':
1173
						if (isset($this->_req->query->current_board))
1174
						{
1175
							moveTopicConcurrence((int) $this->_req->query->current_board, $board, $row['id_topic']);
1176
						}
1177
1178
						// $moveCache[0] is the topic, $moveCache[1] is the board to move to.
1179
						$moveCache[1][$row['id_topic']] = (int) ($this->_req->post->move_tos[$row['id_topic']] ?? $this->_req->post->move_to);
1180
1181
						if (!empty($moveCache[1][$row['id_topic']]))
1182
						{
1183
							$moveCache[0][] = $row['id_topic'];
1184
						}
1185
1186
						break;
1187
					case 'remove':
1188
						$removeCache[] = $row['id_topic'];
1189
						break;
1190
					case 'lock':
1191
						$lockCache[] = $row['id_topic'];
1192
						break;
1193
					case 'approve':
1194
						$approveCache[] = $row['id_topic'];
1195
						break;
1196
				}
1197
			}
1198
		}
1199
1200
		$affectedBoards = empty($board) ? [] : [$board => [0, 0]];
1201
1202
		// Do all the stickies...
1203
		if (!empty($stickyCache))
1204
		{
1205
			toggleTopicSticky($stickyCache, true);
1206
		}
1207
1208
		// Move sucka! (this is, by the by, probably the most complicated part....)
1209
		if (!empty($moveCache[0]))
1210
		{
1211
			moveTopicsPermissions($moveCache);
1212
		}
1213
1214
		// Now delete the topics...
1215
		if (!empty($removeCache))
1216
		{
1217
			removeTopicsPermissions($removeCache);
1218
		}
1219
1220
		// Approve the topics...
1221
		if (!empty($approveCache))
1222
		{
1223
			approveTopics($approveCache, true, true);
1224
		}
1225
1226
		// And (almost) lastly, lock the topics...
1227
		if (!empty($lockCache))
1228
		{
1229
			toggleTopicsLock($lockCache, true);
1230
		}
1231
1232
		if (!empty($markCache))
1233
		{
1234
			$logged_topics = getLoggedTopics($this->user->id, $markCache);
1235
1236
			$markArray = [];
1237
			foreach ($markCache as $topic)
1238
			{
1239
				$markArray[] = [$this->user->id, $topic, $modSettings['maxMsgID'], (int) !empty($logged_topics[$topic])];
1240
			}
1241
1242
			markTopicsRead($markArray, true);
1243
		}
1244
1245
		updateTopicStats();
1246
		require_once(SUBSDIR . '/Messages.subs.php');
1247
		updateMessageStats();
1248
		updateSettings(['calendar_updated' => time(),]);
1249
1250
		if (!empty($affectedBoards))
1251
		{
1252
			updateLastMessages(array_keys($affectedBoards));
1253
		}
1254
1255
		redirectexit($redirect_url);
1256
	}
1257
1258
	/**
1259
	 * Just what actions can they perform on this board?
1260
	 *
1261
	 * Checks if they can markread, sticky, move, remove, lock, or merge
1262
	 *
1263
	 * @param array $boards_can
1264
	 * @return array
1265
	 */
1266
	public function setPossibleQmActions($boards_can): array
1267
	{
1268
		$possibleActions = [];
1269
1270
		if ($this->user->is_guest === false)
0 ignored issues
show
Bug Best Practice introduced by
The property is_guest does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
1271
		{
1272
			$possibleActions[] = 'markread';
1273
		}
1274
1275
		if (!empty($boards_can['make_sticky']))
1276
		{
1277
			$possibleActions[] = 'sticky';
1278
		}
1279
1280
		if (!empty($boards_can['move_any']) || !empty($boards_can['move_own']))
1281
		{
1282
			$possibleActions[] = 'move';
1283
		}
1284
1285
		if (!empty($boards_can['remove_any']) || !empty($boards_can['remove_own']))
1286
		{
1287
			$possibleActions[] = 'remove';
1288
		}
1289
1290
		if (!empty($boards_can['lock_any']) || !empty($boards_can['lock_own']))
1291
		{
1292
			$possibleActions[] = 'lock';
1293
		}
1294
1295
		if (!empty($boards_can['merge_any']))
1296
		{
1297
			$possibleActions[] = 'merge';
1298
		}
1299
1300
		if (!empty($boards_can['approve_posts']))
1301
		{
1302
			$possibleActions[] = 'approve';
1303
		}
1304
1305
		return $possibleActions;
1306
	}
1307
1308
	/**
1309
	 * Can they sticky a topic?
1310
	 *
1311
	 * @param array $boards_can
1312
	 * @param array $row
1313
	 * @return bool
1314
	 */
1315
	public function canMakeSticky($boards_can, $row): bool
1316
	{
1317
		return in_array(0, $boards_can['make_sticky'])
1318
			|| in_array($row['id_board'], $boards_can['make_sticky']);
1319
	}
1320
1321
	/**
1322
	 * Can they move a topic?
1323
	 *
1324
	 * @param array $boards_can
1325
	 * @param array $row
1326
	 * @return bool
1327
	 */
1328
	public function canMove($boards_can, $row): bool
1329
	{
1330
		return in_array(0, $boards_can['move_any'])
1331
			|| in_array($row['id_board'], $boards_can['move_any'])
1332
			|| ($row['id_member_started'] == $this->user->id
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
1333
				&& (in_array(0, $boards_can['move_own']) || in_array($row['id_board'], $boards_can['move_own'])));
1334
	}
1335
1336
	/**
1337
	 * Can they remove a topic?
1338
	 *
1339
	 * @param array $boards_can
1340
	 * @param array $row
1341
	 * @return bool
1342
	 */
1343
	public function canRemove($boards_can, $row): bool
1344
	{
1345
		return in_array(0, $boards_can['remove_any'])
1346
			|| in_array($row['id_board'], $boards_can['remove_any'])
1347
			|| ($row['id_member_started'] == $this->user->id
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
1348
				&& (in_array(0, $boards_can['remove_own']) || in_array($row['id_board'], $boards_can['remove_own'])));
1349
1350
	}
1351
1352
	/**
1353
	 * Can they lock a topic?
1354
	 *
1355
	 * @param array $boards_can
1356
	 * @param array $row
1357
	 * @return bool
1358
	 */
1359
	public function canLock($boards_can, $row): bool
1360
	{
1361
		return in_array(0, $boards_can['lock_any'])
1362
			|| in_array($row['id_board'], $boards_can['lock_any'])
1363
			|| ($row['id_member_started'] == $this->user->id
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
1364
				&& $row['locked'] != 1
1365
				&& (in_array(0, $boards_can['lock_own']) || in_array($row['id_board'], $boards_can['lock_own'])));
1366
	}
1367
}
1368