diff --git a/composer.json b/composer.json index d156a159e1777f51640c60fa80752d32a228d4a1..e99780a25997659e33fcdecb1fa402b7fff4ccad 100644 --- a/composer.json +++ b/composer.json @@ -107,7 +107,7 @@ "drupal/content_access": "1.0-alpha3", "drupal/core-composer-scaffold": "^9.0", "drupal/core-recommended": "^9.0", - "drupal/crop": "2.1", + "drupal/crop": "2.2", "drupal/ctools": "3.7", "drupal/dropzonejs": "2.5", "drupal/editor_advanced_link": "1.9", diff --git a/composer.lock b/composer.lock index 3be22a5b50b04bb477c1c35d5196b924cd177896..ca3c06a373344b126b57b531b767d9fef89072d0 100644 --- a/composer.lock +++ b/composer.lock @@ -3360,17 +3360,17 @@ }, { "name": "drupal/crop", - "version": "2.1.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://git.drupalcode.org/project/crop.git", - "reference": "8.x-2.1" + "reference": "8.x-2.2" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/crop-8.x-2.1.zip", - "reference": "8.x-2.1", - "shasum": "c03541907d59874ca8a81f574258f6c0de8cbdc8" + "url": "https://ftp.drupal.org/files/projects/crop-8.x-2.2.zip", + "reference": "8.x-2.2", + "shasum": "b5a84c6b85b9582e686ccf421295611d9dd52055" }, "require": { "drupal/core": "^8.8 || ^9" @@ -3378,8 +3378,8 @@ "type": "drupal-module", "extra": { "drupal": { - "version": "8.x-2.1", - "datestamp": "1585251827", + "version": "8.x-2.2", + "datestamp": "1645187494", "security-coverage": { "status": "covered", "message": "Covered by Drupal's security advisory policy" diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 653e7f75f7f5ca4326d72de2fd7337cc0ccef33f..f767c1726f5bec44a1e1ee0bacb39c6208a548cc 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -3455,18 +3455,18 @@ }, { "name": "drupal/crop", - "version": "2.1.0", - "version_normalized": "2.1.0.0", + "version": "2.2.0", + "version_normalized": "2.2.0.0", "source": { "type": "git", "url": "https://git.drupalcode.org/project/crop.git", - "reference": "8.x-2.1" + "reference": "8.x-2.2" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/crop-8.x-2.1.zip", - "reference": "8.x-2.1", - "shasum": "c03541907d59874ca8a81f574258f6c0de8cbdc8" + "url": "https://ftp.drupal.org/files/projects/crop-8.x-2.2.zip", + "reference": "8.x-2.2", + "shasum": "b5a84c6b85b9582e686ccf421295611d9dd52055" }, "require": { "drupal/core": "^8.8 || ^9" @@ -3474,8 +3474,8 @@ "type": "drupal-module", "extra": { "drupal": { - "version": "8.x-2.1", - "datestamp": "1585251827", + "version": "8.x-2.2", + "datestamp": "1645187494", "security-coverage": { "status": "covered", "message": "Covered by Drupal's security advisory policy" diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index db280c548458f2ab02c3ffef1e15913df4071d28..8ff238594d07536083ea99056007165232749d34 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -782,12 +782,12 @@ ), ), 'drupal/crop' => array( - 'pretty_version' => '2.1.0', - 'version' => '2.1.0.0', + 'pretty_version' => '2.2.0', + 'version' => '2.2.0.0', 'type' => 'drupal-module', 'install_path' => __DIR__ . '/../../web/modules/crop', 'aliases' => array(), - 'reference' => '8.x-2.1', + 'reference' => '8.x-2.2', 'dev_requirement' => false, ), 'drupal/ctools' => array( diff --git a/web/modules/crop/config/schema/crop.schema.yml b/web/modules/crop/config/schema/crop.schema.yml index e4285ac20b2ed4bef12c598073d55b6f15a6536c..abf43db9d5ea7493e17c250bfb38cf426ed8f99a 100644 --- a/web/modules/crop/config/schema/crop.schema.yml +++ b/web/modules/crop/config/schema/crop.schema.yml @@ -47,6 +47,9 @@ image.effect.crop_crop: crop_type: label: 'Crop type' type: string + automatic_crop_provider: + label: 'Automatic crop provider' + type: string crop.settings: type: config_object diff --git a/web/modules/crop/crop.info.yml b/web/modules/crop/crop.info.yml index 4417fea4399e644e620f50ffd8acfc02fb1e0a6f..836e552e367e7341f78f2860fe244714ddfc1839 100644 --- a/web/modules/crop/crop.info.yml +++ b/web/modules/crop/crop.info.yml @@ -6,8 +6,9 @@ type: module dependencies: - drupal:image - drupal:user +configure: crop.overview_types -# Information added by Drupal.org packaging script on 2020-03-26 -version: '8.x-2.1' +# Information added by Drupal.org packaging script on 2022-02-18 +version: '8.x-2.2' project: 'crop' -datestamp: 1585251788 +datestamp: 1645187495 diff --git a/web/modules/crop/modules/crop_media_entity/crop_media_entity.info.yml b/web/modules/crop/modules/crop_media_entity/crop_media_entity.info.yml index c041b23a3e679bd9085f13674543e5c4be55838b..bbf5e8e1b96cd08053e8d3028de804cea1b4ea97 100644 --- a/web/modules/crop/modules/crop_media_entity/crop_media_entity.info.yml +++ b/web/modules/crop/modules/crop_media_entity/crop_media_entity.info.yml @@ -5,7 +5,7 @@ package: Media type: module hidden: true -# Information added by Drupal.org packaging script on 2020-03-26 -version: '8.x-2.1' +# Information added by Drupal.org packaging script on 2022-02-18 +version: '8.x-2.2' project: 'crop' -datestamp: 1585251788 +datestamp: 1645187495 diff --git a/web/modules/crop/src/Entity/CropType.php b/web/modules/crop/src/Entity/CropType.php index fc6fdd9059a01b8d13dcc88a63f0c6d4295eb00e..249738cd59c6689bd833e3c00aec33264ddf50d7 100644 --- a/web/modules/crop/src/Entity/CropType.php +++ b/web/modules/crop/src/Entity/CropType.php @@ -130,6 +130,7 @@ public function validate() { /** * {@inheritdoc} */ + #[\ReturnTypeWillChange] public function getIterator() { return new \ArrayIterator(); } diff --git a/web/modules/crop/src/Events/AutomaticCrop.php b/web/modules/crop/src/Events/AutomaticCrop.php new file mode 100644 index 0000000000000000000000000000000000000000..cc893d41fc2fd880ee8a77c79ac52f7ae25073af --- /dev/null +++ b/web/modules/crop/src/Events/AutomaticCrop.php @@ -0,0 +1,107 @@ +<?php + +namespace Drupal\crop\Events; + +use Drupal\Core\Image\ImageInterface; +use Drupal\crop\CropInterface; +use Drupal\crop\Entity\CropType; +use Symfony\Component\EventDispatcher\Event; + +/** + * Represents automatic crop action as event. + */ +class AutomaticCrop extends Event { + + /** + * The crop entity. + * + * @var \Drupal\crop\CropInterface|false + */ + protected $crop = FALSE; + + + /** + * The image resource to crop. + * + * @var \Drupal\Core\Image\ImageInterface + */ + protected $image; + + /** + * The crop type loaded. + * + * @var \Drupal\crop\Entity\CropType + */ + protected $cropType; + + /** + * All data required by crop providers. + * + * @var array + */ + protected $configuration; + + /** + * Constructs a EntitySelectionEvent object. + * + * @param \Drupal\Core\Image\ImageInterface $image + * @param \Drupal\crop\Entity\CropType $cropType + * @param $configuration + */ + public function __construct(ImageInterface $image, CropType $cropType, array $configuration) { + $this->image = $image; + $this->cropType = $cropType; + $this->configuration = $configuration; + } + + /** + * Set calculated crop instance. + * + * @param \Drupal\crop\CropInterface $crop + * The crop entity instance. + */ + public function setCrop(CropInterface $crop) { + $this->crop = $crop; + } + + /** + * Get crop instance. + * + * @return \Drupal\crop\CropInterface|false + * List of fallbacks. + */ + public function getCrop() { + return $this->crop; + } + + /** + * Get the crop type entity. + * + * @return \Drupal\crop\Entity\CropType + * The crop type entity loaded. + */ + public function getCropType() { + return $this->cropType; + } + + /** + * Get image to crop. + * + * @return \Drupal\Core\Image\ImageInterface + * The image resource. + */ + public function getImage() { + return $this->image; + } + + /** + * Get all configurations to generate automatic crop. + * + * @return array + * All data to be used by automatic crop providers. + */ + public function getConfiguration() { + return $this->configuration; + } + +} diff --git a/web/modules/crop/src/Events/AutomaticCropProviders.php b/web/modules/crop/src/Events/AutomaticCropProviders.php new file mode 100644 index 0000000000000000000000000000000000000000..05daad3fd1618efe9b02ce4b2e043b8fd6bd2337 --- /dev/null +++ b/web/modules/crop/src/Events/AutomaticCropProviders.php @@ -0,0 +1,49 @@ +<?php + +namespace Drupal\crop\Events; + +use Symfony\Component\EventDispatcher\Event; + +/** + * Collects "Automatic crop" providers. + */ +class AutomaticCropProviders extends Event { + + /** + * Automatic Crop provider list. + * + * @var array + */ + protected $providers = []; + + /** + * Adds provider. + * + * @param array $provider + * Register provider to providers list. + */ + public function registerProvider(array $provider) { + $this->providers[key($provider)] = current($provider); + } + + /** + * Sets automatic crop providers. + * + * @param array $providers + * List of automatic crop providers. + */ + public function setProviders(array $providers) { + $this->providers = $providers; + } + + /** + * Gets automatic crop providers. + * + * @return array + * List of providers. + */ + public function getProviders() { + return $this->providers; + } + +} diff --git a/web/modules/crop/src/Events/Events.php b/web/modules/crop/src/Events/Events.php new file mode 100644 index 0000000000000000000000000000000000000000..671f74adde288568031b19886abc9e5b7fd0deca --- /dev/null +++ b/web/modules/crop/src/Events/Events.php @@ -0,0 +1,24 @@ +<?php + +namespace Drupal\crop\Events; + +/** + * Contains all events thrown by crop API. + */ +final class Events { + + /** + * The event to subscribe to add provider as manual crop fallback provider. + * + * @var string + */ + const AUTOMATIC_CROP_PROVIDERS = 'crop.automatic_crop_providers'; + + /** + * The event to subscribe to automatic crop generate for crop API. + * + * @var string + */ + const AUTOMATIC_CROP = 'crop.automatic_crop'; + +} diff --git a/web/modules/crop/src/Plugin/ImageEffect/CropEffect.php b/web/modules/crop/src/Plugin/ImageEffect/CropEffect.php index 5f41e6b7d6e000fdf88af3a5665244502f7f1641..f6930b67bebeacd34487c09065872ed61e8966b3 100644 --- a/web/modules/crop/src/Plugin/ImageEffect/CropEffect.php +++ b/web/modules/crop/src/Plugin/ImageEffect/CropEffect.php @@ -10,9 +10,13 @@ use Drupal\crop\CropInterface; use Drupal\crop\CropStorageInterface; use Drupal\crop\Entity\Crop; +use Drupal\crop\Events\AutomaticCrop; +use Drupal\crop\Events\AutomaticCropProviders; +use Drupal\crop\Events\Events; use Drupal\image\ConfigurableImageEffectBase; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Crops an image resource. @@ -40,11 +44,11 @@ class CropEffect extends ConfigurableImageEffectBase implements ContainerFactory protected $typeStorage; /** - * Crop entity. + * Crop entity or Automated Crop Plugin. * - * @var \Drupal\crop\CropInterface + * @var \Drupal\crop\CropInterface|false */ - protected $crop; + protected $crop = FALSE; /** * The image factory service. @@ -53,14 +57,30 @@ class CropEffect extends ConfigurableImageEffectBase implements ContainerFactory */ protected $imageFactory; + /** + * Event dispatcher service. + * + * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface + */ + protected $eventDispatcher; + + /** + * The automatic crop providers list. + * + * @var array + */ + protected $automaticCropProviders; + /** * {@inheritdoc} */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, LoggerInterface $logger, CropStorageInterface $crop_storage, ConfigEntityStorageInterface $crop_type_storage, ImageFactory $image_factory) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, LoggerInterface $logger, CropStorageInterface $crop_storage, ConfigEntityStorageInterface $crop_type_storage, ImageFactory $image_factory, EventDispatcherInterface $event_dispatcher) { parent::__construct($configuration, $plugin_id, $plugin_definition, $logger); $this->storage = $crop_storage; $this->typeStorage = $crop_type_storage; $this->imageFactory = $image_factory; + $this->eventDispatcher = $event_dispatcher; + $this->automaticCropProviders = $this->getAutomaticCropProvidersList(); } /** @@ -74,7 +94,8 @@ public static function create(ContainerInterface $container, array $configuratio $container->get('logger.factory')->get('image'), $container->get('entity_type.manager')->getStorage('crop'), $container->get('entity_type.manager')->getStorage('crop_type'), - $container->get('image.factory') + $container->get('image.factory'), + $container->get('event_dispatcher') ); } @@ -87,21 +108,24 @@ public function applyEffect(ImageInterface $image) { return FALSE; } - if ($crop = $this->getCrop($image)) { - $anchor = $crop->anchor(); - $size = $crop->size(); + $this->getCrop($image); + if (!$this->crop) { + return FALSE; + } - if (!$image->crop($anchor['x'], $anchor['y'], $size['width'], $size['height'])) { - $this->logger->error('Manual image crop failed using the %toolkit toolkit on %path (%mimetype, %width x %height)', [ + $anchor = $this->crop->anchor(); + $size = $this->crop->size(); + + if (!$image->crop($anchor['x'], $anchor['y'], $size['width'], $size['height'])) { + $this->logger->error('Manual image crop failed using the %toolkit toolkit on %path (%mimetype, %width x %height)', [ '%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%width' => $image->getWidth(), '%height' => $image->getHeight(), ] - ); - return FALSE; - } + ); + return FALSE; } return TRUE; @@ -126,6 +150,7 @@ public function getSummary() { public function defaultConfiguration() { return parent::defaultConfiguration() + [ 'crop_type' => NULL, + 'automatic_crop_provider' => NULL, ]; } @@ -133,19 +158,25 @@ public function defaultConfiguration() { * {@inheritdoc} */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { - $options = []; - foreach ($this->typeStorage->loadMultiple() as $type) { - $options[$type->id()] = $type->label(); - } - $form['crop_type'] = [ '#type' => 'select', - '#title' => t('Crop type'), + '#title' => $this->t('Crop type'), '#default_value' => $this->configuration['crop_type'], - '#options' => $options, - '#description' => t('Crop type to be used for the image style.'), + '#options' => $this->getCropTypeOptions(), + '#description' => $this->t('Crop type to be used for the image style.'), ]; + if (!empty($this->automaticCropProviders)) { + $form['automatic_crop_provider'] = [ + '#type' => 'select', + '#title' => $this->t('Automatic crop provider'), + '#empty_option' => $this->t("- Select a Provider -"), + '#options' => $this->automaticCropProviders, + '#default_value' => $this->configuration['automatic_crop_provider'], + '#description' => $this->t('The name of automatic crop provider to use if crop is not set for an image.'), + ]; + } + return $form; } @@ -155,6 +186,22 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { parent::submitConfigurationForm($form, $form_state); $this->configuration['crop_type'] = $form_state->getValue('crop_type'); + $this->configuration['automatic_crop_provider'] = $form_state->getValue('automatic_crop_provider'); + } + + /** + * Get the available cropType options list. + * + * @return array + * The cropType options list. + */ + public function getCropTypeOptions() { + $options = []; + foreach ($this->typeStorage->loadMultiple() as $type) { + $options[$type->id()] = $type->label(); + } + + return $options; } /** @@ -164,14 +211,19 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s * Image object. * * @return \Drupal\Core\Entity\EntityInterface|\Drupal\crop\CropInterface|false - * Crop entity or FALSE if crop doesn't exist. + * Crop entity or FALSE. */ protected function getCrop(ImageInterface $image) { - if (!isset($this->crop)) { - $this->crop = FALSE; - if ($crop = Crop::findCrop($image->getSource(), $this->configuration['crop_type'])) { - $this->crop = $crop; - } + if ($crop = Crop::findCrop($image->getSource(), $this->configuration['crop_type'])) { + $this->crop = $crop; + } + + if (!$this->crop && !empty($this->configuration['automatic_crop_provider'])) { + /** @var \Drupal\crop\Entity\CropType $crop_type */ + $crop_type = $this->typeStorage->load($this->configuration['crop_type']); + $automatic_crop_event = new AutomaticCrop($image, $crop_type, $this->configuration); + $this->eventDispatcher->dispatch(Events::AUTOMATIC_CROP, $automatic_crop_event); + $this->crop = $automatic_crop_event->getCrop(); } return $this->crop; @@ -185,11 +237,10 @@ public function transformDimensions(array &$dimensions, $uri) { if (!$crop instanceof CropInterface) { return; } - $size = $crop->size(); // The new image will have the exact dimensions defined for the crop effect. - $dimensions['width'] = $size['width']; - $dimensions['height'] = $size['height']; + $dimensions['width'] = $crop->size()['width']; + $dimensions['height'] = $crop->size()['height']; } /** @@ -205,4 +256,17 @@ public function calculateDependencies() { return $dependencies; } + /** + * Collect automatic crop providers. + * + * @return array + * All provider + */ + public function getAutomaticCropProvidersList() { + $event = new AutomaticCropProviders(); + $this->eventDispatcher->dispatch(Events::AUTOMATIC_CROP_PROVIDERS, $event); + + return $event->getProviders(); + } + } diff --git a/web/modules/crop/templates/crop-crop-summary.html.twig b/web/modules/crop/templates/crop-crop-summary.html.twig index 7ba7cc5c08fb4c7054d6db22c8b3dd723f5cc74c..94613bda282d2d4bfc606547853735fa4d59ae1d 100644 --- a/web/modules/crop/templates/crop-crop-summary.html.twig +++ b/web/modules/crop/templates/crop-crop-summary.html.twig @@ -18,4 +18,8 @@ {% trans %} uses <em>{{ data.crop_type|e }}</em> crop type {% endtrans %} + <br> + {% if data.automatic_crop_provider|length %} + <em>Automated Crop activated</em> + {%- endif %} {%- endif %} diff --git a/web/modules/crop/tests/src/Functional/CropFunctionalTest.php b/web/modules/crop/tests/src/Functional/CropFunctionalTest.php index 333db96c87b02f6c6434e8d1009b3e9af5dafbfc..5580b822c790b3d5ec8a7f77837073f6703c7359 100644 --- a/web/modules/crop/tests/src/Functional/CropFunctionalTest.php +++ b/web/modules/crop/tests/src/Functional/CropFunctionalTest.php @@ -125,7 +125,7 @@ public function testCropTypeCrud() { $this->testStyle = $this->container->get('entity_type.manager')->getStorage('image_style')->loadUnchanged($this->testStyle->id()); self::assertEquals($this->testStyle->getEffects()->count(), 1, 'One image effect added to test image style.'); $effect_configuration = $this->testStyle->getEffects()->getIterator()->current()->getConfiguration(); - self::assertEquals($effect_configuration['data'], ['crop_type' => $edit['id']], 'Manual crop effect uses correct image style.'); + self::assertEquals($effect_configuration['data'], ['crop_type' => $edit['id'], 'automatic_crop_provider' => NULL], 'Manual crop effect uses correct image style.'); // Tests the image URI is extended with shortened hash in case of image // style and corresponding crop existence.