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

engine/classes/Elgg/EntityIconService.php (2 issues)

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);
0 ignored issues
show
Are you sure the assignment to $url is correct as $this->hooks->trigger(En...ty_type, $params, null) targeting Elgg\PluginHooksService::trigger() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
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
Bug Best Practice introduced by Brett Profitt
The expression $entity_type of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch 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:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
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