1 | <?php |
||
2 | |||
3 | namespace Elgg; |
||
4 | |||
5 | use Elgg\Database\EntityTable; |
||
6 | use Elgg\Filesystem\MimeTypeDetector; |
||
7 | use Elgg\Http\Request; |
||
8 | use Elgg\UploadService; |
||
9 | use ElggEntity; |
||
10 | use ElggFile; |
||
11 | use ElggIcon; |
||
12 | use InvalidParameterException; |
||
13 | use Symfony\Component\HttpFoundation\BinaryFileResponse; |
||
14 | use Symfony\Component\HttpFoundation\Response; |
||
15 | |||
16 | /** |
||
17 | * WARNING: API IN FLUX. DO NOT USE DIRECTLY. |
||
18 | * |
||
19 | * Use the elgg_* versions instead. |
||
20 | * |
||
21 | * @access private |
||
22 | * @since 2.2 |
||
23 | */ |
||
24 | class EntityIconService { |
||
25 | use TimeUsing; |
||
26 | |||
27 | /** |
||
28 | * @var Config |
||
29 | */ |
||
30 | private $config; |
||
31 | |||
32 | /** |
||
33 | * @var PluginHooksService |
||
34 | */ |
||
35 | private $hooks; |
||
36 | |||
37 | /** |
||
38 | * @var Request |
||
39 | */ |
||
40 | private $request; |
||
41 | |||
42 | /** |
||
43 | * @var Logger |
||
44 | */ |
||
45 | private $logger; |
||
46 | |||
47 | /** |
||
48 | * @var EntityTable |
||
49 | */ |
||
50 | private $entities; |
||
51 | |||
52 | /** |
||
53 | * @var UploadService |
||
54 | */ |
||
55 | private $uploads; |
||
56 | |||
57 | /** |
||
58 | * Constructor |
||
59 | * |
||
60 | * @param Config $config Config |
||
61 | * @param PluginHooksService $hooks Hook registration service |
||
62 | * @param Request $request Http request |
||
63 | * @param Logger $logger Logger |
||
64 | * @param EntityTable $entities Entity table |
||
65 | * @param UploadService $uploads Upload service |
||
66 | */ |
||
67 | 62 | public function __construct(Config $config, PluginHooksService $hooks, Request $request, Logger $logger, EntityTable $entities, UploadService $uploads) { |
|
68 | 62 | $this->config = $config; |
|
69 | 62 | $this->hooks = $hooks; |
|
70 | 62 | $this->request = $request; |
|
71 | 62 | $this->logger = $logger; |
|
72 | 62 | $this->entities = $entities; |
|
73 | 62 | $this->uploads = $uploads; |
|
74 | 62 | } |
|
75 | |||
76 | /** |
||
77 | * Saves icons using an uploaded file as the source. |
||
78 | * |
||
79 | * @param ElggEntity $entity Entity to own the icons |
||
80 | * @param string $input_name Form input name |
||
81 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
82 | * @param array $coords An array of cropping coordinates x1, y1, x2, y2 |
||
83 | * @return bool |
||
84 | */ |
||
85 | 1 | public function saveIconFromUploadedFile(ElggEntity $entity, $input_name, $type = 'icon', array $coords = []) { |
|
86 | 1 | $input = $this->uploads->getFile($input_name); |
|
87 | 1 | if (empty($input)) { |
|
88 | return false; |
||
89 | } |
||
90 | |||
91 | 1 | $tmp_filename = time() . $input->getClientOriginalName(); |
|
92 | 1 | $tmp = new ElggFile(); |
|
93 | 1 | $tmp->owner_guid = $entity->guid; |
|
94 | 1 | $tmp->setFilename("tmp/$tmp_filename"); |
|
95 | 1 | $tmp->open('write'); |
|
96 | 1 | $tmp->close(); |
|
97 | // not using move_uploaded_file() for testing purposes |
||
98 | 1 | copy($input->getPathname(), $tmp->getFilenameOnFilestore()); |
|
99 | |||
100 | 1 | $tmp->mimetype = (new MimeTypeDetector())->getType($tmp_filename, $input->getClientMimeType()); |
|
101 | 1 | $tmp->simpletype = elgg_get_file_simple_type($tmp->mimetype); |
|
102 | |||
103 | 1 | $result = $this->saveIcon($entity, $tmp, $type, $coords); |
|
104 | |||
105 | 1 | unlink($input->getPathname()); |
|
106 | 1 | $tmp->delete(); |
|
107 | |||
108 | 1 | return $result; |
|
109 | } |
||
110 | |||
111 | /** |
||
112 | * Saves icons using a local file as the source. |
||
113 | * |
||
114 | * @param ElggEntity $entity Entity to own the icons |
||
115 | * @param string $filename The full path to the local file |
||
116 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
117 | * @param array $coords An array of cropping coordinates x1, y1, x2, y2 |
||
118 | * @return bool |
||
119 | * @throws InvalidParameterException |
||
120 | */ |
||
121 | 5 | public function saveIconFromLocalFile(ElggEntity $entity, $filename, $type = 'icon', array $coords = []) { |
|
122 | 5 | if (!file_exists($filename) || !is_readable($filename)) { |
|
123 | 1 | throw new InvalidParameterException(__METHOD__ . " expects a readable local file. $filename is not readable"); |
|
124 | } |
||
125 | |||
126 | 4 | $tmp_filename = time() . pathinfo($filename, PATHINFO_BASENAME); |
|
127 | 4 | $tmp = new ElggFile(); |
|
128 | 4 | $tmp->owner_guid = $entity->guid; |
|
129 | 4 | $tmp->setFilename("tmp/$tmp_filename"); |
|
130 | 4 | $tmp->open('write'); |
|
131 | 4 | $tmp->close(); |
|
132 | 4 | copy($filename, $tmp->getFilenameOnFilestore()); |
|
133 | |||
134 | 4 | $tmp->mimetype = (new MimeTypeDetector())->getType($tmp->getFilenameOnFilestore()); |
|
135 | 4 | $tmp->simpletype = elgg_get_file_simple_type($tmp->mimetype); |
|
136 | |||
137 | 4 | $result = $this->saveIcon($entity, $tmp, $type, $coords); |
|
138 | |||
139 | 4 | $tmp->delete(); |
|
140 | |||
141 | 4 | return $result; |
|
142 | } |
||
143 | |||
144 | /** |
||
145 | * Saves icons using a file located in the data store as the source. |
||
146 | * |
||
147 | * @param ElggEntity $entity Entity to own the icons |
||
148 | * @param ElggFile $file An ElggFile instance |
||
149 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
150 | * @param array $coords An array of cropping coordinates x1, y1, x2, y2 |
||
151 | * @return bool |
||
152 | * @throws InvalidParameterException |
||
153 | */ |
||
154 | 41 | public function saveIconFromElggFile(ElggEntity $entity, ElggFile $file, $type = 'icon', array $coords = []) { |
|
155 | 41 | if (!$file->exists()) { |
|
156 | 1 | throw new InvalidParameterException(__METHOD__ . ' expects an instance of ElggFile with an existing file on filestore'); |
|
157 | } |
||
158 | |||
159 | 40 | $tmp_filename = time() . pathinfo($file->getFilenameOnFilestore(), PATHINFO_BASENAME); |
|
160 | 40 | $tmp = new ElggFile(); |
|
161 | 40 | $tmp->owner_guid = $entity->guid; |
|
162 | 40 | $tmp->setFilename("tmp/$tmp_filename"); |
|
163 | 40 | $tmp->open('write'); |
|
164 | 40 | $tmp->close(); |
|
165 | 40 | copy($file->getFilenameOnFilestore(), $tmp->getFilenameOnFilestore()); |
|
166 | |||
167 | 40 | $tmp->mimetype = (new MimeTypeDetector())->getType($tmp->getFilenameOnFilestore(), $file->getMimeType()); |
|
168 | 40 | $tmp->simpletype = elgg_get_file_simple_type($tmp->mimetype); |
|
169 | |||
170 | 40 | $result = $this->saveIcon($entity, $tmp, $type, $coords); |
|
171 | |||
172 | 40 | $tmp->delete(); |
|
173 | |||
174 | 40 | return $result; |
|
175 | } |
||
176 | |||
177 | /** |
||
178 | * Saves icons using a created temporary file |
||
179 | * |
||
180 | * @param ElggEntity $entity Temporary ElggFile instance |
||
181 | * @param ElggFile $file Temporary ElggFile instance |
||
182 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
183 | * @param array $coords An array of cropping coordinates x1, y1, x2, y2 |
||
184 | * @return bool |
||
185 | */ |
||
186 | 45 | public function saveIcon(ElggEntity $entity, ElggFile $file, $type = 'icon', array $coords = []) { |
|
187 | |||
188 | 45 | $type = (string) $type; |
|
189 | 45 | if (!strlen($type)) { |
|
190 | $this->logger->error('Icon type passed to ' . __METHOD__ . ' can not be empty'); |
||
191 | return false; |
||
192 | } |
||
193 | |||
194 | 45 | $entity_type = $entity->getType(); |
|
195 | |||
196 | 45 | $file = $this->hooks->trigger("entity:$type:prepare", $entity_type, [ |
|
197 | 45 | 'entity' => $entity, |
|
198 | 45 | 'file' => $file, |
|
199 | 45 | ], $file); |
|
200 | |||
201 | 45 | if (!$file instanceof ElggFile || !$file->exists() || $file->getSimpleType() !== 'image') { |
|
202 | $this->logger->error('Source file passed to ' . __METHOD__ . ' can not be resolved to a valid image'); |
||
203 | return false; |
||
204 | } |
||
205 | |||
206 | 45 | $x1 = (int) elgg_extract('x1', $coords); |
|
207 | 45 | $y1 = (int) elgg_extract('y1', $coords); |
|
208 | 45 | $x2 = (int) elgg_extract('x2', $coords); |
|
209 | 45 | $y2 = (int) elgg_extract('y2', $coords); |
|
210 | |||
211 | 45 | $created = $this->hooks->trigger("entity:$type:save", $entity_type, [ |
|
212 | 45 | 'entity' => $entity, |
|
213 | 45 | 'file' => $file, |
|
214 | 45 | 'x1' => $x1, |
|
215 | 45 | 'y1' => $y1, |
|
216 | 45 | 'x2' => $x2, |
|
217 | 45 | 'y2' => $y2, |
|
218 | 45 | ], false); |
|
219 | |||
220 | // did someone else handle saving the icon? |
||
221 | 45 | if ($created !== true) { |
|
222 | // remove existing icons |
||
223 | 44 | $this->deleteIcon($entity, $type, true); |
|
224 | |||
225 | // save master image |
||
226 | 44 | $store = $this->generateIcon($entity, $file, $type, $coords, 'master'); |
|
227 | |||
228 | 44 | if (!$store) { |
|
229 | $this->deleteIcon($entity, $type); |
||
230 | return false; |
||
231 | } |
||
232 | } |
||
233 | |||
234 | 45 | if ($type == 'icon') { |
|
235 | 44 | $entity->icontime = time(); |
|
236 | 44 | if ($x1 || $y1 || $x2 || $y2) { |
|
237 | 7 | $entity->x1 = $x1; |
|
238 | 7 | $entity->y1 = $y1; |
|
239 | 7 | $entity->x2 = $x2; |
|
240 | 44 | $entity->y2 = $y2; |
|
241 | } |
||
242 | } else { |
||
243 | 1 | if ($x1 || $y1 || $x2 || $y2) { |
|
244 | 1 | $entity->{"{$type}_coords"} = serialize([ |
|
245 | 1 | 'x1' => $x1, |
|
246 | 1 | 'y1' => $y1, |
|
247 | 1 | 'x2' => $x2, |
|
248 | 1 | 'y2' => $y2, |
|
249 | ]); |
||
250 | } |
||
251 | } |
||
252 | |||
253 | 45 | $this->hooks->trigger("entity:$type:saved", $entity->getType(), [ |
|
254 | 45 | 'entity' => $entity, |
|
255 | 45 | 'x1' => $x1, |
|
256 | 45 | 'y1' => $y1, |
|
257 | 45 | 'x2' => $x2, |
|
258 | 45 | 'y2' => $y2, |
|
259 | ]); |
||
260 | |||
261 | 45 | return true; |
|
262 | } |
||
263 | |||
264 | /** |
||
265 | * Generate an icon for the given entity |
||
266 | * |
||
267 | * @param ElggEntity $entity Temporary ElggFile instance |
||
268 | * @param ElggFile $file Temporary ElggFile instance |
||
269 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
270 | * @param array $coords An array of cropping coordinates x1, y1, x2, y2 |
||
271 | * @param string $icon_size The icon size to generate (leave empty to generate all supported sizes) |
||
272 | * |
||
273 | * @return bool |
||
274 | */ |
||
275 | 44 | protected function generateIcon(ElggEntity $entity, ElggFile $file, $type = 'icon', $coords = [], $icon_size = '') { |
|
276 | |||
277 | 44 | if (!$file->exists()) { |
|
278 | $this->logger->error('Trying to generate an icon from a non-existing file'); |
||
279 | return false; |
||
280 | } |
||
281 | |||
282 | 44 | $x1 = (int) elgg_extract('x1', $coords); |
|
283 | 44 | $y1 = (int) elgg_extract('y1', $coords); |
|
284 | 44 | $x2 = (int) elgg_extract('x2', $coords); |
|
285 | 44 | $y2 = (int) elgg_extract('y2', $coords); |
|
286 | |||
287 | 44 | $cropping_mode = ($x2 > $x1) && ($y2 > $y1); |
|
288 | |||
289 | 44 | $sizes = $this->getSizes($entity->getType(), $entity->getSubtype(), $type); |
|
290 | |||
291 | 44 | if (!empty($icon_size) && !isset($sizes[$icon_size])) { |
|
292 | $this->logger->warn("The provided icon size '{$icon_size}' doesn't exist for icon type '{$type}'"); |
||
293 | return false; |
||
294 | } |
||
295 | |||
296 | 44 | foreach ($sizes as $size => $opts) { |
|
297 | 44 | if (!empty($icon_size) && ($icon_size !== $size)) { |
|
298 | // only generate the given icon size |
||
299 | 44 | continue; |
|
300 | } |
||
301 | |||
302 | 44 | $square = (bool) elgg_extract('square', $opts); |
|
303 | |||
304 | 44 | if ($type === 'icon' && $cropping_mode) { |
|
305 | 7 | $cropping_ratio = ($x2 - $x1) / ($y2 - $y1); |
|
306 | 7 | if ($cropping_ratio == 1 && $square === false) { |
|
307 | // Do not crop out non-square icons if cropping coordinates are a square |
||
308 | $coords = [ |
||
309 | 7 | 'x1' => 0, |
|
310 | 'y1' => 0, |
||
311 | 'x2' => 0, |
||
312 | 'y2' => 0, |
||
313 | ]; |
||
314 | } |
||
315 | } |
||
316 | |||
317 | 44 | $icon = $this->getIcon($entity, $size, $type, false); |
|
318 | |||
319 | // We need to make sure that file path is readable by |
||
320 | // Imagine\Image\ImagineInterface::save(), as it fails to |
||
321 | // build the directory structure on owner's filestore otherwise |
||
322 | 44 | $icon->open('write'); |
|
323 | 44 | $icon->close(); |
|
324 | |||
325 | // Save the image without resizing or cropping if the |
||
326 | // image size value is an empty array |
||
327 | 44 | if (is_array($opts) && empty($opts)) { |
|
328 | 1 | copy($file->getFilenameOnFilestore(), $icon->getFilenameOnFilestore()); |
|
329 | 1 | continue; |
|
330 | } |
||
331 | |||
332 | 44 | $source = $file->getFilenameOnFilestore(); |
|
333 | 44 | $destination = $icon->getFilenameOnFilestore(); |
|
334 | |||
335 | 44 | $resize_params = array_merge($opts, $coords); |
|
336 | |||
337 | 44 | $image_service = _elgg_services()->imageService; |
|
338 | 44 | $image_service->setLogger($this->logger); |
|
339 | |||
340 | 44 | if (!_elgg_services()->imageService->resize($source, $destination, $resize_params)) { |
|
341 | $this->logger->error("Failed to create {$size} icon from |
||
342 | {$file->getFilenameOnFilestore()} with coords [{$x1}, {$y1}],[{$x2}, {$y2}]"); |
||
343 | 44 | return false; |
|
344 | } |
||
345 | } |
||
346 | |||
347 | 44 | return true; |
|
348 | } |
||
349 | |||
350 | /** |
||
351 | * Returns entity icon as an ElggIcon object |
||
352 | * The icon file may or may not exist on filestore |
||
353 | * |
||
354 | * @note Returned ElggIcon object may be a placeholder. Use ElggIcon::exists() to validate if file has been written to filestore |
||
355 | * |
||
356 | * @param ElggEntity $entity Entity that owns the icon |
||
357 | * @param string $size Size of the icon |
||
358 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
359 | * @param bool $generate Try to generate an icon based on master if size doesn't exists |
||
360 | * |
||
361 | * @return ElggIcon |
||
362 | * |
||
363 | * @throws InvalidParameterException |
||
364 | */ |
||
365 | 70 | public function getIcon(ElggEntity $entity, $size, $type = 'icon', $generate = true) { |
|
366 | |||
367 | 70 | $size = elgg_strtolower($size); |
|
368 | |||
369 | $params = [ |
||
370 | 70 | 'entity' => $entity, |
|
371 | 70 | 'size' => $size, |
|
372 | 70 | 'type' => $type, |
|
373 | ]; |
||
374 | |||
375 | 70 | $entity_type = $entity->getType(); |
|
376 | |||
377 | 70 | $default_icon = new ElggIcon(); |
|
378 | 70 | $default_icon->owner_guid = $entity->guid; |
|
379 | 70 | $default_icon->setFilename("icons/$type/$size.jpg"); |
|
380 | |||
381 | 70 | $icon = $this->hooks->trigger("entity:$type:file", $entity_type, $params, $default_icon); |
|
382 | 70 | if (!$icon instanceof ElggIcon) { |
|
383 | 1 | throw new InvalidParameterException("'entity:$type:file', $entity_type hook must return an instance of ElggIcon"); |
|
384 | } |
||
385 | |||
386 | 69 | if ($icon->exists() || !$generate) { |
|
387 | 68 | return $icon; |
|
388 | } |
||
389 | |||
390 | 62 | if ($size === 'master') { |
|
391 | // don't try to generate for master |
||
392 | 10 | return $icon; |
|
393 | } |
||
394 | |||
395 | // try to generate icon based on master size |
||
396 | 61 | $master_icon = $this->getIcon($entity, 'master', $type, false); |
|
397 | 61 | if (!$master_icon->exists()) { |
|
398 | 25 | return $icon; |
|
399 | } |
||
400 | |||
401 | 37 | if ($type === 'icon') { |
|
402 | $coords = [ |
||
403 | 36 | 'x1' => $entity->x1, |
|
404 | 36 | 'y1' => $entity->y1, |
|
405 | 36 | 'x2' => $entity->x2, |
|
406 | 36 | 'y2' => $entity->y2, |
|
407 | ]; |
||
408 | } else { |
||
409 | 1 | $coords = $entity->{"{$type}_coords"}; |
|
410 | 1 | $coords = empty($coords) ? [] : unserialize($coords); |
|
411 | } |
||
412 | |||
413 | 37 | $this->generateIcon($entity, $master_icon, $type, $coords, $size); |
|
414 | |||
415 | 37 | return $icon; |
|
416 | } |
||
417 | |||
418 | /** |
||
419 | * Removes all icon files and metadata for the passed type of icon. |
||
420 | * |
||
421 | * @param ElggEntity $entity Entity that owns icons |
||
422 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
423 | * @param bool $retain_master Keep the master icon (default: false) |
||
424 | * |
||
425 | * @return bool |
||
426 | */ |
||
427 | 45 | public function deleteIcon(ElggEntity $entity, $type = 'icon', $retain_master = false) { |
|
428 | 45 | $delete = $this->hooks->trigger("entity:$type:delete", $entity->getType(), [ |
|
429 | 45 | 'entity' => $entity, |
|
430 | 45 | ], true); |
|
431 | |||
432 | 45 | if ($delete === false) { |
|
433 | 1 | return; |
|
434 | } |
||
435 | |||
436 | 44 | $sizes = array_keys($this->getSizes($entity->getType(), $entity->getSubtype(), $type)); |
|
437 | 44 | foreach ($sizes as $size) { |
|
438 | 44 | if ($size === 'master' && $retain_master) { |
|
439 | 43 | continue; |
|
440 | } |
||
441 | |||
442 | 44 | $icon = $this->getIcon($entity, $size, $type, false); |
|
443 | 44 | $icon->delete(); |
|
444 | } |
||
445 | |||
446 | 44 | if ($type == 'icon') { |
|
447 | 43 | unset($entity->icontime); |
|
448 | 43 | unset($entity->x1); |
|
449 | 43 | unset($entity->y1); |
|
450 | 43 | unset($entity->x2); |
|
451 | 43 | unset($entity->y2); |
|
452 | } |
||
453 | 44 | } |
|
454 | |||
455 | /** |
||
456 | * Get the URL for this entity's icon |
||
457 | * |
||
458 | * Plugins can register for the 'entity:icon:url', <type> plugin hook to customize the icon for an entity. |
||
459 | * |
||
460 | * @param ElggEntity $entity Entity that owns the icon |
||
461 | * @param mixed $params A string defining the size of the icon (e.g. tiny, small, medium, large) |
||
462 | * or an array of parameters including 'size' |
||
463 | * @return string|void |
||
464 | */ |
||
465 | 14 | public function getIconURL(ElggEntity $entity, $params = []) { |
|
466 | 14 | if (is_array($params)) { |
|
467 | 1 | $size = elgg_extract('size', $params, 'medium'); |
|
468 | } else { |
||
469 | 14 | $size = is_string($params) ? $params : 'medium'; |
|
470 | 14 | $params = []; |
|
471 | } |
||
472 | |||
473 | 14 | $size = elgg_strtolower($size); |
|
474 | |||
475 | 14 | $params['entity'] = $entity; |
|
476 | 14 | $params['size'] = $size; |
|
477 | |||
478 | 14 | $type = elgg_extract('type', $params) ? : 'icon'; |
|
479 | 14 | $entity_type = $entity->getType(); |
|
480 | |||
481 | 14 | $url = $this->hooks->trigger("entity:$type:url", $entity_type, $params, null); |
|
482 | 14 | if ($url == null) { |
|
483 | 12 | if ($this->hasIcon($entity, $size, $type)) { |
|
484 | 1 | $icon = $this->getIcon($entity, $size, $type); |
|
485 | 1 | $url = $icon->getInlineURL(true); |
|
486 | } else { |
||
487 | 11 | $url = $this->getFallbackIconUrl($entity, $params); |
|
488 | } |
||
489 | } |
||
490 | |||
491 | 14 | if ($url) { |
|
492 | 10 | return elgg_normalize_url($url); |
|
493 | } |
||
494 | 4 | } |
|
495 | |||
496 | /** |
||
497 | * Returns default/fallback icon |
||
498 | * |
||
499 | * @param ElggEntity $entity Entity |
||
500 | * @param array $params Icon params |
||
501 | * @return string |
||
502 | */ |
||
503 | 11 | public function getFallbackIconUrl(ElggEntity $entity, array $params = []) { |
|
504 | |||
505 | 11 | $type = elgg_extract('type', $params) ? : 'icon'; |
|
506 | 11 | $size = elgg_extract('size', $params) ? : 'medium'; |
|
507 | |||
508 | 11 | $entity_type = $entity->getType(); |
|
509 | 11 | $entity_subtype = $entity->getSubtype() ? : 'default'; |
|
510 | |||
511 | 11 | $exts = ['svg', 'gif', 'png', 'jpg']; |
|
512 | |||
513 | 11 | foreach ($exts as $ext) { |
|
514 | 11 | if ($ext == 'svg' && elgg_view_exists("$type/$entity_type/$entity_subtype.svg")) { |
|
515 | return elgg_get_simplecache_url("$type/$entity_type/$entity_subtype.svg"); |
||
516 | } |
||
517 | 11 | if (elgg_view_exists("$type/$entity_type/$entity_subtype/$size.$ext")) { |
|
518 | 11 | return elgg_get_simplecache_url("$type/$entity_type/$entity_subtype/$size.$ext"); |
|
519 | } |
||
520 | } |
||
521 | |||
522 | 11 | if (elgg_view_exists("$type/default/$size.png")) { |
|
523 | 7 | return elgg_get_simplecache_url("$type/default/$size.png"); |
|
524 | } |
||
525 | 4 | } |
|
526 | |||
527 | /** |
||
528 | * Returns the timestamp of when the icon was changed. |
||
529 | * |
||
530 | * @param ElggEntity $entity Entity that owns the icon |
||
531 | * @param string $size The size of the icon |
||
532 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
533 | * |
||
534 | * @return int|null A unix timestamp of when the icon was last changed, or null if not set. |
||
535 | */ |
||
536 | 1 | public function getIconLastChange(ElggEntity $entity, $size, $type = 'icon') { |
|
537 | 1 | $icon = $this->getIcon($entity, $size, $type); |
|
538 | 1 | if ($icon->exists()) { |
|
539 | 1 | return $icon->getModifiedTime(); |
|
540 | } |
||
541 | } |
||
542 | |||
543 | /** |
||
544 | * Returns if the entity has an icon of the passed type. |
||
545 | * |
||
546 | * @param ElggEntity $entity Entity that owns the icon |
||
547 | * @param string $size The size of the icon |
||
548 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
549 | * @return bool |
||
550 | */ |
||
551 | 27 | public function hasIcon(\ElggEntity $entity, $size, $type = 'icon') { |
|
552 | 27 | return $this->getIcon($entity, $size, $type)->exists(); |
|
553 | } |
||
554 | |||
555 | /** |
||
556 | * Returns a configuration array of icon sizes |
||
557 | * |
||
558 | * @param string $entity_type Entity type |
||
559 | * @param string $entity_subtype Entity subtype |
||
560 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
561 | * @return array |
||
562 | * @throws InvalidParameterException |
||
563 | */ |
||
564 | 63 | public function getSizes($entity_type = null, $entity_subtype = null, $type = 'icon') { |
|
565 | 63 | $sizes = []; |
|
566 | 63 | if (!$type) { |
|
567 | $type = 'icon'; |
||
568 | } |
||
569 | 63 | if ($type == 'icon') { |
|
570 | 61 | $sizes = $this->config->icon_sizes; |
|
571 | } |
||
572 | $params = [ |
||
573 | 63 | 'type' => $type, |
|
574 | 63 | 'entity_type' => $entity_type, |
|
575 | 63 | 'entity_subtype' => $entity_subtype, |
|
576 | ]; |
||
577 | 63 | if ($entity_type) { |
|
0 ignored issues
–
show
|
|||
578 | 62 | $sizes = $this->hooks->trigger("entity:$type:sizes", $entity_type, $params, $sizes); |
|
579 | } |
||
580 | |||
581 | 63 | if (!is_array($sizes)) { |
|
582 | throw new InvalidParameterException("The icon size configuration for image type '$type' " . |
||
583 | "must be an associative array of image size names and their properties"); |
||
584 | } |
||
585 | |||
586 | // lazy generation of icons requires a 'master' size |
||
587 | // this ensures a default config for 'master' size |
||
588 | 63 | $sizes['master'] = elgg_extract('master', $sizes, [ |
|
589 | 63 | 'w' => 2048, |
|
590 | 'h' => 2048, |
||
591 | 'square' => false, |
||
592 | 'upscale' => false, |
||
593 | ]); |
||
594 | |||
595 | 63 | return $sizes; |
|
596 | } |
||
597 | |||
598 | /** |
||
599 | * Handle request to /serve-icon handler |
||
600 | * |
||
601 | * @param bool $allow_removing_headers Alter PHP's global headers to allow caching |
||
602 | * @return BinaryFileResponse |
||
603 | */ |
||
604 | 3 | public function handleServeIconRequest($allow_removing_headers = true) { |
|
605 | |||
606 | 3 | $response = new Response(); |
|
607 | 3 | $response->setExpires($this->getCurrentTime('-1 day')); |
|
608 | 3 | $response->prepare($this->request); |
|
609 | |||
610 | 3 | if ($allow_removing_headers) { |
|
611 | // clear cache-boosting headers set by PHP session |
||
612 | header_remove('Cache-Control'); |
||
613 | header_remove('Pragma'); |
||
614 | header_remove('Expires'); |
||
615 | } |
||
616 | |||
617 | 3 | $path = implode('/', $this->request->getUrlSegments()); |
|
618 | 3 | if (!preg_match('~serve-icon/(\d+)/(.*+)$~', $path, $m)) { |
|
619 | 1 | return $response->setStatusCode(400)->setContent('Malformatted request URL'); |
|
620 | } |
||
621 | |||
622 | 2 | list(, $guid, $size) = $m; |
|
623 | |||
624 | 2 | $entity = $this->entities->get($guid); |
|
625 | 2 | if (!$entity instanceof \ElggEntity) { |
|
626 | 1 | return $response->setStatusCode(404)->setContent('Item does not exist'); |
|
627 | } |
||
628 | |||
629 | 1 | $thumbnail = $entity->getIcon($size); |
|
630 | 1 | if (!$thumbnail->exists()) { |
|
631 | return $response->setStatusCode(404)->setContent('Icon does not exist'); |
||
632 | } |
||
633 | |||
634 | 1 | $if_none_match = $this->request->headers->get('if_none_match'); |
|
635 | 1 | if (!empty($if_none_match)) { |
|
636 | // strip mod_deflate suffixes |
||
637 | 1 | $this->request->headers->set('if_none_match', str_replace('-gzip', '', $if_none_match)); |
|
638 | } |
||
639 | |||
640 | 1 | $filenameonfilestore = $thumbnail->getFilenameOnFilestore(); |
|
641 | 1 | $last_updated = filemtime($filenameonfilestore); |
|
642 | 1 | $etag = '"' . $last_updated . '"'; |
|
643 | |||
644 | 1 | $response->setPrivate() |
|
645 | 1 | ->setEtag($etag) |
|
646 | 1 | ->setExpires($this->getCurrentTime('+1 day')) |
|
647 | 1 | ->setMaxAge(86400); |
|
648 | |||
649 | 1 | if ($response->isNotModified($this->request)) { |
|
650 | 1 | return $response; |
|
651 | } |
||
652 | |||
653 | $headers = [ |
||
654 | 1 | 'Content-Type' => (new MimeTypeDetector())->getType($filenameonfilestore), |
|
655 | ]; |
||
656 | 1 | $response = new BinaryFileResponse($filenameonfilestore, 200, $headers, false, 'inline'); |
|
657 | 1 | $response->prepare($this->request); |
|
658 | |||
659 | 1 | $response->setPrivate() |
|
660 | 1 | ->setEtag($etag) |
|
661 | 1 | ->setExpires($this->getCurrentTime('+1 day')) |
|
662 | 1 | ->setMaxAge(86400); |
|
663 | |||
664 | 1 | return $response; |
|
665 | } |
||
666 | |||
667 | } |
||
668 |
In PHP, under loose comparison (like
==
, or!=
, orswitch
conditions), values of different types might be equal.For
string
values, the empty string''
is a special case, in particular the following results might be unexpected: