3 namespace Drupal\image_widget_crop;
5 use Drupal\Core\Entity\EntityInterface;
6 use Drupal\Core\Entity\EntityTypeManagerInterface;
7 use Drupal\Core\Entity\FieldableEntityInterface;
8 use Drupal\Core\Form\FormStateInterface;
9 use Drupal\crop\Entity\Crop;
10 use Drupal\crop\Entity\CropType;
11 use Drupal\file\Plugin\Field\FieldType\FileFieldItemList;
12 use Drupal\image\Entity\ImageStyle;
15 * ImageWidgetCropManager calculation class.
17 class ImageWidgetCropManager implements ImageWidgetCropInterface {
20 * The entity type manager service.
22 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
24 protected $entityTypeManager;
29 * @var \Drupal\Core\Entity\EntityStorageInterface
31 protected $cropStorage;
36 * @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
38 protected $cropTypeStorage;
41 * The image style storage.
43 * @var \Drupal\Core\Entity\EntityStorageInterface
45 protected $imageStyleStorage;
50 * @var \Drupal\Core\Entity\EntityStorageInterface
52 protected $fileStorage;
55 * Constructs a ImageWidgetCropManager object.
57 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
58 * Entity type manager service.
60 public function __construct(EntityTypeManagerInterface $entity_type_manager) {
61 $this->entityTypeManager = $entity_type_manager;
62 $this->cropStorage = $this->entityTypeManager->getStorage('crop');
63 $this->cropTypeStorage = $this->entityTypeManager->getStorage('crop_type');
64 $this->imageStyleStorage = $this->entityTypeManager->getStorage('image_style');
65 $this->fileStorage = $this->entityTypeManager->getStorage('file');
71 public function applyCrop(array $properties, $field_value, CropType $crop_type) {
72 $crop_properties = $this->getCropOriginalDimension($field_value, $properties);
73 if (!empty($crop_properties)) {
86 public function updateCrop(array $properties, $field_value, CropType $crop_type) {
87 $crop_properties = $this->getCropOriginalDimension($field_value, $properties);
88 if (!empty($crop_properties)) {
89 $image_styles = $this->getImageStylesByCrop($crop_type->id());
91 if (!empty($image_styles)) {
92 $crops = $this->loadImageStyleByCrop($image_styles, $crop_type, $field_value['file-uri']);
96 $this->saveCrop($crop_properties, $field_value, $crop_type);
100 /** @var \Drupal\crop\Entity\Crop $crop */
101 foreach ($crops as $crop) {
102 if (!$this->cropHasChanged($crop_properties, array_merge($crop->position(), $crop->size()))) {
106 $this->updateCropProperties($crop, $crop_properties);
107 drupal_set_message(t('The crop "@cropType" were successfully updated for image "@filename".', ['@cropType' => $crop_type->label(), '@filename' => $this->fileStorage->load($field_value['file-id'])->getFilename()]));
115 public function saveCrop(array $crop_properties, $field_value, CropType $crop_type, $notify = TRUE) {
117 'type' => $crop_type->id(),
118 'entity_id' => $field_value['file-id'],
119 'entity_type' => 'file',
120 'uri' => $field_value['file-uri'],
121 'x' => $crop_properties['x'],
122 'y' => $crop_properties['y'],
123 'width' => $crop_properties['width'],
124 'height' => $crop_properties['height'],
127 /** @var \Drupal\crop\CropInterface $crop */
128 $crop = $this->cropStorage->create($values);
132 drupal_set_message(t('The crop "@cropType" was successfully added for image "@filename".', ['@cropType' => $crop_type->label(), '@filename' => $this->fileStorage->load($field_value['file-id'])->getFilename()]));
139 public function deleteCrop($file_uri, CropType $crop_type, $file_id) {
140 $image_styles = $this->getImageStylesByCrop($crop_type->id());
141 $crop = $this->cropStorage->loadByProperties([
142 'type' => $crop_type->id(),
145 $this->cropStorage->delete($crop);
146 $this->imageStylesOperations($image_styles, $file_uri);
147 drupal_set_message(t('The crop "@cropType" was successfully deleted for image "@filename".', [
148 '@cropType' => $crop_type->label(),
149 '@filename' => $this->fileStorage->load($file_id)->getFilename(),
156 public function getAxisCoordinates(array $axis, array $crop_selection) {
158 'x' => (int) round($axis['x'] + ($crop_selection['width'] / 2)),
159 'y' => (int) round($axis['y'] + ($crop_selection['height'] / 2)),
166 public function getCropOriginalDimension(array $field_values, array $properties) {
167 $crop_coordinates = [];
169 /** @var \Drupal\Core\Image\Image $image */
170 $image = \Drupal::service('image.factory')->get($field_values['file-uri']);
171 if (!$image->isValid()) {
172 drupal_set_message(t('The file "@file" is not valid, your crop is not applied.', [
173 '@file' => $field_values['file-uri'],
175 return $crop_coordinates;
178 // Get Center coordinate of crop zone on original image.
179 $axis_coordinate = $this->getAxisCoordinates(
180 ['x' => $properties['x'], 'y' => $properties['y']],
181 ['width' => $properties['width'], 'height' => $properties['height']]
184 // Calculate coordinates (position & sizes) of crop zone on original image.
185 $crop_coordinates['width'] = $properties['width'];
186 $crop_coordinates['height'] = $properties['height'];
187 $crop_coordinates['x'] = $axis_coordinate['x'];
188 $crop_coordinates['y'] = $axis_coordinate['y'];
190 return $crop_coordinates;
196 public function getEffectData(ImageStyle $image_style, $data_type) {
198 /* @var \Drupal\image\ImageEffectInterface $effect */
199 foreach ($image_style->getEffects() as $uuid => $effect) {
200 $data_effect = $image_style->getEffect($uuid)->getConfiguration()['data'];
201 if (isset($data_effect[$data_type])) {
202 $data = $data_effect[$data_type];
212 public function getImageStylesByCrop($crop_type_name) {
214 $image_styles = $this->imageStyleStorage->loadMultiple();
216 /** @var \Drupal\image\Entity\ImageStyle $image_style */
217 foreach ($image_styles as $image_style) {
218 $image_style_data = $this->getEffectData($image_style, 'crop_type');
219 if (!empty($image_style_data) && ($image_style_data == $crop_type_name)) {
220 $styles[] = $image_style;
230 public function imageStylesOperations(array $image_styles, $file_uri, $create_derivative = FALSE) {
231 /** @var \Drupal\image\Entity\ImageStyle $image_style */
232 foreach ($image_styles as $image_style) {
233 if ($create_derivative) {
234 // Generate the image derivative uri.
235 $destination_uri = $image_style->buildUri($file_uri);
237 // Create a derivative of the original image with a good uri.
238 $image_style->createDerivative($file_uri, $destination_uri);
240 // Flush the cache of this ImageStyle.
241 $image_style->flush($file_uri);
248 public function updateCropProperties(Crop $crop, array $crop_properties) {
249 // Parse all properties if this crop have changed.
250 foreach ($crop_properties as $crop_coordinate => $value) {
251 // Edit the crop properties if he have changed.
252 $crop->set($crop_coordinate, $value, TRUE);
261 public function loadImageStyleByCrop(array $image_styles, CropType $crop_type, $file_uri) {
263 /** @var \Drupal\image\Entity\ImageStyle $image_style */
264 foreach ($image_styles as $image_style) {
265 /** @var \Drupal\crop\Entity\Crop $crop */
266 $crop = Crop::findCrop($file_uri, $crop_type->id());
268 $crops[$image_style->id()] = $crop;
278 public function cropHasChanged(array $crop_properties, array $old_crop) {
279 return !empty(array_diff_assoc($crop_properties, $old_crop));
285 public function getAvailableCropType(array $crop_list) {
286 $available_crop = [];
287 foreach ($crop_list as $crop_machine_name => $crop_label) {
288 $image_styles = $this->getImageStylesByCrop($crop_machine_name);
289 if (!empty($image_styles)) {
290 $available_crop[$crop_machine_name] = $crop_label;
294 return $available_crop;
300 public static function getCropProperties(Crop $crop) {
301 $anchor = $crop->anchor();
302 $size = $crop->size();
306 'height' => $size['height'],
307 'width' => $size['width'],
314 public function buildCropToEntity(EntityInterface $entity) {
315 if (isset($entity) && $entity instanceof FieldableEntityInterface) {
316 // Loop all fields of the saved entity.
317 foreach ($entity->getFields() as $entity_fields) {
318 // If current field is FileField and use imageWidgetCrop.
319 if ($entity_fields instanceof FileFieldItemList) {
320 /* First loop to get each elements independently in the field values.
321 Required if the image field cardinality > 1. */
322 foreach ($entity_fields->getValue() as $crop_elements) {
323 foreach ($crop_elements as $crop_element) {
324 if (is_array($crop_element) && isset($crop_element['crop_wrapper'])) {
325 // Reload image since its URI could have been changed,
327 /** @var \Drupal\file_entity\Entity\FileEntity $file */
328 $file = $this->fileStorage->load($crop_element['file-id']);
329 $crop_element['file-uri'] = $file->getFileUri();
330 // Parse all value of a crop_wrapper element and get properties
331 // associate with her CropType.
332 foreach ($crop_element['crop_wrapper'] as $crop_type_name => $properties) {
333 $properties = $properties['crop_container']['values'];
334 /** @var \Drupal\crop\Entity\CropType $crop_type */
335 $crop_type = $this->cropTypeStorage->load($crop_type_name);
337 // If the crop type needed is disabled or delete.
338 if (empty($crop_type) && $crop_type instanceof CropType) {
339 drupal_set_message(t("The CropType ('@cropType') is not active or not defined. Please verify configuration of image style or ImageWidgetCrop formatter configuration", ['@cropType' => $crop_type->id()]), 'error');
343 // If this crop is available to create an crop entity.
344 if ($entity->isNew()) {
345 if ($properties['crop_applied'] == '1' && isset($properties) && (!empty($properties['width']) && !empty($properties['height']))) {
346 $this->applyCrop($properties, $crop_element, $crop_type);
350 // Get all imagesStyle used this crop_type.
351 $image_styles = $this->getImageStylesByCrop($crop_type_name);
352 $crops = $this->loadImageStyleByCrop($image_styles, $crop_type, $crop_element['file-uri']);
353 // If the entity already exist & is not deleted by user
354 // update $crop_type_name crop entity.
355 if ($properties['crop_applied'] == '0' && !empty($crops)) {
356 $this->deleteCrop($crop_element['file-uri'], $crop_type, $crop_element['file-id']);
358 elseif (isset($properties) && (!empty($properties['width']) && !empty($properties['height']))) {
359 $this->updateCrop($properties, $crop_element, $crop_type);
374 public function buildCropToForm(FormStateInterface $form_state) {
375 if ($entity = $form_state->getFormObject()->getEntity()) {
376 $form_state_values = $form_state->getValues();
377 if (is_array($form_state_values['image_crop']) && isset($form_state_values['image_crop']['crop_wrapper'])) {
378 // Parse all values and get properties associate with the crop type.
379 foreach ($form_state_values['image_crop']['crop_wrapper'] as $crop_type_name => $properties) {
380 $properties = $properties['crop_container']['values'];
381 /** @var \Drupal\crop\Entity\CropType $crop_type */
382 $crop_type = $this->cropTypeStorage->load($crop_type_name);
384 // If the crop type needed is disabled or delete.
385 if (empty($crop_type) && $crop_type instanceof CropType) {
386 drupal_set_message(t("The CropType ('@cropType') is not active or not defined. Please verify configuration of image style or ImageWidgetCrop formatter configuration", ['@cropType' => $crop_type->id()]), 'error');
390 if (is_array($properties) && isset($properties)) {
391 $crop_exists = Crop::cropExists($entity->getFileUri(), $crop_type_name);
393 if ($properties['crop_applied'] == '1' && isset($properties) && (!empty($properties['width']) && !empty($properties['height']))) {
394 $this->applyCrop($properties, $form_state_values['image_crop'], $crop_type);
398 // Get all imagesStyle used this crop_type.
399 $image_styles = $this->getImageStylesByCrop($crop_type_name);
400 $crops = $this->loadImageStyleByCrop($image_styles, $crop_type, $entity->getFileUri());
401 // If the entity already exist & is not deleted by user update
402 // $crop_type_name crop entity.
403 if ($properties['crop_applied'] == '0' && !empty($crops)) {
404 $this->deleteCrop($entity->getFileUri(), $crop_type, $entity->id());
406 elseif (isset($properties) && (!empty($properties['width']) && !empty($properties['height']))) {
407 $this->updateCrop($properties, [
408 'file-uri' => $entity->getFileUri(),
409 'file-id' => $entity->id(),
418 drupal_set_message(t('No File element found.'), 'error');