image.field.inc 19.1 KB
Newer Older
1
2
3
4
5
6
7
8
<?php

/**
 * @file
 * Implement an image field, based on the file module's file field.
 */

/**
9
 * Implements hook_field_info().
10
11
12
13
14
15
16
 */
function image_field_info() {
  return array(
    'image' => array(
      'label' => t('Image'),
      'description' => t('This field stores the ID of an image file as an integer value.'),
      'settings' => array(
17
        'uri_scheme' => variable_get('file_default_scheme', 'public'),
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
        'default_image' => 0,
      ),
      'instance_settings' => array(
        'file_extensions' => 'png gif jpg jpeg',
        'file_directory' => '',
        'max_filesize' => '',
        'alt_field' => 0,
        'title_field' => 0,
        'max_resolution' => '',
        'min_resolution' => '',
      ),
      'default_widget' => 'image_image',
      'default_formatter' => 'image',
    ),
  );
}

/**
36
 * Implements hook_field_settings_form().
37
38
39
40
41
42
 */
function image_field_settings_form($field, $instance) {
  $defaults = field_info_field_settings($field['type']);
  $settings = array_merge($defaults, $field['settings']);

  $scheme_options = array();
43
44
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $stream_wrapper) {
    $scheme_options[$scheme] = $stream_wrapper['name'];
45
46
47
48
49
50
51
52
53
  }
  $form['uri_scheme'] = array(
    '#type' => 'radios',
    '#title' => t('Upload destination'),
    '#options' => $scheme_options,
    '#default_value' => $settings['uri_scheme'],
    '#description' => t('Select where the final files should be stored. Private file storage has significantly more overhead than public files, but allows restricted access to files within this field.'),
  );

54
55
56
  // When the user sets the scheme on the UI, even for the first time, it's
  // updating a field because fields are created on the "Manage fields"
  // page. So image_field_update_field() can handle this change.
57
58
59
60
61
  $form['default_image'] = array(
    '#title' => t('Default image'),
    '#type' => 'managed_file',
    '#description' => t('If no image is uploaded, this image will be shown on display.'),
    '#default_value' => $field['settings']['default_image'],
62
    '#upload_location' => $settings['uri_scheme'] . '://default_images/',
63
64
65
66
67
68
  );

  return $form;
}

/**
69
 * Implements hook_field_instance_settings_form().
70
71
72
73
74
75
76
77
78
79
 */
function image_field_instance_settings_form($field, $instance) {
  $settings = $instance['settings'];

  // Use the file field instance settings form as a basis.
  $form = file_field_instance_settings_form($field, $instance);

  // Add maximum and minimum resolution settings.
  $max_resolution = explode('x', $settings['max_resolution']) + array('', '');
  $form['max_resolution'] = array(
80
    '#type' => 'item',
81
82
83
    '#title' => t('Maximum image resolution'),
    '#element_validate' => array('_image_field_resolution_validate'),
    '#weight' => 4.1,
84
85
    '#field_prefix' => '<div class="container-inline">',
    '#field_suffix' => '</div>',
86
87
88
89
    '#description' => t('The maximum allowed image size expressed as WIDTHxHEIGHT (e.g. 640x480). Leave blank for no restriction. If a larger image is uploaded, it will be resized to reflect the given width and height. Resizing images on upload will cause the loss of <a href="http://en.wikipedia.org/wiki/Exchangeable_image_file_format">EXIF data</a> in the image.'),
  );
  $form['max_resolution']['x'] = array(
    '#type' => 'textfield',
90
91
    '#title' => t('Maximum width'),
    '#title_display' => 'invisible',
92
93
94
95
96
97
98
    '#default_value' => $max_resolution[0],
    '#size' => 5,
    '#maxlength' => 5,
    '#field_suffix' => ' x ',
  );
  $form['max_resolution']['y'] = array(
    '#type' => 'textfield',
99
100
    '#title' => t('Maximum height'),
    '#title_display' => 'invisible',
101
102
103
104
105
106
107
108
    '#default_value' => $max_resolution[1],
    '#size' => 5,
    '#maxlength' => 5,
    '#field_suffix' => ' ' . t('pixels'),
  );

  $min_resolution = explode('x', $settings['min_resolution']) + array('', '');
  $form['min_resolution'] = array(
109
    '#type' => 'item',
110
111
112
    '#title' => t('Minimum image resolution'),
    '#element_validate' => array('_image_field_resolution_validate'),
    '#weight' => 4.2,
113
114
    '#field_prefix' => '<div class="container-inline">',
    '#field_suffix' => '</div>',
115
116
117
118
    '#description' => t('The minimum allowed image size expressed as WIDTHxHEIGHT (e.g. 640x480). Leave blank for no restriction. If a smaller image is uploaded, it will be rejected.'),
  );
  $form['min_resolution']['x'] = array(
    '#type' => 'textfield',
119
120
    '#title' => t('Minimum width'),
    '#title_display' => 'invisible',
121
122
123
124
125
126
127
    '#default_value' => $min_resolution[0],
    '#size' => 5,
    '#maxlength' => 5,
    '#field_suffix' => ' x ',
  );
  $form['min_resolution']['y'] = array(
    '#type' => 'textfield',
128
129
    '#title' => t('Minimum height'),
    '#title_display' => 'invisible',
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
    '#default_value' => $min_resolution[1],
    '#size' => 5,
    '#maxlength' => 5,
    '#field_suffix' => ' ' . t('pixels'),
  );

  // Remove the description option.
  unset($form['description_field']);

  // Add title and alt configuration options.
  $form['alt_field'] = array(
    '#type' => 'checkbox',
    '#title' => t('Enable <em>Alt</em> field'),
    '#default_value' => $settings['alt_field'],
    '#description' => t('The alt attribute may be used by search engines, screen readers, and when the image cannot be loaded.'),
    '#weight' => 10,
  );
  $form['title_field'] = array(
    '#type' => 'checkbox',
    '#title' => t('Enable <em>Title</em> field'),
    '#default_value' => $settings['title_field'],
    '#description' => t('The title attribute is used as a tooltip when the mouse hovers over the image.'),
    '#weight' => 11,
  );

  return $form;
}

/**
 * Element validate function for resolution fields.
 */
function _image_field_resolution_validate($element, &$form_state) {
  if (!empty($element['x']['#value']) || !empty($element['y']['#value'])) {
    foreach (array('x', 'y') as $dimension) {
      $value = $element[$dimension]['#value'];
      if (!is_numeric($value)) {
        form_error($element[$dimension], t('Height and width values must be numeric.'));
        return;
      }
      if (intval($value) == 0) {
        form_error($element[$dimension], t('Both a height and width value must be specified in the !name field.', array('!name' => $element['#title'])));
        return;
      }
    }
    form_set_value($element, intval($element['x']['#value']) . 'x' . intval($element['y']['#value']), $form_state);
  }
  else {
    form_set_value($element, '', $form_state);
  }
}

/**
182
 * Implements hook_field_load().
183
 */
184
185
function image_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) {
  file_field_load($entity_type, $entities, $field, $instances, $langcode, $items, $age);
186
187
188
}

/**
189
 * Implements hook_field_prepare_view().
190
 */
191
function image_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) {
192
  // If there are no files specified at all, use the default.
193
  foreach ($entities as $id => $entity) {
194
195
196
197
198
199
200
201
    if (empty($items[$id]) && $field['settings']['default_image']) {
      if ($file = file_load($field['settings']['default_image'])) {
        $items[$id][0] = (array) $file + array(
          'is_default' => TRUE,
          'alt' => '',
          'title' => '',
        );
      }
202
203
204
205
    }
  }
}

206
207
208
/**
 * Implements hook_field_presave().
 */
209
210
function image_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
  file_field_presave($entity_type, $entity, $field, $instance, $langcode, $items);
211
212
213
214
215
216
217
218
219
220
221
222

  // Determine the dimensions if necessary.
  foreach ($items as &$item) {
    if (!isset($item['width']) || !isset($item['height'])) {
      $info = image_get_info(file_load($item['fid'])->uri);

      if (is_array($info)) {
        $item['width'] = $info['width'];
        $item['height'] = $info['height'];
      }
    }
  }
223
224
}

225
226
227
228
229
230
231
/**
 * Implements hook_field_insert().
 */
function image_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
  file_field_insert($entity_type, $entity, $field, $instance, $langcode, $items);
}

232
/**
233
 * Implements hook_field_update().
234
 */
235
236
function image_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
  file_field_update($entity_type, $entity, $field, $instance, $langcode, $items);
237
238
239
}

/**
240
 * Implements hook_field_delete().
241
 */
242
243
function image_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
  file_field_delete($entity_type, $entity, $field, $instance, $langcode, $items);
244
245
246
}

/**
247
 * Implements hook_field_delete_revision().
248
 */
249
250
function image_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, &$items) {
  file_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, $items);
251
252
253
}

/**
254
 * Implements hook_field_is_empty().
255
256
257
258
259
260
 */
function image_field_is_empty($item, $field) {
  return file_field_is_empty($item, $field);
}

/**
261
 * Implements hook_field_widget_info().
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
 */
function image_field_widget_info() {
  return array(
    'image_image' => array(
      'label' => t('Image'),
      'field types' => array('image'),
      'settings' => array(
        'progress_indicator' => 'throbber',
        'preview_image_style' => 'thumbnail',
      ),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_CUSTOM,
        'default value' => FIELD_BEHAVIOR_NONE,
      ),
    ),
  );
}

/**
281
 * Implements hook_field_widget_settings_form().
282
283
284
285
286
287
288
289
290
291
292
 */
function image_field_widget_settings_form($field, $instance) {
  $widget = $instance['widget'];
  $settings = $widget['settings'];

  // Use the file widget settings form.
  $form = file_field_widget_settings_form($field, $instance);

  $form['preview_image_style'] = array(
    '#title' => t('Preview image style'),
    '#type' => 'select',
293
    '#options' => image_style_options(FALSE),
294
    '#empty_option' => '<' . t('no preview') . '>',
295
296
297
298
299
300
301
302
303
    '#default_value' => $settings['preview_image_style'],
    '#description' => t('The preview image will be shown while editing the content.'),
    '#weight' => 15,
  );

  return $form;
}

/**
304
 * Implements hook_field_widget_form().
305
 */
306
function image_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
307
308
309
310

  // Add display_field setting to field because file_field_widget_form() assumes it is set.
  $field['settings']['display_field'] = 0;

311
  $elements = file_field_widget_form($form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
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
338
339
340
341
342
343
344
345
346
347
348
349
350
  $settings = $instance['settings'];

  foreach (element_children($elements) as $delta) {
    // Add upload resolution validation.
    if ($settings['max_resolution'] || $settings['min_resolution']) {
      $elements[$delta]['#upload_validators']['file_validate_image_resolution'] = array($settings['max_resolution'], $settings['min_resolution']);
    }

    // If not using custom extension validation, ensure this is an image.
    $supported_extensions = array('png', 'gif', 'jpg', 'jpeg');
    $extensions = isset($elements[$delta]['#upload_validators']['file_validate_extensions'][0]) ? $elements[$delta]['#upload_validators']['file_validate_extensions'][0] : implode(' ', $supported_extensions);
    $extensions = array_intersect(explode(' ', $extensions), $supported_extensions);
    $elements[$delta]['#upload_validators']['file_validate_extensions'][0] = implode(' ', $extensions);

    // Add all extra functionality provided by the image widget.
    $elements[$delta]['#process'][] = 'image_field_widget_process';
  }

  if ($field['cardinality'] == 1) {
    // If there's only one field, return it as delta 0.
    if (empty($elements[0]['#default_value']['fid'])) {
      $elements[0]['#description'] = theme('file_upload_help', array('description' => $instance['description'], 'upload_validators' => $elements[0]['#upload_validators']));
    }
  }
  else {
    $elements['#file_upload_description'] = theme('file_upload_help', array('upload_validators' => $elements[0]['#upload_validators']));
  }
  return $elements;
}

/**
 * An element #process callback for the image_image field type.
 *
 * Expands the image_image type to include the alt and title fields.
 */
function image_field_widget_process($element, &$form_state, $form) {
  $item = $element['#value'];
  $item['fid'] = $element['fid']['#value'];

351
  $instance = field_widget_instance($element, $form_state);
352

353
354
355
356
357
358
359
360
  $settings = $instance['settings'];
  $widget_settings = $instance['widget']['settings'];

  $element['#theme'] = 'image_widget';
  $element['#attached']['css'][] = drupal_get_path('module', 'image') . '/image.css';

  // Add the image preview.
  if ($element['#file'] && $widget_settings['preview_image_style']) {
361
362
    $variables = array(
      'style_name' => $widget_settings['preview_image_style'],
363
      'uri' => $element['#file']->uri,
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
    );

    // Determine image dimensions.
    if (isset($element['#value']['width']) && isset($element['#value']['height'])) {
      $variables['width'] = $element['#value']['width'];
      $variables['height'] = $element['#value']['height'];
    }
    else {
      $info = image_get_info($element['#file']->uri);

      if (is_array($info)) {
        $variables['width'] = $info['width'];
        $variables['height'] = $info['height'];
      }
      else {
        $variables['width'] = $variables['height'] = NULL;
      }
    }

383
384
    $element['preview'] = array(
      '#type' => 'markup',
385
386
387
388
389
390
391
392
393
394
395
396
      '#markup' => theme('image_style', $variables),
    );

    // Store the dimensions in the form so the file doesn't have to be accessed
    // again. This is important for remote files.
    $element['width'] = array(
      '#type' => 'hidden',
      '#value' => $variables['width'],
    );
    $element['height'] = array(
      '#type' => 'hidden',
      '#value' => $variables['height'],
397
398
399
400
401
402
403
404
405
    );
  }

  // Add the additional alt and title fields.
  $element['alt'] = array(
    '#title' => t('Alternate text'),
    '#type' => 'textfield',
    '#default_value' => isset($item['alt']) ? $item['alt'] : '',
    '#description' => t('This text will be used by screen readers, search engines, or when the image cannot be loaded.'),
406
    // @see http://www.gawds.org/show.php?contentid=28
407
    '#maxlength' => 512,
408
409
410
411
412
413
414
415
    '#weight' => -2,
    '#access' => (bool) $item['fid'] && $settings['alt_field'],
  );
  $element['title'] = array(
    '#type' => 'textfield',
    '#title' => t('Title'),
    '#default_value' => isset($item['title']) ? $item['title'] : '',
    '#description' => t('The title is used as a tool tip when the user hovers the mouse over the image.'),
416
    '#maxlength' => 1024,
417
418
419
420
421
422
423
424
    '#weight' => -1,
    '#access' => (bool) $item['fid'] && $settings['title_field'],
  );

  return $element;
}

/**
425
426
427
428
429
430
431
 * Returns HTML for an image field widget.
 *
 * @param $variables
 *   An associative array containing:
 *   - element: A render element representing the image field widget.
 *
 * @ingroup themeable
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
 */
function theme_image_widget($variables) {
  $element = $variables['element'];
  $output = '';
  $output .= '<div class="image-widget form-managed-file clearfix">';

  if (isset($element['preview'])) {
    $output .= '<div class="image-preview">';
    $output .= drupal_render($element['preview']);
    $output .= '</div>';
  }

  $output .= '<div class="image-widget-data">';
  if ($element['fid']['#value'] != 0) {
    $element['filename']['#markup'] .= ' <span class="file-size">(' . format_size($element['#file']->filesize) . ')</span> ';
  }
  $output .= drupal_render_children($element);
  $output .= '</div>';
  $output .= '</div>';

  return $output;
}

/**
456
 * Implements hook_field_formatter_info().
457
458
459
460
461
462
 */
function image_field_formatter_info() {
  $formatters = array(
    'image' => array(
      'label' => t('Image'),
      'field types' => array('image'),
463
      'settings' => array('image_style' => '', 'image_link' => ''),
464
465
466
    ),
  );

467
468
469
470
471
472
473
474
475
476
  return $formatters;
}

/**
 * Implements hook_field_formatter_settings_form().
 */
function image_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];

477
  $image_styles = image_style_options(FALSE);
478
  $element['image_style'] = array(
479
480
481
    '#title' => t('Image style'),
    '#type' => 'select',
    '#default_value' => $settings['image_style'],
482
    '#empty_option' => t('None (original image)'),
483
484
485
486
487
488
489
    '#options' => $image_styles,
  );

  $link_types = array(
    'content' => t('Content'),
    'file' => t('File'),
  );
490
  $element['image_link'] = array(
491
492
493
    '#title' => t('Link image to'),
    '#type' => 'select',
    '#default_value' => $settings['image_link'],
494
    '#empty_option' => t('Nothing'),
495
496
497
    '#options' => $link_types,
  );

498
  return $element;
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
}

/**
 * Implements hook_field_formatter_settings_summary().
 */
function image_field_formatter_settings_summary($field, $instance, $view_mode) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];

  $summary = array();

  $image_styles = image_style_options(FALSE);
  // Unset possible 'No defined styles' option.
  unset($image_styles['']);
  // Styles could be lost because of enabled/disabled modules that defines
  // their styles in code.
  if (isset($image_styles[$settings['image_style']])) {
    $summary[] = t('Image style: @style', array('@style' => $image_styles[$settings['image_style']]));
  }
  else {
    $summary[] = t('Original image');
520
521
  }

522
523
524
525
526
527
528
529
530
531
  $link_types = array(
    'content' => t('Linked to content'),
    'file' => t('Linked to file'),
  );
  // Display this setting only if image is linked.
  if (isset($link_types[$settings['image_link']])) {
    $summary[] = $link_types[$settings['image_link']];
  }

  return implode('<br />', $summary);
532
533
534
}

/**
535
 * Implements hook_field_formatter_view().
536
 */
537
function image_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
538
  $element = array();
539

540
  // Check if the formatter involves a link.
541
  if ($display['settings']['image_link'] == 'content') {
542
    $uri = entity_uri($entity_type, $entity);
543
  }
544
  elseif ($display['settings']['image_link'] == 'file') {
545
    $link_file = TRUE;
546
547
  }

548
549
  foreach ($items as $delta => $item) {
    if (isset($link_file)) {
550
551
552
553
      $uri = array(
        'path' => file_create_url($item['uri']),
        'options' => array(),
      );
554
555
556
557
    }
    $element[$delta] = array(
      '#theme' => 'image_formatter',
      '#item' => $item,
558
      '#image_style' => $display['settings']['image_style'],
559
      '#path' => isset($uri) ? $uri : '',
560
561
562
    );
  }

563
  return $element;
564
565
566
}

/**
567
568
569
570
571
572
 * Returns HTML for an image field formatter.
 *
 * @param $variables
 *   An associative array containing:
 *   - item: An array of image data.
 *   - image_style: An optional image style.
573
 *   - path: An optional array containing the link 'path' and link 'options'.
574
575
 *
 * @ingroup themeable
576
 */
577
578
function theme_image_formatter($variables) {
  $item = $variables['item'];
579

580
  // Do not output an empty 'title' attribute.
581
582
  if (drupal_strlen($item['title']) == 0) {
    unset($item['title']);
583
  }
584
585

  if ($variables['image_style']) {
586
587
    $item['style_name'] = $variables['image_style'];
    $output = theme('image_style', $item);
588
589
  }
  else {
590
    $output = theme('image', $item);
591
592
  }

593
  if (!empty($variables['path']['path'])) {
594
595
596
597
598
    $path = $variables['path']['path'];
    $options = $variables['path']['options'];
    // When displaying an image inside a link, the html option must be TRUE.
    $options['html'] = TRUE;
    $output = l($output, $path, $options);
599
600
601
  }

  return $output;
602
}