diff options
author | Angie Byron <webchick@24967.no-reply.drupal.org> | 2009-10-12 05:22:57 +0000 |
---|---|---|
committer | Angie Byron <webchick@24967.no-reply.drupal.org> | 2009-10-12 05:22:57 +0000 |
commit | 8d3eaa1ccf1e7f7d20ecbb19a77e39c028a72706 (patch) | |
tree | 7dfe5c496a2dcfc8b4940e72babb48fc67112cc5 /modules/image | |
parent | 33b09cc5eb6f602e8007ce4cf799b30aacc4021e (diff) | |
download | brdo-8d3eaa1ccf1e7f7d20ecbb19a77e39c028a72706.tar.gz brdo-8d3eaa1ccf1e7f7d20ecbb19a77e39c028a72706.tar.bz2 |
#560780 by quicksketch, ksenzee, Arancaytar, yched, and arianek: Added Image Field to image.module. Hellooooo, native image handling in core! :D
Diffstat (limited to 'modules/image')
-rw-r--r-- | modules/image/image-rtl.css | 12 | ||||
-rw-r--r-- | modules/image/image.css | 15 | ||||
-rw-r--r-- | modules/image/image.field.inc | 490 | ||||
-rw-r--r-- | modules/image/image.info | 1 | ||||
-rw-r--r-- | modules/image/image.module | 67 | ||||
-rw-r--r-- | modules/image/image.test | 1 |
6 files changed, 574 insertions, 12 deletions
diff --git a/modules/image/image-rtl.css b/modules/image/image-rtl.css new file mode 100644 index 000000000..59c46605d --- /dev/null +++ b/modules/image/image-rtl.css @@ -0,0 +1,12 @@ +/* $Id$ */ + +/** + * Image upload widget. + */ +div.image-preview { + float: right; /* RTL */ + padding: 0 0 10px 10px; /* RTL */ +} +div.image-widget-data { + float: right; /* RTL */ +} diff --git a/modules/image/image.css b/modules/image/image.css new file mode 100644 index 000000000..3f4cc5cd3 --- /dev/null +++ b/modules/image/image.css @@ -0,0 +1,15 @@ +/* $Id$ */ + +/** + * Image upload widget. + */ +div.image-preview { + float: left; /* RTL */ + padding: 0 10px 10px 0; /* RTL */ +} +div.image-widget-data { + float: left; /* RTL */ +} +div.image-widget-data input.text-field { + width: auto; +} diff --git a/modules/image/image.field.inc b/modules/image/image.field.inc new file mode 100644 index 000000000..963187fdc --- /dev/null +++ b/modules/image/image.field.inc @@ -0,0 +1,490 @@ +<?php +// $Id$ + +/** + * @file + * Implement an image field, based on the file module's file field. + */ + +/** + * Implement hook_field_info(). + */ +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( + 'uri_scheme' => 'public', + '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', + ), + ); +} + +/** + * Implement hook_field_schema(). + */ +function image_field_schema($field) { + return array( + 'columns' => array( + 'fid' => array( + 'description' => 'The {files}.fid being referenced in this field.', + 'type' => 'int', + 'not null' => FALSE, + 'unsigned' => TRUE, + ), + 'alt' => array( + 'description' => "Alternative image text, for the image's 'alt' attribute.", + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + ), + 'title' => array( + 'description' => "Image title text, for the image's 'title' attribute.", + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + ), + ), + 'indexes' => array( + 'fid' => array('fid'), + ), + ); +} + +/** + * Implement hook_field_settings_form(). + */ +function image_field_settings_form($field, $instance) { + $defaults = field_info_field_settings($field['type']); + $settings = array_merge($defaults, $field['settings']); + + $scheme_options = array(); + foreach (file_get_stream_wrappers() as $scheme => $stream_wrapper) { + if ($scheme != 'temporary') { + $scheme_options[$scheme] = $stream_wrapper['name']; + } + } + $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.'), + ); + + $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'], + '#upload_location' => 'public://default_images/', + ); + + return $form; + +} + +/** + * Implement hook_field_instance_settings_form(). + */ +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( + '#title' => t('Maximum image resolution'), + '#element_validate' => array('_image_field_resolution_validate'), + '#theme_wrappers' => array('form_element'), + '#weight' => 4.1, + '#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', + '#default_value' => $max_resolution[0], + '#size' => 5, + '#maxlength' => 5, + '#field_suffix' => ' x ', + '#theme_wrappers' => array(), + ); + $form['max_resolution']['y'] = array( + '#type' => 'textfield', + '#default_value' => $max_resolution[1], + '#size' => 5, + '#maxlength' => 5, + '#field_suffix' => ' ' . t('pixels'), + '#theme_wrappers' => array(), + ); + + $min_resolution = explode('x', $settings['min_resolution']) + array('', ''); + $form['min_resolution'] = array( + '#title' => t('Minimum image resolution'), + '#element_validate' => array('_image_field_resolution_validate'), + '#theme_wrappers' => array('form_element'), + '#weight' => 4.2, + '#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', + '#default_value' => $min_resolution[0], + '#size' => 5, + '#maxlength' => 5, + '#field_suffix' => ' x ', + '#theme_wrappers' => array(), + ); + $form['min_resolution']['y'] = array( + '#type' => 'textfield', + '#default_value' => $min_resolution[1], + '#size' => 5, + '#maxlength' => 5, + '#field_suffix' => ' ' . t('pixels'), + '#theme_wrappers' => array(), + ); + + // 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); + } +} + +/** + * Implement hook_field_load(). + */ +function image_field_load($obj_type, $objects, $field, $instances, $langcode, &$items, $age) { + file_field_load($obj_type, $objects, $field, $instances, $langcode, $items, $age); +} + +/** + * Implement hook_field_sanitize(). + */ +function image_field_sanitize($obj_type, $object, $field, $instance, $langcode, &$items) { + // If there are no files specified at all, use the default. + if (empty($items) && $field['settings']['default_image']) { + if ($file = file_load($field['settings']['default_image'])) { + $items[0] = (array) $file + array( + 'is_default' => TRUE, + 'alt' => '', + 'title' => '', + ); + } + } +} + +/** + * Implement hook_field_insert(). + */ +function image_field_insert($obj_type, $object, $field, $instance, $langcode, &$items) { + image_field_update($obj_type, $object, $field, $instance, $langcode, $items); +} + +/** + * Implement hook_field_update(). + */ +function image_field_update($obj_type, $object, $field, $instance, $langcode, &$items) { + file_field_update($obj_type, $object, $field, $instance, $langcode, $items); +} + +/** + * Implement hook_field_delete(). + */ +function image_field_delete($obj_type, $object, $field, $instance, $langcode, &$items) { + file_field_delete($obj_type, $object, $field, $instance, $langcode, $items); +} + +/** + * Implement hook_field_delete_revision(). + */ +function image_field_delete_revision($obj_type, $object, $field, $instance, $langcode, &$items) { + file_field_delete_revision($obj_type, $object, $field, $instance, $langcode, $items); +} + +/** + * Implement hook_field_is_empty(). + */ +function image_field_is_empty($item, $field) { + return file_field_is_empty($item, $field); +} + +/** + * Implement hook_field_widget_info(). + */ +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, + ), + ), + ); +} + +/** + * Implement hook_field_widget_settings_form(). + */ +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', + '#options' => array('' => '<' . t('no preview') . '>') + image_style_options(FALSE), + '#default_value' => $settings['preview_image_style'], + '#description' => t('The preview image will be shown while editing the content.'), + '#weight' => 15, + ); + + return $form; +} + +/** + * Implementation of hook_field_widget(). + */ +function image_field_widget(&$form, &$form_state, $field, $instance, $items, $delta = 0) { + $elements = file_field_widget($form, $form_state, $field, $instance, $items, $delta); + $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']; + + $field = field_info_field($element['#field_name']); + $instance = field_info_instance($element['#field_name'], $element['#bundle']); + $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']) { + $element['preview'] = array( + '#type' => 'markup', + '#markup' => theme('image_style', array('style_name' => $widget_settings['preview_image_style'], 'path' => $element['#file']->uri, 'getsize' => FALSE)), + ); + } + + // 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.'), + '#maxlength' => variable_get('image_alt_length', 80), // See http://www.gawds.org/show.php?contentid=28. + '#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.'), + '#maxlength' => variable_get('image_title_length', 500), + '#weight' => -1, + '#access' => (bool) $item['fid'] && $settings['title_field'], + ); + + return $element; +} + +/** + * Theme the display of the image field widget. + */ +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; +} + +/** + * Implement hook_field_formatter_info(). + */ +function image_field_formatter_info() { + $formatters = array( + 'image' => array( + 'label' => t('Image'), + 'field types' => array('image'), + ), + 'image_link_content' => array( + 'label' => t('Image linked to content'), + 'field types' => array('image'), + ), + 'image_link_file' => array( + 'label' => t('Image linked to file'), + 'field types' => array('image'), + ), + ); + + foreach (image_styles() as $style) { + $formatters['image__' . $style['name']] = array( + 'label' => t('Image "@style"', array('@style' => $style['name'])), + 'field types' => array('image'), + 'theme' => array('function' => 'theme_field_formatter_image'), + ); + $formatters['image_link_content__' . $style['name']] = array( + 'label' => t('Image "@style" linked to content', array('@style' => $style['name'])), + 'field types' => array('image'), + 'theme' => array('function' => 'theme_field_formatter_image_link_content'), + ); + $formatters['image_link_file__' . $style['name']] = array( + 'label' => t('Image "@style" linked to file', array('@style' => $style['name'])), + 'field types' => array('image'), + 'theme' => array('function' => 'theme_field_formatter_image_link_file'), + ); + } + + return $formatters; +} + +/** + * Theme function for 'image' image field formatter. + */ +function theme_field_formatter_image($variables) { + $element = $variables['element']; + $image = array( + 'path' => $element['#item']['uri'], + 'alt' => $element['#item']['alt'], + 'title' => $element['#item']['title'], + ); + + // Check if this requires a particular image style. + $matches = array(); + if (preg_match('/__([a-z0-9_]+)/', $element['#formatter'], $matches)) { + $image['style_name'] = $matches[1]; + return theme('image_style', $image); + } + else { + return theme('image', $image); + } +} + +/** + * Theme function for 'image_link_content' image field formatter. + */ +function theme_field_formatter_image_link_content($variables) { + $element = $variables['element']; + list($id, $vid, $bundle) = field_extract_ids($element['#object_type'], $element['#object']); + return l(theme('field_formatter_image', $variables), $element['#object_type'] . '/' . $id, array('html' => TRUE)); +} + +/** + * Theme function for 'image_link_file' image field formatter. + */ +function theme_field_formatter_image_link_file($variables) { + $element = $variables['element']; + return l(theme('field_formatter_image', $variables), file_create_url($element['#item']['uri']), array('html' => TRUE)); +} diff --git a/modules/image/image.info b/modules/image/image.info index 0e51ce447..e57d62cee 100644 --- a/modules/image/image.info +++ b/modules/image/image.info @@ -7,5 +7,6 @@ core = 7.x files[] = image.module files[] = image.admin.inc files[] = image.effects.inc +files[] = image.field.inc files[] = image.install files[] = image.test diff --git a/modules/image/image.module b/modules/image/image.module index bb2830407..f745662d3 100644 --- a/modules/image/image.module +++ b/modules/image/image.module @@ -6,6 +6,9 @@ * Exposes global functionality for creating image styles. */ +// Load all Field module hooks for Image. +require_once DRUPAL_ROOT . '/modules/image/image.field.inc'; + /** * Implement of hook_help(). */ @@ -126,6 +129,7 @@ function image_menu() { */ function image_theme() { return array( + // Theme functions in image.module. 'image_style' => array( 'arguments' => array( 'style_name' => NULL, @@ -136,6 +140,8 @@ function image_theme() { 'getsize' => TRUE, ), ), + + // Theme functions in image.admin.inc. 'image_style_list' => array( 'arguments' => array('styles' => NULL), ), @@ -160,6 +166,20 @@ function image_theme() { 'image_rotate_summary' => array( 'arguments' => array('data' => NULL), ), + + // Theme functions in image.field.inc. + 'image_widget' => array( + 'arguments' => array('element' => NULL), + ), + 'field_formatter_image' => array( + 'arguments' => array('element' => NULL), + ), + 'field_formatter_image_link_content' => array( + 'arguments' => array('element' => NULL), + ), + 'field_formatter_image_link_file' => array( + 'arguments' => array('element' => NULL), + ), ); } @@ -187,9 +207,12 @@ function image_flush_caches() { * * Control the access to files underneath the styles directory. */ -function image_file_download($filepath) { - if (strpos($filepath, 'styles/') === 0) { - $args = explode('/', $filepath); +function image_file_download($uri) { + $path = file_uri_target($uri); + + // Private file access for image style derivatives. + if (strpos($path, 'styles/') === 0) { + $args = explode('/', $path); // Discard the first part of the path (styles). array_shift($args); // Get the style name from the second part. @@ -198,7 +221,7 @@ function image_file_download($filepath) { $original_path = implode('/', $args); // Check that the file exists and is an image. - if ($info = image_get_info($filepath)) { + if ($info = image_get_info($uri)) { // Check the permissions of the original to grant access to this image. $headers = module_invoke_all('file_download', $original_path); if (!in_array(-1, $headers)) { @@ -217,6 +240,17 @@ function image_file_download($filepath) { } return -1; } + + // Private file access for the original files. Note that we only + // check access for non-temporary images, since file.module will + // grant access for all temporary files. + $files = file_load_multiple(array(), array('uri' => $uri)); + if (count($files)) { + $file = reset($files); + if ($file->status) { + return file_file_download($uri, 'image'); + } + } } /** @@ -236,6 +270,14 @@ function image_file_delete($file) { } /** + * Implement hook_file_references(). + */ +function image_file_references($file) { + $count = file_get_file_reference_count($file, NULL, 'image'); + return $count ? array('image' => $count) : NULL; +} + +/** * Clear cached versions of a specific file in all styles. * * @param $path @@ -464,7 +506,7 @@ function image_style_generate() { // acquiring the lock. $success = file_exists($destination) || image_style_create_derivative($style, $path, $destination); - if ($lock_acquired) { + if (!empty($lock_acquired)) { lock_release($lock_name); } @@ -600,17 +642,16 @@ function image_style_url($style_name, $path) { * * @param $style_name * The name of the style to be used with this image. - * @param $path - * The path to the image. + * @param $uri + * The URI or path to the image. * @return * The path to an image style image relative to Drupal's root. * @see image_style_url() */ -function image_style_path($style_name, $path) { - if ($target = file_uri_target($path)) { - $path = $target; - } - return variable_get('file_default_scheme', 'public') . '://styles/' . $style_name . '/' . $path; +function image_style_path($style_name, $uri) { + $path = ($path = file_uri_target($uri)) ? $path : $uri; + $scheme = ($scheme = file_uri_scheme($uri)) ? $scheme : variable_get('file_default_scheme', 'public'); + return $scheme . '://styles/' . $style_name . '/' . $path; } /** @@ -773,6 +814,7 @@ function image_effect_delete($effect) { * TRUE on success. FALSE if unable to perform the image effect on the image. */ function image_effect_apply($image, $effect) { + module_load_include('inc', 'image', 'image.effects'); if (function_exists($effect['effect callback'])) { return call_user_func($effect['effect callback'], $image, $effect['data']); } @@ -811,6 +853,7 @@ function theme_image_style($variables) { $style_path = image_style_url($style_name, $path); } $variables['path'] = file_create_url($style_path); + $variables['getsize'] = FALSE; return theme('image', $variables); } diff --git a/modules/image/image.test b/modules/image/image.test index cbdcb1e08..e50caf75b 100644 --- a/modules/image/image.test +++ b/modules/image/image.test @@ -335,6 +335,7 @@ class ImageAdminStylesUnitTest extends DrupalWebTestCase { // Edit effect form. // Revisit each form to make sure the effect was saved. + drupal_static_reset('image_styles'); $style = image_style_load($style_name); foreach ($style['effects'] as $ieid => $effect) { |