-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinline_responsive_images.module
337 lines (301 loc) · 14.1 KB
/
inline_responsive_images.module
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
<?php
/**
* @file
* Form alter and other hooks.
*/
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\file\Entity\File;
use Drupal\image\Entity\ImageStyle;
/**
* Implements hook_help().
*/
function inline_responsive_images_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.inline_responsive_images':
$output = '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('Inserting an image in the text editor dialog today allows the user to fiddle with image dimensions. It does not even have aspect ratio locking.') . '</p>';
$output .= '<p>' . t('Its not great for the authoring experience nor for structured content reasons that users are defining the specific dimensions of every single image they insert. It wouldd be much better to allow them to choose from image styles — just like we do for image fields.') . '</p>';
$output .= '<p>' . t('This module lets users select a responsive style OR a image style to place images in the content.') . '</p>';
$output .= '<h4>' . t('Configuration') . '</h4>';
$output .= '<ul>';
$output .= '<li>' . t("Enable the text format filter <em>Display responsive images</em> or <em>Display image styles</em> and select the images styles/responsive styles that you want to be available to the user.") . '</li>';
$output .= '<li>' . t('Create new content. In the editor, click on the image icon in the toolbar. A popup will open where the user can upload an image and assign an image style or responsive style by selecting style from the dropdown menu.') . '</li>';
$output .= '</ul>';
return $output;
}
}
/**
* Implements hook_form_FORM_ID_alter() for EditorImageDialog.
*
* Alters the image dialog form for text editor, to allow the user to select an
* image style.
*
* @see \Drupal\editor\Form\EditorImageDialog::buildForm()
*/
function inline_responsive_images_form_editor_image_dialog_alter(&$form, FormStateInterface $form_state) {
$editor = $form_state->getBuildInfo()['args'][0];
$filter_format = $editor->getFilterFormat();
$filters = $filter_format->filters()->getAll();
$image_element = $form_state->getStorage()['image_element'];
// When image style functionality is available, disallow the user from
// specifying the dimensions manually, only allow image styles to be picked.
if (isset($filters['filter_imagestyle']) && $filters['filter_imagestyle']->status) {
// Hide the default width/height form items.
$form['dimensions']['#access'] = FALSE;
$form['image_style'] = [
'#type' => 'item',
];
$image_options = [];
$image_styles = \Drupal::entityTypeManager()->getStorage('image_style')->loadMultiple();
if ($image_styles && !empty($image_styles)) {
foreach ($image_styles as $image_style_id => $image_style) {
if ($filters['filter_imagestyle']->settings['image_style_' . $image_style_id] ?? FALSE) {
$image_options[$image_style_id] = $image_style->label();
}
}
}
$image_style_fallback_default = !empty($image_options) ? reset($image_options) : NULL;
$form['image_style']['selection'] = [
'#title' => t('Image style'),
'#type' => 'select',
'#default_value' => isset($image_element['data-image-style']) && $image_element['data-image-style'] !== '' ?
$image_element['data-image-style'] : $image_style_fallback_default,
'#options' => $image_options,
'#required' => TRUE,
'#wrapper_attributes' => ['class' => ['container-inline']],
'#attributes' => ['class' => ['container-inline']],
'#parents' => ['attributes', 'data-image-style'],
];
$form['image_style']['preview_toggle'] = [
'#type' => 'checkbox',
'#title' => t('Show preview'),
];
$image_styles = \Drupal::entityTypeManager()->getStorage('image_style')->loadMultiple();
foreach ($image_styles as $id => $image_style) {
$preview_arguments = [
'#theme' => 'image_style_preview',
'#style' => $image_style,
];
$form['image_style']['preview_' . $id] = [
'#type' => 'fieldset',
'#title' => t('Preview of %image-style image style', ['%image-style' => $image_style->label()]),
'#markup' => \Drupal::service('renderer')->render($preview_arguments),
'#states' => [
'visible' => [
':input[name="image_style[preview_toggle]"]' => ['checked' => TRUE],
':input[name="attributes[data-image-style]"]' => ['value' => $id],
],
],
];
}
$form['#attached']['library'][] = 'image/admin';
$form['actions']['save_modal']['#validate'][] = 'inline_responsive_images_form_editor_image_dialog_imagestyle_validate';
}
// When responsive image functionality is available, disallow the user from
// specifying the dimensions manually, and from selecting an image style, only
// allowing a responsive image style to be selected.
if (isset($filters['filter_responsive_image_style']) && $filters['filter_responsive_image_style']->status) {
// Hide the default width/height form items.
$form['dimensions']['#access'] = FALSE;
// Remove the image style selection, if it exists; it does not make sense to
// use FilterImageStyle when already using FilterPictureMapping!
if (isset($form['image_style'])) {
unset($form['image_style']);
// Remove its #validate callback as well.
$validators = &$form['actions']['save_modal']['#validate'];
$index = array_search('inline_responsive_images_form_editor_image_dialog_imagestyle_validate', $validators);
if ($index !== FALSE) {
unset($validators[$index]);
}
}
$form['responsive_image_style'] = [
'#type' => 'item',
];
$responsive_image_options = [];
$responsive_image_styles = \Drupal::entityTypeManager()->getStorage('responsive_image_style')->loadMultiple();
if ($responsive_image_styles && !empty($responsive_image_styles)) {
foreach ($responsive_image_styles as $responsive_image_style_id => $responsive_image_style) {
if ($responsive_image_style->hasImageStyleMappings()) {
if ($filters['filter_responsive_image_style']->settings['responsive_style_' . $responsive_image_style_id] ?? FALSE) {
$responsive_image_options[$responsive_image_style_id] = $responsive_image_style->label();
}
}
}
}
$form['responsive_image_style']['selection'] = [
'#title' => t('Responsive image style'),
'#type' => 'select',
'#default_value' => isset($image_element['data-responsive-image-style']) && $image_element['data-responsive-image-style'] !== '' ?
$image_element['data-responsive-image-style'] : key($responsive_image_options),
'#options' => $responsive_image_options,
'#required' => TRUE,
'#wrapper_attributes' => ['class' => ['container-inline']],
'#attributes' => ['class' => ['container-inline']],
'#parents' => ['attributes', 'data-responsive-image-style'],
];
$form['responsive_image_style']['preview_toggle'] = [
'#type' => 'checkbox',
'#title' => t('Show preview'),
];
foreach ($responsive_image_styles as $responsive_image_style_id => $responsive_image_style) {
// If this responsive image style is available add a preview image.
if (array_key_exists($responsive_image_style_id, $responsive_image_options)) {
$form['responsive_image_style']['preview_' . $responsive_image_style_id] = [
'#type' => 'fieldset',
'#title' => t('Preview of %responsive-image-style responsive image style', ['%responsive-image-style' => $responsive_image_style->label()]),
'#states' => [
'visible' => [
':input[name="responsive_image_style[preview_toggle]"]' => ['checked' => TRUE],
':input[name="attributes[data-responsive-image-style]"]' => ['value' => $responsive_image_style_id],
],
],
];
// If image style hasn't been previewed before, core's sample.png will
// not be present at the derivative path.
$preview_file_original_path = \Drupal::config('image.settings')->get('preview_image');
foreach ($responsive_image_style->getImageStyleIds() as $image_style) {
$image_style = ImageStyle::load($image_style);
if ($image_style) {
$preview_file = $image_style->buildUri($preview_file_original_path);
// Create derivative if necessary.
if (!file_exists($preview_file)) {
$image_style->createDerivative($preview_file_original_path, $preview_file);
}
}
}
$preview_arguments = [
'#theme' => 'responsive_image',
'#uri' => $preview_file_original_path,
'#responsive_image_style_id' => $responsive_image_style_id,
];
$form['responsive_image_style']['preview_' . $responsive_image_style_id] = [
'#type' => 'item',
'#markup' => \Drupal::service('renderer')->render($preview_arguments),
'#states' => [
'visible' => [
':input[name="responsive_image_style[preview_toggle]"]' => ['checked' => TRUE],
':input[name="attributes[data-responsive-image-style]"]' => ['value' => $responsive_image_style_id],
],
],
];
}
}
$form['actions']['save_modal']['#validate'][] = 'inline_responsive_images_form_editor_image_dialog_responsive_validate';
}
}
/**
* Form validation handler for EditorImageDialog.
*
* Ensures the image shown in the text editor matches the chosen image style.
*
* @param array $form
* The form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @see \Drupal\editor\Form\EditorImageDialog::buildForm()
* @see \Drupal\editor\Form\EditorImageDialog::validateForm()
* @see image_form_editor_image_dialog_alter()
*/
function inline_responsive_images_form_editor_image_dialog_imagestyle_validate(array &$form, FormStateInterface &$form_state) {
$attributes = &$form_state->getValue('attributes');
if (!empty($form_state->getValue('fid')[0])) {
$image_style = NULL;
if (isset($attributes['data-image-style'])) {
$image_style = \Drupal::entityTypeManager()->getStorage('image_style')->load($attributes['data-image-style']);
}
if (!$image_style) {
return;
}
$file = File::load($form_state->getValue('fid')[0]);
$uri = $file->getFileUri();
// Set the 'src' attribute to the image style URL. FilterImageStyle will
// look at the 'data-editor-file-uuid' attribute, not the 'src' attribute to
// render the appropriate output.
$attributes['src'] = $image_style->buildUrl($uri);
// Set the 'width' and 'height' attributes to the image style's transformed
// dimensions.
$image = \Drupal::service('image.factory')->get($uri);
if ($image->isValid()) {
$dimensions = [
'width' => $image->getWidth(),
'height' => $image->getHeight(),
];
$image_style->transformDimensions($dimensions, $attributes['src']);
$attributes['width'] = $dimensions['width'];
$attributes['height'] = $dimensions['height'];
}
}
}
/**
* Validates the image dialog for responsive styles.
*
* @param array $form
* The form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
function inline_responsive_images_form_editor_image_dialog_responsive_validate(array &$form, FormStateInterface &$form_state) {
$attributes = &$form_state->getValue('attributes');
if (!empty($form_state->getValue('fid')[0])) {
$responsive_image_style = NULL;
if (isset($attributes['data-responsive-image-style'])) {
$responsive_image_style = \Drupal::entityTypeManager()->getStorage('responsive_image_style')->load($attributes['data-responsive-image-style']);
}
if (!$responsive_image_style) {
return;
}
$file = File::load($form_state->getValue('fid')[0]);
$uri = $file->getFileUri();
// Set up original file information.
$image = \Drupal::service('image.factory')->get($uri);
if ($image->isValid()) {
$dimensions = [
'width' => $image->getWidth(),
'height' => $image->getHeight(),
];
}
else {
// @todo What if the image is not valid?
$dimensions = [
'width' => 1000,
'height' => 1000,
];
}
// Select the first (i.e. smallest) breakpoint and the 1x multiplier. We
// choose to show the image in the editor as if it were being viewed in the
// narrowest viewport, so that when the user starts to edit this content
// again on a mobile device, it will work fine.
$keyed_image_style_mapping = $responsive_image_style->getKeyedImageStyleMappings();
$first_breakpoint = reset($keyed_image_style_mapping);
$image_style_mapping = reset($first_breakpoint);
switch ($image_style_mapping['image_mapping_type']) {
case 'sizes':
// More than one image style can be mapped. Use the smallest one.
$transformed_dimensions = $dimensions;
foreach ($image_style_mapping['image_mapping']['sizes_image_styles'] as $mapped_image_style) {
$new_dimensions = responsive_image_get_image_dimensions($mapped_image_style, $dimensions, $uri);
if (!$transformed_dimensions || $transformed_dimensions['width'] >= $new_dimensions['width']) {
$image_style_name = $mapped_image_style;
$transformed_dimensions = $new_dimensions;
}
}
break;
case 'image_style':
$image_style_name = $image_style_mapping['image_mapping'];
$transformed_dimensions = responsive_image_get_image_dimensions($image_style_name, $dimensions, $uri);
break;
}
// Set the 'src' attribute to the image style URL. FilterImageStyle will
// look at the 'data-editor-file-uuid' attribute, not the 'src' attribute to
// render the appropriate output.
$attributes['src'] = _responsive_image_image_style_url($image_style_name, $uri);
// Set the 'width' and 'height' attributes to the image style's transformed
// dimensions.
if ($image->isValid()) {
$attributes['width'] = $transformed_dimensions['width'];
$attributes['height'] = $transformed_dimensions['height'];
}
}
}