SearchRenderer::_canMove()   A
last analyzed

Complexity

Conditions 5
Paths 7

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 4
dl 0
loc 6
rs 9.6111
c 0
b 0
f 0
nc 7
nop 2
ccs 0
cts 0
cp 0
crap 30
1
<?php
2
3
/**
4
 * Part of the files dealing with preparing the content for display posts
5
 * via callbacks (Display, PM, Search).
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 dev
15
 *
16
 */
17
18
namespace ElkArte\MessagesCallback;
19
20
use ElkArte\Helper\Util;
21
use ElkArte\Helper\ValuesContainer;
22
use ElkArte\MembersList;
23
use ElkArte\MessagesCallback\BodyParser\BodyParserInterface;
24
use ElkArte\TopicUtil;
25
26
/**
27
 * SearchRenderer
28
 *
29
 * Used by the \ElkArte\Controller\Search to prepare the search results.
30
 */
31
class SearchRenderer extends Renderer
32
{
33
	public const BEFORE_PREPARE_HOOK = 'integrate_before_prepare_search_context';
34
35
	public const CONTEXT_HOOK = 'integrate_prepare_search_context';
36
37
	/** @var array */
38
	protected $_participants = [];
39
40
	/**
41
	 * {@inheritDoc}
42
	 */
43
	public function __construct($request, $user, BodyParserInterface $bodyParser, ValuesContainer $opt = null)
44
	{
45
		parent::__construct($request, $user, $bodyParser, $opt);
46
47
		require_once(SUBSDIR . '/Attachments.subs.php');
48
	}
49
50
	/**
51
	 * @param array $participants
52
	 */
53
	public function setParticipants($participants)
54
	{
55
		$this->_participants = $participants;
56
	}
57
58
	/**
59
	 * {@inheritDoc}
60
	 */
61
	protected function _setupPermissions()
62
	{
63
		global $txt;
64
65
		$this->_this_message['first_subject'] = $this->_this_message['first_subject'] !== '' ? $this->_this_message['first_subject'] : $txt['no_subject'];
66
		$this->_this_message['last_subject'] = $this->_this_message['last_subject'] !== '' ? $this->_this_message['last_subject'] : $txt['no_subject'];
67
	}
68
69
	/**
70
	 * {@inheritDoc}
71
	 */
72
	protected function _adjustMemberContext($member_context)
73
	{
74
	}
75
76
	/**
77
	 * {@inheritDoc}
78
	 */
79
	protected function _adjustAllMembers($member_context)
80
	{
81
		$member = MembersList::get($this->_this_message['id_member']);
82
		$member['ip'] = $this->_this_message['poster_ip'];
83
84
		$this->_this_message['first_subject'] = censor($this->_this_message['first_subject']);
85
		$this->_this_message['last_subject'] = censor($this->_this_message['last_subject']);
86
	}
87
88
	/**
89
	 * {@inheritDoc}
90
	 */
91
	protected function _buildOutputArray()
92
	{
93
		global $modSettings, $context, $options;
94
95
		// Make sure we don't end up with a practically empty message body.
96
		$this->_this_message['body'] = preg_replace('~^(?:&nbsp;)+$~', '', $this->_this_message['body']);
97
98
		// Do we have quote tag enabled?
99
		$quote_enabled = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']));
100
101
		$output_pre = TopicUtil::prepareContext([$this->_this_message])[$this->_this_message['id_topic']];
102
103
		$output = array_merge($context['topics'][$this->_this_message['id_msg']], $output_pre);
104
		$output['posted_in'] = !empty($this->_participants[$this->_this_message['id_topic']]);
105
		$href = getUrl('board', ['board' => $this->_this_message['id_board'], 'start' => '0', 'name' => $this->_this_message['bname']]);
106
107
		$output['board'] = [
108
			'id' => $this->_this_message['id_board'],
109
			'name' => $this->_this_message['bname'],
110
			'href' => $href,
111
			'link' => '<a href="' . $href . '0">' . $this->_this_message['bname'] . '</a>'
112
		];
113
114
		$output['category'] = [
115
			'id' => $this->_this_message['id_cat'],
116
			'name' => $this->_this_message['cat_name'],
117
			'href' => getUrl('action', $modSettings['default_forum_action']) . '#c' . $this->_this_message['id_cat'],
118
			'link' => '<a href="' . getUrl('action', $modSettings['default_forum_action']) . '#c' . $this->_this_message['id_cat'] . '">' . $this->_this_message['cat_name'] . '</a>'
119
		];
120
121
		determineTopicClass($output);
122
123
		if ($output['posted_in'])
124
		{
125
			$output['class'] = 'my_' . $output['class'];
126
		}
127
128
		$body_highlighted = $this->_this_message['body'];
129
		$subject_highlighted = $this->_this_message['subject'];
130
131
		if (!empty($options['display_quick_mod']))
132
		{
133
			$started = (int) $output['first_post']['member']['id'] === $this->user->id;
134
135
			$output['quick_mod'] = [
136
				'lock' => $this->_canLock($output, $started),
137
				'sticky' => $this->_canSticky($output),
138
				'move' => $this->_canMove($output, $started),
139
				'remove' => $this->_canRemove($output, $started),
140
			];
141
142
			$context['can_lock'] |= $output['quick_mod']['lock'];
143
			$context['can_sticky'] |= $output['quick_mod']['sticky'];
144
			$context['can_move'] |= $output['quick_mod']['move'];
145
			$context['can_remove'] |= $output['quick_mod']['remove'];
146
			$context['can_merge'] |= in_array($output['board']['id'], $this->_options['boards_can']['merge_any']);
147
			$context['can_markread'] = $context['user']['is_logged'];
148
149
			$context['can_quick_mod'] = $context['user']['is_logged'] || $context['can_remove'] || $context['can_lock'] || $context['can_sticky'] || $context['can_move'];
150
			if ($context['can_quick_mod'])
151
			{
152
				$context['qmod_actions'] = ['remove', 'lock', 'sticky', 'move', 'markread'];
153
				call_integration_hook('integrate_quick_mod_actions_search');
154
			}
155
		}
156
157
		foreach ($this->_bodyParser->getSearchArray() as $query)
158
		{
159
			// Fix the international characters in the keyword too.
160
			$query = un_htmlspecialchars($query);
161
			$query = trim($query, '\*+');
162
			$query = strtr(Util::htmlspecialchars($query), ['\\\'' => "'"]);
163
164
			$search_highlight = preg_quote(strtr($query, ["'" => '&#039;']), '/');
165
			$body_highlighted = preg_replace_callback('/((<[^>]*)|(\b' . $search_highlight . '\b)|' . $search_highlight . ')/iu',
166
				fn($matches) => $this->_highlighted_callback($matches), $body_highlighted);
167
			$subject_highlighted = preg_replace('/(' . preg_quote($query, '/') . ')/iu', '<strong class="highlight">$1</strong>', $subject_highlighted);
168
		}
169
170
		$member = MembersList::get($this->_this_message['id_member']);
171
		$output['matches'][] = [
172
			'id' => $this->_this_message['id_msg'],
173
			'attachment' => [],
174
			'member' => $member,
175
			'icon' => $this->_options->icon_sources->getIconValue($this->_this_message['icon']),
0 ignored issues
show
Bug introduced by
The method getIconValue() does not exist on null. ( Ignorable by Annotation )

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

175
			'icon' => $this->_options->icon_sources->/** @scrutinizer ignore-call */ getIconValue($this->_this_message['icon']),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug Best Practice introduced by
The property icon_sources does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
176
			'icon_url' => $this->_options->icon_sources->getIconURL($this->_this_message['icon']),
177
			'subject' => $this->_this_message['subject'],
178
			'subject_highlighted' => $subject_highlighted,
179
			'time' => standardTime($this->_this_message['poster_time']),
180
			'html_time' => htmlTime($this->_this_message['poster_time']),
181
			'timestamp' => forum_time(true, $this->_this_message['poster_time']),
182
			'counter' => $this->_counter,
183
			'modified' => [
184
				'time' => standardTime($this->_this_message['modified_time']),
185
				'html_time' => htmlTime($this->_this_message['modified_time']),
186
				'timestamp' => forum_time(true, $this->_this_message['modified_time']),
187
				'name' => $this->_this_message['modified_name']
188
			],
189
			'body' => $this->_this_message['body'],
190
			'body_highlighted' => $body_highlighted,
191
			'start' => 'msg' . $this->_this_message['id_msg'],
192
		];
193
194
		$output['buttons'] = $this->_buildSearchButtons($output, $quote_enabled);
195
196
		return $output;
197
	}
198
199
	/**
200
	 * Generates a PM button array suitable for consumption by template_button_strip
201
	 *
202
	 * @param array $output
203
	 * @param bool $quote_enabled
204
	 * @return array
205
	 */
206
	protected function _buildSearchButtons($output, $quote_enabled)
207
	{
208
		global $context;
209
210
		$searchButtons = [];
211
212
		if (!$context['compact'])
213
		{
214
			$searchButtons = [
215
				// If they can moderate
216
				'inline_mod_check' => [
217
					'class' => 'inline_mod_check',
218
					'value' => $output['id'],
219
					'checkbox' => 'always',
220
					'name' => 'topics',
221
					'enabled' => $context['can_quick_mod'],
222
				],
223
				// Can we request notification of topics?
224
				'notify' => [
225
					'url' => getUrl('action', ['action' => 'notify', 'topic' => $output['id'] . '.msg' . $this->_this_message['id_msg']]),
226
					'text' => 'notify',
227
					'icon' => 'envelope',
228
					'enabled' => in_array($output['board']['id'], $this->_options['boards_can']['mark_any_notify']) || in_array(0, $this->_options['boards_can']['mark_any_notify']) && !$context['user']['is_guest'],
229
				],
230
				// If they *can* reply?
231
				'reply' => [
232
					'url' => getUrl('action', ['action' => 'post', 'topic' => $output['id'] . '.msg' . $this->_this_message['id_msg']]),
233
					'text' => 'reply',
234
					'icon' => 'modify',
235
					'enabled' => in_array($output['board']['id'], $this->_options['boards_can']['post_reply_any']) || in_array(0, $this->_options['boards_can']['post_reply_any']),
236
				],
237
				// If they *can* quote?
238
				'quote' => [
239
					'url' => getUrl('action', ['action' => 'post', 'topic' => $output['id'] . '.msg' . $this->_this_message['id_msg'], 'quote' => $this->_this_message['id_msg']]),
240
					'text' => 'quote',
241
					'icon' => 'quote',
242
					'enabled' => (in_array($output['board']['id'], $this->_options['boards_can']['post_reply_any']) || in_array(0, $this->_options['boards_can']['post_reply_any'])) && $quote_enabled,
243
				],
244
			];
245
		}
246
247
		// Drop any non-enabled ones
248
		return array_filter($searchButtons, static fn($button) => !isset($button['enabled']) || (bool) $button['enabled']);
249
	}
250
251
	/**
252
	 * Used to highlight body text with strings that match the search term
253
	 *
254
	 * Callback function used in $body_highlighted.
255
	 * match[2] would contain terms that start with <
256
	 * match[1] would be a word in a word, and could be just the word
257
	 * match[3] would be the search term as a full word
258
	 *
259
	 * @param string[] $matches
260
	 *
261
	 * @return string
262
	 */
263
	private function _highlighted_callback($matches)
264
	{
265
		if (isset($matches[2]) && $matches[2] === $matches[1])
266
		{
267
			return stripslashes($matches[1]);
268
		}
269
270
		if (isset($matches[3]))
271
		{
272
			return '<span class="highlight">' . $matches[3] . '</span>';
273
		}
274
275
		return '<span class="highlight_sub">' . $matches[1] . '</span>';
276
	}
277
278
	/**
279
	 * Can the item be locked
280
	 *
281
	 * @param array $output
282
	 * @param bool $started
283
	 * @return bool
284
	 */
285
	private function _canLock($output, $started)
286
	{
287
		return in_array(0, $this->_options['boards_can']['lock_any'])
288
			|| in_array($output['board']['id'], $this->_options['boards_can']['lock_any'])
289
			|| ($started && (in_array(0, $this->_options['boards_can']['lock_own'])
290
					|| in_array($output['board']['id'], $this->_options['boards_can']['lock_own'])));
291
	}
292
293
	/**
294
	 * Can the item be pinned
295
	 *
296
	 * @param array $output
297
	 * @return bool
298
	 */
299
	private function _canSticky($output)
300
	{
301
		return in_array(0, $this->_options['boards_can']['make_sticky'])
302
			|| in_array($output['board']['id'], $this->_options['boards_can']['make_sticky']);
303
	}
304
305
	/**
306
	 * Can the item be moved
307
	 *
308
	 * @param array $output
309
	 * @param bool $started
310
	 * @return bool
311
	 */
312
	private function _canMove($output, $started)
313
	{
314
		return in_array(0, $this->_options['boards_can']['move_any'])
315
			|| in_array($output['board']['id'], $this->_options['boards_can']['move_any'])
316
			|| ($started && (in_array(0, $this->_options['boards_can']['move_own'])
317
					|| in_array($output['board']['id'], $this->_options['boards_can']['move_own'])));
318
319
	}
320
321
	/**
322
	 * Can the item be removed
323
	 *
324
	 * @param array $output
325
	 * @param bool $started
326
	 * @return bool
327
	 */
328
	private function _canRemove($output, $started)
329
	{
330
		return in_array(0, $this->_options['boards_can']['remove_any'])
331
			|| in_array($output['board']['id'], $this->_options['boards_can']['remove_any'])
332
			|| ($started && (in_array(0, $this->_options['boards_can']['remove_own'])
333
					|| in_array($output['board']['id'], $this->_options['boards_can']['remove_own'])));
334
335
	}
336
}
337