picture.module 13.3 KB
Newer Older
1
2
3
4
5
6
7
<?php

/**
 * @file
 * Picture display formatter for image fields.
 */

8
use Drupal\picture\Entity\PictureMapping;
9
10
11
12
13
14
15
16
17
18
use \Drupal\Core\Template\Attribute;

/**
 * Implements hook_help().
 */
function picture_help($path, $arg) {
  $output = '';
  switch ($path) {
    case 'admin/help#picture':
      $output .= '<h3>' . t('About') . '</h3>';
19
      $output .= '<p>' . t('The Picture module provides an image formatter and breakpoint mappings to output responsive images using the HTML5 picture tag. For more information, see the <a href="!picture">online documentation for the Picture module</a>.', array( '!picture' => 'https://drupal.org/documentation/modules/picture')) . '</p>';
20
21
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
22
23
24
25
      $output .= '<dt>' . t('Defining picture mappings') . '</dt>';
      $output .= '<dd>' . t('By creating picture mappings you define the image styles that are being used to output images at certain breakpoints. On the <a href="!picture_mapping">Picture mappings</a> page, click <em>Add picture mapping</em> to create a new mapping. First chose a label and a breakpoint group and click Save. After that you can choose the image styles that will be used for each breakpoint. Image styles can be defined on the <a href="!image_styles">Image styles page</a> that is provided by the <a href="!image_help">Image module</a>. Breakpoints are defined in the configuration files of the theme. See the <a href="!breakpoint_help">help page of the Breakpoint module</a> for more information.', array('!picture_mapping' => \Drupal::url('picture.mapping_page'), '!image_styles' => \Drupal::url('image.style_list'),'!image_help' => \Drupal::url('help.page', array('name' => 'image')), '!breakpoint_help' => \Drupal::url('help.page', array('name' => 'breakpoint')))) . '</dd>';
      $output .= '<dt>' . t('Using picture mappings in image fields') . '</dt>';
      $output .= '<dd>' . t('After defining picture mappings, you can use them in the display settings for your image fields, so that the site displays responsive images using the HTML5 picture tag. Open the Manage display page for the entity type (content type, taxonomy vocabulary, etc.) that the Image field is attached to. Choose the format <em>Picture</em>, click the Edit icon and select one of the picture mappings that you have created. For general information on how to manage fields and their display see the <a href="!field_ui">help page of the Field UI module</a>. For information about entities see the <a href="!entity_help">help page of the Entity module</a>.', array('!field_ui' => \Drupal::url('help.page', array('name' => 'field_ui')),'!entity_help' => \Drupal::url('help.page', array('name' => 'entity')))) . '</dd>';
26
      $output .= '</dl>';
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
      break;
    case 'admin/config/media/picturemapping':
      $output .= '<p>' . t('A picture mapping associates an image style with each breakpoint defined by your theme.') . '</p>';
      break;

  }
  return $output;
}

/**
 * Implements hook_permission().
 */
function picture_permission() {
  return array(
    'administer pictures' => array(
      'title' => t('Administer Pictures'),
      'description' => t('Administer Pictures'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function picture_menu() {
  $items = array();

  $items['admin/config/media/picturemapping'] = array(
    'title' => 'Picture Mappings',
    'description' => 'Manage picture mappings',
    'weight' => 10,
58
    'route_name' => 'picture.mapping_page',
59
  );
60
  $items['admin/config/media/picturemapping/%picture_mapping'] = array(
61
    'title' => 'Edit picture mapping',
62
    'route_name' => 'picture.mapping_page_edit',
63
64
65
  );
  $items['admin/config/media/picturemapping/%picture_mapping/duplicate'] = array(
    'title' => 'Duplicate picture mapping',
66
    'route_name' => 'picture.mapping_page_duplicate',
67
68
69
70
71
  );

  return $items;
}

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/**
 * Implements hook_menu_link_defaults().
 */
function picture_menu_link_defaults() {
  $links['picture.admin.config.picturemapping'] = array(
    'link_title' => 'Picture Mappings',
    'description' => 'Manage picture mappings',
    'weight' => 10,
    'route_name' => 'picture.mapping_page',
    'parent' => 'system.admin.config.media',
  );

  return $links;
}

87
88
89
90
91
92
93
/**
 * Implements hook_library_info().
 */
function picture_library_info() {
  $libraries['picturefill'] = array(
    'title' => t('Picturefill'),
    'website' => 'http://drupal.org/node/1775530',
94
    'version' => \Drupal::VERSION,
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
    'js' => array(
      drupal_get_path('module', 'picture') . '/picturefill/picturefill.js' => array('type' => 'file', 'weight' => -10, 'group' => JS_DEFAULT),
    ),
    'dependencies' => array(
      array('system', 'matchmedia'),
    ),
  );
  return $libraries;
}

/**
 * Load one picture by its identifier.
 *
 * @param int $id
 *   The id of the picture mapping to load.
 *
111
112
 * @return \Drupal\picture\Picture
 *   The entity object, or NULL if there is no entity with the given id.
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
 *
 * @todo Needed for menu_callback
 *
 * @see http://drupal.org/node/1798214
 *
 */
function picture_mapping_load($id) {
  return entity_load('picture_mapping', $id);
}

/**
 * Implements hook_theme().
 */
function picture_theme() {
  return array(
    'picture' => array(
      'variables' => array(
        'style_name' => NULL,
131
        'uri' => NULL,
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
        'width' => NULL,
        'height' => NULL,
        'alt' => '',
        'title' => NULL,
        'attributes' => array(),
        'breakpoints' => array(),
      ),
    ),
    'picture_formatter' => array(
      'variables' => array(
        'item' => NULL,
        'path' => NULL,
        'image_style' => NULL,
        'breakpoints' => array(),
      ),
    ),
    'picture_source' => array(
      'variables' => array(
        'src' => NULL,
        'srcset' => NULL,
152
        'dimensions' => NULL,
153
154
155
156
157
158
159
160
161
162
163
        'media' => NULL,
      ),
    ),
  );
}

/**
 * Returns HTML for a picture field formatter.
 *
 * @param array $variables
 *   An associative array containing:
164
 *   - item: An ImageItem object.
165
166
167
168
169
170
171
 *   - image_style: An optional image style.
 *   - path: An optional array containing the link 'path' and link 'options'.
 *   - breakpoints: An array containing breakpoints.
 *
 * @ingroup themeable
 */
function theme_picture_formatter($variables) {
172
  $item = $variables['item'];
173
  if (!isset($variables['breakpoints']) || empty($variables['breakpoints'])) {
174
175
    $image_formatter = array(
      '#theme' => 'image_formatter',
176
      '#item' => $item,
177
178
179
180
      '#image_style' => $variables['image_style'],
      '#path' => $variables['path'],
    );
    return drupal_render($image_formatter);
181
182
  }

183
184
  $picture = array(
    '#theme' => 'picture',
185
186
    '#width' => $item->width,
    '#height' => $item->height,
187
188
189
    '#style_name' => $variables['image_style'],
    '#breakpoints' => $variables['breakpoints'],
  );
190
191
  if (isset($item->uri)) {
    $picture['#uri'] = $item->uri;
192
  }
193
194
195
  elseif ($entity = $item->entity) {
    $picture['#uri'] = $entity->getFileUri();
    $picture['#entity'] = $entity;
196
  }
197
198
199
  $picture['#alt'] = $item->alt;
  if (drupal_strlen($item->title) != 0) {
    $picture['#title'] = $item->title;
200
  }
201
  // @todo Add support for route names.
202
203
204
205
  if (isset($variables['path']['path'])) {
    $path = $variables['path']['path'];
    $options = isset($variables['path']['options']) ? $variables['path']['options'] : array();
    $options['html'] = TRUE;
206
    return l($picture, $path, $options);
207
  }
208
209

  return drupal_render($picture);
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
}

/**
 * Returns HTML for a picture.
 *
 * @param $variables
 *   An associative array containing:
 *   - uri: Either the path of the image file (relative to base_path()) or a
 *     full URL.
 *   - width: The width of the image (if known).
 *   - height: The height of the image (if known).
 *   - alt: The alternative text for text-based browsers.
 *   - breakpoints: An array containing breakpoints.
 *
 * @ingroup themeable
 */
function theme_picture($variables) {
  // Make sure that width and height are proper values
  // If they exists we'll output them
  // @see http://www.w3.org/community/respimg/2012/06/18/florians-compromise/
  if (isset($variables['width']) && empty($variables['width'])) {
    unset($variables['width']);
    unset($variables['height']);
  }
  elseif (isset($variables['height']) && empty($variables['height'])) {
    unset($variables['width']);
    unset($variables['height']);
  }

  $sources = array();
  $output = array();

  // Fallback image, output as source with media query.
  $sources[] = array(
244
    'src' => entity_load('image_style', $variables['style_name'])->buildUrl($variables['uri']),
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
    'dimensions' => picture_get_image_dimensions($variables),
  );

  // All breakpoints and multipliers.
  foreach ($variables['breakpoints'] as $breakpoint_name => $multipliers) {
    $breakpoint = breakpoint_load($breakpoint_name);
    if ($breakpoint) {
      $new_sources = array();
      foreach ($multipliers as $multiplier => $image_style) {
        $new_source = $variables;
        $new_source['style_name'] = $image_style;
        $new_source['#multiplier'] = $multiplier;
        $new_sources[] = $new_source;
      }

      // Only one image, use src.
      if (count($new_sources) == 1) {
        $sources[] = array(
263
          'src' => entity_load('image_style', $new_sources[0]['style_name'])->buildUrl($new_sources[0]['uri']),
264
265
266
267
268
          'dimensions' => picture_get_image_dimensions($new_sources[0]),
          'media' => $breakpoint->mediaQuery,
        );
      }
      else {
269
        // Multiple images, use srcset.
270
271
        $srcset = array();
        foreach ($new_sources as $new_source) {
272
          $srcset[] = entity_load('image_style', $new_source['style_name'])->buildUrl($new_source['uri']) . ' ' . $new_source['#multiplier'];
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
        }
        $sources[] = array(
          'srcset' => implode(', ', $srcset),
          'dimensions' => picture_get_image_dimensions($new_sources[0]),
          'media' => $breakpoint->mediaQuery,
        );
      }
    }
  }

  if (!empty($sources)) {
    $attributes = array();
    foreach (array('alt', 'title') as $key) {
      if (isset($variables[$key])) {
        $attributes[$key] = $variables[$key];
      }
    }
    $output[] = '<picture' . new Attribute($attributes) . '>';

292
    // Add source tags to the output.
293
    foreach ($sources as $source) {
294
295
296
297
298
299
300
301
302
303
304
305
      $picture_source = array(
        '#theme' => 'picture_source',
        '#src' => $source['src'],
        '#dimensions' => $source['dimensions'],
      );
      if (isset($source['media'])) {
        $picture_source['#media'] = $source['media'];
      }
      if (isset($source['srcset'])) {
        $picture_source['#srcset'] = $source['srcset'];
      }
      $output[] = drupal_render($picture_source);
306
307
    }

308
    // Output the fallback image.
309
310
311
312
313
314
315
316
317
318
319
320
    $image_style = array(
      '#theme' => 'image_style',
      '#style_name' => $variables['style_name'],
      '#width' => $variables['width'],
      '#height' => $variables['height'],
    );
    foreach (array('uri', 'alt', 'title', 'attributes') as $key) {
      if (isset($variables[$key])) {
        $image_style["#$key"] = $variables[$key];
      }
    }
    $output[] = '  <noscript>' . drupal_render($image_style) . '</noscript>';
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
    $output[] = '</picture>';
    return implode("\n", $output);
  }
}

/**
 * Returns HTML for a source tag.
 *
 * @param type $variables
 *   An associative array containing:
 *   - media: The media query to use.
 *   - srcset: The srcset containing the the path of the image file or a full
 *     URL and optionally multipliers.
 *   - src: Either the path of the image file (relative to base_path()) or a
 *     full URL.
 *   - dimensions: The width and height of the image (if known).
 *
 * @ingroup themeable
 */
function theme_picture_source($variables) {
  $output = array();
  if (isset($variables['media']) && !empty($variables['media'])) {
    if (!isset($variables['srcset'])) {
      $output[] = '<!-- <source media="' . $variables['media'] . '" src="' . $variables['src'] . '" ' . new Attribute($variables['dimensions']) . ' /> -->';
      $output[] = '<source media="' . $variables['media'] . '" src="' . $variables['src'] . '" ' . new Attribute($variables['dimensions']) . '/>';
    }
    elseif (!isset($variables['src'])) {
      $output[] = '<!-- <source media="' . $variables['media'] . '" srcset="' . $variables['srcset'] . '" ' . new Attribute($variables['dimensions']) . ' /> -->';
      $output[] = '<source media="' . $variables['media'] . '" srcset="' . $variables['srcset'] . '" ' . new Attribute($variables['dimensions']) . ' />';
    }
  }
  else {
    $output[] = '<!-- <source src="' . $variables['src'] . '" ' . new Attribute($variables['dimensions']) . ' /> -->';
    $output[] = '<source src="' . $variables['src'] . '" ' . new Attribute($variables['dimensions']) . '/>';
  }
  return implode("\n", $output);
}

/**
 * Determines the dimensions of an image.
 *
 * @param $variables
 *   An associative array containing:
 *   - style_name: The name of the style to be used to alter the original image.
 *   - width: The width of the source image (if known).
 *   - height: The height of the source image (if known).
 *
 * @return array
 *   Dimensions to be modified - an array with components width and height, in
 *   pixels.
 */
function picture_get_image_dimensions($variables) {
  // Determine the dimensions of the styled image.
  $dimensions = array(
    'width' => $variables['width'],
    'height' => $variables['height'],
  );

379
  entity_load('image_style', $variables['style_name'])->transformDimensions($dimensions);
380
381
382

  return $dimensions;
}