Passed
Push — development ( a254f4...4fadb5 )
by Spuds
01:18 queued 34s
created

MessageIndex::loadBoardTopics()   D

Complexity

Conditions 14
Paths 288

Size

Total Lines 59
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 210

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 14
eloc 30
nc 288
nop 0
dl 0
loc 59
rs 4.3333
c 1
b 0
f 1
ccs 0
cts 26
cp 0
crap 210

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * 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 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 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 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 id's 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 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
			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...
901
			$thisUser = MembersList::get(User::$info->id);
902
			$thisUser->loadContext();
903
904
			$context['thisMember'] = [
905
				'id' => 'new',
906
				'is_message_author' => true,
907
				'member' => $thisUser->toArray()['data']
908
			];
909
			$context['can_issue_warning'] = allowedTo('issue_warning') && featureEnabled('w') && !empty($modSettings['warning_enable']);
910
			$context['can_send_pm'] = allowedTo('pm_send');
911
		}
912
	}
913
914
	/**
915
	 * Build the board buttons for the message index page.
916
	 */
917
	private function buildBoardButtons(): void
918
	{
919
		global $context, $settings, $board;
920
921
		// Build the message index button array.
922
		$context['normal_buttons'] = [
923
			'new_topic' => [
924
				'test' => 'can_post_new',
925
				'text' => 'new_topic',
926
				'lang' => true,
927
				'url' => getUrl('action', ['action' => 'post', 'board' => $board . '.0']),
928
				'active' => true],
929
			'notify' => [
930
				'test' => 'can_mark_notify',
931
				'text' => $this->is_marked_notify ? 'unnotify' : 'notify',
932
				'lang' => true, 'custom' => 'onclick="return notifyboardButton(this);"',
933
				'url' => getUrl('action', ['action' => 'notifyboard', 'sa' => ($this->is_marked_notify ? 'off' : 'on'), 'board' => $board . '.' . $this->sort_start, '{session_data}'])],
934
		];
935
936
		// They can only mark read if they are logged in, and it's enabled!
937
		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...
938
		{
939
			$context['normal_buttons']['markread'] = [
940
				'text' => 'mark_read_short',
941
				'lang' => true,
942
				'url' => getUrl('action', ['action' => 'markasread', 'sa' => 'board', 'board' => $board . '.0', '{session_data}']),
943
				'custom' => 'onclick="return markboardreadButton(this);"'
944
			];
945
		}
946
947
		// Allow adding new buttons easily.
948
		call_integration_hook('integrate_messageindex_buttons');
949
950
		// Trigger a post load event with quick access to normal buttons
951
		$this->_events->trigger('post_load', ['callbacks' => &$context['normal_buttons']]);
952
	}
953
954
	/**
955
	 * Show the list of topics in this board, along with any sub-boards.
956
	 *
957
	 * @uses template_topic_listing() sub template of the MessageIndex template
958
	 */
959
	public function action_messageindex_fp(): void
960
	{
961
		global $modSettings, $board;
962
963
		$board = $modSettings['message_index_frontpage'];
964
		loadBoard();
965
966
		$this->action_messageindex();
967
	}
968
969
	/**
970
	 * Allows for moderation from the message index.
971
	 *
972
	 * @todo refactor this...
973
	 */
974
	public function action_quickmod(): ?bool
975
	{
976
		global $board, $modSettings, $context;
977
978
		// Check the session = get or post.
979
		checkSession('request');
980
981
		// Cleanup
982
		$validator = new DataValidator();
983
		$validator->sanitation_rules([
984
			'topics' => 'intval',
985
			'qaction' => 'trim',
986
			'move_to' => 'intval',
987
			'redirect_topic' => 'intval',
988
			'redirect_expires' => 'intval',
989
		]);
990
		$validator->input_processing(['topics' => 'array']);
991
		$validator->validate($this->_req->post);
992
993
		$selected_topics = $validator->topics;
994
		$selected_qaction = $validator->qaction;
995
996
		// Lets go straight to the restore area.
997
		if ($selected_qaction === 'restore' && !empty($selected_topics))
998
		{
999
			redirectexit('action=restoretopic;topics=' . implode(',', $selected_topics) . ';' . $context['session_var'] . '=' . $context['session_id']);
1000
		}
1001
1002
		if (isset($_SESSION['topicseen_cache']))
1003
		{
1004
			$_SESSION['topicseen_cache'] = [];
1005
		}
1006
1007
		// Remember the last board they moved things to.
1008
		if (!empty($validator->move_to))
1009
		{
1010
			$_SESSION['move_to_topic'] = [
1011
				'move_to' => $validator->move_to,
1012
				// And remember the last expiry period too.
1013
				'redirect_topic' => $validator->redirect_topic,
1014
				'redirect_expires' => $validator->redirect_expires,
1015
			];
1016
		}
1017
1018
		// This is going to be needed to send off the notifications and for updateLastMessages().
1019
		require_once(SUBSDIR . '/Post.subs.php');
1020
		require_once(SUBSDIR . '/Notification.subs.php');
1021
		require_once(SUBSDIR . '/Topic.subs.php');
1022
1023
		// Only a few possible actions.
1024
		$actions = [];
1025
1026
		// Permissions on this board
1027
		if (!empty($board))
1028
		{
1029
			$boards_can = [
1030
				'make_sticky' => allowedTo('make_sticky') ? [$board] : [],
1031
				'move_any' => allowedTo('move_any') ? [$board] : [],
1032
				'move_own' => allowedTo('move_own') ? [$board] : [],
1033
				'remove_any' => allowedTo('remove_any') ? [$board] : [],
1034
				'remove_own' => allowedTo('remove_own') ? [$board] : [],
1035
				'lock_any' => allowedTo('lock_any') ? [$board] : [],
1036
				'lock_own' => allowedTo('lock_own') ? [$board] : [],
1037
				'merge_any' => allowedTo('merge_any') ? [$board] : [],
1038
				'approve_posts' => allowedTo('approve_posts') ? [$board] : [],
1039
			];
1040
1041
			$start = $this->_req->getQuery('start', 'intval', 0);
1042
			$redirect_url = 'board=' . $board . '.' . $start;
1043
		}
1044
		else
1045
		{
1046
			$boards_can = boardsAllowedTo(['make_sticky', 'move_any', 'move_own', 'remove_any', 'remove_own', 'lock_any', 'lock_own', 'merge_any', 'approve_posts'], true, false);
1047
			$redirect_url = $this->_req->getPost('redirect_url', 'trim|strval', $_SESSION['old_url'] ?? getUrlQuery('action', $modSettings['default_forum_action']));
1048
		}
1049
1050
		// Just what actions can they do?, approve, move, remove, lock, sticky, lock, merge, mark read?
1051
		$possibleActions = $this->setPossibleQmActions($boards_can);
1052
1053
		// Two methods:
1054
		// $_REQUEST['actions'] (id_topic => action), and
1055
		// $_REQUEST['topics'] and $this->_req->post->qaction.
1056
		// (if action is 'move', $_REQUEST['move_to'] or $_REQUEST['move_tos'][$topic] is used.)
1057
		if (!empty($selected_topics))
1058
		{
1059
			// If the action isn't valid, just quit now.
1060
			if (empty($selected_qaction) || !in_array($selected_qaction, $possibleActions, true))
1061
			{
1062
				redirectexit($redirect_url);
1063
			}
1064
1065
			// Merge requires all topics as one parameter and can be done at once.
1066
			if ($selected_qaction === 'merge')
1067
			{
1068
				// Merge requires at least two topics.
1069
				if (count($selected_topics) < 2)
1070
				{
1071
					redirectexit($redirect_url);
1072
				}
1073
1074
				$controller = new MergeTopics(new EventManager());
1075
				$controller->setUser(User::$info);
1076
				$controller->pre_dispatch();
1077
1078
				return $controller->action_mergeExecute($selected_topics);
1079
			}
1080
1081
			// Just convert to the other method, to make it easier.
1082
			foreach ($selected_topics as $topic)
1083
			{
1084
				$actions[$topic] = $selected_qaction;
1085
			}
1086
		}
1087
		else
1088
		{
1089
			$actions = $this->_req->getRequest('actions');
1090
		}
1091
1092
		// Weird... how'd you get here?
1093
		if (empty($actions))
1094
		{
1095
			redirectexit($redirect_url);
1096
		}
1097
1098
		// Validate each action.
1099
		$all_actions = [];
1100
		$action = '';
1101
		foreach ($actions as $topic => $action)
1102
		{
1103
			if (in_array($action, $possibleActions, true))
1104
			{
1105
				$all_actions[(int) $topic] = $action;
1106
			}
1107
		}
1108
1109
		$stickyCache = [];
1110
		$moveCache = [0 => [], 1 => []];
1111
		$removeCache = [];
1112
		$lockCache = [];
1113
		$markCache = [];
1114
		$approveCache = [];
1115
1116
		if (!empty($all_actions))
1117
		{
1118
			// Find all topics...
1119
			$topics_info = topicsDetails(array_keys($all_actions));
1120
1121
			foreach ($topics_info as $row)
1122
			{
1123
				if (!empty($board) && ($row['id_board'] != $board || ($modSettings['postmod_active'] && !$row['approved'] && !allowedTo('approve_posts'))))
1124
				{
1125
					continue;
1126
				}
1127
1128
				// Don't allow them to act on unapproved posts they can't see...
1129
				if ($modSettings['postmod_active'] && !$row['approved'] && !in_array(0, $boards_can['approve_posts']) && !in_array($row['id_board'], $boards_can['approve_posts']))
1130
				{
1131
					continue;
1132
				}
1133
1134
				// Goodness, this is fun.  We need to validate the action.
1135
				if ($all_actions[$row['id_topic']] === 'sticky' && !$this->canMakeSticky($boards_can, $row))
1136
				{
1137
					continue;
1138
				}
1139
1140
				if ($all_actions[$row['id_topic']] === 'move' && !$this->canMove($boards_can, $row))
1141
				{
1142
					continue;
1143
				}
1144
1145
				if ($all_actions[$row['id_topic']] === 'remove' && !$this->canRemove($boards_can, $row))
1146
				{
1147
					continue;
1148
				}
1149
1150
				if ($all_actions[$row['id_topic']] === 'lock' && !$this->canLock($boards_can, $row))
1151
				{
1152
					continue;
1153
				}
1154
1155
				// Separate the actions.
1156
				switch ($action)
1157
				{
1158
					case 'markread':
1159
						$markCache[] = $row['id_topic'];
1160
						break;
1161
					case 'sticky':
1162
						$stickyCache[] = $row['id_topic'];
1163
						break;
1164
					case 'move':
1165
						if (isset($this->_req->query->current_board))
1166
						{
1167
							moveTopicConcurrence((int) $this->_req->query->current_board, $board, $row['id_topic']);
1168
						}
1169
1170
						// $moveCache[0] is the topic, $moveCache[1] is the board to move to.
1171
						$moveCache[1][$row['id_topic']] = (int) ($this->_req->post->move_tos[$row['id_topic']] ?? $this->_req->post->move_to);
1172
1173
						if (!empty($moveCache[1][$row['id_topic']]))
1174
						{
1175
							$moveCache[0][] = $row['id_topic'];
1176
						}
1177
1178
						break;
1179
					case 'remove':
1180
						$removeCache[] = $row['id_topic'];
1181
						break;
1182
					case 'lock':
1183
						$lockCache[] = $row['id_topic'];
1184
						break;
1185
					case 'approve':
1186
						$approveCache[] = $row['id_topic'];
1187
						break;
1188
				}
1189
			}
1190
		}
1191
1192
		$affectedBoards = empty($board) ? [] : [(int) $board => [0, 0]];
1193
1194
		// Do all the stickies...
1195
		if (!empty($stickyCache))
1196
		{
1197
			toggleTopicSticky($stickyCache, true);
1198
		}
1199
1200
		// Move sucka! (this is, by the by, probably the most complicated part....)
1201
		if (!empty($moveCache[0]))
1202
		{
1203
			moveTopicsPermissions($moveCache);
1204
		}
1205
1206
		// Now delete the topics...
1207
		if (!empty($removeCache))
1208
		{
1209
			removeTopicsPermissions($removeCache);
1210
		}
1211
1212
		// Approve the topics...
1213
		if (!empty($approveCache))
1214
		{
1215
			approveTopics($approveCache, true, true);
1216
		}
1217
1218
		// And (almost) lastly, lock the topics...
1219
		if (!empty($lockCache))
1220
		{
1221
			toggleTopicsLock($lockCache, true);
1222
		}
1223
1224
		if (!empty($markCache))
1225
		{
1226
			$logged_topics = getLoggedTopics($this->user->id, $markCache);
1227
1228
			$markArray = [];
1229
			foreach ($markCache as $topic)
1230
			{
1231
				$markArray[] = [$this->user->id, $topic, $modSettings['maxMsgID'], (int) !empty($logged_topics[$topic])];
1232
			}
1233
1234
			markTopicsRead($markArray, true);
1235
		}
1236
1237
		updateTopicStats();
1238
		require_once(SUBSDIR . '/Messages.subs.php');
1239
		updateMessageStats();
1240
		updateSettings(['calendar_updated' => time(),]);
1241
1242
		if (!empty($affectedBoards))
1243
		{
1244
			updateLastMessages(array_keys($affectedBoards));
1245
		}
1246
1247
		redirectexit($redirect_url);
1248
		return null;
1249
	}
1250
1251
	/**
1252
	 * Just what actions can they perform on this board
1253
	 *
1254
	 * Checks if they can markread, sticky, move, remove, lock or merge
1255
	 *
1256
	 * @param array $boards_can
1257
	 * @return array
1258
	 */
1259
	public function setPossibleQmActions($boards_can): array
1260
	{
1261
		$possibleActions = [];
1262
1263
		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...
1264
		{
1265
			$possibleActions[] = 'markread';
1266
		}
1267
1268
		if (!empty($boards_can['make_sticky']))
1269
		{
1270
			$possibleActions[] = 'sticky';
1271
		}
1272
1273
		if (!empty($boards_can['move_any']) || !empty($boards_can['move_own']))
1274
		{
1275
			$possibleActions[] = 'move';
1276
		}
1277
1278
		if (!empty($boards_can['remove_any']) || !empty($boards_can['remove_own']))
1279
		{
1280
			$possibleActions[] = 'remove';
1281
		}
1282
1283
		if (!empty($boards_can['lock_any']) || !empty($boards_can['lock_own']))
1284
		{
1285
			$possibleActions[] = 'lock';
1286
		}
1287
1288
		if (!empty($boards_can['merge_any']))
1289
		{
1290
			$possibleActions[] = 'merge';
1291
		}
1292
1293
		if (!empty($boards_can['approve_posts']))
1294
		{
1295
			$possibleActions[] = 'approve';
1296
		}
1297
1298
		return $possibleActions;
1299
	}
1300
1301
	/**
1302
	 * Can they sticky a topic
1303
	 *
1304
	 * @param array $boards_can
1305
	 * @param array $row
1306
	 * @return bool
1307
	 */
1308
	public function canMakeSticky($boards_can, $row): bool
1309
	{
1310
		return in_array(0, $boards_can['make_sticky'])
1311
			|| in_array($row['id_board'], $boards_can['make_sticky']);
1312
	}
1313
1314
	/**
1315
	 * Can they move a topic
1316
	 *
1317
	 * @param array $boards_can
1318
	 * @param array $row
1319
	 * @return bool
1320
	 */
1321
	public function canMove($boards_can, $row): bool
1322
	{
1323
		return in_array(0, $boards_can['move_any'])
1324
			|| in_array($row['id_board'], $boards_can['move_any'])
1325
			|| ($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...
1326
				&& (in_array(0, $boards_can['move_own']) || in_array($row['id_board'], $boards_can['move_own'])));
1327
	}
1328
1329
	/**
1330
	 * Can they remove a topic
1331
	 *
1332
	 * @param array $boards_can
1333
	 * @param array $row
1334
	 * @return bool
1335
	 */
1336
	public function canRemove($boards_can, $row): bool
1337
	{
1338
		return in_array(0, $boards_can['remove_any'])
1339
			|| in_array($row['id_board'], $boards_can['remove_any'])
1340
			|| ($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...
1341
				&& (in_array(0, $boards_can['remove_own']) || in_array($row['id_board'], $boards_can['remove_own'])));
1342
1343
	}
1344
1345
	/**
1346
	 * Can they lock a topic
1347
	 *
1348
	 * @param array $boards_can
1349
	 * @param array $row
1350
	 * @return bool
1351
	 */
1352
	public function canLock($boards_can, $row): bool
1353
	{
1354
		return in_array(0, $boards_can['lock_any'])
1355
			|| in_array($row['id_board'], $boards_can['lock_any'])
1356
			|| ($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...
1357
				&& $row['locked'] != 1
1358
				&& (in_array(0, $boards_can['lock_own']) || in_array($row['id_board'], $boards_can['lock_own'])));
1359
	}
1360
}
1361