5 * The Crop API Drupal module.
7 * Provides storage and API for image crops.
10 use Drupal\Component\Utility\UrlHelper;
11 use Drupal\Core\Form\FormStateInterface;
12 use Drupal\Core\StreamWrapper\PublicStream;
13 use Drupal\crop\Entity\Crop;
14 use Drupal\image\Entity\ImageStyle;
15 use Drupal\media\MediaSourceInterface;
16 use Drupal\media\MediaTypeInterface;
17 use Drupal\file\FileInterface;
20 * Implements hook_theme().
22 function crop_theme() {
24 'crop_crop_summary' => [
25 'variables' => ['data' => [], 'effect' => []],
31 * Prepares variables for crop_crop summary template.
33 * Default template: crop-crop-summary.twig.html.
35 function template_preprocess_crop_crop_summary(&$variables) {
36 if (!empty($variables['data']['crop_type'])) {
37 $type = \Drupal::entityTypeManager()->getStorage('crop_type')->load($variables['data']['crop_type']);
38 $variables['data']['crop_type'] = $type->label();
43 * Implements hook_form_FORM_ID_alter().
45 * Adds crop configuration fields to media form.
47 function crop_form_media_type_edit_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
48 /** @var \Drupal\media\Entity\MediaType $entity_type */
49 $entity_type = $form_state->getFormObject()->getEntity();
51 $allowed_field_types = ['file', 'image'];
53 /** @var \Drupal\Core\Field\FieldDefinitionInterface[] $fields */
54 $fields = \Drupal::service('entity_field.manager')->getFieldDefinitions('media', $entity_type->id());
55 foreach ($fields as $field_name => $field) {
56 if (in_array($field->getType(), $allowed_field_types) && !$field->getFieldStorageDefinition()->isBaseField()) {
57 $options[$field_name] = $field->getLabel();
61 $form['#entity_builders'][] = 'crop_media_type_form_builder';
63 '#type' => 'fieldset',
64 '#title' => t('Crop configuration'),
65 '#group' => 'source_dependent',
68 if (empty($options)) {
69 $form['crop']['image_field'] = [
74 $form['crop']['message'] = [
75 '#markup' => t('There are no file or image fields on this bundle at the moment. In order to configure crop add at least one such field and come back.'),
81 $form['crop']['image_field'] = [
83 '#title' => t('Image field'),
84 '#default_value' => $entity_type->getThirdPartySetting('crop', 'image_field'),
85 '#options' => $options,
86 '#empty_option' => t('- Skip field -'),
87 '#empty_value' => MediaSourceInterface::METADATA_FIELD_EMPTY,
88 '#description' => t('Select field that stores image which needs to be cropped.'),
93 * Entity builder for Media type.
95 * Adds third party settings to Media type config entity.
97 * @see crop_form_media_type_edit_form_alter()
99 function crop_media_type_form_builder($entity_type, MediaTypeInterface $bundle, array &$form, FormStateInterface $form_state) {
100 $bundle->setThirdPartySetting('crop', 'image_field', $form_state->getValue('image_field'));
104 * Implements hook_ENTITY_TYPE_delete().
106 * Deletes orphaned crops when a file is deleted.
108 function crop_file_delete(FileInterface $file) {
109 // Get all crops for the file being deleted.
110 $crops = \Drupal::entityTypeManager()
112 ->loadByProperties(['uri' => $file->getFileUri()]);
114 foreach ($crops as $crop) {
120 * Implements hook_file_url_alter().
122 function crop_file_url_alter(&$uri) {
123 // Process only files that are stored in "styles" directory.
124 if (strpos($uri, '/styles/') !== FALSE && preg_match('/\/styles\/(.*?)\/(.*?)\/(.+)/', $uri, $match)) {
125 // Match image style, schema, file subdirectory and file name.
126 // Get the image style ID.
127 $image_style = $match[1];
128 // Get the file path without query parameter.
129 $parsed_uri = UrlHelper::parse($match[3]);
130 // Get the file URI using parsed schema and file path.
131 $file_uri = $match[2] . '://' . $parsed_uri['path'];
133 // Prevent double hashing, if there is a hash argument already, do not add
135 if (!empty($parsed_uri['query']['h'])) {
139 /** @var \Drupal\image\Entity\ImageStyle $image_style */
140 if (!$image_style = ImageStyle::load($image_style)) {
144 if ($crop = Crop::getCropFromImageStyle($file_uri, $image_style)) {
145 // Found a crop for this image, append a hash of it to the URL,
146 // so that browsers reload the image and CDNs and proxies can be bypassed.
147 $shortened_hash = substr(md5(implode($crop->position()) . implode($crop->anchor())), 0, 8);
149 // If the URI has a schema and that is not http, https or data, convert
150 // the URI to the external URL. Otherwise the appended query argument
152 // @see file_create_url()
153 $scheme = \Drupal::service('file_system')->uriScheme($uri);
154 if ($scheme && !in_array($scheme, ['http', 'https', 'data'])) {
155 if ($wrapper = \Drupal::service('stream_wrapper_manager')->getViaUri($uri)) {
156 $uri = $wrapper->getExternalUrl();
160 // Append either with a ? or a & if there are existing query arguments.
161 if (strpos($uri, '?') === FALSE) {
162 $uri .= '?h=' . $shortened_hash;
165 $uri .= '&h=' . $shortened_hash;