| Code Coverage | ||||||||||
| Classes and Traits | Functions and Methods | Lines | ||||||||
| Total |  | 0.00% | 0 / 1 |  | 7.69% | 2 / 26 | CRAP |  | 7.02% | 8 / 114 | 
| ImageStyle |  | 0.00% | 0 / 1 |  | 7.69% | 2 / 26 | 3461.49 |  | 7.02% | 8 / 114 | 
| id |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1 | |||
| postSave |  | 0.00% | 0 / 1 | 30 |  | 0.00% | 0 / 8 | |||
| postDelete |  | 0.00% | 0 / 1 | 6 |  | 0.00% | 0 / 5 | |||
| replaceImageStyle |  | 0.00% | 0 / 1 | 156 |  | 0.00% | 0 / 14 | |||
| buildUri |  | 0.00% | 0 / 1 | 2.15 |  | 66.67% | 4 / 6 | |||
| buildUrl |  | 0.00% | 0 / 1 | 110 |  | 0.00% | 0 / 17 | |||
| flush |  | 0.00% | 0 / 1 | 30 |  | 0.00% | 0 / 14 | |||
| createDerivative |  | 0.00% | 0 / 1 | 42 |  | 0.00% | 0 / 14 | |||
| transformDimensions |  | 0.00% | 0 / 1 | 6 |  | 0.00% | 0 / 3 | |||
| getDerivativeExtension |  | 100.00% | 1 / 1 | 2 |  | 100.00% | 3 / 3 | |||
| getPathToken |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 1 / 1 | |||
| deleteImageEffect |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 3 | |||
| getEffect |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1 | |||
| getEffects |  | 0.00% | 0 / 1 | 6 |  | 0.00% | 0 / 4 | |||
| getPluginCollections |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1 | |||
| addImageEffect |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 3 | |||
| getReplacementID |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 2 | |||
| getName |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1 | |||
| setName |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 2 | |||
| getImageEffectPluginManager |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1 | |||
| getPrivateKey |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1 | |||
| getHashSalt |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1 | |||
| addExtension |  | 0.00% | 0 / 1 | 6 |  | 0.00% | 0 / 5 | |||
| fileUriScheme |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1 | |||
| fileUriTarget |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1 | |||
| fileDefaultScheme |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1 | |||
| <?php | |
| /** | |
| * @file | |
| * Contains \Drupal\image\Entity\ImageStyle. | |
| */ | |
| namespace Drupal\image\Entity; | |
| use Drupal\Core\Cache\Cache; | |
| use Drupal\Core\Config\Entity\ConfigEntityBase; | |
| use Drupal\Core\Entity\EntityStorageInterface; | |
| use Drupal\Core\Entity\EntityWithPluginCollectionInterface; | |
| use Drupal\Core\Routing\RequestHelper; | |
| use Drupal\Core\Site\Settings; | |
| use Drupal\Core\Url; | |
| use Drupal\image\ImageEffectPluginCollection; | |
| use Drupal\image\ImageEffectInterface; | |
| use Drupal\image\ImageStyleInterface; | |
| use Drupal\Component\Utility\Crypt; | |
| use Drupal\Component\Utility\UrlHelper; | |
| use Drupal\Core\StreamWrapper\StreamWrapperInterface; | |
| use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; | |
| /** | |
| * Defines an image style configuration entity. | |
| * | |
| * @ConfigEntityType( | |
| * id = "image_style", | |
| * label = @Translation("Image style"), | |
| * handlers = { | |
| * "form" = { | |
| * "add" = "Drupal\image\Form\ImageStyleAddForm", | |
| * "edit" = "Drupal\image\Form\ImageStyleEditForm", | |
| * "delete" = "Drupal\image\Form\ImageStyleDeleteForm", | |
| * "flush" = "Drupal\image\Form\ImageStyleFlushForm" | |
| * }, | |
| * "list_builder" = "Drupal\image\ImageStyleListBuilder", | |
| * "storage" = "Drupal\image\ImageStyleStorage", | |
| * }, | |
| * admin_permission = "administer image styles", | |
| * config_prefix = "style", | |
| * entity_keys = { | |
| * "id" = "name", | |
| * "label" = "label" | |
| * }, | |
| * links = { | |
| * "flush-form" = "/admin/config/media/image-styles/manage/{image_style}/flush", | |
| * "edit-form" = "/admin/config/media/image-styles/manage/{image_style}", | |
| * "delete-form" = "/admin/config/media/image-styles/manage/{image_style}/delete", | |
| * "collection" = "/admin/config/media/image-styles", | |
| * }, | |
| * config_export = { | |
| * "name", | |
| * "label", | |
| * "effects", | |
| * } | |
| * ) | |
| */ | |
| class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, EntityWithPluginCollectionInterface { | |
| /** | |
| * The name of the image style. | |
| * | |
| * @var string | |
| */ | |
| protected $name; | |
| /** | |
| * The image style label. | |
| * | |
| * @var string | |
| */ | |
| protected $label; | |
| /** | |
| * The array of image effects for this image style. | |
| * | |
| * @var array | |
| */ | |
| protected $effects = array(); | |
| /** | |
| * Holds the collection of image effects that are used by this image style. | |
| * | |
| * @var \Drupal\image\ImageEffectPluginCollection | |
| */ | |
| protected $effectsCollection; | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function id() { | |
| return $this->name; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function postSave(EntityStorageInterface $storage, $update = TRUE) { | |
| parent::postSave($storage, $update); | |
| if ($update) { | |
| if (!empty($this->original) && $this->id() !== $this->original->id()) { | |
| // The old image style name needs flushing after a rename. | |
| $this->original->flush(); | |
| // Update field settings if necessary. | |
| if (!$this->isSyncing()) { | |
| static::replaceImageStyle($this); | |
| } | |
| } | |
| else { | |
| // Flush image style when updating without changing the name. | |
| $this->flush(); | |
| } | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public static function postDelete(EntityStorageInterface $storage, array $entities) { | |
| parent::postDelete($storage, $entities); | |
| /** @var \Drupal\image\ImageStyleInterface[] $entities */ | |
| foreach ($entities as $style) { | |
| // Flush cached media for the deleted style. | |
| $style->flush(); | |
| // Clear the replacement ID, if one has been previously stored. | |
| /** @var \Drupal\image\ImageStyleStorageInterface $storage */ | |
| $storage->clearReplacementId($style->id()); | |
| } | |
| } | |
| /** | |
| * Update field settings if the image style name is changed. | |
| * | |
| * @param \Drupal\image\ImageStyleInterface $style | |
| * The image style. | |
| */ | |
| protected static function replaceImageStyle(ImageStyleInterface $style) { | |
| if ($style->id() != $style->getOriginalId()) { | |
| // Loop through all entity displays looking for formatters / widgets using | |
| // the image style. | |
| foreach (entity_load_multiple('entity_view_display') as $display) { | |
| foreach ($display->getComponents() as $name => $options) { | |
| if (isset($options['type']) && $options['type'] == 'image' && $options['settings']['image_style'] == $style->getOriginalId()) { | |
| $options['settings']['image_style'] = $style->id(); | |
| $display->setComponent($name, $options) | |
| ->save(); | |
| } | |
| } | |
| } | |
| foreach (entity_load_multiple('entity_form_display') as $display) { | |
| foreach ($display->getComponents() as $name => $options) { | |
| if (isset($options['type']) && $options['type'] == 'image_image' && $options['settings']['preview_image_style'] == $style->getOriginalId()) { | |
| $options['settings']['preview_image_style'] = $style->id(); | |
| $display->setComponent($name, $options) | |
| ->save(); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function buildUri($uri) { | |
| $scheme = $this->fileUriScheme($uri); | |
| if ($scheme) { | |
| $path = $this->fileUriTarget($uri); | |
| } | |
| else { | |
| $path = $uri; | |
| $scheme = $this->fileDefaultScheme(); | |
| } | |
| return $scheme . '://styles/' . $this->id() . '/' . $scheme . '/' . $this->addExtension($path); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function buildUrl($path, $clean_urls = NULL) { | |
| $uri = $this->buildUri($path); | |
| // The token query is added even if the | |
| // 'image.settings:allow_insecure_derivatives' configuration is TRUE, so | |
| // that the emitted links remain valid if it is changed back to the default | |
| // FALSE. However, sites which need to prevent the token query from being | |
| // emitted at all can additionally set the | |
| // 'image.settings:suppress_itok_output' configuration to TRUE to achieve | |
| // that (if both are set, the security token will neither be emitted in the | |
| // image derivative URL nor checked for in | |
| // \Drupal\image\ImageStyleInterface::deliver()). | |
| $token_query = array(); | |
| if (!\Drupal::config('image.settings')->get('suppress_itok_output')) { | |
| // The passed $path variable can be either a relative path or a full URI. | |
| $original_uri = file_uri_scheme($path) ? file_stream_wrapper_uri_normalize($path) : file_build_uri($path); | |
| $token_query = array(IMAGE_DERIVATIVE_TOKEN => $this->getPathToken($original_uri)); | |
| } | |
| if ($clean_urls === NULL) { | |
| // Assume clean URLs unless the request tells us otherwise. | |
| $clean_urls = TRUE; | |
| try { | |
| $request = \Drupal::request(); | |
| $clean_urls = RequestHelper::isCleanUrl($request); | |
| } | |
| catch (ServiceNotFoundException $e) { | |
| } | |
| } | |
| // If not using clean URLs, the image derivative callback is only available | |
| // with the script path. If the file does not exist, use Url::fromUri() to | |
| // ensure that it is included. Once the file exists it's fine to fall back | |
| // to the actual file path, this avoids bootstrapping PHP once the files are | |
| // built. | |
| if ($clean_urls === FALSE && file_uri_scheme($uri) == 'public' && !file_exists($uri)) { | |
| $directory_path = \Drupal::service('stream_wrapper_manager')->getViaUri($uri)->getDirectoryPath(); | |
| return Url::fromUri('base:' . $directory_path . '/' . file_uri_target($uri), array('absolute' => TRUE, 'query' => $token_query))->toString(); | |
| } | |
| $file_url = file_create_url($uri); | |
| // Append the query string with the token, if necessary. | |
| if ($token_query) { | |
| $file_url .= (strpos($file_url, '?') !== FALSE ? '&' : '?') . UrlHelper::buildQuery($token_query); | |
| } | |
| return $file_url; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function flush($path = NULL) { | |
| // A specific image path has been provided. Flush only that derivative. | |
| if (isset($path)) { | |
| $derivative_uri = $this->buildUri($path); | |
| if (file_exists($derivative_uri)) { | |
| file_unmanaged_delete($derivative_uri); | |
| } | |
| return $this; | |
| } | |
| // Delete the style directory in each registered wrapper. | |
| $wrappers = \Drupal::service('stream_wrapper_manager')->getWrappers(StreamWrapperInterface::WRITE_VISIBLE); | |
| foreach ($wrappers as $wrapper => $wrapper_data) { | |
| if (file_exists($directory = $wrapper . '://styles/' . $this->id())) { | |
| file_unmanaged_delete_recursive($directory); | |
| } | |
| } | |
| // Let other modules update as necessary on flush. | |
| $module_handler = \Drupal::moduleHandler(); | |
| $module_handler->invokeAll('image_style_flush', array($this)); | |
| // Clear caches so that formatters may be added for this style. | |
| drupal_theme_rebuild(); | |
| Cache::invalidateTags($this->getCacheTagsToInvalidate()); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function createDerivative($original_uri, $derivative_uri) { | |
| // If the source file doesn't exist, return FALSE without creating folders. | |
| $image = \Drupal::service('image.factory')->get($original_uri); | |
| if (!$image->isValid()) { | |
| return FALSE; | |
| } | |
| // Get the folder for the final location of this style. | |
| $directory = drupal_dirname($derivative_uri); | |
| // Build the destination folder tree if it doesn't already exist. | |
| if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { | |
| \Drupal::logger('image')->error('Failed to create style directory: %directory', array('%directory' => $directory)); | |
| return FALSE; | |
| } | |
| foreach ($this->getEffects() as $effect) { | |
| $effect->applyEffect($image); | |
| } | |
| if (!$image->save($derivative_uri)) { | |
| if (file_exists($derivative_uri)) { | |
| \Drupal::logger('image')->error('Cached image file %destination already exists. There may be an issue with your rewrite configuration.', array('%destination' => $derivative_uri)); | |
| } | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function transformDimensions(array &$dimensions, $uri) { | |
| foreach ($this->getEffects() as $effect) { | |
| $effect->transformDimensions($dimensions, $uri); | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getDerivativeExtension($extension) { | |
| foreach ($this->getEffects() as $effect) { | |
| $extension = $effect->getDerivativeExtension($extension); | |
| } | |
| return $extension; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getPathToken($uri) { | |
| // Return the first 8 characters. | |
| return substr(Crypt::hmacBase64($this->id() . ':' . $this->addExtension($uri), $this->getPrivateKey() . $this->getHashSalt()), 0, 8); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function deleteImageEffect(ImageEffectInterface $effect) { | |
| $this->getEffects()->removeInstanceId($effect->getUuid()); | |
| $this->save(); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getEffect($effect) { | |
| return $this->getEffects()->get($effect); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getEffects() { | |
| if (!$this->effectsCollection) { | |
| $this->effectsCollection = new ImageEffectPluginCollection($this->getImageEffectPluginManager(), $this->effects); | |
| $this->effectsCollection->sort(); | |
| } | |
| return $this->effectsCollection; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getPluginCollections() { | |
| return array('effects' => $this->getEffects()); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function addImageEffect(array $configuration) { | |
| $configuration['uuid'] = $this->uuidGenerator()->generate(); | |
| $this->getEffects()->addInstanceId($configuration['uuid'], $configuration); | |
| return $configuration['uuid']; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getReplacementID() { | |
| /** @var \Drupal\image\ImageStyleStorageInterface $storage */ | |
| $storage = $this->entityTypeManager()->getStorage($this->getEntityTypeId()); | |
| return $storage->getReplacementId($this->id()); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getName() { | |
| return $this->get('name'); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function setName($name) { | |
| $this->set('name', $name); | |
| return $this; | |
| } | |
| /** | |
| * Returns the image effect plugin manager. | |
| * | |
| * @return \Drupal\Component\Plugin\PluginManagerInterface | |
| * The image effect plugin manager. | |
| */ | |
| protected function getImageEffectPluginManager() { | |
| return \Drupal::service('plugin.manager.image.effect'); | |
| } | |
| /** | |
| * Gets the Drupal private key. | |
| * | |
| * @return string | |
| * The Drupal private key. | |
| */ | |
| protected function getPrivateKey() { | |
| return \Drupal::service('private_key')->get(); | |
| } | |
| /** | |
| * Gets a salt useful for hardening against SQL injection. | |
| * | |
| * @return string | |
| * A salt based on information in settings.php, not in the database. | |
| * | |
| * @throws \RuntimeException | |
| */ | |
| protected function getHashSalt() { | |
| return Settings::getHashSalt(); | |
| } | |
| /** | |
| * Adds an extension to a path. | |
| * | |
| * If this image style changes the extension of the derivative, this method | |
| * adds the new extension to the given path. This way we avoid filename | |
| * clashes while still allowing us to find the source image. | |
| * | |
| * @param string $path | |
| * The path to add the extension to. | |
| * | |
| * @return string | |
| * The given path if this image style doesn't change its extension, or the | |
| * path with the added extension if it does. | |
| */ | |
| protected function addExtension($path) { | |
| $original_extension = pathinfo($path, PATHINFO_EXTENSION); | |
| $extension = $this->getDerivativeExtension($original_extension); | |
| if ($original_extension !== $extension) { | |
| $path .= '.' . $extension; | |
| } | |
| return $path; | |
| } | |
| /** | |
| * Provides a wrapper for file_uri_scheme() to allow unit testing. | |
| * | |
| * Returns the scheme of a URI (e.g. a stream). | |
| * | |
| * @param string $uri | |
| * A stream, referenced as "scheme://target" or "data:target". | |
| * | |
| * @see file_uri_target() | |
| * | |
| * @todo: Remove when https://www.drupal.org/node/2050759 is in. | |
| * | |
| * @return string | |
| * A string containing the name of the scheme, or FALSE if none. For | |
| * example, the URI "public://example.txt" would return "public". | |
| */ | |
| protected function fileUriScheme($uri) { | |
| return file_uri_scheme($uri); | |
| } | |
| /** | |
| * Provides a wrapper for file_uri_target() to allow unit testing. | |
| * | |
| * Returns the part of a URI after the schema. | |
| * | |
| * @param string $uri | |
| * A stream, referenced as "scheme://target" or "data:target". | |
| * | |
| * @see file_uri_scheme() | |
| * | |
| * @todo: Convert file_uri_target() into a proper injectable service. | |
| * | |
| * @return string|bool | |
| * A string containing the target (path), or FALSE if none. | |
| * For example, the URI "public://sample/test.txt" would return | |
| * "sample/test.txt". | |
| */ | |
| protected function fileUriTarget($uri) { | |
| return file_uri_target($uri); | |
| } | |
| /** | |
| * Provides a wrapper for file_default_scheme() to allow unit testing. | |
| * | |
| * Gets the default file stream implementation. | |
| * | |
| * @todo: Convert file_default_scheme() into a proper injectable service. | |
| * | |
| * @return string | |
| * 'public', 'private' or any other file scheme defined as the default. | |
| */ | |
| protected function fileDefaultScheme() { | |
| return file_default_scheme(); | |
| } | |
| } |