Elgg  Version 2.3
EntityIconService.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg;
4 
16 
26  use TimeUsing;
27 
31  private $config;
32 
36  private $hooks;
37 
41  private $request;
42 
46  private $logger;
47 
51  private $entities;
52 
62  public function __construct(Config $config, PluginHooksService $hooks, Request $request, Logger $logger, EntityTable $entities) {
63  $this->config = $config;
64  $this->hooks = $hooks;
65  $this->request = $request;
66  $this->logger = $logger;
67  $this->entities = $entities;
68  }
69 
79  public function saveIconFromUploadedFile(ElggEntity $entity, $input_name, $type = 'icon', array $coords = array()) {
80  $files = $this->request->files;
81  if (!$files->has($input_name)) {
82  return false;
83  }
84 
85  $input = $files->get($input_name);
86  if (!$input instanceof UploadedFile || !$input->isValid()) {
87  return false;
88  }
89 
90  $tmp_filename = time() . $input->getClientOriginalName();
91  $tmp = new ElggFile();
92  $tmp->owner_guid = $entity->guid;
93  $tmp->setFilename("tmp/$tmp_filename");
94  $tmp->open('write');
95  $tmp->close();
96  // not using move_uploaded_file() for testing purposes
97  copy($input->getPathname(), $tmp->getFilenameOnFilestore());
98 
99  $tmp->mimetype = (new MimeTypeDetector())->getType($tmp_filename, $input->getClientMimeType());
100  $tmp->simpletype = elgg_get_file_simple_type($tmp->mimetype);
101 
102  $result = $this->saveIcon($entity, $tmp, $type, $coords);
103 
104  unlink($input->getPathname());
105  $tmp->delete();
106 
107  return $result;
108  }
109 
120  public function saveIconFromLocalFile(ElggEntity $entity, $filename, $type = 'icon', array $coords = array()) {
121  if (!file_exists($filename) || !is_readable($filename)) {
122  throw new InvalidParameterException(__METHOD__ . " expects a readable local file. $filename is not readable");
123  }
124 
125  $tmp_filename = time() . pathinfo($filename, PATHINFO_BASENAME);
126  $tmp = new ElggFile();
127  $tmp->owner_guid = $entity->guid;
128  $tmp->setFilename("tmp/$tmp_filename");
129  $tmp->open('write');
130  $tmp->close();
131  copy($filename, $tmp->getFilenameOnFilestore());
132 
133  $tmp->mimetype = (new MimeTypeDetector())->getType($tmp->getFilenameOnFilestore());
134  $tmp->simpletype = elgg_get_file_simple_type($tmp->mimetype);
135 
136  $result = $this->saveIcon($entity, $tmp, $type, $coords);
137 
138  $tmp->delete();
139 
140  return $result;
141  }
142 
153  public function saveIconFromElggFile(ElggEntity $entity, ElggFile $file, $type = 'icon', array $coords = array()) {
154  if (!$file->exists()) {
155  throw new InvalidParameterException(__METHOD__ . ' expects an instance of ElggFile with an existing file on filestore');
156  }
157 
158  $tmp_filename = time() . pathinfo($file->getFilenameOnFilestore(), PATHINFO_BASENAME);
159  $tmp = new ElggFile();
160  $tmp->owner_guid = $entity->guid;
161  $tmp->setFilename("tmp/$tmp_filename");
162  $tmp->open('write');
163  $tmp->close();
164  copy($file->getFilenameOnFilestore(), $tmp->getFilenameOnFilestore());
165 
166  $tmp->mimetype = (new MimeTypeDetector())->getType($tmp->getFilenameOnFilestore(), $file->getMimeType());
167  $tmp->simpletype = elgg_get_file_simple_type($tmp->mimetype);
168 
169  $result = $this->saveIcon($entity, $tmp, $type, $coords);
170 
171  $tmp->delete();
172 
173  return $result;
174  }
175 
185  public function saveIcon(ElggEntity $entity, ElggFile $file, $type = 'icon', array $coords = array()) {
186 
187  $entity_type = $entity->getType();
188  $entity_subtype = $entity->getSubtype();
189 
190  $x1 = (int) elgg_extract('x1', $coords);
191  $y1 = (int) elgg_extract('y1', $coords);
192  $x2 = (int) elgg_extract('x2', $coords);
193  $y2 = (int) elgg_extract('y2', $coords);
194 
195  $file = $this->hooks->trigger("entity:$type:prepare", $entity_type, [
196  'entity' => $entity,
197  'file' => $file,
198  ], $file);
199 
200  if (!$file instanceof ElggFile || !$file->exists() || $file->getSimpleType() !== 'image') {
201  $this->logger->error('Source file passed to ' . __METHOD__ . ' can not be resolved to a valid image');
202  return false;
203  }
204 
205  $cropping_mode = ($x2 > $x1) && ($y2 > $y1);
206  if (!$cropping_mode) {
207  $this->deleteIcon($entity, $type);
208  }
209 
210  $success = function() use ($entity, $type, $x1, $y1, $x2, $y2) {
211  if ($type == 'icon') {
212  $entity->icontime = time();
213  if ($x1 || $y1 || $x2 || $y2) {
214  $entity->x1 = $x1;
215  $entity->y1 = $y1;
216  $entity->x2 = $x2;
217  $entity->y2 = $y2;
218  }
219  }
220  $this->hooks->trigger("entity:$type:saved", $entity->getType(), [
221  'entity' => $entity,
222  'x1' => $x1,
223  'y1' => $y1,
224  'x2' => $x2,
225  'y2' => $y2,
226  ]);
227  return true;
228  };
229 
230  $fail = function() use ($entity, $type) {
231  $this->deleteIcon($entity, $type);
232  return false;
233  };
234 
235  $created = $this->hooks->trigger("entity:$type:save", $entity_type, [
236  'entity' => $entity,
237  'file' => $file,
238  'x1' => $x1,
239  'y1' => $y1,
240  'x2' => $x2,
241  'y2' => $y2,
242  ], false);
243 
244  if ($created === true) {
245  return $success();
246  }
247 
248  $sizes = $this->getSizes($entity_type, $entity_subtype, $type);
249 
250  foreach ($sizes as $size => $opts) {
251 
252  $square = (bool) elgg_extract('square', $opts);
253 
254  if ($type === 'icon' && $cropping_mode) {
255  // Do not crop out non-square icons if cropping coordinates are a square
256  $cropping_ratio = ($x2 - $x1) / ($y2 - $y1);
257  if ($cropping_ratio == 1 && $square === false) {
258  continue;
259  }
260  }
261 
262  $icon = $this->getIcon($entity, $size, $type);
263 
264  // We need to make sure that file path is readable by
265  // Imagine\Image\ImagineInterface::save(), as it fails to
266  // build the directory structure on owner's filestore otherwise
267  $icon->open('write');
268  $icon->close();
269 
270  // Save the image without resizing or cropping if the
271  // image size value is an empty array
272  if (is_array($opts) && empty($opts)) {
273  copy($file->getFilenameOnFilestore(), $icon->getFilenameOnFilestore());
274  continue;
275  }
276 
277  $source = $file->getFilenameOnFilestore();
278  $destination = $icon->getFilenameOnFilestore();
279 
280  $resize_params = array_merge($opts, $coords);
281 
282  if (!_elgg_services()->imageService->resize($source, $destination, $resize_params)) {
283  $this->logger->error("Failed to create {$size} icon from
284  {$file->getFilenameOnFilestore()} with coords [{$x1}, {$y1}],[{$x2}, {$y2}]");
285  return $fail();
286  }
287  }
288 
289  return $success();
290  }
291 
304  public function getIcon(ElggEntity $entity, $size, $type = 'icon') {
305 
307 
308  $params = [
309  'entity' => $entity,
310  'size' => $size,
311  'type' => $type,
312  ];
313 
314  $entity_type = $entity->getType();
315 
316  $default_icon = new ElggIcon();
317  $default_icon->owner_guid = $entity->guid;
318  $default_icon->setFilename("icons/$type/$size.jpg");
319 
320  $icon = $this->hooks->trigger("entity:$type:file", $entity_type, $params, $default_icon);
321  if (!$icon instanceof ElggIcon) {
322  throw new InvalidParameterException("'entity:$type:file', $entity_type hook must return an instance of ElggIcon");
323  }
324 
325  return $icon;
326  }
327 
335  public function deleteIcon(ElggEntity $entity, $type = 'icon') {
336  $delete = $this->hooks->trigger("entity:$type:delete", $entity->getType(), [
337  'entity' => $entity,
338  ], true);
339 
340  if ($delete === false) {
341  return;
342  }
343 
344  $sizes = array_keys($this->getSizes($entity->getType(), $entity->getSubtype(), $type));
345  foreach ($sizes as $size) {
346  $icon = $this->getIcon($entity, $size, $type);
347  $icon->delete();
348  }
349 
350  if ($type == 'icon') {
351  unset($entity->icontime);
352  unset($entity->x1);
353  unset($entity->y1);
354  unset($entity->x2);
355  unset($entity->y2);
356  }
357  }
358 
369  public function getIconURL(ElggEntity $entity, $params = array()) {
370  if (is_array($params)) {
371  $size = elgg_extract('size', $params, 'medium');
372  } else {
373  $size = is_string($params) ? $params : 'medium';
374  $params = array();
375  }
376 
378 
379  $params['entity'] = $entity;
380  $params['size'] = $size;
381 
382  $type = elgg_extract('type', $params) ? : 'icon';
383  $entity_type = $entity->getType();
384 
385  $url = $this->hooks->trigger("entity:$type:url", $entity_type, $params, null);
386  if ($url == null) {
387  $icon = $this->getIcon($entity, $size, $type);
388  $url = elgg_get_inline_url($icon, true);
389  if (!$url && $type == 'icon') {
390  $url = elgg_get_simplecache_url("icons/default/$size.png");
391  }
392  }
393 
394  return elgg_normalize_url($url);
395  }
396 
406  public function getIconLastChange(ElggEntity $entity, $size, $type = 'icon') {
407  $icon = $this->getIcon($entity, $size, $type);
408  if ($icon->exists()) {
409  return $icon->getModifiedTime();
410  }
411  }
412 
421  public function hasIcon(\ElggEntity $entity, $size, $type = 'icon') {
422  return $this->getIcon($entity, $size, $type)->exists();
423  }
424 
434  public function getSizes($entity_type = null, $entity_subtype = null, $type = 'icon') {
435  $sizes = [];
436  if (!$type) {
437  $type = 'icon';
438  }
439  if ($type == 'icon') {
440  $sizes = $this->config->get('icon_sizes');
441  }
442  $params = [
443  'type' => $type,
444  'entity_type' => $entity_type,
445  'entity_subtype' => $entity_subtype,
446  ];
447  if ($entity_type) {
448  $sizes = $this->hooks->trigger("entity:$type:sizes", $entity_type, $params, $sizes);
449  }
450 
451  if (!is_array($sizes)) {
452  throw new InvalidParameterException("The icon size configuration for image type '$type' " .
453  "must be an associative array of image size names and their properties");
454  }
455 
456  if (empty($sizes)) {
457  $this->logger->error("Failed to find size configuration for image of type '$type' for entity type " .
458  "'$entity_type'. Use the 'entity:$type:sizes, $entity_type' hook to define the icon sizes");
459 
460  }
461 
462  return $sizes;
463  }
464 
471  public function handleServeIconRequest($allow_removing_headers = true) {
472 
473  $response = new Response();
474  $response->setExpires($this->getCurrentTime('-1 day'));
475  $response->prepare($this->request);
476 
477  if ($allow_removing_headers) {
478  // clear cache-boosting headers set by PHP session
479  header_remove('Cache-Control');
480  header_remove('Pragma');
481  header_remove('Expires');
482  }
483 
484  $path = implode('/', $this->request->getUrlSegments());
485  if (!preg_match('~serve-icon/(\d+)/(.*+)$~', $path, $m)) {
486  return $response->setStatusCode(400)->setContent('Malformatted request URL');
487  }
488 
489  list(, $guid, $size) = $m;
490 
491  $entity = $this->entities->get($guid);
492  if (!$entity instanceof \ElggEntity) {
493  return $response->setStatusCode(404)->setContent('Item does not exist');
494  }
495 
496  $thumbnail = $entity->getIcon($size);
497  if (!$thumbnail->exists()) {
498  return $response->setStatusCode(404)->setContent('Icon does not exist');
499  }
500 
501  $if_none_match = $this->request->headers->get('if_none_match');
502  if (!empty($if_none_match)) {
503  // strip mod_deflate suffixes
504  $this->request->headers->set('if_none_match', str_replace('-gzip', '', $if_none_match));
505  }
506 
507  $filenameonfilestore = $thumbnail->getFilenameOnFilestore();
508  $last_updated = filemtime($filenameonfilestore);
509  $etag = '"' . $last_updated . '"';
510 
511  $response->setPrivate()
512  ->setEtag($etag)
513  ->setExpires($this->getCurrentTime('+1 day'))
514  ->setMaxAge(86400);
515 
516  if ($response->isNotModified($this->request)) {
517  return $response;
518  }
519 
520  $headers = [
521  'Content-Type' => (new MimeTypeDetector())->getType($filenameonfilestore),
522  ];
523  $response = new BinaryFileResponse($filenameonfilestore, 200, $headers, false, 'inline');
524  $response->prepare($this->request);
525 
526  $response->setPrivate()
527  ->setEtag($etag)
528  ->setExpires($this->getCurrentTime('+1 day'))
529  ->setMaxAge(86400);
530 
531  return $response;
532  }
533 
534 }
getSubtype()
Get the entity subtype.
Elgg HTTP request.
Definition: Request.php:12
get($name)
Return the value of an attribute or metadata.
Definition: ElggEntity.php:287
if(!array_key_exists($filename, $text_files)) $file
$m
Definition: metadata.php:11
elgg_normalize_url($url)
Definition: output.php:280
if(!$owner||!($owner instanceof ElggUser)||!$owner->canEdit()) $input
Definition: edit.php:19
$input_name
Definition: item.php:14
elgg_get_simplecache_url($view, $subview= '')
Get the URL for the cached view.
Definition: cache.php:136
$headers
Definition: default.php:14
$path
Definition: details.php:88
Access to configuration values.
Definition: Config.php:11
exists()
Returns if the file exists.
Definition: ElggFile.php:402
getSizes($entity_type=null, $entity_subtype=null, $type= 'icon')
Returns a configuration array of icon sizes.
getSimpleType()
Get the simple type of the file.
Definition: ElggFile.php:189
getCurrentTime($modifier= '')
Get the (cloned) time.
Definition: TimeUsing.php:26
hasIcon(\ElggEntity $entity, $size, $type= 'icon')
Returns if the entity has an icon of the passed type.
$guid
Removes an admin notice.
getIconURL(ElggEntity $entity, $params=array())
Get the URL for this entity&#39;s icon.
elgg_strtolower()
Wrapper function for mb_strtolower().
Definition: mb_wrapper.php:174
saveIconFromLocalFile(ElggEntity $entity, $filename, $type= 'icon', array $coords=array())
Saves icons using a local file as the source.
$url
Definition: exceptions.php:24
getIcon($size, $type= 'icon')
Returns entity icon as an ElggIcon object The icon file may or may not exist on filestore.
if(!$owner) $icon
Definition: default.php:16
$params
Definition: login.php:72
getFilenameOnFilestore()
Return the filename of this file as it is/will be stored on the filestore, which may be different to ...
Definition: ElggFile.php:101
handleServeIconRequest($allow_removing_headers=true)
Handle request to /serve-icon handler.
elgg_get_inline_url(\ElggFile $file, $use_cookie=false, $expires= '')
Returns file&#39;s URL for inline display Suitable for displaying cacheable resources, such as user avatars.
Definition: filestore.php:502
saveIconFromUploadedFile(ElggEntity $entity, $input_name, $type= 'icon', array $coords=array())
Saves icons using an uploaded file as the source.
Save menu items.
WARNING: API IN FLUX.
__construct(Config $config, PluginHooksService $hooks, Request $request, Logger $logger, EntityTable $entities)
Constructor.
getMimeType()
Get the mime type of the file.
Definition: ElggFile.php:127
deleteIcon(ElggEntity $entity, $type= 'icon')
Removes all icon files and metadata for the passed type of icon.
_elgg_services(\Elgg\Di\ServiceProvider $services=null)
Get the global service provider.
Definition: autoloader.php:17
elgg_extract($key, $array, $default=null, $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:1375
getIconLastChange(ElggEntity $entity, $size, $type= 'icon')
Returns the timestamp of when the icon was changed.
saveIconFromElggFile(ElggEntity $entity, ElggFile $file, $type= 'icon', array $coords=array())
Saves icons using a file located in the data store as the source.
$size
Definition: default.php:20
elgg subtext time
and give any other recipients of the Program a copy of this License along with the Program You may charge a fee for the physical act of transferring a copy
Definition: GPL-LICENSE.txt:87
Detect the MIME type of a file.
getType()
Returns the entity type.
saveIcon(ElggEntity $entity, ElggFile $file, $type= 'icon', array $coords=array())
Saves icons using a created temporary file.
$entity
Definition: delete.php:7
$filename
trait TimeUsing
Adds methods for setting the current time (for testing)
Definition: TimeUsing.php:11
if(!$owner||!($owner instanceof ElggUser)||!$owner->canEdit()) $coords
Definition: crop.php:15
foreach($resources as $id=> $href) if(!empty($resources_html)) $files
Definition: details.php:141
elgg ElggEntity
Definition: ElggEntity.js:16
http free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:5
getIcon(ElggEntity $entity, $size, $type= 'icon')
Returns entity icon as an ElggIcon object The icon file may or may not exist on filestore.
elgg_get_file_simple_type($mime_type)
Returns the category of a file from its MIME type.
Definition: filestore.php:352
if(!$display_name) $type
Definition: delete.php:27