Search::action_results()   F
last analyzed

Complexity

Conditions 29
Paths > 20000

Size

Total Lines 257
Code Lines 117

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 870

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 29
eloc 117
nc 220416
nop 0
dl 0
loc 257
ccs 0
cts 174
cp 0
crap 870
rs 0
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Handle all searching from here.
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * This file contains code covered by:
11
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
12
 *
13
 * @version 2.0 Beta 1
14
 *
15
 */
16
17
namespace ElkArte\Controller;
18
19
use ElkArte\AbstractController;
20
use ElkArte\Action;
21
use ElkArte\Cache\Cache;
22
use ElkArte\Exceptions\Exception;
23
use ElkArte\Helper\Util;
24
use ElkArte\Helper\ValuesContainer;
25
use ElkArte\Languages\Txt;
26
use ElkArte\MembersList;
27
use ElkArte\MessagesCallback\BodyParser\Compact;
28
use ElkArte\MessagesCallback\BodyParser\Normal;
29
use ElkArte\MessagesCallback\SearchRenderer;
30
use ElkArte\MessageTopicIcons;
31
use ElkArte\Search\SearchApiWrapper;
32
use ElkArte\Search\SearchParams;
33
use ElkArte\Search\WeightFactors;
34
use ElkArte\VerificationControls\VerificationControlsIntegrate;
35
36
/**
37
 * Handle the searching for the site
38
 *
39
 * @package Search
40
 */
41
class Search extends AbstractController
42
{
43
	/** @var \ElkArte\Search\Search Holds the search object */
44
	protected $_search;
45
46
	/** @var null|MessageTopicIcons The class that takes care of rendering the message icons */
47
	protected $_icon_sources;
48
49
	/** @var array */
50
	protected $_participants = [];
51
52
	/**
53
	 * Called before any other action method in this class.
54
	 *
55
	 * - If coming from the quick reply allows routeing to the proper action
56
	 * - If needed (for example, external search engine or members search
57
	 */
58
	public function pre_dispatch()
59
	{
60
		global $modSettings;
61
62
		// Coming from a quick search box and going to some custom place?
63
		$search_selection = $this->_req->getRequest('search_selection', 'trim');
64
		$search = $this->_req->getRequest('search', 'trim');
65
66
		$search_selection = is_scalar($search_selection) ? $search_selection : 'NA';
67
		$search = is_scalar($search) ? $search : '';
68
69
		if (isset($search_selection) && !empty($modSettings['additional_search_engines']))
70
		{
71
			$engines = prepareSearchEngines();
72
			if (isset($engines[$search_selection]))
73
			{
74
				$engine = $engines[$search_selection];
75
				redirectexit($engine['url'] . urlencode(implode($engine['separator'], explode(' ', $search))));
76
			}
77
		}
78
79
		// If coming from the quick search box, and we want to search on members, well, we need to do that ;)
80
		if (isset($search_selection) && $search_selection === 'members')
81
		{
82
			redirectexit('action=memberlist;sa=search;fields=name,email;search=' . urlencode($search));
83
		}
84
85
		// If load management is on and the load is high, no need to even show the form.
86
		if (!empty($modSettings['loadavg_search']) && $modSettings['current_load'] >= $modSettings['loadavg_search'])
87
		{
88
			throw new Exception('loadavg_search_disabled', false);
89
		}
90
	}
91
92
	/**
93
	 * Intended entry point for this class.
94
	 *
95
	 * - The default action for no sub-action is... present on the search screen
96
	 *
97
	 * @see AbstractController::action_index
98
	 */
99
	public function action_index()
100
	{
101
		global $context;
102
103
		$subActions = [
104
			'search' => [$this, 'action_search'],
105
			'results' => [$this, 'action_results'],
106
		];
107
108
		// Set up the action control
109
		$action = new Action('modify_features');
110
111
		$subAction = $action->initialize($subActions, 'search');
112
		$context['sub_action'] = $subAction;
113
114
		// Call the right method.
115
		$action->dispatch($subAction);
116
	}
117
118
	/**
119
	 * Ask the user what they want to search for.
120
	 *
121
	 * What it does:
122
	 *
123
	 * - Shows the screen to search forum posts (action=search),
124
	 * - Uses the main sub template of the Search template.
125
	 * - Uses the Search language file.
126
	 * - Requires the search_posts permission.
127
	 * - Decodes and loads search parameters given in the URL (if any).
128
	 * - The form redirects to index.php?action=search;sa=results.
129
	 *
130
	 * @throws Exception loadavg_search_disabled
131
	 * @uses Search template, searchform sub template
132
	 *
133
	 * @uses Search language file and Errors language when needed
134
	 */
135
	public function action_search(): void
136
	{
137
		global $txt, $modSettings, $context;
138
139
		// Is the load average too high to allow searching just now?
140
		if (!empty($modSettings['loadavg_search']) && $modSettings['current_load'] >= $modSettings['loadavg_search'])
141
		{
142
			throw new Exception('loadavg_search_disabled', false);
143
		}
144
145
		Txt::load('Search');
146
147
		// Don't load this in XML mode.
148
		if ($this->getApi() === false)
149
		{
150
			theme()->getTemplates()->load('Search');
151
			$context['sub_template'] = 'searchform';
152
			loadJavascriptFile('suggest.js', ['defer' => false]);
153
		}
154
155
		// Check the user's permissions.
156
		isAllowedTo('search_posts');
157
158
		// Link tree....
159
		$context['breadcrumbs'][] = [
160
			'url' => getUrl('action', ['action' => 'search']),
161
			'name' => $txt['search']
162
		];
163
164
		// This is hard coded maximum string length.
165
		$context['search_string_limit'] = 100;
166
167
		$context['require_verification'] = $this->user->is_guest && !empty($modSettings['search_enable_captcha']) && empty($_SESSION['ss_vv_passed']);
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...
168
		if ($context['require_verification'])
169
		{
170
			// Build a verification control for the form
171
			$verificationOptions = [
172
				'id' => 'search',
173
			];
174
175
			$context['require_verification'] = VerificationControlsIntegrate::create($verificationOptions);
176
			$context['visual_verification_id'] = $verificationOptions['id'];
177
		}
178
179
		// If you got back from search;sa=results by using the breadcrumbs, you get your original search parameters back.
180
		$params = $this->_req->getQuery('params');
181
		if ($this->_search === null && isset($params))
182
		{
183
			$search_params = new SearchParams($params);
184
185
			$context['search_params'] = $search_params->get();
186
		}
187
188
		$search = $this->_req->getRequest('search', 'un_htmlspecialchars|trim');
189
		if (isset($search))
190
		{
191
			$context['search_params']['search'] = $search;
192
		}
193
194
		if (isset($context['search_params']['search']))
195
		{
196
			$context['search_params']['search'] = Util::htmlspecialchars($context['search_params']['search']);
197
		}
198
199
		if (isset($context['search_params']['userspec']))
200
		{
201
			$context['search_params']['userspec'] = htmlspecialchars($context['search_params']['userspec'], ENT_COMPAT, 'UTF-8');
202
		}
203
204
		if (!empty($context['search_params']['searchtype']))
205
		{
206
			$context['search_params']['searchtype'] = 2;
207
		}
208
209
		if (!empty($context['search_params']['minage']))
210
		{
211
			$context['search_params']['minage'] = date("Y-m-d", strtotime('-' . $context['search_params']['minage'] . ' days'));
212
		}
213
214
		if (!empty($context['search_params']['maxage']))
215
		{
216
			if ($context['search_params']['maxage'] === 9999)
217
			{
218
				$context['search_params']['maxage'] = 0;
219
			}
220
			else
221
			{
222
				$context['search_params']['maxage'] = date("Y-m-d", strtotime('-' . $context['search_params']['maxage'] . ' days'));
223
			}
224
		}
225
226
		$context['search_params']['show_complete'] = !empty($context['search_params']['show_complete']);
227
		$context['search_params']['subject_only'] = !empty($context['search_params']['subject_only']);
228
229
		// Load the error text strings if there were errors in the search.
230
		if (!empty($context['search_errors']))
231
		{
232
			Txt::load('Errors');
233
			$context['search_errors']['messages'] = [];
234
			foreach ($context['search_errors'] as $search_error => $dummy)
235
			{
236
				if ($search_error === 'messages')
237
				{
238
					continue;
239
				}
240
241
				if ($search_error === 'string_too_long')
242
				{
243
					$txt['error_string_too_long'] = sprintf($txt['error_string_too_long'], $context['search_string_limit']);
244
				}
245
246
				$context['search_errors']['messages'][] = $txt['error_' . $search_error];
247
			}
248
		}
249
250
		require_once(SUBSDIR . '/Boards.subs.php');
251
		$context += getBoardList(['not_redirection' => true]);
252
253
		$context['boards_in_category'] = [];
254
		foreach ($context['categories'] as $cat => &$category)
255
		{
256
			$context['boards_in_category'][$cat] = count($category['boards']);
257
			$category['child_ids'] = array_keys($category['boards']);
258
			foreach ($category['boards'] as &$board)
259
			{
260
				$board['selected'] = (empty($context['search_params']['brd']) && (empty($modSettings['recycle_enable']) || $board['id'] != $modSettings['recycle_board']) && !in_array($board['id'], (array) $this->user->ignoreboards)) || (!empty($context['search_params']['brd']) && in_array($board['id'], $context['search_params']['brd']));
0 ignored issues
show
Bug Best Practice introduced by
The property ignoreboards does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
261
			}
262
		}
263
264
		$topic = $this->_req->getRequest('topic', 'intval', 0);
265
		if (!empty($topic))
266
		{
267
			$context['search_params']['topic'] = $topic;
268
			$context['search_params']['show_complete'] = true;
269
		}
270
271
		if (!empty($context['search_params']['topic']))
272
		{
273
			$context['search_params']['topic'] = (int) $context['search_params']['topic'];
274
275
			$context['search_topic'] = [
276
				'id' => $context['search_params']['topic'],
277
				'href' => getUrl('action', ['topic' => $context['search_params']['topic'] . '.0']),
278
			];
279
280
			require_once(SUBSDIR . '/Topic.subs.php');
281
			$context['search_topic']['subject'] = getSubject($context['search_params']['topic']);
282
			$context['search_topic']['link'] = '<a href="' . $context['search_topic']['href'] . '">' . $context['search_topic']['subject'] . '</a>';
283
		}
284
285
		$context['page_title'] = $txt['set_parameters'];
286
		$context['search_params'] = $this->_fill_default_search_params($context['search_params']);
287
288
		// Start guest off collapsed
289
		if ($context['user']['is_guest'] && !isset($context['minmax_preferences']['asearch']))
290
		{
291
			$context['minmax_preferences']['asearch'] = 1;
292
		}
293
294
		call_integration_hook('integrate_search');
295
	}
296
297
	/**
298
	 * Fills the empty spaces in an array with the default values for search params
299
	 *
300
	 * @param array $array
301
	 *
302
	 * @return array
303
	 */
304
	private function _fill_default_search_params($array): array
305
	{
306
		$default = [
307
			'search' => '',
308
			'userspec' => '*',
309
			'searchtype' => 0,
310
			'show_complete' => 0,
311
			'subject_only' => 0,
312
			'minage' => 0,
313
			'maxage' => 9999,
314
			'sort' => 'relevance',
315
		];
316
317
		$array = array_merge($default, $array);
318
		if (empty($array['userspec']))
319
		{
320
			$array['userspec'] = '*';
321
		}
322
323
		$array['show_complete'] = (int) $array['show_complete'];
324
		$array['subject_only'] = (int) $array['subject_only'];
325
326
		return $array;
327
	}
328
329
	/**
330
	 * Gather the results and show them.
331
	 *
332
	 * What it does:
333
	 *
334
	 * - Checks user input and searches the messages table for messages matching the query.
335
	 * - Requires the search_posts permission.
336
	 * - Uses the results sub template of the Search template.
337
	 * - Uses the Search language file.
338
	 * - Stores the results into the search cache.
339
	 * - Show the results of the search query.
340
	 */
341
	public function action_results()
342
	{
343
		global $modSettings, $txt, $settings, $context, $options, $messages_request;
344
345
		// No, no, no... this is a bit hard on the server, so don't you go prefetching it!
346
		stop_prefetching();
347
348
		// These vars don't require an interface, they're just here for tweaking.
349
		$recentPercentage = 0.30;
350
351
		// Message length used to tweak messages relevance of the results
352
		$humungousTopicPosts = 200;
353
		$shortTopicPosts = 5;
354
		$maxMembersToSearch = 500;
355
356
		// Maximum number of results
357
		$maxMessageResults = empty($modSettings['search_max_results']) ? 0 : $modSettings['search_max_results'] * 5;
358
359
		// Start with no errors.
360
		$context['search_errors'] = [];
361
362
		// Number of pages hard maximum - normally not set at all.
363
		$modSettings['search_max_results'] = empty($modSettings['search_max_results']) ? 200 * $modSettings['search_results_per_page'] : (int) $modSettings['search_max_results'];
364
365
		// Maximum length of the string.
366
		$context['search_string_limit'] = 100;
367
368
		Txt::load('Search');
369
		if ($this->getApi() === false)
370
		{
371
			theme()->getTemplates()->load('Search');
372
		}
373
		// If we're doing XML, we need to use the results template regardless really.
374
		else
375
		{
376
			$context['sub_template'] = 'results';
377
		}
378
379
		// Are you allowed?
380
		isAllowedTo('search_posts');
381
382
		$this->_search = new \ElkArte\Search\Search();
383
		$this->_search->setWeights(new WeightFactors($modSettings, $this->user->is_admin));
384
385
		$params = $this->_req->getRequest('params', '', '');
386
		$search_params = new SearchParams($params);
387
		$search_params->merge((array) $this->_req->post, $recentPercentage, $maxMembersToSearch);
388
389
		$this->_search->setParams($search_params, !empty($modSettings['search_simple_fulltext']));
390
391
		$context['compact'] = $this->_search->isCompact();
392
393
		// Nothing??
394
		if ($this->_search->param('search') === false || $this->_search->param('search') === '')
395
		{
396
			$context['search_errors']['invalid_search_string'] = true;
397
		}
398
		// Too long?
399
		elseif (Util::strlen($this->_search->param('search')) > $context['search_string_limit'])
400
		{
401
			$context['search_errors']['string_too_long'] = true;
402
		}
403
404
		// Build the search array
405
		// $modSettings ['search_simple_fulltext'] is a hidden setting that will
406
		// do fulltext searching in the most basic way.
407
		$searchArray = $this->_search->getSearchArray();
408
409
		// This is used to remember words that will be ignored (because too short usually)
410
		$context['search_ignored'] = $this->_search->getIgnored();
411
412
		// Make sure at least one word is being searched for.
413
		if (empty($searchArray))
414
		{
415
			if (!empty($context['search_ignored']))
416
			{
417
				$context['search_errors']['search_string_small_words'] = true;
418
			}
419
			else
420
			{
421
				$context['search_errors']['invalid_search_string' . ($this->_search->foundBlockListedWords() ? '_blocklist' : '')] = true;
422
			}
423
424
			// Don't allow duplicate error messages if one string is too short.
425
			if (isset($context['search_errors']['search_string_small_words'], $context['search_errors']['invalid_search_string']))
426
			{
427
				unset($context['search_errors']['invalid_search_string']);
428
			}
429
		}
430
431
		// Let the user adjust the search query, should they wish?
432
		$context['search_params'] = (array) $this->_search->getSearchParams(true);
433
		if (isset($context['search_params']['search']))
434
		{
435
			$context['search_params']['search'] = Util::htmlspecialchars($context['search_params']['search']);
436
		}
437
438
		if (isset($context['search_params']['userspec']))
439
		{
440
			$context['search_params']['userspec'] = Util::htmlspecialchars($context['search_params']['userspec']);
441
		}
442
443
		if (empty($context['search_params']['minage']))
444
		{
445
			$context['search_params']['minage'] = 0;
446
		}
447
448
		if (empty($context['search_params']['maxage']))
449
		{
450
			$context['search_params']['maxage'] = 9999;
451
		}
452
453
		$context['search_params'] = $this->_fill_default_search_params($context['search_params']);
454
455
		$this->_controlVerifications();
456
457
		$context['params'] = $this->_search->compileURLparams();
458
459
		// ... and add the links to the link tree.
460
		$context['breadcrumbs'][] = [
461
			'url' => getUrl('action', ['action' => 'search', 'params' => $context['params']]),
462
			'name' => $txt['search']
463
		];
464
465
		$context['breadcrumbs'][] = [
466
			'url' => getUrl('action', ['action' => 'search', 'sa' => 'results', 'params' => $context['params']]),
467
			'name' => $txt['search_results']
468
		];
469
470
		// Start guest off collapsed
471
		if ($context['user']['is_guest'] && !isset($context['minmax_preferences']['asearch']))
472
		{
473
			$context['minmax_preferences']['asearch'] = 1;
474
		}
475
476
		// *** A last error check
477
		call_integration_hook('integrate_search_errors');
478
479
		// One or more search errors? Go back to the first search screen.
480
		if (!empty($context['search_errors']))
481
		{
482
			$this->action_search();
483
			return null;
484
		}
485
486
		// Spam me not, Spam-a-lot?
487
		if (empty($_SESSION['last_ss']) || $_SESSION['last_ss'] !== $this->_search->param('search'))
488
		{
489
			spamProtection('search');
490
		}
491
492
		// Store the last search string to allow pages of results to be browsed.
493
		$_SESSION['last_ss'] = $this->_search->param('search');
494
495
		try
496
		{
497
			$search_config = new ValuesContainer([
498
				'humungousTopicPosts' => $humungousTopicPosts,
499
				'shortTopicPosts' => $shortTopicPosts,
500
				'maxMessageResults' => $maxMessageResults,
501
				'search_index' => empty($modSettings['search_index']) ? '' : $modSettings['search_index'],
502
				'banned_words' => empty($modSettings['search_banned_words']) ? [] : explode(',', $modSettings['search_banned_words']),
503
			]);
504
			$context['topics'] = $this->_search->searchQuery(
505
				new SearchApiWrapper($search_config, $this->_search->getSearchParams())
506
			);
507
		}
508
		catch (\Exception $exception)
509
		{
510
			$context['search_errors'][$exception->getMessage()] = true;
511
512
			$this->action_search();
513
			return null;
514
		}
515
516
		// Did we find anything?
517
		if (!empty($context['topics']))
518
		{
519
			// Create an array for the permissions.
520
			$boards_can = boardsAllowedTo(['post_reply_own', 'post_reply_any', 'mark_any_notify'], true, false);
521
522
			// How's about some quick moderation?
523
			if (!empty($options['display_quick_mod']))
524
			{
525
				$boards_can = array_merge($boards_can, boardsAllowedTo(['lock_any', 'lock_own', 'make_sticky', 'move_any', 'move_own', 'remove_any', 'remove_own', 'merge_any'], true, false));
526
527
				$context['can_lock'] = in_array(0, $boards_can['lock_any']);
528
				$context['can_sticky'] = in_array(0, $boards_can['make_sticky']);
529
				$context['can_move'] = in_array(0, $boards_can['move_any']);
530
				$context['can_remove'] = in_array(0, $boards_can['remove_any']);
531
				$context['can_merge'] = in_array(0, $boards_can['merge_any']);
532
			}
533
534
			// What messages are we using?
535
			$msg_list = array_keys($context['topics']);
536
			$posters = $this->_search->loadPosters($msg_list, count($context['topics']));
537
538
			call_integration_hook('integrate_search_message_list', [&$msg_list, &$posters]);
539
540
			if (!empty($posters))
541
			{
542
				MembersList::load(array_unique($posters));
543
			}
544
545
			// Get the messages out for the callback - select enough that it can be made to look just like Display.
546
			$messages_request = $this->_search->loadMessagesRequest($msg_list, count($context['topics']));
547
548
			// If there are no results, that means the things in the cache got deleted, so pretend we have no topics anymore.
549
			if ($this->_search->noMessages($messages_request))
550
			{
551
				$context['topics'] = [];
552
			}
553
554
			$this->_prepareParticipants(!empty($modSettings['enableParticipation']), (int) $this->user->id);
555
		}
556
557
		// Now that we know how many results to expect, we can start calculating the page numbers.
558
		$start = $this->_req->getRequest('start', 'intval', 0);
559
		$context['page_index'] = constructPageIndex('{scripturl}?action=search;sa=results;' . $context['session_var'] . '=' . $context['session_id'] . ';params=' . $context['params'], $start, $this->_search->getNumResults(), $modSettings['search_results_per_page']);
560
561
		// Consider the search complete!
562
		Cache::instance()->remove('search_start:' . ($this->user->is_guest ? $this->user->ip : $this->user->id));
563
564
		$context['sub_template'] = 'results';
565
		$context['page_title'] = $txt['search_results'];
566
567
		$this->_icon_sources = new MessageTopicIcons(!empty($modSettings['messageIconChecks_enable']), $settings['theme_dir']);
568
569
		// Set the callback.  (do you REALIZE how much memory all the messages would take?!?)
570
		// This will be called from the template.
571
		if ($this->_search->isCompact())
572
		{
573
			$bodyParser = new Compact($this->_search->getSearchArray(), empty($modSettings['search_method']));
574
		}
575
		else
576
		{
577
			$bodyParser = new Normal($this->_search->getSearchArray(), empty($modSettings['search_method']));
578
		}
579
580
		$opt = new ValuesContainer([
581
			'icon_sources' => $this->_icon_sources,
582
			'show_signatures' => false,
583
			'boards_can' => $boards_can ?? [],
584
		]);
585
		$renderer = new SearchRenderer($messages_request, $this->user, $bodyParser, $opt);
586
		$renderer->setParticipants($this->_participants);
587
588
		$context['topic_starter_id'] = 0;
589
		$context['get_topics'] = [$renderer, 'getContext'];
590
591
		$context['jump_to'] = [
592
			'label' => addslashes(un_htmlspecialchars($txt['jump_to'])),
593
			'board_name' => addslashes(un_htmlspecialchars($txt['select_destination'])),
594
		];
595
596
		loadJavascriptFile('topic.js');
597
		$this->buildQuickModerationButtons();
598
	}
599
600
	/**
601
	 * Show an anti-spam verification control
602
	 */
603
	protected function _controlVerifications(): void
604
	{
605
		global $modSettings, $context;
606
607
		// Do we have captcha enabled?
608
		if ($this->user->is_guest && !empty($modSettings['search_enable_captcha']) && empty($_SESSION['ss_vv_passed']) && (empty($_SESSION['last_ss']) || $_SESSION['last_ss'] !== $this->_search->param('search')))
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...
609
		{
610
			$verificationOptions = [
611
				'id' => 'search',
612
			];
613
			$context['require_verification'] = VerificationControlsIntegrate::create($verificationOptions, true);
614
615
			if (is_array($context['require_verification']))
616
			{
617
				foreach ($context['require_verification'] as $error)
618
				{
619
					$context['search_errors'][$error] = true;
620
				}
621
			}
622
			// Don't keep asking for it - they've proven themselves worthy.
623
			else
624
			{
625
				$_SESSION['ss_vv_passed'] = true;
626
			}
627
		}
628
	}
629
630
	/**
631
	 * Prepares the participants data
632
	 *
633
	 * @param bool $participationEnabled Indicates if participation is enabled
634
	 * @param int $user_id The ID of the user
635
	 *
636
	 * @return void
637
	 */
638
	protected function _prepareParticipants($participationEnabled, $user_id): void
639
	{
640
		// If we want to know who participated in what, then load this now.
641
		if ($participationEnabled === true && $user_id !== 0)
642
		{
643
			$this->_participants = $this->_search->getParticipants();
644
645
			require_once(SUBSDIR . '/MessageIndex.subs.php');
646
			$topics_participated_in = topicsParticipation($user_id, array_keys($this->_participants));
647
648
			foreach ($topics_participated_in as $topic)
649
			{
650
				$this->_participants[$topic['id_topic']] = true;
651
			}
652
		}
653
	}
654
655
	/**
656
	 * Loads into $context the moderation button array for template use.
657
	 * Call integrate_message_index_mod_buttons hook
658
	 */
659
	protected function buildQuickModerationButtons(): void
660
	{
661
		global $context;
662
663
		// Build the mod button array with buttons that are valid for, at least some, of the messages
664
		$context['mod_buttons'] = [
665
			'move' => [
666
				'test' => 'can_move',
667
				'text' => 'move_topic',
668
				'id' => 'move',
669
				'lang' => true,
670
				'url' => 'javascript:void(0);',
671
			],
672
			'remove' => [
673
				'test' => 'can_remove',
674
				'text' => 'remove_topic',
675
				'id' => 'remove',
676
				'lang' => true,
677
				'url' => 'javascript:void(0);',
678
			],
679
			'lock' => [
680
				'test' => 'can_lock',
681
				'text' => 'set_lock',
682
				'id' => 'lock',
683
				'lang' => true,
684
				'url' => 'javascript:void(0);',
685
			],
686
			'sticky' => [
687
				'test' => 'can_sticky',
688
				'text' => 'set_sticky',
689
				'id' => 'sticky',
690
				'lang' => true,
691
				'url' => 'javascript:void(0);',
692
			],
693
			'markread' => [
694
				'test' => 'can_markread',
695
				'text' => 'mark_read_short',
696
				'id' => 'markread',
697
				'lang' => true,
698
				'url' => 'javascript:void(0);',
699
			],
700
		];
701
702
		// Allow adding new buttons easily.
703
		call_integration_hook('integrate_search_quickmod_buttons');
704
705
		$context['mod_buttons'] = array_reverse($context['mod_buttons']);
706
	}
707
}
708