| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 <?php 2 // $Id: imagecache.module,v 1.112.2.5 2009/08/19 20:59:07 drewish Exp $ 3 4 /** 5 * @file 6 * Dynamic image resizer and image cacher. 7 * 8 * ImageCache allows you to setup presets for image processing. 9 * If an ImageCache derivative doesn't exist the web server's 10 * rewrite rules will pass the request to Drupal which in turn 11 * hands it off to imagecache to dynamically generate the file. 12 * 13 * To view a derivative image you request a special url containing 14 * 'imagecache/<presetname>/path/to/file.ext. 15 * 16 * Presets can be managed at http://example.com/admin/build/imagecache. 17 * 18 * To view a derivative image you request a special url containing 19 * 'imagecache/<presetname>/path/to/file.ext. 20 * 21 * If you had a preset names 'thumbnail' and you wanted to see the 22 * thumbnail version of http://example.com/files/path/to/myimage.jpg you 23 * would use http://example.com/files/imagecache/thumbnail/path/to/myimage.jpg 24 * 25 * ImageCache provides formatters for CCK Imagefields and is leveraged by several 26 * other modules. ImageCache also relies heavily on ImageAPI for it's image processing. 27 * If there are errors with actual image processing look to ImageAPI first. 28 * 29 * @todo: add watermarking capabilities. 30 * 31 */ 32 33 /** 34 * Imagecache preset storage constant for user-defined presets in the DB. 35 */ 36 define('IMAGECACHE_STORAGE_NORMAL', 0); 37 38 /** 39 * Imagecache preset storage constant for module-defined presets in code. 40 */ 41 define('IMAGECACHE_STORAGE_DEFAULT', 1); 42 43 /** 44 * Imagecache preset storage constant for user-defined presets that override 45 * module-defined presets. 46 */ 47 define('IMAGECACHE_STORAGE_OVERRIDE', 2); 48 49 /********************************************************************************************* 50 * Drupal Hooks 51 *********************************************************************************************/ 52 53 /** 54 * Implementation of hook_perm(). 55 */ 56 function imagecache_perm() { 57 $perms = array('administer imagecache', 'flush imagecache'); 58 foreach (imagecache_presets() as $preset) { 59 $perms[] = 'view imagecache '. $preset['presetname']; 60 } 61 return $perms; 62 } 63 64 /** 65 * Implementation of hook_menu(). 66 */ 67 function imagecache_menu() { 68 $items = array(); 69 70 // standard imagecache callback. 71 $items[file_directory_path() .'/imagecache'] = array( 72 'page callback' => 'imagecache_cache', 73 'access callback' => TRUE, 74 'type' => MENU_CALLBACK 75 ); 76 // private downloads imagecache callback 77 $items['system/files/imagecache'] = array( 78 'page callback' => 'imagecache_cache_private', 79 'access callback' => TRUE, 80 'type' => MENU_CALLBACK 81 ); 82 83 return $items; 84 } 85 86 /** 87 * Clear imagecache presets cache on admin/build/modules form. 88 */ 89 function imagecache_form_system_modules_alter(&$form, $form_state) { 90 imagecache_presets(TRUE); 91 } 92 93 /** 94 * Implementation of hook_theme(). 95 */ 96 function imagecache_theme() { 97 $theme = array( 98 'imagecache' => array( 99 'arguments' => array( 100 'namespace' => NULL, 101 'path' => NULL, 102 'alt' => NULL, 103 'title' => NULL, 104 )), 105 'imagecache_imagelink' => array( 106 'arguments' => array( 107 'namespace' => NULL, 108 'path' => NULL, 109 'alt' => NULL, 110 'title' => NULL, 111 'attributes' => array(), 112 )), 113 'imagecache_resize' => array( 114 'file' => 'imagecache_actions.inc', 115 'arguments' => array('element' => NULL), 116 ), 117 'imagecache_scale' => array( 118 'file' => 'imagecache_actions.inc', 119 'arguments' => array('element' => NULL), 120 ), 121 'imagecache_scale_and_crop' => array( 122 'file' => 'imagecache_actions.inc', 123 'arguments' => array('element' => NULL), 124 ), 125 'imagecache_deprecated_scale' => array( 126 'file' => 'imagecache_actions.inc', 127 'arguments' => array('element' => NULL), 128 ), 129 'imagecache_crop' => array( 130 'file' => 'imagecache_actions.inc', 131 'arguments' => array('element' => NULL), 132 ), 133 'imagecache_desaturate' => array( 134 'file' => 'imagecache_actions.inc', 135 'arguments' => array('element' => NULL), 136 ), 137 'imagecache_rotate' => array( 138 'file' => 'imagecache_actions.inc', 139 'arguments' => array('element' => NULL), 140 ), 141 'imagecache_sharpen' => array( 142 'file' => 'imagecache_actions.inc', 143 'arguments' => array('element' => NULL), 144 ), 145 ); 146 147 foreach (imagecache_presets() as $preset) { 148 $theme['imagecache_formatter_'. $preset['presetname'] .'_default'] = array( 149 'arguments' => array('element' => NULL), 150 'function' => 'theme_imagecache_formatter_default', 151 ); 152 $theme['imagecache_formatter_'. $preset['presetname'] .'_linked'] = array( 153 'arguments' => array('element' => NULL), 154 'function' => 'theme_imagecache_formatter_linked', 155 ); 156 $theme['imagecache_formatter_'. $preset['presetname'] .'_imagelink'] = array( 157 'arguments' => array('element' => NULL), 158 'function' => 'theme_imagecache_formatter_imagelink', 159 ); 160 $theme['imagecache_formatter_'. $preset['presetname'] .'_path'] = array( 161 'arguments' => array('element' => NULL), 162 'function' => 'theme_imagecache_formatter_path', 163 ); 164 $theme['imagecache_formatter_'. $preset['presetname'] .'_url'] = array( 165 'arguments' => array('element' => NULL), 166 'function' => 'theme_imagecache_formatter_url', 167 ); 168 } 169 170 return $theme; 171 172 } 173 174 /** 175 * Implementation of hook_imagecache_actions. 176 * 177 * @return array 178 * An array of information on the actions implemented by a module. The array 179 * contains a sub-array for each action node type, with the machine-readable 180 * action name as the key. Each sub-array has up to 3 attributes. Possible 181 * attributes: 182 * 183 * "name": the human-readable name of the action. Required. 184 * "description": a brief description of the action. Required. 185 * "file": the name of the include file the action can be found 186 * in relative to the implementing module's path. 187 */ 188 function imagecache_imagecache_actions() { 189 $actions = array( 190 'imagecache_resize' => array( 191 'name' => 'Resize', 192 'description' => 'Resize an image to an exact set of dimensions, ignoring aspect ratio.', 193 'file' => 'imagecache_actions.inc', 194 ), 195 'imagecache_scale' => array( 196 'name' => 'Scale', 197 'description' => 'Resize an image maintaining the original aspect-ratio (only one value necessary).', 198 'file' => 'imagecache_actions.inc', 199 ), 200 'imagecache_deprecated_scale' => array( 201 'name' => 'Deprecated Scale', 202 'description' => 'Precursor to Scale and Crop. Has inside and outside dimension support. This action will be removed in ImageCache 2.1).', 203 'file' => 'imagecache_actions.inc', 204 ), 205 'imagecache_scale_and_crop' => array( 206 'name' => 'Scale And Crop', 207 'description' => 'Resize an image while maintaining aspect ratio, then crop it to the specified dimensions.', 208 'file' => 'imagecache_actions.inc', 209 ), 210 'imagecache_crop' => array( 211 'name' => 'Crop', 212 'description' => 'Crop an image to the rectangle specified by the given offsets and dimensions.', 213 'file' => 'imagecache_actions.inc', 214 ), 215 'imagecache_desaturate' => array( 216 'name' => 'Desaturate', 217 'description' => 'Convert an image to grey scale.', 218 'file' => 'imagecache_actions.inc', 219 ), 220 'imagecache_rotate' => array( 221 'name' => 'Rotate', 222 'description' => 'Rotate an image.', 223 'file' => 'imagecache_actions.inc', 224 ), 225 'imagecache_sharpen' => array( 226 'name' => 'Sharpen', 227 'description' => 'Sharpen an image using unsharp masking.', 228 'file' => 'imagecache_actions.inc', 229 ), 230 ); 231 232 return $actions; 233 } 234 235 /** 236 * Pull in actions exposed by other modules using hook_imagecache_actions(). 237 * 238 * @param $reset 239 * Boolean flag indicating whether the cached data should be 240 * wiped and recalculated. 241 * 242 * @return 243 * An array of actions to be used when transforming images. 244 */ 245 function imagecache_action_definitions($reset = FALSE) { 246 static $actions; 247 if (!isset($actions) || $reset) { 248 if (!$reset && ($cache = cache_get('imagecache_actions')) && !empty($cache->data)) { 249 $actions = $cache->data; 250 } 251 else { 252 foreach (module_implements('imagecache_actions') as $module) { 253 foreach (module_invoke($module, 'imagecache_actions') as $key => $action) { 254 $action['module'] = $module; 255 if (!empty($action['file'])) { 256 $action['file'] = drupal_get_path('module', $action['module']) .'/'. $action['file']; 257 } 258 $actions[$key] = $action; 259 }; 260 } 261 uasort($actions, '_imagecache_definitions_sort'); 262 cache_set('imagecache_actions', $actions); 263 } 264 } 265 return $actions; 266 } 267 268 function _imagecache_definitions_sort($a, $b) { 269 $a = $a['name']; 270 $b = $b['name']; 271 if ($a == $b) { 272 return 0; 273 } 274 return ($a < $b) ? -1 : 1; 275 } 276 277 function imagecache_action_definition($action) { 278 static $definition_cache; 279 if (!isset($definition_cache[$action])) { 280 $definitions = imagecache_action_definitions(); 281 $definition = (isset($definitions[$action])) ? $definitions[$action] : array(); 282 283 if (isset($definition['file'])) { 284 require_once($definition['file']); 285 } 286 $definition_cache[$action] = $definition; 287 } 288 return $definition_cache[$action]; 289 } 290 291 /** 292 * Return a URL that points to the location of a derivative of the 293 * original image transformed with the given preset. 294 * 295 * Special care is taken to make this work with the possible combinations of 296 * Clean URLs and public/private downloads. For example, when Clean URLs are not 297 * available an URL with query should be returned, like 298 * http://example.com/?q=files/imagecache/foo.jpg, so that imagecache is able 299 * intercept the request for this file. 300 * 301 * This code is very similar to the Drupal core function file_create_url(), but 302 * handles the case of Clean URLs and public downloads differently however. 303 * 304 * @param $presetname 305 * @param $filepath 306 * String specifying the path to the image file. 307 * @param $bypass_browser_cache 308 * A Boolean indicating that the URL for the image should be distinct so that 309 * the visitors browser will not be able to use a previously cached version. 310 * This is 311 */ 312 function imagecache_create_url($presetname, $filepath, $bypass_browser_cache = FALSE) { 313 $path = _imagecache_strip_file_directory($filepath); 314 if (module_exists('transliteration')) { 315 $path = transliteration_get($path); 316 } 317 318 $args = array('absolute' => TRUE, 'query' => empty($bypass_browser_cache) ? NULL : time()); 319 switch (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC)) { 320 case FILE_DOWNLOADS_PUBLIC: 321 return url($GLOBALS['base_url'] . '/' . file_directory_path() .'/imagecache/'. $presetname .'/'. $path, $args); 322 case FILE_DOWNLOADS_PRIVATE: 323 return url('system/files/imagecache/'. $presetname .'/'. $path, $args); 324 } 325 } 326 327 /** 328 * Return a file system location that points to the location of a derivative 329 * of the original image at @p $path, transformed with the given @p $preset. 330 * Keep in mind that the image might not yet exist and won't be created. 331 */ 332 function imagecache_create_path($presetname, $path) { 333 $path = _imagecache_strip_file_directory($path); 334 return file_create_path() .'/imagecache/'. $presetname .'/'. $path; 335 } 336 337 /** 338 * Remove a possible leading file directory path from the given path. 339 */ 340 function _imagecache_strip_file_directory($path) { 341 $dirpath = file_directory_path(); 342 $dirlen = strlen($dirpath); 343 if (substr($path, 0, $dirlen + 1) == $dirpath .'/') { 344 $path = substr($path, $dirlen + 1); 345 } 346 return $path; 347 } 348 349 350 /** 351 * callback for handling public files imagecache requests. 352 */ 353 function imagecache_cache() { 354 $args = func_get_args(); 355 $preset = check_plain(array_shift($args)); 356 $path = implode('/', $args); 357 _imagecache_cache($preset, $path); 358 } 359 360 /** 361 * callback for handling private files imagecache requests 362 */ 363 function imagecache_cache_private() { 364 $args = func_get_args(); 365 $preset = check_plain(array_shift($args)); 366 $source = implode('/', $args); 367 368 if (user_access('view imagecache '. $preset) && !in_array(-1, module_invoke_all('file_download', $source))) { 369 _imagecache_cache($preset, $source); 370 } 371 else { 372 // if there is a 403 image, display it. 373 $accesspath = file_create_path('imagecache/'. $preset .'.403.png'); 374 if (is_file($accesspath)) { 375 imagecache_transfer($accesspath); 376 exit; 377 } 378 header('HTTP/1.0 403 Forbidden'); 379 exit; 380 } 381 } 382 383 /** 384 * handle request validation and responses to imagecache requests. 385 */ 386 function _imagecache_cache($presetname, $path) { 387 if (!$preset = imagecache_preset_by_name($presetname)) { 388 // Send a 404 if we don't know of a preset. 389 header("HTTP/1.0 404 Not Found"); 390 exit; 391 } 392 393 // umm yeah deliver it early if it is there. especially useful 394 // to prevent lock files from being created when delivering private files. 395 $dst = imagecache_create_path($preset['presetname'], $path); 396 if (is_file($dst)) { 397 imagecache_transfer($dst); 398 } 399 400 // preserve path for watchdog. 401 $src = $path; 402 403 // Check if the path to the file exists. 404 if (!is_file($src) && !is_file($src = file_create_path($src))) { 405 watchdog('imagecache', '404: Unable to find %image ', array('%image' => $src), WATCHDOG_ERROR); 406 header("HTTP/1.0 404 Not Found"); 407 exit; 408 }; 409 410 // Bail if the requested file isn't an image you can't request .php files 411 // etc... 412 if (!getimagesize($src)) { 413 watchdog('imagecache', '403: File is not an image %image ', array('%image' => $src), WATCHDOG_ERROR); 414 header('HTTP/1.0 403 Forbidden'); 415 exit; 416 } 417 418 $lockfile = file_directory_temp() .'/'. $preset['presetname'] . basename($src); 419 if (file_exists($lockfile)) { 420 watchdog('imagecache', 'ImageCache already generating: %dst, Lock file: %tmp.', array('%dst' => $dst, '%tmp' => $lockfile), WATCHDOG_NOTICE); 421 // 307 Temporary Redirect, to myself. Lets hope the image is done next time around. 422 header('Location: '. request_uri(), TRUE, 307); 423 exit; 424 } 425 touch($lockfile); 426 // register the shtdown function to clean up lock files. by the time shutdown 427 // functions are being called the cwd has changed from document root, to 428 // server root so absolute paths must be used for files in shutdown functions. 429 register_shutdown_function('file_delete', realpath($lockfile)); 430 431 // check if deriv exists... (file was created between apaches request handler and reaching this code) 432 // otherwise try to create the derivative. 433 if (file_exists($dst) || imagecache_build_derivative($preset['actions'], $src, $dst)) { 434 imagecache_transfer($dst); 435 } 436 // Generate an error if image could not generate. 437 watchdog('imagecache', 'Failed generating an image from %image using imagecache preset %preset.', array('%image' => $path, '%preset' => $preset['presetname']), WATCHDOG_ERROR); 438 header("HTTP/1.0 500 Internal Server Error"); 439 exit; 440 } 441 442 /** 443 * Apply an action to an image. 444 * 445 * @param $action 446 * Action array 447 * @param $image 448 * Image object 449 * @return 450 * Boolean, TRUE indicating success and FALSE failure. 451 */ 452 function _imagecache_apply_action($action, $image) { 453 $actions = imagecache_action_definitions(); 454 455 if ($definition = imagecache_action_definition($action['action'])) { 456 $function = $action['action'] .'_image'; 457 if (function_exists($function)) { 458 return $function($image, $action['data']); 459 } 460 } 461 // skip undefined actions.. module probably got uninstalled or disabled. 462 watchdog('imagecache', 'non-existant action %action', array('%action' => $action['action']), WATCHDOG_NOTICE); 463 return TRUE; 464 } 465 466 /** 467 * Helper function to transfer files from imagecache. 468 * 469 * Determines MIME type and sets a last modified header. 470 * 471 * @param $path 472 * String containing the path to file to be transferred. 473 * @return 474 * This function does not return. It calls exit(). 475 */ 476 477 function imagecache_transfer($path) { 478 $size = getimagesize($path); 479 $headers = array('Content-Type: '. mime_header_encode($size['mime'])); 480 481 if ($fileinfo = stat($path)) { 482 $headers[] = 'Content-Length: '. $fileinfo[7]; 483 $headers[] = 'Expires: ' . gmdate('D, d M Y H:i:s', time() + 1209600) .' GMT'; 484 $headers[] = 'Cache-Control: max-age=1209600, private, must-revalidate'; 485 _imagecache_cache_set_cache_headers($fileinfo, $headers); 486 } 487 file_transfer($path, $headers); 488 exit; 489 } 490 491 /** 492 * Set file headers that handle "If-Modified-Since" correctly for the 493 * given fileinfo. 494 * 495 * Note that this function may return or may call exit(). 496 * 497 * Most code has been taken from drupal_page_cache_header(). 498 * 499 * @param $fileinfo 500 * Array returned by stat(). 501 * @param 502 * Array of existing headers. 503 * @return 504 * Nothing but beware that this function may not return. 505 */ 506 function _imagecache_cache_set_cache_headers($fileinfo, &$headers) { 507 // Set default values: 508 $last_modified = gmdate('D, d M Y H:i:s', $fileinfo[9]) .' GMT'; 509 $etag = '"'. md5($last_modified) .'"'; 510 511 // See if the client has provided the required HTTP headers: 512 $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) 513 ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) 514 : FALSE; 515 $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) 516 ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) 517 : FALSE; 518 519 if ($if_modified_since && $if_none_match 520 && $if_none_match == $etag // etag must match 521 && $if_modified_since == $last_modified) { // if-modified-since must match 522 header('HTTP/1.1 304 Not Modified'); 523 // All 304 responses must send an etag if the 200 response 524 // for the same object contained an etag 525 header('Etag: '. $etag); 526 // We must also set Last-Modified again, so that we overwrite Drupal's 527 // default Last-Modified header with the right one 528 header('Last-Modified: '. $last_modified); 529 exit; 530 } 531 532 // Send appropriate response: 533 $headers[] = 'Last-Modified: '. $last_modified; 534 $headers[] = 'ETag: '. $etag; 535 } 536 537 /** 538 * Create a new image based on an image preset. 539 * 540 * @param $preset 541 * An image preset array. 542 * @param $source 543 * Path of the source file. 544 * @param $destination 545 * Path of the destination file. 546 * @return 547 * TRUE if an image derivative is generated, FALSE if no image 548 * derivative is generated. NULL if the derivative is being generated. 549 */ 550 function imagecache_build_derivative($actions, $src, $dst) { 551 // get the folder for the final location of this preset... 552 $dir = dirname($dst); 553 554 // Build the destination folder tree if it doesn't already exists. 555 if (!file_check_directory($dir, FILE_CREATE_DIRECTORY) && !mkdir($dir, 0775, TRUE)) { 556 watchdog('imagecache', 'Failed to create imagecache directory: %dir', array('%dir' => $dir), WATCHDOG_ERROR); 557 return FALSE; 558 } 559 560 // Simply copy the file if there are no actions. 561 if (empty($actions)) { 562 return file_copy($src, $dst, FILE_EXISTS_REPLACE); 563 } 564 565 if (!$image = imageapi_image_open($src)) { 566 return FALSE; 567 } 568 569 if (file_exists($dst)) { 570 watchdog('imagecache', 'Cached image file %dst already exists but is being regenerated. There may be an issue with your rewrite configuration.', array('%dst' => $dst), WATCHDOG_WARNING); 571 } 572 573 foreach ($actions as $action) { 574 if (!empty($action['data'])) { 575 // Make sure the width and height are computed first so they can be used 576 // in relative x/yoffsets like 'center' or 'bottom'. 577 if (isset($action['data']['width'])) { 578 $action['data']['width'] = _imagecache_percent_filter($action['data']['width'], $image->info['width']); 579 } 580 if (isset($action['data']['height'])) { 581 $action['data']['height'] = _imagecache_percent_filter($action['data']['height'], $image->info['height']); 582 } 583 if (isset($action['data']['xoffset'])) { 584 $action['data']['xoffset'] = _imagecache_keyword_filter($action['data']['xoffset'], $image->info['width'], $action['data']['width']); 585 } 586 if (isset($action['data']['yoffset'])) { 587 $action['data']['yoffset'] = _imagecache_keyword_filter($action['data']['yoffset'], $image->info['height'], $action['data']['height']); 588 } 589 } 590 if (!_imagecache_apply_action($action, $image)) { 591 watchdog('imagecache', 'action(id:%id): %action failed for %src', array('%id' => $action['actionid'], '%action' => $action['action'], '%src' => $src), WATCHDOG_ERROR); 592 return FALSE; 593 } 594 } 595 596 if (!imageapi_image_close($image, $dst)) { 597 watchdog('imagecache', 'There was an error saving the new image file %dst.', array('%dst' => $dst), WATCHDOG_ERROR); 598 return FALSE; 599 } 600 601 return TRUE; 602 } 603 604 /** 605 * Implementation of hook_user(). 606 */ 607 function imagecache_user($op, &$edit, &$account, $category = NULL) { 608 // Flush cached old user picture. 609 if ($op == 'update' && !empty($account->picture)) { 610 imagecache_image_flush($account->picture); 611 } 612 } 613 614 /** 615 * Implementation of filefield.module's hook_file_delete(). 616 * 617 * Remove derivative images after the originals are deleted by filefield. 618 */ 619 function imagecache_file_delete($file) { 620 imagecache_image_flush($file->filepath); 621 } 622 623 /** 624 * Implementation of hook_field_formatter_info(). 625 * 626 * imagecache formatters are named as $presetname_$style 627 * $style is used to determine how the preset should be rendered. 628 * If you are implementing custom imagecache formatters please treat _ as 629 * reserved. 630 * 631 * @todo: move the linking functionality up to imagefield and clean up the default image 632 * integration. 633 */ 634 function imagecache_field_formatter_info() { 635 $formatters = array(); 636 foreach (imagecache_presets() as $preset) { 637 $formatters[$preset['presetname'] .'_default'] = array( 638 'label' => t('@preset image', array('@preset' => $preset['presetname'])), 639 'field types' => array('image', 'filefield'), 640 ); 641 $formatters[$preset['presetname'] .'_linked'] = array( 642 'label' => t('@preset image linked to node', array('@preset' => $preset['presetname'])), 643 'field types' => array('image', 'filefield'), 644 ); 645 $formatters[$preset['presetname'] .'_imagelink'] = array( 646 'label' => t('@preset image linked to image', array('@preset' => $preset['presetname'])), 647 'field types' => array('image', 'filefield'), 648 ); 649 $formatters[$preset['presetname'] .'_path'] = array( 650 'label' => t('@preset file path', array('@preset' => $preset['presetname'])), 651 'field types' => array('image', 'filefield'), 652 ); 653 $formatters[$preset['presetname'] .'_url'] = array( 654 'label' => t('@preset URL', array('@preset' => $preset['presetname'])), 655 'field types' => array('image', 'filefield'), 656 ); 657 } 658 return $formatters; 659 } 660 661 function theme_imagecache_formatter_default($element) { 662 // Inside a view $element may contain NULL data. In that case, just return. 663 if (empty($element['#item']['fid'])) { 664 return ''; 665 } 666 667 // Extract the preset name from the formatter name. 668 $presetname = substr($element['#formatter'], 0, strrpos($element['#formatter'], '_')); 669 $style = 'linked'; 670 $style = 'default'; 671 672 $item = $element['#item']; 673 $item['data']['alt'] = isset($item['data']['alt']) ? $item['data']['alt'] : ''; 674 $item['data']['title'] = isset($item['data']['title']) ? $item['data']['title'] : NULL; 675 676 $class = "imagecache imagecache-$presetname imagecache-$style imagecache-{$element['#formatter']}"; 677 return theme('imagecache', $presetname, $item['filepath'], $item['data']['alt'], $item['data']['title'], array('class' => $class)); 678 } 679 680 function theme_imagecache_formatter_linked($element) { 681 // Inside a view $element may contain NULL data. In that case, just return. 682 if (empty($element['#item']['fid'])) { 683 return ''; 684 } 685 686 // Extract the preset name from the formatter name. 687 $presetname = substr($element['#formatter'], 0, strrpos($element['#formatter'], '_')); 688 $style = 'linked'; 689 690 $item = $element['#item']; 691 $item['data']['alt'] = isset($item['data']['alt']) ? $item['data']['alt'] : ''; 692 $item['data']['title'] = isset($item['data']['title']) ? $item['data']['title'] : NULL; 693 694 $imagetag = theme('imagecache', $presetname, $item['filepath'], $item['data']['alt'], $item['data']['title']); 695 $path = empty($item['nid']) ? '' : 'node/'. $item['nid']; 696 $class = "imagecache imagecache-$presetname imagecache-$style imagecache-{$element['#formatter']}"; 697 return l($imagetag, $path, array('attributes' => array('class' => $class), 'html' => TRUE)); 698 } 699 700 function theme_imagecache_formatter_imagelink($element) { 701 // Inside a view $element may contain NULL data. In that case, just return. 702 if (empty($element['#item']['fid'])) { 703 return ''; 704 } 705 706 // Extract the preset name from the formatter name. 707 $presetname = substr($element['#formatter'], 0, strrpos($element['#formatter'], '_')); 708 $style = 'imagelink'; 709 710 $item = $element['#item']; 711 $item['data']['alt'] = isset($item['data']['alt']) ? $item['data']['alt'] : ''; 712 $item['data']['title'] = isset($item['data']['title']) ? $item['data']['title'] : NULL; 713 714 $imagetag = theme('imagecache', $presetname, $item['filepath'], $item['data']['alt'], $item['data']['title']); 715 $path = file_create_url($item['filepath']); 716 $class = "imagecache imagecache-$presetname imagecache-$style imagecache-{$element['#formatter']}"; 717 return l($imagetag, $path, array('attributes' => array('class' => $class), 'html' => TRUE)); 718 } 719 720 function theme_imagecache_formatter_path($element) { 721 // Inside a view $element may contain NULL data. In that case, just return. 722 if (empty($element['#item']['fid'])) { 723 return ''; 724 } 725 726 // Extract the preset name from the formatter name. 727 $presetname = substr($element['#formatter'], 0, strrpos($element['#formatter'], '_')); 728 729 return imagecache_create_path($presetname, $element['#item']['filepath']); 730 } 731 732 function theme_imagecache_formatter_url($element) { 733 // Inside a view $element may contain NULL data. In that case, just return. 734 if (empty($element['#item']['fid'])) { 735 return ''; 736 } 737 738 // Extract the preset name from the formatter name. 739 $presetname = substr($element['#formatter'], 0, strrpos($element['#formatter'], '_')); 740 741 return imagecache_create_url($presetname, $element['#item']['filepath']); 742 } 743 744 /** 745 * Accept a percentage and return it in pixels. 746 */ 747 function _imagecache_percent_filter($value, $current_pixels) { 748 if (strpos($value, '%') !== FALSE) { 749 $value = str_replace('%', '', $value) * 0.01 * $current_pixels; 750 } 751 return $value; 752 } 753 754 /** 755 * Accept a keyword (center, top, left, etc) and return it as an offset in pixels. 756 */ 757 function _imagecache_keyword_filter($value, $current_pixels, $new_pixels) { 758 switch ($value) { 759 case 'top': 760 case 'left': 761 $value = 0; 762 break; 763 case 'bottom': 764 case 'right': 765 $value = $current_pixels - $new_pixels; 766 break; 767 case 'center': 768 $value = $current_pixels/2 - $new_pixels/2; 769 break; 770 } 771 return $value; 772 } 773 774 /** 775 * Recursively delete all files and folders in the specified filepath, then 776 * delete the containing folder. 777 * 778 * Note that this only deletes visible files with write permission. 779 * 780 * @param string $path 781 * A filepath relative to file_directory_path. 782 */ 783 function _imagecache_recursive_delete($path) { 784 if (is_file($path) || is_link($path)) { 785 unlink($path); 786 } 787 elseif (is_dir($path)) { 788 $d = dir($path); 789 while (($entry = $d->read()) !== FALSE) { 790 if ($entry == '.' || $entry == '..') continue; 791 $entry_path = $path .'/'. $entry; 792 _imagecache_recursive_delete($entry_path); 793 } 794 $d->close(); 795 rmdir($path); 796 } 797 else { 798 watchdog('imagecache', 'Unknown file type(%path) stat: %stat ', 799 array('%path' => $path, '%stat' => print_r(stat($path),1)), WATCHDOG_ERROR); 800 } 801 802 } 803 804 /** 805 * Create and image tag for an imagecache derivative 806 * 807 * @param $presetname 808 * String with the name of the preset used to generate the derivative image. 809 * @param $path 810 * String path to the original image you wish to create a derivative image 811 * tag for. 812 * @param $alt 813 * Optional string with alternate text for the img element. 814 * @param $title 815 * Optional string with title for the img element. 816 * @param $attributes 817 * Optional drupal_attributes() array. If $attributes is an array then the 818 * default imagecache classes will not be set automatically, you must do this 819 * manually. 820 * @param $getsize 821 * If set to TRUE, the image's dimension are fetched and added as width/height 822 * attributes. 823 * @return 824 * HTML img element string. 825 */ 826 function theme_imagecache($presetname, $path, $alt = '', $title = '', $attributes = NULL, $getsize = TRUE) { 827 // Check is_null() so people can intentionally pass an empty array of 828 // to override the defaults completely. 829 if (is_null($attributes)) { 830 $attributes = array('class' => 'imagecache imagecache-'. $presetname); 831 } 832 if ($getsize && ($image = image_get_info(imagecache_create_path($presetname, $path)))) { 833 $attributes['width'] = $image['width']; 834 $attributes['height'] = $image['height']; 835 } 836 837 $attributes = drupal_attributes($attributes); 838 $imagecache_url = imagecache_create_url($presetname, $path); 839 return '<img src="'. $imagecache_url .'" alt="'. check_plain($alt) .'" title="'. check_plain($title) .'" '. $attributes .' />'; 840 } 841 842 /** 843 * Create a link the original image that wraps the derivative image. 844 * 845 * @param $presetname 846 * String with the name of the preset used to generate the derivative image. 847 * @param $path 848 * String path to the original image you wish to create a derivative image 849 * tag for. 850 * @param $alt 851 * Optional string with alternate text for the img element. 852 * @param $title 853 * Optional string with title for the img element. 854 * @param attributes 855 * Optional drupal_attributes() array for the link. 856 * @return 857 * An HTML string. 858 */ 859 function theme_imagecache_imagelink($presetname, $path, $alt = '', $title = '', $attributes = NULL) { 860 $image = theme('imagecache', $presetname, $path, $alt, $title); 861 $original_image_url = file_create_url($path); 862 return l($image, $original_image_url, array('absolute' => FALSE, 'html' => TRUE)); 863 } 864 865 /** 866 * ImageCache 2.x API 867 * 868 * The API for imagecache has changed. The 2.x API returns more structured 869 * data, has shorter function names, and implements more aggressive metadata 870 * caching. 871 * 872 */ 873 874 /** 875 * Get an array of all presets and their settings. 876 * 877 * @param reset 878 * if set to TRUE it will clear the preset cache 879 * 880 * @return 881 * array of presets array( $preset_id => array('presetid' => integer, 'presetname' => string)) 882 */ 883 function imagecache_presets($reset = FALSE) { 884 static $presets = array(); 885 886 // Clear caches if $reset is TRUE; 887 if ($reset) { 888 $presets = array(); 889 cache_clear_all('imagecache:presets', 'cache'); 890 891 // Clear the content.module cache (refreshes the list of formatters provided by imagefield.module). 892 if (module_exists('content')) { 893 content_clear_type_cache(); 894 } 895 } 896 // Return presets if the array is populated. 897 if (!empty($presets)) { 898 return $presets; 899 } 900 901 // Grab from cache or build the array. To ensure that the Drupal 5 upgrade 902 // path works, we also check whether the presets list is an array. 903 if (($cache = cache_get('imagecache:presets', 'cache')) && is_array($cache->data)) { 904 $presets = $cache->data; 905 } 906 else { 907 $normal_presets = array(); 908 909 $result = db_query('SELECT * FROM {imagecache_preset} ORDER BY presetname'); 910 while ($preset = db_fetch_array($result)) { 911 $presets[$preset['presetid']] = $preset; 912 $presets[$preset['presetid']]['actions'] = imagecache_preset_actions($preset); 913 $presets[$preset['presetid']]['storage'] = IMAGECACHE_STORAGE_NORMAL; 914 915 // Collect normal preset names so we can skip defaults and mark overrides accordingly 916 $normal_presets[$preset['presetname']] = $preset['presetid']; 917 } 918 919 // Collect default presets and allow modules to modify them before they 920 // are cached. 921 $default_presets = module_invoke_all('imagecache_default_presets'); 922 drupal_alter('imagecache_default_presets', $default_presets); 923 924 // Add in default presets if they don't conflict with any normal presets. 925 // Mark normal presets that take the same preset namespace as overrides. 926 foreach ($default_presets as $preset) { 927 if (!empty($preset['presetname'])) { 928 if (!isset($normal_presets[$preset['presetname']])) { 929 $preset['storage'] = IMAGECACHE_STORAGE_DEFAULT; 930 // Use a string preset identifier 931 $preset['presetid'] = $preset['presetname']; 932 $presets[$preset['presetname']] = $preset; 933 } 934 else { 935 $presetid = $normal_presets[$preset['presetname']]; 936 $presets[$presetid]['storage'] = IMAGECACHE_STORAGE_OVERRIDE; 937 } 938 } 939 } 940 941 cache_set('imagecache:presets', $presets); 942 } 943 return $presets; 944 } 945 946 /** 947 * Load a preset by preset_id. 948 * 949 * @param preset_id 950 * The numeric id of a preset. 951 * 952 * @return 953 * preset array( 'presetname' => string, 'presetid' => integet) 954 * empty array if preset_id is an invalid preset 955 */ 956 function imagecache_preset($preset_id, $reset = FALSE) { 957 $presets = imagecache_presets($reset); 958 return (isset($presets[$preset_id])) ? $presets[$preset_id] : array(); 959 } 960 961 /** 962 * Load a preset by name. 963 * 964 * @param preset_name 965 * 966 * @return 967 * preset array( 'presetname' => string, 'presetid' => integer) 968 * empty array if preset_name is an invalid preset 969 */ 970 971 function imagecache_preset_by_name($preset_name) { 972 static $presets_by_name = array(); 973 if (!$presets_by_name && $presets = imagecache_presets()) { 974 foreach ($presets as $preset) { 975 $presets_by_name[$preset['presetname']] = $preset; 976 } 977 } 978 return (isset($presets_by_name[$preset_name])) ? $presets_by_name[$preset_name] : array(); 979 } 980 981 /** 982 * Save an ImageCache preset. 983 * 984 * @param preset 985 * an imagecache preset array. 986 * @return 987 * a preset array. In the case of a new preset, 'presetid' will be populated. 988 */ 989 function imagecache_preset_save($preset) { 990 // @todo: CRUD level validation? 991 if (isset($preset['presetid']) && is_numeric($preset['presetid'])) { 992 drupal_write_record('imagecache_preset', $preset, 'presetid'); 993 } 994 else { 995 drupal_write_record('imagecache_preset', $preset); 996 } 997 998 // Reset presets cache. 999 imagecache_preset_flush($preset); 1000 imagecache_presets(TRUE); 1001 1002 // Rebuild Theme Registry 1003 drupal_rebuild_theme_registry(); 1004 1005 return $preset; 1006 } 1007 1008 function imagecache_preset_delete($preset) { 1009 imagecache_preset_flush($preset); 1010 db_query('DELETE FROM {imagecache_action} where presetid = %d', $preset['presetid']); 1011 db_query('DELETE FROM {imagecache_preset} where presetid = %d', $preset['presetid']); 1012 imagecache_presets(TRUE); 1013 return TRUE; 1014 } 1015 1016 function imagecache_preset_actions($preset, $reset = FALSE) { 1017 static $actions_cache = array(); 1018 1019 if ($reset || empty($actions_cache[$preset['presetid']])) { 1020 $result = db_query('SELECT * FROM {imagecache_action} where presetid = %d order by weight', $preset['presetid']); 1021 while ($row = db_fetch_array($result)) { 1022 $row['data'] = unserialize($row['data']); 1023 $actions_cache[$preset['presetid']][] = $row; 1024 } 1025 } 1026 1027 return isset($actions_cache[$preset['presetid']]) ? $actions_cache[$preset['presetid']] : array(); 1028 } 1029 1030 /** 1031 * Flush cached media for a preset. 1032 * 1033 * @param preset 1034 * an imagecache preset array. 1035 */ 1036 function imagecache_preset_flush($preset) { 1037 if (user_access('flush imagecache')) { 1038 $presetdir = realpath(file_directory_path() .'/imagecache/'. $preset['presetname']); 1039 if (is_dir($presetdir)) { 1040 _imagecache_recursive_delete($presetdir); 1041 } 1042 } 1043 } 1044 1045 /** 1046 * Clear cached versions of a specific file in all presets. 1047 * @param $path 1048 * The Drupal file path to the original image. 1049 */ 1050 function imagecache_image_flush($path) { 1051 foreach (imagecache_presets() as $preset) { 1052 file_delete(imagecache_create_path($preset['presetname'], $path)); 1053 } 1054 } 1055 1056 function imagecache_action($actionid) { 1057 static $actions; 1058 1059 if (!isset($actions[$actionid])) { 1060 $action = array(); 1061 1062 $result = db_query('SELECT * FROM {imagecache_action} WHERE actionid=%d', $actionid); 1063 if ($row = db_fetch_array($result)) { 1064 $action = $row; 1065 $action['data'] = unserialize($action['data']); 1066 1067 $definition = imagecache_action_definition($action['action']); 1068 $action = array_merge($definition, $action); 1069 $actions[$actionid] = $action; 1070 } 1071 } 1072 return $actions[$actionid]; 1073 } 1074 1075 function imagecache_action_load($actionid) { 1076 return imagecache_action($actionid, TRUE); 1077 } 1078 1079 function imagecache_action_save($action) { 1080 $definition = imagecache_action_definition($action['action']); 1081 $action = array_merge($definition, $action); 1082 1083 // Some actions don't have data. Make an empty one to prevent SQL errors. 1084 if (!isset($action['data'])) { 1085 $action['data'] = array(); 1086 } 1087 1088 if (!empty($action['actionid'])) { 1089 drupal_write_record('imagecache_action', $action, 'actionid'); 1090 } 1091 else { 1092 drupal_write_record('imagecache_action', $action); 1093 } 1094 $preset = imagecache_preset($action['presetid']); 1095 imagecache_preset_flush($preset); 1096 imagecache_presets(TRUE); 1097 return $action; 1098 } 1099 1100 function imagecache_action_delete($action) { 1101 db_query('DELETE FROM {imagecache_action} WHERE actionid=%d', $action['actionid']); 1102 $preset = imagecache_preset($action['presetid']); 1103 imagecache_preset_flush($preset); 1104 imagecache_presets(TRUE); 1105 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |