Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

mod/search/classes/Elgg/Search/Formatter.php (4 issues)

1
<?php
2
3
namespace Elgg\Search;
4
5
use ElggEntity;
6
use ElggUser;
7
8
/**
9
 * Populates volatile data with details needed to render a search view
10
 *
11
 * @access private
12
 */
13
class Formatter {
14
15
	/**
16
	 * @var ElggEntity
17
	 */
18
	protected $entity;
19
20
	/**
21
	 * @var array
22
	 */
23
	protected $params = [];
24
25
	/**
26
	 * @var Highlighter
27
	 */
28
	protected $highlighter;
29
30
	/**
31
	 * Formatter constructor.
32
	 *
33
	 * @param ElggEntity $entity  Entity
34
	 * @param Search     $service Service
35
	 */
36 2
	public function __construct(ElggEntity $entity, Search $service) {
37 2
		$this->entity = $entity;
38 2
		$this->params = $service->getParams();
39 2
		$this->highlighter = $service->getHighlighter();
40 2
	}
41
42
	/**
43
	 * Populate search-related volatile data
44
	 * @return void
45
	 */
46 2
	public function format() {
47 2
		_elgg_services()->hooks->trigger('search:format', 'entity', $this->params, $this->entity);
48
49 2
		$this->formatTitle();
50 2
		$this->formatDescription();
51 2
		$this->formatExtras();
52 2
		$this->formatIcon();
53 2
		$this->formatURL();
54 2
		$this->formatTime();
55 2
	}
56
57
	/**
58
	 * Format title
59
	 * @return void
60
	 */
61 2
	protected function formatTitle() {
62
63 2
		if ($this->entity->getVolatileData('search_matched_title')) {
64 1
			return;
65
		}
66
67 1
		$title = $this->entity->getDisplayName();
68 1
		if ($this->entity instanceof ElggUser) {
69
			$title .= " (@{$this->entity->username})";
70
		}
71
72 1
		$title = $this->highlighter->highlight($title, 1, 300);
73 1
		$this->entity->setVolatileData('search_matched_title', $title);
74 1
	}
75
76
	/**
77
	 * Format description
78
	 * @return void
79
	 */
80 2
	protected function formatDescription() {
81
82 2
		if ($this->entity->getVolatileData('search_matched_description')) {
83
			return;
84
		}
85
86 2
		$description = $this->entity->description;
87 2
		$description = $this->highlighter->highlight($description, 10, 300);
0 ignored issues
show
It seems like $description can also be of type array; however, parameter $text of Elgg\Search\Highlighter::highlight() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

87
		$description = $this->highlighter->highlight(/** @scrutinizer ignore-type */ $description, 10, 300);
Loading history...
88 2
		$this->entity->setVolatileData('search_matched_description', $description);
89 2
	}
90
91
	/**
92
	 * Format extras
93
	 * @return void
94
	 */
95 2
	protected function formatExtras() {
96 2
		if ($this->entity->getVolatileData('search_matched_extra')) {
97
			return;
98
		}
99
100 2
		$extra = [];
101 2
		$matches = $this->getPropertyMatches();
102
103 2
		foreach ($matches as $property_type => $fields) {
104 1
			foreach ($fields as $field => $match) {
105 1
				$label = elgg_format_element('strong', [
106 1
					'class' => 'search-match-extra-label',
107 1
				], $this->getFieldLabel($property_type, $field));
108
109
110 1
				$extra_row = elgg_format_element('p', [
111 1
					'class' => 'elgg-output search-match-extra',
112 1
				], $label . ': ' . implode(', ', $match));
113
114 1
				$extra[] = $extra_row;
115
			}
116
		}
117
118 2
		$this->entity->setVolatileData('search_matched_extra', implode('', $extra));
119 2
	}
120
121
	/**
122
	 * Format entity properties
123
	 *
124
	 * @todo Match individual words instead of entire query
125
	 *
126
	 * @return array
127
	 */
128 2
	protected function getPropertyMatches() {
129
130 2
		$type = $this->entity->getType();
131 2
		$query = elgg_extract('query', $this->params);
132 2
		$fields = elgg_extract('fields', $this->params);
133
134 2
		switch ($type) {
135
			case 'user' :
136
				$exclude = ['metadata' => ['name', 'username', 'description']];
137
				break;
138
			case 'group' :
139
				$exclude = ['metadata' => ['name', 'description']];
140
				break;
141
			case 'object' :
142 2
				$exclude = ['metadata' => ['title', 'description']];
143 2
				break;
144
		}
145
146 2
		$matches = [];
147 2
		if (!empty($fields)) {
148 1
			foreach ($fields as $property_type => $property_type_fields) {
149 1
				foreach ($property_type_fields as $field) {
150 1
					if (!empty($exclude[$property_type]) && in_array($field, $exclude[$property_type])) {
151
						continue;
152
					}
153
154 1
					switch ($property_type) {
155
						case 'attributes' :
156
						case 'metadata' :
157 1
							$property_values = $this->entity->$field;
158 1
							break;
159
160
						case 'annotations' :
161
							$property_values = [];
162
							$annotations = $this->entity->getAnnotations([
163
								'annotation_names' => $field,
164
								'limit' => 0,
165
							]);
166
							foreach ($annotations as $annotation) {
167
								$property_values[] = $annotation->value;
168
							}
169
							break;
170
171
						case 'private_settings' :
172
							$property_values = $this->entity->getPrivateSetting($field);
173
							break;
174
					}
175
176 1
					if (is_array($property_values)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $property_values does not seem to be defined for all execution paths leading up to this point.
Loading history...
177
						foreach ($property_values as $text) {
178
							if (stristr($text, $query)) {
179
								$matches[$property_type][$field][] = $this->highlighter->highlight($text, 1, 300);
180
							}
181
						}
182
					} else {
183 1
						if (stristr($property_values, $query)) {
0 ignored issues
show
It seems like $property_values can also be of type false; however, parameter $haystack of stristr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

183
						if (stristr(/** @scrutinizer ignore-type */ $property_values, $query)) {
Loading history...
184 1
							$matches[$property_type][$field][] = $this->highlighter->highlight($property_values, 1, 300);
0 ignored issues
show
It seems like $property_values can also be of type false; however, parameter $text of Elgg\Search\Highlighter::highlight() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

184
							$matches[$property_type][$field][] = $this->highlighter->highlight(/** @scrutinizer ignore-type */ $property_values, 1, 300);
Loading history...
185
						}
186
					}
187
				}
188
			}
189
		}
190
191 2
		return $matches;
192
	}
193
194
	/**
195
	 * Get label for a property
196
	 *
197
	 * @param string $property_type Property type
198
	 * @param string $property_name Property name
199
	 *
200
	 * @return string
201
	 */
202 1
	protected function getFieldLabel($property_type, $property_name) {
203
204 1
		$type = $this->entity->getType();
205 1
		$subtype = $this->entity->getSubtype();
206
207 1
		$prefix = 'search';
208
209 1
		switch ($type) {
210
			case 'user' :
211
				$prefix = 'profile';
212
				break;
213
			case 'group' :
214
				$prefix = 'group';
215
				break;
216
			case 'object' :
217 1
				$prefix = 'tag_names';
218 1
				break;
219
		}
220
221
		$keys = [
222 1
			"$type:$subtype:$property_type:field:$property_name",
223 1
			"$type:$property_type:$property_name",
224 1
			"$prefix:$property_type:$property_name",
225 1
			"$type:$subtype:field:$property_name",
226 1
			"$type:$property_name",
227 1
			"$prefix:$property_name",
228
		];
229
230 1
		$label = elgg_echo("tag_names:$property_name");
231
232 1
		foreach ($keys as $key) {
233 1
			if (elgg_language_key_exists($key)) {
234
				$label = elgg_echo($key);
235 1
				break;
236
			}
237
		}
238
239 1
		return $label;
240
	}
241
242
	/**
243
	 * Format icon
244
	 * @return void
245
	 */
246 2
	protected function formatIcon() {
247 2
		if ($this->entity->getVolatileData('search_icon')) {
248
			return;
249
		}
250
251 2
		$type = $this->entity->getType();
252 2
		$owner = $this->entity->getOwnerEntity();
253 2
		$container = $this->entity->getContainerEntity();
254
255 2
		$size = elgg_extract('size', $this->params, 'small');
256
257 2
		$icon = '';
258
259 2
		if ($this->entity->hasIcon($size) || $this->entity instanceof \ElggFile) {
260
			$icon = elgg_view_entity_icon($this->entity, $size);
261 2
		} else if ($type == 'user' || $type == 'group') {
262
			$icon = elgg_view_entity_icon($this->entity, $size);
263 2
		} else if ($owner instanceof ElggUser) {
264
			$icon = elgg_view_entity_icon($owner, $size);
265 2
		} else if ($container instanceof ElggUser) {
266
			// display a generic icon if no owner, though there will probably be
267
			// other problems if the owner can't be found.
268
			$icon = elgg_view_entity_icon($this->entity, $size);
269
		}
270
271 2
		$this->entity->setVolatileData('search_icon', $icon);
272 2
	}
273
274
	/**
275
	 * Format URL
276
	 * @return void
277
	 */
278 2
	protected function formatURL() {
279 2
		if (!$this->entity->getVolatileData('search_url')) {
280 2
			$url = $this->entity->getURL();
281 2
			$this->entity->setVolatileData('search_url', $url);
282
		}
283 2
	}
284
285
	/**
286
	 * Format time
287
	 * @return void
288
	 */
289 2
	protected function formatTime() {
290 2
		if (!$this->entity->getVolatileData('search_time')) {
291 2
			$this->entity->setVolatileData('search_time', $this->entity->time_created);
292
		}
293 2
	}
294
}
295