cms/drupal/modules/image/image.module
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  * Exposes global functionality for creating image styles.
       
     6  */
       
     7 
       
     8 /**
       
     9  * Image style constant for user presets in the database.
       
    10  */
       
    11 define('IMAGE_STORAGE_NORMAL', 1);
       
    12 
       
    13 /**
       
    14  * Image style constant for user presets that override module-defined presets.
       
    15  */
       
    16 define('IMAGE_STORAGE_OVERRIDE', 2);
       
    17 
       
    18 /**
       
    19  * Image style constant for module-defined presets in code.
       
    20  */
       
    21 define('IMAGE_STORAGE_DEFAULT', 4);
       
    22 
       
    23 /**
       
    24  * Image style constant to represent an editable preset.
       
    25  */
       
    26 define('IMAGE_STORAGE_EDITABLE', IMAGE_STORAGE_NORMAL | IMAGE_STORAGE_OVERRIDE);
       
    27 
       
    28 /**
       
    29  * Image style constant to represent any module-based preset.
       
    30  */
       
    31 define('IMAGE_STORAGE_MODULE', IMAGE_STORAGE_OVERRIDE | IMAGE_STORAGE_DEFAULT);
       
    32 
       
    33 /**
       
    34  * The name of the query parameter for image derivative tokens.
       
    35  */
       
    36 define('IMAGE_DERIVATIVE_TOKEN', 'itok');
       
    37 
       
    38 // Load all Field module hooks for Image.
       
    39 require_once DRUPAL_ROOT . '/modules/image/image.field.inc';
       
    40 
       
    41 /**
       
    42  * Implements hook_help().
       
    43  */
       
    44 function image_help($path, $arg) {
       
    45   switch ($path) {
       
    46     case 'admin/help#image':
       
    47       $output = '';
       
    48       $output .= '<h3>' . t('About') . '</h3>';
       
    49       $output .= '<p>' . t('The Image module allows you to manipulate images on your website. It exposes a setting for using the <em>Image toolkit</em>, allows you to configure <em>Image styles</em> that can be used for resizing or adjusting images on display, and provides an <em>Image</em> field for attaching images to content. For more information, see the online handbook entry for <a href="@image">Image module</a>.', array('@image' => 'http://drupal.org/documentation/modules/image')) . '</p>';
       
    50       $output .= '<h3>' . t('Uses') . '</h3>';
       
    51       $output .= '<dl>';
       
    52       $output .= '<dt>' . t('Manipulating images') . '</dt>';
       
    53       $output .= '<dd>' . t('With the Image module you can scale, crop, resize, rotate and desaturate images without affecting the original image using <a href="@image">image styles</a>. When you change an image style, the module automatically refreshes all created images. Every image style must have a name, which will be used in the URL of the generated images. There are two common approaches to naming image styles (which you use will depend on how the image style is being applied):',array('@image' => url('admin/config/media/image-styles')));
       
    54       $output .= '<ul><li>' . t('Based on where it will be used: eg. <em>profile-picture</em>') . '</li>';
       
    55       $output .= '<li>' . t('Describing its appearance: eg. <em>square-85x85</em>') . '</li></ul>';
       
    56       $output .=  t('After you create an image style, you can add effects: crop, scale, resize, rotate, and desaturate (other contributed modules provide additional effects). For example, by combining effects as crop, scale, and desaturate, you can create square, grayscale thumbnails.') . '<dd>';
       
    57       $output .= '<dt>' . t('Attaching images to content as fields') . '</dt>';
       
    58       $output .= '<dd>' . t("Image module also allows you to attach images to content as fields. To add an image field to a <a href='@content-type'>content type</a>, go to the content type's <em>manage fields</em> page, and add a new field of type <em>Image</em>. Attaching images to content this way allows image styles to be applied and maintained, and also allows you more flexibility when theming.", array('@content-type' => url('admin/structure/types'))) . '</dd>';
       
    59       $output .= '</dl>';
       
    60       return $output;
       
    61     case 'admin/config/media/image-styles':
       
    62       return '<p>' . t('Image styles commonly provide thumbnail sizes by scaling and cropping images, but can also add various effects before an image is displayed. When an image is displayed with a style, a new file is created and the original image is left unchanged.') . '</p>';
       
    63     case 'admin/config/media/image-styles/edit/%/add/%':
       
    64       $effect = image_effect_definition_load($arg[7]);
       
    65       return isset($effect['help']) ? ('<p>' . $effect['help'] . '</p>') : NULL;
       
    66     case 'admin/config/media/image-styles/edit/%/effects/%':
       
    67       $effect = ($arg[5] == 'add') ? image_effect_definition_load($arg[6]) : image_effect_load($arg[7], $arg[5]);
       
    68       return isset($effect['help']) ? ('<p>' . $effect['help'] . '</p>') : NULL;
       
    69   }
       
    70 }
       
    71 
       
    72 /**
       
    73  * Implements hook_menu().
       
    74  */
       
    75 function image_menu() {
       
    76   $items = array();
       
    77 
       
    78   // Generate image derivatives of publicly available files.
       
    79   // If clean URLs are disabled, image derivatives will always be served
       
    80   // through the menu system.
       
    81   // If clean URLs are enabled and the image derivative already exists,
       
    82   // PHP will be bypassed.
       
    83   $directory_path = file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath();
       
    84   $items[$directory_path . '/styles/%image_style'] = array(
       
    85     'title' => 'Generate image style',
       
    86     'page callback' => 'image_style_deliver',
       
    87     'page arguments' => array(count(explode('/', $directory_path)) + 1),
       
    88     'access callback' => TRUE,
       
    89     'type' => MENU_CALLBACK,
       
    90   );
       
    91   // Generate and deliver image derivatives of private files.
       
    92   // These image derivatives are always delivered through the menu system.
       
    93   $items['system/files/styles/%image_style'] = array(
       
    94     'title' => 'Generate image style',
       
    95     'page callback' => 'image_style_deliver',
       
    96     'page arguments' => array(3),
       
    97     'access callback' => TRUE,
       
    98     'type' => MENU_CALLBACK,
       
    99   );
       
   100   $items['admin/config/media/image-styles'] = array(
       
   101     'title' => 'Image styles',
       
   102     'description' => 'Configure styles that can be used for resizing or adjusting images on display.',
       
   103     'page callback' => 'image_style_list',
       
   104     'access arguments' => array('administer image styles'),
       
   105     'file' => 'image.admin.inc',
       
   106   );
       
   107   $items['admin/config/media/image-styles/list'] = array(
       
   108     'title' => 'List',
       
   109     'description' => 'List the current image styles on the site.',
       
   110     'page callback' => 'image_style_list',
       
   111     'access arguments' => array('administer image styles'),
       
   112     'type' => MENU_DEFAULT_LOCAL_TASK,
       
   113     'weight' => 1,
       
   114     'file' => 'image.admin.inc',
       
   115   );
       
   116   $items['admin/config/media/image-styles/add'] = array(
       
   117     'title' => 'Add style',
       
   118     'description' => 'Add a new image style.',
       
   119     'page callback' => 'drupal_get_form',
       
   120     'page arguments' => array('image_style_add_form'),
       
   121     'access arguments' => array('administer image styles'),
       
   122     'type' => MENU_LOCAL_ACTION,
       
   123     'weight' => 2,
       
   124     'file' => 'image.admin.inc',
       
   125   );
       
   126   $items['admin/config/media/image-styles/edit/%image_style'] = array(
       
   127     'title' => 'Edit style',
       
   128     'description' => 'Configure an image style.',
       
   129     'page callback' => 'drupal_get_form',
       
   130     'page arguments' => array('image_style_form', 5),
       
   131     'access arguments' => array('administer image styles'),
       
   132     'file' => 'image.admin.inc',
       
   133   );
       
   134   $items['admin/config/media/image-styles/delete/%image_style'] = array(
       
   135     'title' => 'Delete style',
       
   136     'description' => 'Delete an image style.',
       
   137     'load arguments' => array(NULL, (string) IMAGE_STORAGE_NORMAL),
       
   138     'page callback' => 'drupal_get_form',
       
   139     'page arguments' => array('image_style_delete_form', 5),
       
   140     'access arguments' => array('administer image styles'),
       
   141     'file' => 'image.admin.inc',
       
   142   );
       
   143   $items['admin/config/media/image-styles/revert/%image_style'] = array(
       
   144     'title' => 'Revert style',
       
   145     'description' => 'Revert an image style.',
       
   146     'load arguments' => array(NULL, (string) IMAGE_STORAGE_OVERRIDE),
       
   147     'page callback' => 'drupal_get_form',
       
   148     'page arguments' => array('image_style_revert_form', 5),
       
   149     'access arguments' => array('administer image styles'),
       
   150     'file' => 'image.admin.inc',
       
   151   );
       
   152   $items['admin/config/media/image-styles/edit/%image_style/effects/%image_effect'] = array(
       
   153     'title' => 'Edit image effect',
       
   154     'description' => 'Edit an existing effect within a style.',
       
   155     'load arguments' => array(5, (string) IMAGE_STORAGE_EDITABLE),
       
   156     'page callback' => 'drupal_get_form',
       
   157     'page arguments' => array('image_effect_form', 5, 7),
       
   158     'access arguments' => array('administer image styles'),
       
   159     'file' => 'image.admin.inc',
       
   160   );
       
   161   $items['admin/config/media/image-styles/edit/%image_style/effects/%image_effect/delete'] = array(
       
   162     'title' => 'Delete image effect',
       
   163     'description' => 'Delete an existing effect from a style.',
       
   164     'load arguments' => array(5, (string) IMAGE_STORAGE_EDITABLE),
       
   165     'page callback' => 'drupal_get_form',
       
   166     'page arguments' => array('image_effect_delete_form', 5, 7),
       
   167     'access arguments' => array('administer image styles'),
       
   168     'file' => 'image.admin.inc',
       
   169   );
       
   170   $items['admin/config/media/image-styles/edit/%image_style/add/%image_effect_definition'] = array(
       
   171     'title' => 'Add image effect',
       
   172     'description' => 'Add a new effect to a style.',
       
   173     'load arguments' => array(5),
       
   174     'page callback' => 'drupal_get_form',
       
   175     'page arguments' => array('image_effect_form', 5, 7),
       
   176     'access arguments' => array('administer image styles'),
       
   177     'file' => 'image.admin.inc',
       
   178   );
       
   179 
       
   180   return $items;
       
   181 }
       
   182 
       
   183 /**
       
   184  * Implements hook_theme().
       
   185  */
       
   186 function image_theme() {
       
   187   return array(
       
   188     // Theme functions in image.module.
       
   189     'image_style' => array(
       
   190       'variables' => array(
       
   191         'style_name' => NULL,
       
   192         'path' => NULL,
       
   193         'width' => NULL,
       
   194         'height' => NULL,
       
   195         'alt' => '',
       
   196         'title' => NULL,
       
   197         'attributes' => array(),
       
   198       ),
       
   199     ),
       
   200 
       
   201     // Theme functions in image.admin.inc.
       
   202     'image_style_list' => array(
       
   203       'variables' => array('styles' => NULL),
       
   204     ),
       
   205     'image_style_effects' => array(
       
   206       'render element' => 'form',
       
   207     ),
       
   208     'image_style_preview' => array(
       
   209       'variables' => array('style' => NULL),
       
   210     ),
       
   211     'image_anchor' => array(
       
   212       'render element' => 'element',
       
   213     ),
       
   214     'image_resize_summary' => array(
       
   215       'variables' => array('data' => NULL),
       
   216     ),
       
   217     'image_scale_summary' => array(
       
   218       'variables' => array('data' => NULL),
       
   219     ),
       
   220     'image_crop_summary' => array(
       
   221       'variables' => array('data' => NULL),
       
   222     ),
       
   223     'image_rotate_summary' => array(
       
   224       'variables' => array('data' => NULL),
       
   225     ),
       
   226 
       
   227     // Theme functions in image.field.inc.
       
   228     'image_widget' => array(
       
   229       'render element' => 'element',
       
   230     ),
       
   231     'image_formatter' => array(
       
   232       'variables' => array('item' => NULL, 'path' => NULL, 'image_style' => NULL),
       
   233     ),
       
   234   );
       
   235 }
       
   236 
       
   237 /**
       
   238  * Implements hook_permission().
       
   239  */
       
   240 function image_permission() {
       
   241   return array(
       
   242     'administer image styles' => array(
       
   243       'title' => t('Administer image styles'),
       
   244       'description' => t('Create and modify styles for generating image modifications such as thumbnails.'),
       
   245     ),
       
   246   );
       
   247 }
       
   248 
       
   249 /**
       
   250  * Implements hook_form_FORM_ID_alter().
       
   251  */
       
   252 function image_form_system_file_system_settings_alter(&$form, &$form_state) {
       
   253   $form['#submit'][] = 'image_system_file_system_settings_submit';
       
   254 }
       
   255 
       
   256 /**
       
   257  * Form submission handler for system_file_system_settings().
       
   258  *
       
   259  * Adds a menu rebuild after the public file path has been changed, so that the
       
   260  * menu router item depending on that file path will be regenerated.
       
   261  */
       
   262 function image_system_file_system_settings_submit($form, &$form_state) {
       
   263   if ($form['file_public_path']['#default_value'] !== $form_state['values']['file_public_path']) {
       
   264     variable_set('menu_rebuild_needed', TRUE);
       
   265   }
       
   266 }
       
   267 
       
   268 /**
       
   269  * Implements hook_flush_caches().
       
   270  */
       
   271 function image_flush_caches() {
       
   272   return array('cache_image');
       
   273 }
       
   274 
       
   275 /**
       
   276  * Implements hook_file_download().
       
   277  *
       
   278  * Control the access to files underneath the styles directory.
       
   279  */
       
   280 function image_file_download($uri) {
       
   281   $path = file_uri_target($uri);
       
   282 
       
   283   // Private file access for image style derivatives.
       
   284   if (strpos($path, 'styles/') === 0) {
       
   285     $args = explode('/', $path);
       
   286     // Discard the first part of the path (styles).
       
   287     array_shift($args);
       
   288     // Get the style name from the second part.
       
   289     $style_name = array_shift($args);
       
   290     // Remove the scheme from the path.
       
   291     array_shift($args);
       
   292 
       
   293     // Then the remaining parts are the path to the image.
       
   294     $original_uri = file_uri_scheme($uri) . '://' . implode('/', $args);
       
   295 
       
   296     // Check that the file exists and is an image.
       
   297     if ($info = image_get_info($uri)) {
       
   298       // Check the permissions of the original to grant access to this image.
       
   299       $headers = module_invoke_all('file_download', $original_uri);
       
   300       // Confirm there's at least one module granting access and none denying access.
       
   301       if (!empty($headers) && !in_array(-1, $headers)) {
       
   302         return array(
       
   303           // Send headers describing the image's size, and MIME-type...
       
   304           'Content-Type' => $info['mime_type'],
       
   305           'Content-Length' => $info['file_size'],
       
   306           // By not explicitly setting them here, this uses normal Drupal
       
   307           // Expires, Cache-Control and ETag headers to prevent proxy or
       
   308           // browser caching of private images.
       
   309         );
       
   310       }
       
   311     }
       
   312     return -1;
       
   313   }
       
   314 
       
   315   // Private file access for the original files. Note that we only check access
       
   316   // for non-temporary images, since file.module will grant access for all
       
   317   // temporary files.
       
   318   $files = file_load_multiple(array(), array('uri' => $uri));
       
   319   if (count($files)) {
       
   320     $file = reset($files);
       
   321     if ($file->status) {
       
   322       return file_file_download($uri, 'image');
       
   323     }
       
   324   }
       
   325 }
       
   326 
       
   327 /**
       
   328  * Implements hook_file_move().
       
   329  */
       
   330 function image_file_move($file, $source) {
       
   331   // Delete any image derivatives at the original image path.
       
   332   image_path_flush($source->uri);
       
   333 }
       
   334 
       
   335 /**
       
   336  * Implements hook_file_delete().
       
   337  */
       
   338 function image_file_delete($file) {
       
   339   // Delete any image derivatives of this image.
       
   340   image_path_flush($file->uri);
       
   341 }
       
   342 
       
   343 /**
       
   344  * Implements hook_image_default_styles().
       
   345  */
       
   346 function image_image_default_styles() {
       
   347   $styles = array();
       
   348 
       
   349   $styles['thumbnail'] = array(
       
   350     'label' => 'Thumbnail (100x100)',
       
   351     'effects' => array(
       
   352       array(
       
   353         'name' => 'image_scale',
       
   354         'data' => array('width' => 100, 'height' => 100, 'upscale' => 1),
       
   355         'weight' => 0,
       
   356       ),
       
   357     )
       
   358   );
       
   359 
       
   360   $styles['medium'] = array(
       
   361     'label' => 'Medium (220x220)',
       
   362     'effects' => array(
       
   363       array(
       
   364         'name' => 'image_scale',
       
   365         'data' => array('width' => 220, 'height' => 220, 'upscale' => 1),
       
   366         'weight' => 0,
       
   367       ),
       
   368     )
       
   369   );
       
   370 
       
   371   $styles['large'] = array(
       
   372     'label' => 'Large (480x480)',
       
   373     'effects' => array(
       
   374       array(
       
   375         'name' => 'image_scale',
       
   376         'data' => array('width' => 480, 'height' => 480, 'upscale' => 0),
       
   377         'weight' => 0,
       
   378       ),
       
   379     )
       
   380   );
       
   381 
       
   382   return $styles;
       
   383 }
       
   384 
       
   385 /**
       
   386  * Implements hook_image_style_save().
       
   387  */
       
   388 function image_image_style_save($style) {
       
   389   if (isset($style['old_name']) && $style['old_name'] != $style['name']) {
       
   390     $instances = field_read_instances();
       
   391     // Loop through all fields searching for image fields.
       
   392     foreach ($instances as $instance) {
       
   393       if ($instance['widget']['module'] == 'image') {
       
   394         $instance_changed = FALSE;
       
   395         foreach ($instance['display'] as $view_mode => $display) {
       
   396           // Check if the formatter involves an image style.
       
   397           if ($display['type'] == 'image' && $display['settings']['image_style'] == $style['old_name']) {
       
   398             // Update display information for any instance using the image
       
   399             // style that was just deleted.
       
   400             $instance['display'][$view_mode]['settings']['image_style'] = $style['name'];
       
   401             $instance_changed = TRUE;
       
   402           }
       
   403         }
       
   404         if ($instance['widget']['settings']['preview_image_style'] == $style['old_name']) {
       
   405           $instance['widget']['settings']['preview_image_style'] = $style['name'];
       
   406           $instance_changed = TRUE;
       
   407         }
       
   408         if ($instance_changed) {
       
   409           field_update_instance($instance);
       
   410         }
       
   411       }
       
   412     }
       
   413   }
       
   414 }
       
   415 
       
   416 /**
       
   417  * Implements hook_image_style_delete().
       
   418  */
       
   419 function image_image_style_delete($style) {
       
   420   image_image_style_save($style);
       
   421 }
       
   422 
       
   423 /**
       
   424  * Implements hook_field_delete_field().
       
   425  */
       
   426 function image_field_delete_field($field) {
       
   427   if ($field['type'] != 'image') {
       
   428     return;
       
   429   }
       
   430 
       
   431   // The value of a managed_file element can be an array if #extended == TRUE.
       
   432   $fid = (is_array($field['settings']['default_image']) ? $field['settings']['default_image']['fid'] : $field['settings']['default_image']);
       
   433   if ($fid && ($file = file_load($fid))) {
       
   434     file_usage_delete($file, 'image', 'default_image', $field['id']);
       
   435   }
       
   436 }
       
   437 
       
   438 /**
       
   439  * Implements hook_field_update_field().
       
   440  */
       
   441 function image_field_update_field($field, $prior_field, $has_data) {
       
   442   if ($field['type'] != 'image') {
       
   443     return;
       
   444   }
       
   445 
       
   446   // The value of a managed_file element can be an array if #extended == TRUE.
       
   447   $fid_new = (is_array($field['settings']['default_image']) ? $field['settings']['default_image']['fid'] : $field['settings']['default_image']);
       
   448   $fid_old = (is_array($prior_field['settings']['default_image']) ? $prior_field['settings']['default_image']['fid'] : $prior_field['settings']['default_image']);
       
   449 
       
   450   $file_new = $fid_new ? file_load($fid_new) : FALSE;
       
   451 
       
   452   if ($fid_new != $fid_old) {
       
   453 
       
   454     // Is there a new file?
       
   455     if ($file_new) {
       
   456       $file_new->status = FILE_STATUS_PERMANENT;
       
   457       file_save($file_new);
       
   458       file_usage_add($file_new, 'image', 'default_image', $field['id']);
       
   459     }
       
   460 
       
   461     // Is there an old file?
       
   462     if ($fid_old && ($file_old = file_load($fid_old))) {
       
   463       file_usage_delete($file_old, 'image', 'default_image', $field['id']);
       
   464     }
       
   465   }
       
   466 
       
   467   // If the upload destination changed, then move the file.
       
   468   if ($file_new && (file_uri_scheme($file_new->uri) != $field['settings']['uri_scheme'])) {
       
   469     $directory = $field['settings']['uri_scheme'] . '://default_images/';
       
   470     file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
       
   471     file_move($file_new, $directory . $file_new->filename);
       
   472   }
       
   473 }
       
   474 
       
   475 /**
       
   476  * Implements hook_field_delete_instance().
       
   477  */
       
   478 function image_field_delete_instance($instance) {
       
   479   // Only act on image fields.
       
   480   $field = field_read_field($instance['field_name']);
       
   481   if ($field['type'] != 'image') {
       
   482     return;
       
   483   }
       
   484 
       
   485   // The value of a managed_file element can be an array if the #extended
       
   486   // property is set to TRUE.
       
   487   $fid = $instance['settings']['default_image'];
       
   488   if (is_array($fid)) {
       
   489     $fid = $fid['fid'];
       
   490   }
       
   491 
       
   492   // Remove the default image when the instance is deleted.
       
   493   if ($fid && ($file = file_load($fid))) {
       
   494     file_usage_delete($file, 'image', 'default_image', $instance['id']);
       
   495   }
       
   496 }
       
   497 
       
   498 /**
       
   499  * Implements hook_field_update_instance().
       
   500  */
       
   501 function image_field_update_instance($instance, $prior_instance) {
       
   502   // Only act on image fields.
       
   503   $field = field_read_field($instance['field_name']);
       
   504   if ($field['type'] != 'image') {
       
   505     return;
       
   506   }
       
   507 
       
   508   // The value of a managed_file element can be an array if the #extended
       
   509   // property is set to TRUE.
       
   510   $fid_new = $instance['settings']['default_image'];
       
   511   if (is_array($fid_new)) {
       
   512     $fid_new = $fid_new['fid'];
       
   513   }
       
   514   $fid_old = $prior_instance['settings']['default_image'];
       
   515   if (is_array($fid_old)) {
       
   516     $fid_old = $fid_old['fid'];
       
   517   }
       
   518 
       
   519   // If the old and new files do not match, update the default accordingly.
       
   520   $file_new = $fid_new ? file_load($fid_new) : FALSE;
       
   521   if ($fid_new != $fid_old) {
       
   522     // Save the new file, if present.
       
   523     if ($file_new) {
       
   524       $file_new->status = FILE_STATUS_PERMANENT;
       
   525       file_save($file_new);
       
   526       file_usage_add($file_new, 'image', 'default_image', $instance['id']);
       
   527     }
       
   528     // Delete the old file, if present.
       
   529     if ($fid_old && ($file_old = file_load($fid_old))) {
       
   530       file_usage_delete($file_old, 'image', 'default_image', $instance['id']);
       
   531     }
       
   532   }
       
   533 
       
   534   // If the upload destination changed, then move the file.
       
   535   if ($file_new && (file_uri_scheme($file_new->uri) != $field['settings']['uri_scheme'])) {
       
   536     $directory = $field['settings']['uri_scheme'] . '://default_images/';
       
   537     file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
       
   538     file_move($file_new, $directory . $file_new->filename);
       
   539   }
       
   540 }
       
   541 
       
   542 /**
       
   543  * Clears cached versions of a specific file in all styles.
       
   544  *
       
   545  * @param $path
       
   546  *   The Drupal file path to the original image.
       
   547  */
       
   548 function image_path_flush($path) {
       
   549   $styles = image_styles();
       
   550   foreach ($styles as $style) {
       
   551     $image_path = image_style_path($style['name'], $path);
       
   552     if (file_exists($image_path)) {
       
   553       file_unmanaged_delete($image_path);
       
   554     }
       
   555   }
       
   556 }
       
   557 
       
   558 /**
       
   559  * Gets an array of all styles and their settings.
       
   560  *
       
   561  * @return
       
   562  *   An array of styles keyed by the image style ID (isid).
       
   563  * @see image_style_load()
       
   564  */
       
   565 function image_styles() {
       
   566   $styles = &drupal_static(__FUNCTION__);
       
   567 
       
   568   // Grab from cache or build the array.
       
   569   if (!isset($styles)) {
       
   570     if ($cache = cache_get('image_styles', 'cache')) {
       
   571       $styles = $cache->data;
       
   572     }
       
   573     else {
       
   574       $styles = array();
       
   575 
       
   576       // Select the module-defined styles.
       
   577       foreach (module_implements('image_default_styles') as $module) {
       
   578         $module_styles = module_invoke($module, 'image_default_styles');
       
   579         foreach ($module_styles as $style_name => $style) {
       
   580           $style['name'] = $style_name;
       
   581           $style['label'] = empty($style['label']) ? $style_name : $style['label'];
       
   582           $style['module'] = $module;
       
   583           $style['storage'] = IMAGE_STORAGE_DEFAULT;
       
   584           foreach ($style['effects'] as $key => $effect) {
       
   585             $definition = image_effect_definition_load($effect['name']);
       
   586             $effect = array_merge($definition, $effect);
       
   587             $style['effects'][$key] = $effect;
       
   588           }
       
   589           $styles[$style_name] = $style;
       
   590         }
       
   591       }
       
   592 
       
   593       // Select all the user-defined styles.
       
   594       $user_styles = db_select('image_styles', NULL, array('fetch' => PDO::FETCH_ASSOC))
       
   595         ->fields('image_styles')
       
   596         ->orderBy('name')
       
   597         ->execute()
       
   598         ->fetchAllAssoc('name', PDO::FETCH_ASSOC);
       
   599 
       
   600       // Allow the user styles to override the module styles.
       
   601       foreach ($user_styles as $style_name => $style) {
       
   602         $style['module'] = NULL;
       
   603         $style['storage'] = IMAGE_STORAGE_NORMAL;
       
   604         $style['effects'] = image_style_effects($style);
       
   605         if (isset($styles[$style_name]['module'])) {
       
   606           $style['module'] = $styles[$style_name]['module'];
       
   607           $style['storage'] = IMAGE_STORAGE_OVERRIDE;
       
   608         }
       
   609         $styles[$style_name] = $style;
       
   610       }
       
   611 
       
   612       drupal_alter('image_styles', $styles);
       
   613       cache_set('image_styles', $styles);
       
   614     }
       
   615   }
       
   616 
       
   617   return $styles;
       
   618 }
       
   619 
       
   620 /**
       
   621  * Loads a style by style name or ID.
       
   622  *
       
   623  * May be used as a loader for menu items.
       
   624  *
       
   625  * @param $name
       
   626  *   The name of the style.
       
   627  * @param $isid
       
   628  *   Optional. The numeric id of a style if the name is not known.
       
   629  * @param $include
       
   630  *   If set, this loader will restrict to a specific type of image style, may be
       
   631  *   one of the defined Image style storage constants.
       
   632  *
       
   633  * @return
       
   634  *   An image style array containing the following keys:
       
   635  *   - "isid": The unique image style ID.
       
   636  *   - "name": The unique image style name.
       
   637  *   - "effects": An array of image effects within this image style.
       
   638  *   If the image style name or ID is not valid, an empty array is returned.
       
   639  * @see image_effect_load()
       
   640  */
       
   641 function image_style_load($name = NULL, $isid = NULL, $include = NULL) {
       
   642   $styles = image_styles();
       
   643 
       
   644   // If retrieving by name.
       
   645   if (isset($name) && isset($styles[$name])) {
       
   646     $style = $styles[$name];
       
   647   }
       
   648 
       
   649   // If retrieving by image style id.
       
   650   if (!isset($name) && isset($isid)) {
       
   651     foreach ($styles as $name => $database_style) {
       
   652       if (isset($database_style['isid']) && $database_style['isid'] == $isid) {
       
   653         $style = $database_style;
       
   654         break;
       
   655       }
       
   656     }
       
   657   }
       
   658 
       
   659   // Restrict to the specific type of flag. This bitwise operation basically
       
   660   // states "if the storage is X, then allow".
       
   661   if (isset($style) && (!isset($include) || ($style['storage'] & (int) $include))) {
       
   662     return $style;
       
   663   }
       
   664 
       
   665   // Otherwise the style was not found.
       
   666   return FALSE;
       
   667 }
       
   668 
       
   669 /**
       
   670  * Saves an image style.
       
   671  *
       
   672  * @param array $style
       
   673  *   An image style array containing:
       
   674  *   - name: A unique name for the style.
       
   675  *   - isid: (optional) An image style ID.
       
   676  *
       
   677  * @return array
       
   678  *   An image style array containing:
       
   679  *   - name: An unique name for the style.
       
   680  *   - old_name: The original name for the style.
       
   681  *   - isid: An image style ID.
       
   682  *   - is_new: TRUE if this is a new style, and FALSE if it is an existing
       
   683  *     style.
       
   684  */
       
   685 function image_style_save($style) {
       
   686   if (isset($style['isid']) && is_numeric($style['isid'])) {
       
   687     // Load the existing style to make sure we account for renamed styles.
       
   688     $old_style = image_style_load(NULL, $style['isid']);
       
   689     image_style_flush($old_style);
       
   690     drupal_write_record('image_styles', $style, 'isid');
       
   691     if ($old_style['name'] != $style['name']) {
       
   692       $style['old_name'] = $old_style['name'];
       
   693     }
       
   694   }
       
   695   else {
       
   696     // Add a default label when not given.
       
   697     if (empty($style['label'])) {
       
   698       $style['label'] = $style['name'];
       
   699     }
       
   700     drupal_write_record('image_styles', $style);
       
   701     $style['is_new'] = TRUE;
       
   702   }
       
   703 
       
   704   // Let other modules update as necessary on save.
       
   705   module_invoke_all('image_style_save', $style);
       
   706 
       
   707   // Clear all caches and flush.
       
   708   image_style_flush($style);
       
   709 
       
   710   return $style;
       
   711 }
       
   712 
       
   713 /**
       
   714  * Deletes an image style.
       
   715  *
       
   716  * @param $style
       
   717  *   An image style array.
       
   718  * @param $replacement_style_name
       
   719  *   (optional) When deleting a style, specify a replacement style name so
       
   720  *   that existing settings (if any) may be converted to a new style.
       
   721  *
       
   722  * @return
       
   723  *   TRUE on success.
       
   724  */
       
   725 function image_style_delete($style, $replacement_style_name = '') {
       
   726   image_style_flush($style);
       
   727 
       
   728   db_delete('image_effects')->condition('isid', $style['isid'])->execute();
       
   729   db_delete('image_styles')->condition('isid', $style['isid'])->execute();
       
   730 
       
   731   // Let other modules update as necessary on save.
       
   732   $style['old_name'] = $style['name'];
       
   733   $style['name'] = $replacement_style_name;
       
   734   module_invoke_all('image_style_delete', $style);
       
   735 
       
   736   return TRUE;
       
   737 }
       
   738 
       
   739 /**
       
   740  * Loads all the effects for an image style.
       
   741  *
       
   742  * @param array $style
       
   743  *   An image style array containing:
       
   744  *   - isid: The unique image style ID that contains this image effect.
       
   745  *
       
   746  * @return array
       
   747  *   An array of image effects associated with specified image style in the
       
   748  *   format array('isid' => array()), or an empty array if the specified style
       
   749  *   has no effects.
       
   750  * @see image_effects()
       
   751  */
       
   752 function image_style_effects($style) {
       
   753   $effects = image_effects();
       
   754   $style_effects = array();
       
   755   foreach ($effects as $effect) {
       
   756     if ($style['isid'] == $effect['isid']) {
       
   757       $style_effects[$effect['ieid']] = $effect;
       
   758     }
       
   759   }
       
   760 
       
   761   return $style_effects;
       
   762 }
       
   763 
       
   764 /**
       
   765  * Gets an array of image styles suitable for using as select list options.
       
   766  *
       
   767  * @param $include_empty
       
   768  *   If TRUE a <none> option will be inserted in the options array.
       
   769  * @param $output
       
   770  *   Optional flag determining how the options will be sanitized on output.
       
   771  *   Leave this at the default (CHECK_PLAIN) if you are using the output of
       
   772  *   this function directly in an HTML context, such as for checkbox or radio
       
   773  *   button labels, and do not plan to sanitize it on your own. If using the
       
   774  *   output of this function as select list options (its primary use case), you
       
   775  *   should instead set this flag to PASS_THROUGH to avoid double-escaping of
       
   776  *   the output (the form API sanitizes select list options by default).
       
   777  *
       
   778  * @return
       
   779  *   Array of image styles with the machine name as key and the label as value.
       
   780  */
       
   781 function image_style_options($include_empty = TRUE, $output = CHECK_PLAIN) {
       
   782   $styles = image_styles();
       
   783   $options = array();
       
   784   if ($include_empty && !empty($styles)) {
       
   785     $options[''] = t('<none>');
       
   786   }
       
   787   foreach ($styles as $name => $style) {
       
   788     $options[$name] = ($output == PASS_THROUGH) ? $style['label'] : check_plain($style['label']);
       
   789   }
       
   790 
       
   791   if (empty($options)) {
       
   792     $options[''] = t('No defined styles');
       
   793   }
       
   794   return $options;
       
   795 }
       
   796 
       
   797 /**
       
   798  * Page callback: Generates a derivative, given a style and image path.
       
   799  *
       
   800  * After generating an image, transfer it to the requesting agent.
       
   801  *
       
   802  * @param $style
       
   803  *   The image style
       
   804  * @param $scheme
       
   805  *   The file scheme, for example 'public' for public files.
       
   806  */
       
   807 function image_style_deliver($style, $scheme) {
       
   808   $args = func_get_args();
       
   809   array_shift($args);
       
   810   array_shift($args);
       
   811   $target = implode('/', $args);
       
   812 
       
   813   // Check that the style is defined, the scheme is valid, and the image
       
   814   // derivative token is valid. (Sites which require image derivatives to be
       
   815   // generated without a token can set the 'image_allow_insecure_derivatives'
       
   816   // variable to TRUE to bypass the latter check, but this will increase the
       
   817   // site's vulnerability to denial-of-service attacks. To prevent this
       
   818   // variable from leaving the site vulnerable to the most serious attacks, a
       
   819   // token is always required when a derivative of a derivative is requested.)
       
   820   $valid = !empty($style) && file_stream_wrapper_valid_scheme($scheme);
       
   821   if (!variable_get('image_allow_insecure_derivatives', FALSE) || strpos(ltrim($target, '\/'), 'styles/') === 0) {
       
   822     $valid = $valid && isset($_GET[IMAGE_DERIVATIVE_TOKEN]) && $_GET[IMAGE_DERIVATIVE_TOKEN] === image_style_path_token($style['name'], $scheme . '://' . $target);
       
   823   }
       
   824   if (!$valid) {
       
   825     return MENU_ACCESS_DENIED;
       
   826   }
       
   827 
       
   828   $image_uri = $scheme . '://' . $target;
       
   829   $derivative_uri = image_style_path($style['name'], $image_uri);
       
   830 
       
   831   // If using the private scheme, let other modules provide headers and
       
   832   // control access to the file.
       
   833   if ($scheme == 'private') {
       
   834     if (file_exists($derivative_uri)) {
       
   835       file_download($scheme, file_uri_target($derivative_uri));
       
   836     }
       
   837     else {
       
   838       $headers = file_download_headers($image_uri);
       
   839       if (empty($headers)) {
       
   840         return MENU_ACCESS_DENIED;
       
   841       }
       
   842       if (count($headers)) {
       
   843         foreach ($headers as $name => $value) {
       
   844           drupal_add_http_header($name, $value);
       
   845         }
       
   846       }
       
   847     }
       
   848   }
       
   849 
       
   850   // Confirm that the original source image exists before trying to process it.
       
   851   if (!is_file($image_uri)) {
       
   852     watchdog('image', 'Source image at %source_image_path not found while trying to generate derivative image at %derivative_path.',  array('%source_image_path' => $image_uri, '%derivative_path' => $derivative_uri));
       
   853     return MENU_NOT_FOUND;
       
   854   }
       
   855 
       
   856   // Don't start generating the image if the derivative already exists or if
       
   857   // generation is in progress in another thread.
       
   858   $lock_name = 'image_style_deliver:' . $style['name'] . ':' . drupal_hash_base64($image_uri);
       
   859   if (!file_exists($derivative_uri)) {
       
   860     $lock_acquired = lock_acquire($lock_name);
       
   861     if (!$lock_acquired) {
       
   862       // Tell client to retry again in 3 seconds. Currently no browsers are known
       
   863       // to support Retry-After.
       
   864       drupal_add_http_header('Status', '503 Service Unavailable');
       
   865       drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
       
   866       drupal_add_http_header('Retry-After', 3);
       
   867       print t('Image generation in progress. Try again shortly.');
       
   868       drupal_exit();
       
   869     }
       
   870   }
       
   871 
       
   872   // Try to generate the image, unless another thread just did it while we were
       
   873   // acquiring the lock.
       
   874   $success = file_exists($derivative_uri) || image_style_create_derivative($style, $image_uri, $derivative_uri);
       
   875 
       
   876   if (!empty($lock_acquired)) {
       
   877     lock_release($lock_name);
       
   878   }
       
   879 
       
   880   if ($success) {
       
   881     $image = image_load($derivative_uri);
       
   882     file_transfer($image->source, array('Content-Type' => $image->info['mime_type'], 'Content-Length' => $image->info['file_size']));
       
   883   }
       
   884   else {
       
   885     watchdog('image', 'Unable to generate the derived image located at %path.', array('%path' => $derivative_uri));
       
   886     drupal_add_http_header('Status', '500 Internal Server Error');
       
   887     drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
       
   888     print t('Error generating image.');
       
   889     drupal_exit();
       
   890   }
       
   891 }
       
   892 
       
   893 /**
       
   894  * Creates a new image derivative based on an image style.
       
   895  *
       
   896  * Generates an image derivative by creating the destination folder (if it does
       
   897  * not already exist), applying all image effects defined in $style['effects'],
       
   898  * and saving a cached version of the resulting image.
       
   899  *
       
   900  * @param $style
       
   901  *   An image style array.
       
   902  * @param $source
       
   903  *   Path of the source file.
       
   904  * @param $destination
       
   905  *   Path or URI of the destination file.
       
   906  *
       
   907  * @return
       
   908  *   TRUE if an image derivative was generated, or FALSE if the image derivative
       
   909  *   could not be generated.
       
   910  *
       
   911  * @see image_style_load()
       
   912  */
       
   913 function image_style_create_derivative($style, $source, $destination) {
       
   914   // If the source file doesn't exist, return FALSE without creating folders.
       
   915   if (!$image = image_load($source)) {
       
   916     return FALSE;
       
   917   }
       
   918 
       
   919   // Get the folder for the final location of this style.
       
   920   $directory = drupal_dirname($destination);
       
   921 
       
   922   // Build the destination folder tree if it doesn't already exist.
       
   923   if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
       
   924     watchdog('image', 'Failed to create style directory: %directory', array('%directory' => $directory), WATCHDOG_ERROR);
       
   925     return FALSE;
       
   926   }
       
   927 
       
   928   foreach ($style['effects'] as $effect) {
       
   929     image_effect_apply($image, $effect);
       
   930   }
       
   931 
       
   932   if (!image_save($image, $destination)) {
       
   933     if (file_exists($destination)) {
       
   934       watchdog('image', 'Cached image file %destination already exists. There may be an issue with your rewrite configuration.', array('%destination' => $destination), WATCHDOG_ERROR);
       
   935     }
       
   936     return FALSE;
       
   937   }
       
   938 
       
   939   return TRUE;
       
   940 }
       
   941 
       
   942 /**
       
   943  * Determines the dimensions of the styled image.
       
   944  *
       
   945  * Applies all of an image style's effects to $dimensions.
       
   946  *
       
   947  * @param $style_name
       
   948  *   The name of the style to be applied.
       
   949  * @param $dimensions
       
   950  *   Dimensions to be modified - an array with components width and height, in
       
   951  *   pixels.
       
   952  */
       
   953 function image_style_transform_dimensions($style_name, array &$dimensions) {
       
   954   module_load_include('inc', 'image', 'image.effects');
       
   955   $style = image_style_load($style_name);
       
   956 
       
   957   if (!is_array($style)) {
       
   958     return;
       
   959   }
       
   960 
       
   961   foreach ($style['effects'] as $effect) {
       
   962     if (isset($effect['dimensions passthrough'])) {
       
   963       continue;
       
   964     }
       
   965 
       
   966     if (isset($effect['dimensions callback'])) {
       
   967       $effect['dimensions callback']($dimensions, $effect['data']);
       
   968     }
       
   969     else {
       
   970       $dimensions['width'] = $dimensions['height'] = NULL;
       
   971     }
       
   972   }
       
   973 }
       
   974 
       
   975 /**
       
   976  * Flushes cached media for a style.
       
   977  *
       
   978  * @param $style
       
   979  *   An image style array.
       
   980  */
       
   981 function image_style_flush($style) {
       
   982   // Delete the style directory in each registered wrapper.
       
   983   $wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE);
       
   984   foreach ($wrappers as $wrapper => $wrapper_data) {
       
   985     if (file_exists($directory = $wrapper . '://styles/' . $style['name'])) {
       
   986       file_unmanaged_delete_recursive($directory);
       
   987     }
       
   988   }
       
   989 
       
   990   // Let other modules update as necessary on flush.
       
   991   module_invoke_all('image_style_flush', $style);
       
   992 
       
   993   // Clear image style and effect caches.
       
   994   cache_clear_all('image_styles', 'cache');
       
   995   cache_clear_all('image_effects:', 'cache', TRUE);
       
   996   drupal_static_reset('image_styles');
       
   997   drupal_static_reset('image_effects');
       
   998 
       
   999   // Clear field caches so that formatters may be added for this style.
       
  1000   field_info_cache_clear();
       
  1001   drupal_theme_rebuild();
       
  1002 
       
  1003   // Clear page caches when flushing.
       
  1004   if (module_exists('block')) {
       
  1005     cache_clear_all('*', 'cache_block', TRUE);
       
  1006   }
       
  1007   cache_clear_all('*', 'cache_page', TRUE);
       
  1008 }
       
  1009 
       
  1010 /**
       
  1011  * Returns the URL for an image derivative given a style and image path.
       
  1012  *
       
  1013  * @param $style_name
       
  1014  *   The name of the style to be used with this image.
       
  1015  * @param $path
       
  1016  *   The path to the image.
       
  1017  *
       
  1018  * @return
       
  1019  *   The absolute URL where a style image can be downloaded, suitable for use
       
  1020  *   in an <img> tag. Requesting the URL will cause the image to be created.
       
  1021  * @see image_style_deliver()
       
  1022  */
       
  1023 function image_style_url($style_name, $path) {
       
  1024   $uri = image_style_path($style_name, $path);
       
  1025 
       
  1026   // The passed-in $path variable can be either a relative path or a full URI.
       
  1027   $original_uri = file_uri_scheme($path) ? file_stream_wrapper_uri_normalize($path) : file_build_uri($path);
       
  1028 
       
  1029   // The token query is added even if the 'image_allow_insecure_derivatives'
       
  1030   // variable is TRUE, so that the emitted links remain valid if it is changed
       
  1031   // back to the default FALSE.
       
  1032   // However, sites which need to prevent the token query from being emitted at
       
  1033   // all can additionally set the 'image_suppress_itok_output' variable to TRUE
       
  1034   // to achieve that (if both are set, the security token will neither be
       
  1035   // emitted in the image derivative URL nor checked for in
       
  1036   // image_style_deliver()).
       
  1037   $token_query = array();
       
  1038   if (!variable_get('image_suppress_itok_output', FALSE)) {
       
  1039     $token_query = array(IMAGE_DERIVATIVE_TOKEN => image_style_path_token($style_name, $original_uri));
       
  1040   }
       
  1041 
       
  1042   // If not using clean URLs, the image derivative callback is only available
       
  1043   // with the query string. If the file does not exist, use url() to ensure
       
  1044   // that it is included. Once the file exists it's fine to fall back to the
       
  1045   // actual file path, this avoids bootstrapping PHP once the files are built.
       
  1046   if (!variable_get('clean_url') && file_uri_scheme($uri) == 'public' && !file_exists($uri)) {
       
  1047     $directory_path = file_stream_wrapper_get_instance_by_uri($uri)->getDirectoryPath();
       
  1048     return url($directory_path . '/' . file_uri_target($uri), array('absolute' => TRUE, 'query' => $token_query));
       
  1049   }
       
  1050 
       
  1051   $file_url = file_create_url($uri);
       
  1052   // Append the query string with the token, if necessary.
       
  1053   if ($token_query) {
       
  1054     $file_url .= (strpos($file_url, '?') !== FALSE ? '&' : '?') . drupal_http_build_query($token_query);
       
  1055   }
       
  1056 
       
  1057   return $file_url;
       
  1058 }
       
  1059 
       
  1060 /**
       
  1061  * Generates a token to protect an image style derivative.
       
  1062  *
       
  1063  * This prevents unauthorized generation of an image style derivative,
       
  1064  * which can be costly both in CPU time and disk space.
       
  1065  *
       
  1066  * @param $style_name
       
  1067  *   The name of the image style.
       
  1068  * @param $uri
       
  1069  *   The URI of the image for this style, for example as returned by
       
  1070  *   image_style_path().
       
  1071  *
       
  1072  * @return
       
  1073  *   An eight-character token which can be used to protect image style
       
  1074  *   derivatives against denial-of-service attacks.
       
  1075  */
       
  1076 function image_style_path_token($style_name, $uri) {
       
  1077   // Return the first eight characters.
       
  1078   return substr(drupal_hmac_base64($style_name . ':' . $uri, drupal_get_private_key() . drupal_get_hash_salt()), 0, 8);
       
  1079 }
       
  1080 
       
  1081 /**
       
  1082  * Returns the URI of an image when using a style.
       
  1083  *
       
  1084  * The path returned by this function may not exist. The default generation
       
  1085  * method only creates images when they are requested by a user's browser.
       
  1086  *
       
  1087  * @param $style_name
       
  1088  *   The name of the style to be used with this image.
       
  1089  * @param $uri
       
  1090  *   The URI or path to the image.
       
  1091  *
       
  1092  * @return
       
  1093  *   The URI to an image style image.
       
  1094  * @see image_style_url()
       
  1095  */
       
  1096 function image_style_path($style_name, $uri) {
       
  1097   $scheme = file_uri_scheme($uri);
       
  1098   if ($scheme) {
       
  1099     $path = file_uri_target($uri);
       
  1100   }
       
  1101   else {
       
  1102     $path = $uri;
       
  1103     $scheme = file_default_scheme();
       
  1104   }
       
  1105   return $scheme . '://styles/' . $style_name . '/' . $scheme . '/' . $path;
       
  1106 }
       
  1107 
       
  1108 /**
       
  1109  * Saves a default image style to the database.
       
  1110  *
       
  1111  * @param style
       
  1112  *   An image style array provided by a module.
       
  1113  *
       
  1114  * @return
       
  1115  *   An image style array. The returned style array will include the new 'isid'
       
  1116  *   assigned to the style.
       
  1117  */
       
  1118 function image_default_style_save($style) {
       
  1119   $style = image_style_save($style);
       
  1120   $effects = array();
       
  1121   foreach ($style['effects'] as $effect) {
       
  1122     $effect['isid'] = $style['isid'];
       
  1123     $effect = image_effect_save($effect);
       
  1124     $effects[$effect['ieid']] = $effect;
       
  1125   }
       
  1126   $style['effects'] = $effects;
       
  1127   return $style;
       
  1128 }
       
  1129 
       
  1130 /**
       
  1131  * Reverts the changes made by users to a default image style.
       
  1132  *
       
  1133  * @param style
       
  1134  *   An image style array.
       
  1135  * @return
       
  1136  *   Boolean TRUE if the operation succeeded.
       
  1137  */
       
  1138 function image_default_style_revert($style) {
       
  1139   image_style_flush($style);
       
  1140 
       
  1141   db_delete('image_effects')->condition('isid', $style['isid'])->execute();
       
  1142   db_delete('image_styles')->condition('isid', $style['isid'])->execute();
       
  1143 
       
  1144   return TRUE;
       
  1145 }
       
  1146 
       
  1147 /**
       
  1148  * Returns a set of image effects.
       
  1149  *
       
  1150  * These image effects are exposed by modules implementing
       
  1151  * hook_image_effect_info().
       
  1152  *
       
  1153  * @return
       
  1154  *   An array of image effects to be used when transforming images.
       
  1155  * @see hook_image_effect_info()
       
  1156  * @see image_effect_definition_load()
       
  1157  */
       
  1158 function image_effect_definitions() {
       
  1159   global $language;
       
  1160 
       
  1161   // hook_image_effect_info() includes translated strings, so each language is
       
  1162   // cached separately.
       
  1163   $langcode = $language->language;
       
  1164 
       
  1165   $effects = &drupal_static(__FUNCTION__);
       
  1166 
       
  1167   if (!isset($effects)) {
       
  1168     if ($cache = cache_get("image_effects:$langcode")) {
       
  1169       $effects = $cache->data;
       
  1170     }
       
  1171     else {
       
  1172       $effects = array();
       
  1173       include_once DRUPAL_ROOT . '/modules/image/image.effects.inc';
       
  1174       foreach (module_implements('image_effect_info') as $module) {
       
  1175         foreach (module_invoke($module, 'image_effect_info') as $name => $effect) {
       
  1176           // Ensure the current toolkit supports the effect.
       
  1177           $effect['module'] = $module;
       
  1178           $effect['name'] = $name;
       
  1179           $effect['data'] = isset($effect['data']) ? $effect['data'] : array();
       
  1180           $effects[$name] = $effect;
       
  1181         }
       
  1182       }
       
  1183       uasort($effects, '_image_effect_definitions_sort');
       
  1184       drupal_alter('image_effect_info', $effects);
       
  1185       cache_set("image_effects:$langcode", $effects);
       
  1186     }
       
  1187   }
       
  1188 
       
  1189   return $effects;
       
  1190 }
       
  1191 
       
  1192 /**
       
  1193  * Loads the definition for an image effect.
       
  1194  *
       
  1195  * The effect definition is a set of core properties for an image effect, not
       
  1196  * containing any user-settings. The definition defines various functions to
       
  1197  * call when configuring or executing an image effect. This loader is mostly for
       
  1198  * internal use within image.module. Use image_effect_load() or
       
  1199  * image_style_load() to get image effects that contain configuration.
       
  1200  *
       
  1201  * @param $effect
       
  1202  *   The name of the effect definition to load.
       
  1203  * @param $style
       
  1204  *   An image style array to which this effect will be added.
       
  1205  *
       
  1206  * @return
       
  1207  *   An array containing the image effect definition with the following keys:
       
  1208  *   - "effect": The unique name for the effect being performed. Usually prefixed
       
  1209  *     with the name of the module providing the effect.
       
  1210  *   - "module": The module providing the effect.
       
  1211  *   - "help": A description of the effect.
       
  1212  *   - "function": The name of the function that will execute the effect.
       
  1213  *   - "form": (optional) The name of a function to configure the effect.
       
  1214  *   - "summary": (optional) The name of a theme function that will display a
       
  1215  *     one-line summary of the effect. Does not include the "theme_" prefix.
       
  1216  */
       
  1217 function image_effect_definition_load($effect, $style_name = NULL) {
       
  1218   $definitions = image_effect_definitions();
       
  1219 
       
  1220   // If a style is specified, do not allow loading of default style
       
  1221   // effects.
       
  1222   if (isset($style_name)) {
       
  1223     $style = image_style_load($style_name, NULL);
       
  1224     if ($style['storage'] == IMAGE_STORAGE_DEFAULT) {
       
  1225       return FALSE;
       
  1226     }
       
  1227   }
       
  1228 
       
  1229   return isset($definitions[$effect]) ? $definitions[$effect] : FALSE;
       
  1230 }
       
  1231 
       
  1232 /**
       
  1233  * Loads all image effects from the database.
       
  1234  *
       
  1235  * @return
       
  1236  *   An array of all image effects.
       
  1237  * @see image_effect_load()
       
  1238  */
       
  1239 function image_effects() {
       
  1240   $effects = &drupal_static(__FUNCTION__);
       
  1241 
       
  1242   if (!isset($effects)) {
       
  1243     $effects = array();
       
  1244 
       
  1245     // Add database image effects.
       
  1246     $result = db_select('image_effects', NULL, array('fetch' => PDO::FETCH_ASSOC))
       
  1247       ->fields('image_effects')
       
  1248       ->orderBy('image_effects.weight', 'ASC')
       
  1249       ->execute();
       
  1250     foreach ($result as $effect) {
       
  1251       $effect['data'] = unserialize($effect['data']);
       
  1252       $definition = image_effect_definition_load($effect['name']);
       
  1253       // Do not load image effects whose definition cannot be found.
       
  1254       if ($definition) {
       
  1255         $effect = array_merge($definition, $effect);
       
  1256         $effects[$effect['ieid']] = $effect;
       
  1257       }
       
  1258     }
       
  1259   }
       
  1260 
       
  1261   return $effects;
       
  1262 }
       
  1263 
       
  1264 /**
       
  1265  * Loads a single image effect.
       
  1266  *
       
  1267  * @param $ieid
       
  1268  *   The image effect ID.
       
  1269  * @param $style_name
       
  1270  *   The image style name.
       
  1271  * @param $include
       
  1272  *   If set, this loader will restrict to a specific type of image style, may be
       
  1273  *   one of the defined Image style storage constants.
       
  1274  *
       
  1275  * @return
       
  1276  *   An image effect array, consisting of the following keys:
       
  1277  *   - "ieid": The unique image effect ID.
       
  1278  *   - "isid": The unique image style ID that contains this image effect.
       
  1279  *   - "weight": The weight of this image effect within the image style.
       
  1280  *   - "name": The name of the effect definition that powers this image effect.
       
  1281  *   - "data": An array of configuration options for this image effect.
       
  1282  *   Besides these keys, the entirety of the image definition is merged into
       
  1283  *   the image effect array. Returns FALSE if the specified effect cannot be
       
  1284  *   found.
       
  1285  * @see image_style_load()
       
  1286  * @see image_effect_definition_load()
       
  1287  */
       
  1288 function image_effect_load($ieid, $style_name, $include = NULL) {
       
  1289   if (($style = image_style_load($style_name, NULL, $include)) && isset($style['effects'][$ieid])) {
       
  1290     return $style['effects'][$ieid];
       
  1291   }
       
  1292   return FALSE;
       
  1293 }
       
  1294 
       
  1295 /**
       
  1296  * Saves an image effect.
       
  1297  *
       
  1298  * @param $effect
       
  1299  *   An image effect array.
       
  1300  *
       
  1301  * @return
       
  1302  *   An image effect array. In the case of a new effect, 'ieid' will be set.
       
  1303  */
       
  1304 function image_effect_save($effect) {
       
  1305   if (!empty($effect['ieid'])) {
       
  1306     drupal_write_record('image_effects', $effect, 'ieid');
       
  1307   }
       
  1308   else {
       
  1309     drupal_write_record('image_effects', $effect);
       
  1310   }
       
  1311   $style = image_style_load(NULL, $effect['isid']);
       
  1312   image_style_flush($style);
       
  1313   return $effect;
       
  1314 }
       
  1315 
       
  1316 /**
       
  1317  * Deletes an image effect.
       
  1318  *
       
  1319  * @param $effect
       
  1320  *   An image effect array.
       
  1321  */
       
  1322 function image_effect_delete($effect) {
       
  1323   db_delete('image_effects')->condition('ieid', $effect['ieid'])->execute();
       
  1324   $style = image_style_load(NULL, $effect['isid']);
       
  1325   image_style_flush($style);
       
  1326 }
       
  1327 
       
  1328 /**
       
  1329  * Applies an image effect to the image object.
       
  1330  *
       
  1331  * @param $image
       
  1332  *   An image object returned by image_load().
       
  1333  * @param $effect
       
  1334  *   An image effect array.
       
  1335  *
       
  1336  * @return
       
  1337  *   TRUE on success. FALSE if unable to perform the image effect on the image.
       
  1338  */
       
  1339 function image_effect_apply($image, $effect) {
       
  1340   module_load_include('inc', 'image', 'image.effects');
       
  1341   $function = $effect['effect callback'];
       
  1342   if (function_exists($function)) {
       
  1343     return $function($image, $effect['data']);
       
  1344   }
       
  1345   return FALSE;
       
  1346 }
       
  1347 
       
  1348 /**
       
  1349  * Returns HTML for an image using a specific image style.
       
  1350  *
       
  1351  * @param $variables
       
  1352  *   An associative array containing:
       
  1353  *   - style_name: The name of the style to be used to alter the original image.
       
  1354  *   - path: The path of the image file relative to the Drupal files directory.
       
  1355  *     This function does not work with images outside the files directory nor
       
  1356  *     with remotely hosted images. This should be in a format such as
       
  1357  *     'images/image.jpg', or using a stream wrapper such as
       
  1358  *     'public://images/image.jpg'.
       
  1359  *   - width: The width of the source image (if known).
       
  1360  *   - height: The height of the source image (if known).
       
  1361  *   - alt: The alternative text for text-based browsers.
       
  1362  *   - title: The title text is displayed when the image is hovered in some
       
  1363  *     popular browsers.
       
  1364  *   - attributes: Associative array of attributes to be placed in the img tag.
       
  1365  *
       
  1366  * @ingroup themeable
       
  1367  */
       
  1368 function theme_image_style($variables) {
       
  1369   // Determine the dimensions of the styled image.
       
  1370   $dimensions = array(
       
  1371     'width' => $variables['width'],
       
  1372     'height' => $variables['height'],
       
  1373   );
       
  1374 
       
  1375   image_style_transform_dimensions($variables['style_name'], $dimensions);
       
  1376 
       
  1377   $variables['width'] = $dimensions['width'];
       
  1378   $variables['height'] = $dimensions['height'];
       
  1379 
       
  1380   // Determine the URL for the styled image.
       
  1381   $variables['path'] = image_style_url($variables['style_name'], $variables['path']);
       
  1382   return theme('image', $variables);
       
  1383 }
       
  1384 
       
  1385 /**
       
  1386  * Accepts a keyword (center, top, left, etc) and returns it as a pixel offset.
       
  1387  *
       
  1388  * @param $value
       
  1389  * @param $current_pixels
       
  1390  * @param $new_pixels
       
  1391  */
       
  1392 function image_filter_keyword($value, $current_pixels, $new_pixels) {
       
  1393   switch ($value) {
       
  1394     case 'top':
       
  1395     case 'left':
       
  1396       return 0;
       
  1397 
       
  1398     case 'bottom':
       
  1399     case 'right':
       
  1400       return $current_pixels - $new_pixels;
       
  1401 
       
  1402     case 'center':
       
  1403       return $current_pixels / 2 - $new_pixels / 2;
       
  1404   }
       
  1405   return $value;
       
  1406 }
       
  1407 
       
  1408 /**
       
  1409  * Internal function for sorting image effect definitions through uasort().
       
  1410  *
       
  1411  * @see image_effect_definitions()
       
  1412  */
       
  1413 function _image_effect_definitions_sort($a, $b) {
       
  1414   return strcasecmp($a['name'], $b['name']);
       
  1415 }