|
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 } |