| Drupal | PHP Cross Reference | Content Management Systems |
1 <?php 2 3 /** 4 * @file 5 * Implement an image field, based on the file module's file field. 6 */ 7 8 /** 9 * Implements hook_field_info(). 10 */ 11 function image_field_info() { 12 return array( 13 'image' => array( 14 'label' => t('Image'), 15 'description' => t('This field stores the ID of an image file as an integer value.'), 16 'settings' => array( 17 'uri_scheme' => variable_get('file_default_scheme', 'public'), 18 'default_image' => 0, 19 ), 20 'instance_settings' => array( 21 'file_extensions' => 'png gif jpg jpeg', 22 'file_directory' => '', 23 'max_filesize' => '', 24 'alt_field' => 0, 25 'title_field' => 0, 26 'max_resolution' => '', 27 'min_resolution' => '', 28 'default_image' => 0, 29 ), 30 'default_widget' => 'image_image', 31 'default_formatter' => 'image', 32 ), 33 ); 34 } 35 36 /** 37 * Implements hook_field_settings_form(). 38 */ 39 function image_field_settings_form($field, $instance) { 40 $defaults = field_info_field_settings($field['type']); 41 $settings = array_merge($defaults, $field['settings']); 42 43 $scheme_options = array(); 44 foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $stream_wrapper) { 45 $scheme_options[$scheme] = $stream_wrapper['name']; 46 } 47 $form['uri_scheme'] = array( 48 '#type' => 'radios', 49 '#title' => t('Upload destination'), 50 '#options' => $scheme_options, 51 '#default_value' => $settings['uri_scheme'], 52 '#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.'), 53 ); 54 55 // When the user sets the scheme on the UI, even for the first time, it's 56 // updating a field because fields are created on the "Manage fields" 57 // page. So image_field_update_field() can handle this change. 58 $form['default_image'] = array( 59 '#title' => t('Default image'), 60 '#type' => 'managed_file', 61 '#description' => t('If no image is uploaded, this image will be shown on display.'), 62 '#default_value' => $field['settings']['default_image'], 63 '#upload_location' => $settings['uri_scheme'] . '://default_images/', 64 ); 65 66 return $form; 67 } 68 69 /** 70 * Implements hook_field_instance_settings_form(). 71 */ 72 function image_field_instance_settings_form($field, $instance) { 73 $settings = $instance['settings']; 74 75 // Use the file field instance settings form as a basis. 76 $form = file_field_instance_settings_form($field, $instance); 77 78 // Add maximum and minimum resolution settings. 79 $max_resolution = explode('x', $settings['max_resolution']) + array('', ''); 80 $form['max_resolution'] = array( 81 '#type' => 'item', 82 '#title' => t('Maximum image resolution'), 83 '#element_validate' => array('_image_field_resolution_validate'), 84 '#weight' => 4.1, 85 '#field_prefix' => '<div class="container-inline">', 86 '#field_suffix' => '</div>', 87 '#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.'), 88 ); 89 $form['max_resolution']['x'] = array( 90 '#type' => 'textfield', 91 '#title' => t('Maximum width'), 92 '#title_display' => 'invisible', 93 '#default_value' => $max_resolution[0], 94 '#size' => 5, 95 '#maxlength' => 5, 96 '#field_suffix' => ' x ', 97 ); 98 $form['max_resolution']['y'] = array( 99 '#type' => 'textfield', 100 '#title' => t('Maximum height'), 101 '#title_display' => 'invisible', 102 '#default_value' => $max_resolution[1], 103 '#size' => 5, 104 '#maxlength' => 5, 105 '#field_suffix' => ' ' . t('pixels'), 106 ); 107 108 $min_resolution = explode('x', $settings['min_resolution']) + array('', ''); 109 $form['min_resolution'] = array( 110 '#type' => 'item', 111 '#title' => t('Minimum image resolution'), 112 '#element_validate' => array('_image_field_resolution_validate'), 113 '#weight' => 4.2, 114 '#field_prefix' => '<div class="container-inline">', 115 '#field_suffix' => '</div>', 116 '#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.'), 117 ); 118 $form['min_resolution']['x'] = array( 119 '#type' => 'textfield', 120 '#title' => t('Minimum width'), 121 '#title_display' => 'invisible', 122 '#default_value' => $min_resolution[0], 123 '#size' => 5, 124 '#maxlength' => 5, 125 '#field_suffix' => ' x ', 126 ); 127 $form['min_resolution']['y'] = array( 128 '#type' => 'textfield', 129 '#title' => t('Minimum height'), 130 '#title_display' => 'invisible', 131 '#default_value' => $min_resolution[1], 132 '#size' => 5, 133 '#maxlength' => 5, 134 '#field_suffix' => ' ' . t('pixels'), 135 ); 136 137 // Remove the description option. 138 unset($form['description_field']); 139 140 // Add title and alt configuration options. 141 $form['alt_field'] = array( 142 '#type' => 'checkbox', 143 '#title' => t('Enable <em>Alt</em> field'), 144 '#default_value' => $settings['alt_field'], 145 '#description' => t('The alt attribute may be used by search engines, screen readers, and when the image cannot be loaded.'), 146 '#weight' => 10, 147 ); 148 $form['title_field'] = array( 149 '#type' => 'checkbox', 150 '#title' => t('Enable <em>Title</em> field'), 151 '#default_value' => $settings['title_field'], 152 '#description' => t('The title attribute is used as a tooltip when the mouse hovers over the image.'), 153 '#weight' => 11, 154 ); 155 156 // Add the default image to the instance. 157 $form['default_image'] = array( 158 '#title' => t('Default image'), 159 '#type' => 'managed_file', 160 '#description' => t("If no image is uploaded, this image will be shown on display and will override the field's default image."), 161 '#default_value' => $settings['default_image'], 162 '#upload_location' => $field['settings']['uri_scheme'] . '://default_images/', 163 ); 164 165 return $form; 166 } 167 168 /** 169 * Element validate function for resolution fields. 170 */ 171 function _image_field_resolution_validate($element, &$form_state) { 172 if (!empty($element['x']['#value']) || !empty($element['y']['#value'])) { 173 foreach (array('x', 'y') as $dimension) { 174 $value = $element[$dimension]['#value']; 175 if (!is_numeric($value)) { 176 form_error($element[$dimension], t('Height and width values must be numeric.')); 177 return; 178 } 179 if (intval($value) == 0) { 180 form_error($element[$dimension], t('Both a height and width value must be specified in the !name field.', array('!name' => $element['#title']))); 181 return; 182 } 183 } 184 form_set_value($element, intval($element['x']['#value']) . 'x' . intval($element['y']['#value']), $form_state); 185 } 186 else { 187 form_set_value($element, '', $form_state); 188 } 189 } 190 191 /** 192 * Implements hook_field_load(). 193 */ 194 function image_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) { 195 file_field_load($entity_type, $entities, $field, $instances, $langcode, $items, $age); 196 } 197 198 /** 199 * Implements hook_field_prepare_view(). 200 */ 201 function image_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) { 202 // If there are no files specified at all, use the default. 203 foreach ($entities as $id => $entity) { 204 if (empty($items[$id])) { 205 $fid = 0; 206 // Use the default for the instance if one is available. 207 if (!empty($instances[$id]['settings']['default_image'])) { 208 $fid = $instances[$id]['settings']['default_image']; 209 } 210 // Otherwise, use the default for the field. 211 elseif (!empty($field['settings']['default_image'])) { 212 $fid = $field['settings']['default_image']; 213 } 214 215 // Add the default image if one is found. 216 if ($fid && ($file = file_load($fid))) { 217 $items[$id][0] = (array) $file + array( 218 'is_default' => TRUE, 219 'alt' => '', 220 'title' => '', 221 ); 222 } 223 } 224 } 225 } 226 227 /** 228 * Implements hook_field_presave(). 229 */ 230 function image_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) { 231 file_field_presave($entity_type, $entity, $field, $instance, $langcode, $items); 232 233 // Determine the dimensions if necessary. 234 foreach ($items as &$item) { 235 if (!isset($item['width']) || !isset($item['height'])) { 236 $info = image_get_info(file_load($item['fid'])->uri); 237 238 if (is_array($info)) { 239 $item['width'] = $info['width']; 240 $item['height'] = $info['height']; 241 } 242 } 243 } 244 } 245 246 /** 247 * Implements hook_field_insert(). 248 */ 249 function image_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) { 250 file_field_insert($entity_type, $entity, $field, $instance, $langcode, $items); 251 } 252 253 /** 254 * Implements hook_field_update(). 255 */ 256 function image_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) { 257 file_field_update($entity_type, $entity, $field, $instance, $langcode, $items); 258 } 259 260 /** 261 * Implements hook_field_delete(). 262 */ 263 function image_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) { 264 file_field_delete($entity_type, $entity, $field, $instance, $langcode, $items); 265 } 266 267 /** 268 * Implements hook_field_delete_revision(). 269 */ 270 function image_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, &$items) { 271 file_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, $items); 272 } 273 274 /** 275 * Implements hook_field_is_empty(). 276 */ 277 function image_field_is_empty($item, $field) { 278 return file_field_is_empty($item, $field); 279 } 280 281 /** 282 * Implements hook_field_widget_info(). 283 */ 284 function image_field_widget_info() { 285 return array( 286 'image_image' => array( 287 'label' => t('Image'), 288 'field types' => array('image'), 289 'settings' => array( 290 'progress_indicator' => 'throbber', 291 'preview_image_style' => 'thumbnail', 292 ), 293 'behaviors' => array( 294 'multiple values' => FIELD_BEHAVIOR_CUSTOM, 295 'default value' => FIELD_BEHAVIOR_NONE, 296 ), 297 ), 298 ); 299 } 300 301 /** 302 * Implements hook_field_widget_settings_form(). 303 */ 304 function image_field_widget_settings_form($field, $instance) { 305 $widget = $instance['widget']; 306 $settings = $widget['settings']; 307 308 // Use the file widget settings form. 309 $form = file_field_widget_settings_form($field, $instance); 310 311 $form['preview_image_style'] = array( 312 '#title' => t('Preview image style'), 313 '#type' => 'select', 314 '#options' => image_style_options(FALSE), 315 '#empty_option' => '<' . t('no preview') . '>', 316 '#default_value' => $settings['preview_image_style'], 317 '#description' => t('The preview image will be shown while editing the content.'), 318 '#weight' => 15, 319 ); 320 321 return $form; 322 } 323 324 /** 325 * Implements hook_field_widget_form(). 326 */ 327 function image_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { 328 329 // Add display_field setting to field because file_field_widget_form() assumes it is set. 330 $field['settings']['display_field'] = 0; 331 332 $elements = file_field_widget_form($form, $form_state, $field, $instance, $langcode, $items, $delta, $element); 333 $settings = $instance['settings']; 334 335 foreach (element_children($elements) as $delta) { 336 // Add upload resolution validation. 337 if ($settings['max_resolution'] || $settings['min_resolution']) { 338 $elements[$delta]['#upload_validators']['file_validate_image_resolution'] = array($settings['max_resolution'], $settings['min_resolution']); 339 } 340 341 // If not using custom extension validation, ensure this is an image. 342 $supported_extensions = array('png', 'gif', 'jpg', 'jpeg'); 343 $extensions = isset($elements[$delta]['#upload_validators']['file_validate_extensions'][0]) ? $elements[$delta]['#upload_validators']['file_validate_extensions'][0] : implode(' ', $supported_extensions); 344 $extensions = array_intersect(explode(' ', $extensions), $supported_extensions); 345 $elements[$delta]['#upload_validators']['file_validate_extensions'][0] = implode(' ', $extensions); 346 347 // Add all extra functionality provided by the image widget. 348 $elements[$delta]['#process'][] = 'image_field_widget_process'; 349 } 350 351 if ($field['cardinality'] == 1) { 352 // If there's only one field, return it as delta 0. 353 if (empty($elements[0]['#default_value']['fid'])) { 354 $elements[0]['#description'] = theme('file_upload_help', array('description' => $instance['description'], 'upload_validators' => $elements[0]['#upload_validators'])); 355 } 356 } 357 else { 358 $elements['#file_upload_description'] = theme('file_upload_help', array('upload_validators' => $elements[0]['#upload_validators'])); 359 } 360 return $elements; 361 } 362 363 /** 364 * An element #process callback for the image_image field type. 365 * 366 * Expands the image_image type to include the alt and title fields. 367 */ 368 function image_field_widget_process($element, &$form_state, $form) { 369 $item = $element['#value']; 370 $item['fid'] = $element['fid']['#value']; 371 372 $instance = field_widget_instance($element, $form_state); 373 374 $settings = $instance['settings']; 375 $widget_settings = $instance['widget']['settings']; 376 377 $element['#theme'] = 'image_widget'; 378 $element['#attached']['css'][] = drupal_get_path('module', 'image') . '/image.css'; 379 380 // Add the image preview. 381 if ($element['#file'] && $widget_settings['preview_image_style']) { 382 $variables = array( 383 'style_name' => $widget_settings['preview_image_style'], 384 'path' => $element['#file']->uri, 385 ); 386 387 // Determine image dimensions. 388 if (isset($element['#value']['width']) && isset($element['#value']['height'])) { 389 $variables['width'] = $element['#value']['width']; 390 $variables['height'] = $element['#value']['height']; 391 } 392 else { 393 $info = image_get_info($element['#file']->uri); 394 395 if (is_array($info)) { 396 $variables['width'] = $info['width']; 397 $variables['height'] = $info['height']; 398 } 399 else { 400 $variables['width'] = $variables['height'] = NULL; 401 } 402 } 403 404 $element['preview'] = array( 405 '#type' => 'markup', 406 '#markup' => theme('image_style', $variables), 407 ); 408 409 // Store the dimensions in the form so the file doesn't have to be accessed 410 // again. This is important for remote files. 411 $element['width'] = array( 412 '#type' => 'hidden', 413 '#value' => $variables['width'], 414 ); 415 $element['height'] = array( 416 '#type' => 'hidden', 417 '#value' => $variables['height'], 418 ); 419 } 420 421 // Add the additional alt and title fields. 422 $element['alt'] = array( 423 '#title' => t('Alternate text'), 424 '#type' => 'textfield', 425 '#default_value' => isset($item['alt']) ? $item['alt'] : '', 426 '#description' => t('This text will be used by screen readers, search engines, or when the image cannot be loaded.'), 427 // @see http://www.gawds.org/show.php?contentid=28 428 '#maxlength' => 512, 429 '#weight' => -2, 430 '#access' => (bool) $item['fid'] && $settings['alt_field'], 431 ); 432 $element['title'] = array( 433 '#type' => 'textfield', 434 '#title' => t('Title'), 435 '#default_value' => isset($item['title']) ? $item['title'] : '', 436 '#description' => t('The title is used as a tool tip when the user hovers the mouse over the image.'), 437 '#maxlength' => 1024, 438 '#weight' => -1, 439 '#access' => (bool) $item['fid'] && $settings['title_field'], 440 ); 441 442 return $element; 443 } 444 445 /** 446 * Returns HTML for an image field widget. 447 * 448 * @param $variables 449 * An associative array containing: 450 * - element: A render element representing the image field widget. 451 * 452 * @ingroup themeable 453 */ 454 function theme_image_widget($variables) { 455 $element = $variables['element']; 456 $output = ''; 457 $output .= '<div class="image-widget form-managed-file clearfix">'; 458 459 if (isset($element['preview'])) { 460 $output .= '<div class="image-preview">'; 461 $output .= drupal_render($element['preview']); 462 $output .= '</div>'; 463 } 464 465 $output .= '<div class="image-widget-data">'; 466 if ($element['fid']['#value'] != 0) { 467 $element['filename']['#markup'] .= ' <span class="file-size">(' . format_size($element['#file']->filesize) . ')</span> '; 468 } 469 $output .= drupal_render_children($element); 470 $output .= '</div>'; 471 $output .= '</div>'; 472 473 return $output; 474 } 475 476 /** 477 * Implements hook_field_formatter_info(). 478 */ 479 function image_field_formatter_info() { 480 $formatters = array( 481 'image' => array( 482 'label' => t('Image'), 483 'field types' => array('image'), 484 'settings' => array('image_style' => '', 'image_link' => ''), 485 ), 486 ); 487 488 return $formatters; 489 } 490 491 /** 492 * Implements hook_field_formatter_settings_form(). 493 */ 494 function image_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) { 495 $display = $instance['display'][$view_mode]; 496 $settings = $display['settings']; 497 498 $image_styles = image_style_options(FALSE); 499 $element['image_style'] = array( 500 '#title' => t('Image style'), 501 '#type' => 'select', 502 '#default_value' => $settings['image_style'], 503 '#empty_option' => t('None (original image)'), 504 '#options' => $image_styles, 505 ); 506 507 $link_types = array( 508 'content' => t('Content'), 509 'file' => t('File'), 510 ); 511 $element['image_link'] = array( 512 '#title' => t('Link image to'), 513 '#type' => 'select', 514 '#default_value' => $settings['image_link'], 515 '#empty_option' => t('Nothing'), 516 '#options' => $link_types, 517 ); 518 519 return $element; 520 } 521 522 /** 523 * Implements hook_field_formatter_settings_summary(). 524 */ 525 function image_field_formatter_settings_summary($field, $instance, $view_mode) { 526 $display = $instance['display'][$view_mode]; 527 $settings = $display['settings']; 528 529 $summary = array(); 530 531 $image_styles = image_style_options(FALSE); 532 // Unset possible 'No defined styles' option. 533 unset($image_styles['']); 534 // Styles could be lost because of enabled/disabled modules that defines 535 // their styles in code. 536 if (isset($image_styles[$settings['image_style']])) { 537 $summary[] = t('Image style: @style', array('@style' => $image_styles[$settings['image_style']])); 538 } 539 else { 540 $summary[] = t('Original image'); 541 } 542 543 $link_types = array( 544 'content' => t('Linked to content'), 545 'file' => t('Linked to file'), 546 ); 547 // Display this setting only if image is linked. 548 if (isset($link_types[$settings['image_link']])) { 549 $summary[] = $link_types[$settings['image_link']]; 550 } 551 552 return implode('<br />', $summary); 553 } 554 555 /** 556 * Implements hook_field_formatter_view(). 557 */ 558 function image_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { 559 $element = array(); 560 561 // Check if the formatter involves a link. 562 if ($display['settings']['image_link'] == 'content') { 563 $uri = entity_uri($entity_type, $entity); 564 } 565 elseif ($display['settings']['image_link'] == 'file') { 566 $link_file = TRUE; 567 } 568 569 foreach ($items as $delta => $item) { 570 if (isset($link_file)) { 571 $uri = array( 572 'path' => file_create_url($item['uri']), 573 'options' => array(), 574 ); 575 } 576 $element[$delta] = array( 577 '#theme' => 'image_formatter', 578 '#item' => $item, 579 '#image_style' => $display['settings']['image_style'], 580 '#path' => isset($uri) ? $uri : '', 581 ); 582 } 583 584 return $element; 585 } 586 587 /** 588 * Returns HTML for an image field formatter. 589 * 590 * @param $variables 591 * An associative array containing: 592 * - item: Associative array of image data, which may include "uri", "alt", 593 * "width", "height", "title" and "attributes". 594 * - image_style: An optional image style. 595 * - path: An array containing the link 'path' and link 'options'. 596 * 597 * @ingroup themeable 598 */ 599 function theme_image_formatter($variables) { 600 $item = $variables['item']; 601 $image = array( 602 'path' => $item['uri'], 603 ); 604 605 if (array_key_exists('alt', $item)) { 606 $image['alt'] = $item['alt']; 607 } 608 609 if (isset($item['attributes'])) { 610 $image['attributes'] = $item['attributes']; 611 } 612 613 if (isset($item['width']) && isset($item['height'])) { 614 $image['width'] = $item['width']; 615 $image['height'] = $item['height']; 616 } 617 618 // Do not output an empty 'title' attribute. 619 if (isset($item['title']) && drupal_strlen($item['title']) > 0) { 620 $image['title'] = $item['title']; 621 } 622 623 if ($variables['image_style']) { 624 $image['style_name'] = $variables['image_style']; 625 $output = theme('image_style', $image); 626 } 627 else { 628 $output = theme('image', $image); 629 } 630 631 // The link path and link options are both optional, but for the options to be 632 // processed, the link path must at least be an empty string. 633 if (isset($variables['path']['path'])) { 634 $path = $variables['path']['path']; 635 $options = isset($variables['path']['options']) ? $variables['path']['options'] : array(); 636 // When displaying an image inside a link, the html option must be TRUE. 637 $options['html'] = TRUE; 638 $output = l($output, $path, $options); 639 } 640 641 return $output; 642 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title