Elgg /
Elgg
| 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!=, orswitchconditions), values of different types might be equal.For
stringvalues, the empty string''is a special case, in particular the following results might be unexpected: