image_resize_filter.module

Tracking master branch
  1. drupal
    1. 6 contributions/image_resize_filter/image_resize_filter.module
    2. 7 contributions/image_resize_filter/image_resize_filter.module

image_resize_filter.module

After adding to a text format, this filter will parse the contents of submitted content and automatically scale image files to match the set dimensions of img tags.

Image that have been created take on the ownership of the original file. Making so when the primary node is deleted, the images it provided are deleted also.

Functions & methods

NameDescription
image_resize_filter_delete_allDelete the entire set of cached images.
image_resize_filter_delete_derivativesDelete all generated image when the original file is removed.
image_resize_filter_delete_temp_fileA short-cut function to delete temporary remote images.
image_resize_filter_file_deleteImplements hook_file_delete().
image_resize_filter_file_downloadImplements hook_file_download().
image_resize_filter_file_system_settings_submitAdditional #submit handler for the system_file_system_settings form.
image_resize_filter_filter_infoImplements hook_filter_info().
image_resize_filter_formThe form for configuring the Image Resize Filter.
image_resize_filter_form_system_file_system_settings_alterImplements hook_form_[form_id]_alter().
image_resize_filter_get_imagesParsing function to locate all images in a piece of text that need replacing.
image_resize_filter_helpImplements hook_help().
image_resize_filter_image_tagGenerate a themed image tag based on an image array.
image_resize_filter_pathinfoUtility function to return path information.
image_resize_filter_process_filterFilter callback function.
image_resize_filter_process_imagesProcessing function for image resize filter. Replace img src properties with a URL to a resized image.
image_resize_filter_themeImplements hook_theme().
theme_image_resize_filter_formTheme callback to theme the Image Resize Filter form.
theme_image_resize_filter_imageGenerate a themed image tag based on an image array.

File

View source
  1. <?php
  2. /**
  3. * @file image_resize_filter.module
  4. *
  5. * After adding to a text format, this filter will parse the contents of
  6. * submitted content and automatically scale image files to match the set
  7. * dimensions of img tags.
  8. *
  9. * Image that have been created take on the ownership of the original file.
  10. * Making so when the primary node is deleted, the images it provided are
  11. * deleted also.
  12. */
  13. /**
  14. * Implements hook_help().
  15. */
  16. function image_resize_filter_help($path, $arg) {
  17. switch ($path) {
  18. case 'admin/help#image_resize_filter':
  19. $instructions = array(
  20. t('Visit the <a href="!url">Text formats</a> configuration page. Click "configure" next to the text format you want to enable the image resize filter on.', array('!url' => 'admin/config/content/formats')),
  21. t('Check the box for "Image resize filter" under the list of filters.'),
  22. t('<strong>Important</strong>: Re-order your enabled filters under "Filter processing order".') .
  23. '<p>' . t('If using the Image Resize Filter on the "Filtered HTML" text format, you <strong>must</strong> ensure A) that the &lt;img&gt; tag is in the list of allowed tags and B) The "Image resize filter" is run <strong>before</strong> the "HTML filter".') . '</p>' .
  24. '<p>' . t('If using the Image Resize Filter with BBCode or some other non-HTML filter, the "Image resize filter" must be run AFTER the BBCode filter.') . '</p>',
  25. t('Optional. Click the Image resize filter tab underneath "Filter settings" to set additional configuration for the the image resize filter.'),
  26. );
  27. $output = '';
  28. $output .= '<h3>' . t('About') . '</h3>';
  29. $output .= '<p>' . t('The image resize filter module provides a <a href="!url">filter</a> that makes it easy to resize images, especially when combined with a WYSIWYG editor such as tinyMCE or FCKeditor. Users never have to worry about scaling image sizes again, just insert an image and set it\'s height and width properties in HTML and the image is resized on output. To use the image resize filter, follow these steps.', array('!url' => url('admin/help/filter'))) . '</p>';
  30. $output .= theme('item_list', array('items' => $instructions, 'type' => 'ol', 'title' => t('Installation and use')));
  31. return $output;
  32. }
  33. }
  34. /**
  35. * Implements hook_filter_info().
  36. */
  37. function image_resize_filter_filter_info() {
  38. $filters = array();
  39. $filters['image_resize_filter'] = array(
  40. 'title' => t('Image resize filter'),
  41. 'process callback' => 'image_resize_filter_process_filter',
  42. 'settings callback' => 'image_resize_filter_form',
  43. 'default settings' => array(
  44. 'link' => 0,
  45. 'link_class' => '',
  46. 'link_rel' => '',
  47. 'image_locations' => array('local'),
  48. ),
  49. 'weight' => 0,
  50. );
  51. return $filters;
  52. }
  53. /**
  54. * Filter callback function.
  55. */
  56. function image_resize_filter_process_filter($text, $filter) {
  57. $settings['link'] = $filter->settings['link'];
  58. $settings['class'] = $filter->settings['link_class'];
  59. $settings['rel'] = $filter->settings['link_rel'];
  60. $settings['image_locations'] = array_filter($filter->settings['image_locations']);
  61. $images = image_resize_filter_get_images($settings, $text);
  62. return $images ? image_resize_filter_process_images($images, $text, $settings) : $text;
  63. }
  64. /**
  65. * Implements hook_file_delete().
  66. */
  67. function image_resize_filter_file_delete($file) {
  68. if (isset($file->uri)) {
  69. image_resize_filter_delete_derivatives($file->uri);
  70. }
  71. }
  72. /**
  73. * Implements hook_theme().
  74. */
  75. function image_resize_filter_theme() {
  76. return array(
  77. 'image_resize_filter_image' => array(
  78. 'variables' => array('image' => NULL, 'settings' => NULL),
  79. ),
  80. 'image_resize_filter_form' => array(
  81. 'render element' => 'form',
  82. ),
  83. );
  84. }
  85. /**
  86. * Implements hook_file_download().
  87. */
  88. function image_resize_filter_file_download($uri) {
  89. // If this is a resized image, use the same access as the original image.
  90. $matches = array();
  91. if (preg_match('/^([a-z0-9\-_]+:\/\/)resize\/(.*)?-\d+x\d+(\.png|\.jpg|\.jpeg|\.gif)$/i', $uri, $matches)) {
  92. $headers = module_invoke_all('file_download', $matches[1] . $matches[2] . $matches[3]);
  93. if (in_array(-1, $headers)) {
  94. return -1;
  95. }
  96. if (count($headers)) {
  97. return array(
  98. 'Content-Type' => file_get_mimetype($uri),
  99. 'Content-Length' => filesize($uri),
  100. );
  101. }
  102. }
  103. }
  104. /**
  105. * Implements hook_form_[form_id]_alter().
  106. */
  107. function image_resize_filter_form_system_file_system_settings_alter($form, $form_state) {
  108. $form['#submit'][] = 'image_resize_filter_file_system_settings_submit';
  109. }
  110. /**
  111. * Additional #submit handler for the system_file_system_settings form.
  112. */
  113. function image_resize_filter_file_system_settings_submit($form, $form_state) {
  114. // Clear filter caches when changing file system information.
  115. cache_clear_all('*', 'cache_filter');
  116. }
  117. /**
  118. * The form for configuring the Image Resize Filter.
  119. */
  120. function image_resize_filter_form($form, &$form_state, $filter, $format, $defaults) {
  121. $filter->settings += $defaults;
  122. $settings['image_resize'] = array(
  123. '#description' => t('The image resize filter will analyze &lt;img&gt; tags and compare the given height and width attributes to the actual file. If the file dimensions are different than those given in the &lt;img&gt; tag, the image will be copied and the src attribute will be updated to point to the resized image.'),
  124. '#theme' => 'image_resize_filter_form',
  125. '#format' => $format,
  126. '#parents' => array('filters', 'image_resize_filter', 'settings'),
  127. );
  128. $settings['image_resize']['image_locations'] = array(
  129. '#type' => 'checkboxes',
  130. '#title' => t('Resize images stored'),
  131. '#options' => array('local' => t('Locally'), 'remote' => t('On remote servers (note: this copies <em>all</em> remote images locally)')),
  132. '#default_value' => $filter->settings['image_locations'],
  133. '#description' => t('This option will determine which images will be analyzed for &lt;img&gt; tag differences. Enabling resizing of remote images can have performance impacts, as all images in the filtered text needs to be transfered via HTTP each time the filter cache is cleared.'),
  134. );
  135. $settings['image_resize']['link'] = array(
  136. '#type' => 'checkbox',
  137. '#title' => t('If resized, add a link to the original image.'),
  138. '#default_value' => $filter->settings['link'],
  139. );
  140. $settings['image_resize']['link_class'] = array(
  141. '#type' => 'textfield',
  142. '#title' => t('Optionally, give it the class'),
  143. '#size' => '10',
  144. '#default_value' => $filter->settings['link_class'],
  145. );
  146. $settings['image_resize']['link_rel'] = array(
  147. '#type' => 'textfield',
  148. '#title' => t('and/or a rel attribute'),
  149. '#size' => '10',
  150. '#default_value' => $filter->settings['link_rel'],
  151. );
  152. return $settings;
  153. }
  154. /**
  155. * Theme callback to theme the Image Resize Filter form.
  156. */
  157. function theme_image_resize_filter_form($variables) {
  158. $form = $variables['form'];
  159. $format = $form['#format'];
  160. $link = 'link';
  161. $class = 'link_class';
  162. $rel = 'link_rel';
  163. $class_element = ' ';
  164. $class_element .= '<span class="image-resize-filter-class">';
  165. $class_element .= check_plain($form[$class]['#title']) .': ';
  166. $form[$class]['#title'] = NULL;
  167. $class_element .= drupal_render($form[$class]);
  168. $class_element .= '</span>';
  169. $rel_element = ' ';
  170. $rel_element .= '<span class="image-resize-filter-rel">';
  171. $rel_element .= check_plain($form[$rel]['#title']) .': ';
  172. $form[$rel]['#title'] = NULL;
  173. $rel_element .= drupal_render($form[$rel]);
  174. $rel_element .= '</span>';
  175. $link_element = drupal_render($form[$link]);
  176. $output = '';
  177. $output .= '<div class="container-inline image-resize-filter-link-options">';
  178. $output .= $link_element;
  179. $output .= $class_element;
  180. $output .= $rel_element;
  181. $output .= '</div>';
  182. $form['placeholder'] = array(
  183. '#type' => 'element',
  184. '#title' => t('Link to the original'),
  185. '#title_display' => 'before',
  186. '#children' => $output,
  187. '#description' => t('Linking to the original can be helpful to give users a full-size view of the image. Adding the class "thickbox" is helpful if you have installed the <a href="http://drupal.org/project/thickbox">thickbox module</a>. The rel attribute may be useful when used with the <a href="http://drupal.org/project/lightbox2">lightbox2</a> and <a href="http://drupal.org/project/shadowbox">shadowbox</a> modules.'),
  188. );
  189. // Add a little bit of JavaScript. Not cached since it's only used here.
  190. drupal_add_js(drupal_get_path('module', 'image_resize_filter') . '/image_resize_filter.js', array('aggregate' => FALSE));
  191. return filter_xss_admin($form['#description']) . drupal_render_children($form);
  192. }
  193. /**
  194. * Parsing function to locate all images in a piece of text that need replacing.
  195. *
  196. * @param $settings
  197. * An array of settings that will be used to identify which images need
  198. * updating. Includes the following:
  199. *
  200. * - image_locations: An array of acceptable image locations. May contain any
  201. * of the following values: "remote". Remote image will be downloaded and
  202. * saved locally. This procedure is intensive as the images need to
  203. * be retrieved to have their dimensions checked.
  204. *
  205. * @param $text
  206. * The text to be updated with the new img src tags.
  207. */
  208. function image_resize_filter_get_images($settings, $text) {
  209. $images = array();
  210. // Find all image tags, ensuring that they have a src.
  211. $matches = array();
  212. preg_match_all('/((<a [^>]*>)[ ]*)?(<img[^>]*?src[ ]*=[ ]*"([^"]+)"[^>]*>)/i', $text, $matches);
  213. // Loop through matches and find if replacements are necessary.
  214. // $matches[0]: All complete image tags and preceeding anchors.
  215. // $matches[1]: The anchor tag of each match (if any).
  216. // $matches[2]: The anchor tag and trailing whitespace of each match (if any).
  217. // $matches[3]: The complete img tag.
  218. // $matches[4]: The src value of each match.
  219. foreach ($matches[0] as $key => $match) {
  220. $has_link = (bool) $matches[1][$key];
  221. $img_tag = $matches[3][$key];
  222. $src = $matches[4][$key];
  223. $resize = NULL;
  224. $image_size = NULL;
  225. $attributes = array();
  226. // Find attributes of this image tag.
  227. $attribute_matches = array();
  228. preg_match_all('/([a-z]+)[ ]*=[ ]*"([^"]*)"/i', $img_tag, $attribute_matches);
  229. foreach ($attribute_matches[0] as $key => $match) {
  230. $attribute = $attribute_matches[1][$key];
  231. $attribute_value = $attribute_matches[2][$key];
  232. $attributes[$attribute] = $attribute_value;
  233. }
  234. // Height and width need to be matched specifically because they may come as
  235. // either an HTML attribute or as part of a style attribute. FCKeditor
  236. // specifically has a habit of using style tags instead of height and width.
  237. foreach (array('width', 'height') as $property) {
  238. $property_matches = array();
  239. preg_match_all('/[ \'";]' . $property . '[ ]*([=:])[ ]*"?([0-9]+)(%?)"?/i', $img_tag, $property_matches);
  240. // If this image uses percentage width or height, do not process it.
  241. if (in_array('%', $property_matches[3])) {
  242. $resize = FALSE;
  243. break;
  244. }
  245. // In the odd scenario there is both a style="width: xx" and a width="xx"
  246. // tag, base our calculations off the style tag, since that's what the
  247. // browser will display.
  248. $property_key = 0;
  249. $property_count = count($property_matches[1]);
  250. if ($property_count) {
  251. $property_key = array_search(':', $property_matches[1]);
  252. }
  253. $attributes[$property] = !empty($property_matches[2][$property_key]) ? $property_matches[2][$property_key] : '';
  254. }
  255. // Determine if this is a local or remote file.
  256. $location = 'unknown';
  257. if (strpos($src, '/') === 0) {
  258. $location = 'local';
  259. }
  260. elseif (preg_match('/http[s]?:\/\/' . preg_quote($_SERVER['HTTP_HOST'], '/') . '/', $src)) {
  261. $location = 'local';
  262. }
  263. elseif (strpos($src, 'http') === 0) {
  264. $location = 'remote';
  265. }
  266. // If not resizing images in this location, continue on to the next image.
  267. if (!in_array($location, $settings['image_locations'])) {
  268. continue;
  269. }
  270. // Convert the URL to a local path.
  271. $local_path = NULL;
  272. if ($location == 'local') {
  273. // Remove the http:// and base path.
  274. $local_path = preg_replace('/(http[s]?:\/\/' . preg_quote($_SERVER['HTTP_HOST'], '/') . ')?' . preg_quote(base_path(), '/') . '/', '', $src, 1);
  275. // Build a list of acceptable language prefixes.
  276. $lang_codes = '';
  277. if (array_key_exists('locale-url', variable_get('language_negotiation_language', array())) && variable_get('locale_language_negotiation_url_part', 0) == 0) {
  278. $languages = language_list();
  279. $lang_codes = array();
  280. foreach ($languages as $key => $language) {
  281. if ($language->prefix) {
  282. $lang_codes[$key] = preg_quote($language->prefix, '!');
  283. }
  284. }
  285. $lang_codes = $lang_codes ? '((' . implode('|', $lang_codes) . ')/)?' : '';
  286. }
  287. // Convert to a public file system URI.
  288. $directory_path = file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/';
  289. if (preg_match('!^' . preg_quote($directory_path, '!') . '!', $local_path)) {
  290. $local_path = 'public://' . preg_replace('!^' . preg_quote($directory_path, '!') . '!', '', $local_path);
  291. }
  292. // Convert to a file system path if using private files.
  293. elseif (preg_match('!^(\?q\=)?' . $lang_codes . 'system/files/!', $local_path)) {
  294. $local_path = 'private://' . preg_replace('!^(\?q\=)?' . $lang_codes . 'system/files/!', '', $local_path);
  295. }
  296. $local_path = rawurldecode($local_path);
  297. }
  298. // If this is an Image preset, generate the source image if necessary.
  299. // Formatted as "uri://styles/[style-name]/[schema-name]/[original-path]".
  300. $image_style_matches = array();
  301. $scheme = file_uri_scheme($local_path);
  302. if (!file_exists($local_path) && preg_match('!^' . $scheme . '://styles/([a-z0-9_\-]+)/([a-z0-9_\-]+)/(.*)$!i', $local_path, $image_style_matches) && function_exists('image_style_path')) {
  303. $style_name = $image_style_matches[1];
  304. $original_path = $scheme . '://' . $image_style_matches[3];
  305. if ($style = image_style_load($style_name)) {
  306. $derivative_path = image_style_path($style_name, $original_path);
  307. image_style_create_derivative($style, $original_path, $local_path);
  308. }
  309. }
  310. // If this is a remote image, retreive it to check its size.
  311. if ($location == 'remote') {
  312. $result = drupal_http_request($src);
  313. if ($result->code == 200) {
  314. $tmp_file = drupal_tempnam('temporary://', 'image_resize_filter_');
  315. $handle = fopen($tmp_file, 'w');
  316. fwrite($handle, $result->data);
  317. fclose($handle);
  318. $local_path = $tmp_file;
  319. }
  320. }
  321. // Get the image size.
  322. if (is_file($local_path)) {
  323. $image_size = @getimagesize($local_path);
  324. }
  325. // All this work and the image isn't even there. Bummer. Next image please.
  326. if (empty($image_size)) {
  327. image_resize_filter_delete_temp_file($location, $local_path);
  328. continue;
  329. }
  330. $actual_width = (int) $image_size[0];
  331. $actual_height = (int) $image_size[1];
  332. // If either height or width is missing, calculate the other.
  333. if (!$attributes['width'] && !$attributes['height']) {
  334. $attributes['width'] = $actual_width;
  335. $attributes['height'] = $actual_height;
  336. }
  337. if (!$attributes['height'] && is_numeric($attributes['width'])) {
  338. $ratio = $actual_height/$actual_width;
  339. $attributes['height'] = (int) round($ratio * $attributes['width']);
  340. }
  341. elseif (!$attributes['width'] && is_numeric($attributes['height'])) {
  342. $ratio = $actual_width/$actual_height;
  343. $attributes['width'] = (int) round($ratio * $attributes['height']);
  344. }
  345. // Determine if this image requires a resize.
  346. if (!isset($resize)) {
  347. $resize = ($actual_width != $attributes['width'] || $actual_height != $attributes['height']);
  348. }
  349. // Skip processing if the image is a remote tracking image.
  350. if ($location == 'remote' && $actual_width == 1 && $actual_height == 1) {
  351. image_resize_filter_delete_temp_file($location, $local_path);
  352. continue;
  353. }
  354. // Check the image extension by name.
  355. $extension_matches = array();
  356. preg_match('/\.([a-zA-Z0-9]+)$/', $src, $extension_matches);
  357. if (!empty($extension_matches)) {
  358. $extension = strtolower($extension_matches[1]);
  359. }
  360. // If the name extension failed (such as an image generated by a script),
  361. // See if we can determine an extension by MIME type.
  362. elseif (isset($image_size['mime'])) {
  363. switch ($image_size['mime']) {
  364. case 'image/png':
  365. $extension = 'png';
  366. break;
  367. case 'image/gif':
  368. $extension = 'gif';
  369. break;
  370. case 'image/jpeg':
  371. case 'image/pjpeg':
  372. $extension = 'jpg';
  373. break;
  374. }
  375. }
  376. // If we're not certain we can resize this image, skip it.
  377. if (!isset($extension) || !in_array(strtolower($extension), array('png', 'jpg', 'jpeg', 'gif'))) {
  378. image_resize_filter_delete_temp_file($location, $local_path);
  379. continue;
  380. }
  381. // If getting this far, the image exists and is not the right size, needs
  382. // to be saved locally from a remote server, or needs attributes added.
  383. // Add all information to a list of images that need resizing.
  384. $images[] = array(
  385. 'expected_size' => array('width' => $attributes['width'], 'height' => $attributes['height']),
  386. 'actual_size' => array('width' => $image_size[0], 'height' => $image_size[1]),
  387. 'attributes' => $attributes,
  388. 'resize' => $resize,
  389. 'img_tag' => $img_tag,
  390. 'has_link' => $has_link,
  391. 'original' => $src,
  392. 'location' => $location,
  393. 'local_path' => $local_path,
  394. 'mime' => $image_size['mime'],
  395. 'extension' => $extension,
  396. );
  397. }
  398. return $images;
  399. }
  400. /**
  401. * Processing function for image resize filter. Replace img src properties
  402. * with a URL to a resized image.
  403. *
  404. * @param $images
  405. * An array of image information, detailing images that need to be replaced.
  406. * @param $text
  407. * The original text of the post that needs src tags updated.
  408. * @param $settings
  409. * An array of setting for generating the image tag.
  410. */
  411. function image_resize_filter_process_images($images, $text, $settings) {
  412. $search = array();
  413. $replace = array();
  414. foreach ($images as $image) {
  415. // Copy remote images locally.
  416. if ($image['location'] == 'remote') {
  417. $local_file_path = 'resize/remote/' . md5(file_get_contents($image['local_path'])) . '-' . $image['expected_size']['width'] . 'x' . $image['expected_size']['height'] . '.'. $image['extension'];
  418. $image['destination'] = variable_get('file_default_scheme', 'public') . '://' . $local_file_path;
  419. }
  420. // Destination and local path are the same if we're just adding attributes.
  421. elseif (!$image['resize']) {
  422. $local_file_path = '';
  423. $image['destination'] = $image['local_path'];
  424. }
  425. else {
  426. $path_info = image_resize_filter_pathinfo($image['local_path']);
  427. $local_file_dir = file_uri_target($path_info['dirname']);
  428. $local_file_path = 'resize/' . ($local_file_dir ? $local_file_dir . '/' : '') . $path_info['filename'] . '-' . $image['expected_size']['width'] . 'x' . $image['expected_size']['height'] . '.' . $path_info['extension'];
  429. $image['destination'] = $path_info['scheme'] . '://' . $local_file_path;
  430. }
  431. if (!file_exists($image['destination'])) {
  432. // Create the resize directory.
  433. $directory = dirname($image['destination']);
  434. file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
  435. // Move remote images into place if they are already the right size.
  436. if ($image['location'] == 'remote' && !$image['resize']) {
  437. $handle = fopen($image['destination'], 'w');
  438. fwrite($handle, file_get_contents($image['local_path']));
  439. fclose($handle);
  440. }
  441. // Resize the local image if the sizes don't match.
  442. elseif ($image['resize']) {
  443. $res = image_load($image['local_path']);
  444. image_resize($res, $image['expected_size']['width'], $image['expected_size']['height']);
  445. image_save($res, $image['destination']);
  446. }
  447. @chmod($image['destination'], 0664);
  448. }
  449. // Delete our temporary file if this is a remote image.
  450. image_resize_filter_delete_temp_file($image['location'], $image['local_path']);
  451. // Replace the existing image source with the resized image.
  452. // Set the image we're currently updating in the callback function.
  453. $search[] = $image['img_tag'];
  454. $replace[] = image_resize_filter_image_tag($image, $settings);
  455. }
  456. return str_replace($search, $replace, $text);
  457. }
  458. /**
  459. * Generate a themed image tag based on an image array.
  460. *
  461. * @param $image
  462. * An array containing image information and properties.
  463. * @param $settings
  464. * Settings for the input filter.
  465. */
  466. function image_resize_filter_image_tag($image = NULL, $settings = NULL) {
  467. global $base_url;
  468. $src = file_create_url($image['destination']);
  469. // Strip the http:// from the path if the original did not include it.
  470. if (!preg_match('/^http[s]?:\/\/' . preg_quote($_SERVER['HTTP_HOST']) . '/', $image['original'])) {
  471. $src = preg_replace('/^http[s]?:\/\/' . preg_quote($_SERVER['HTTP_HOST']) . '/', '', $src);
  472. }
  473. $image['attributes']['src'] = $src;
  474. // Set the link properties if necessary.
  475. $image['link'] = FALSE;
  476. if ($image['resize'] && $settings['link'] && !$image['has_link']) {
  477. $image['link'] = array();
  478. $image['link']['attributes'] = array('href' => $image['original']);
  479. if (!empty($settings['class'])) {
  480. $image['link']['attributes']['class'] = $settings['class'];
  481. }
  482. if (!empty($settings['rel'])) {
  483. $image['link']['attributes']['rel'] = $settings['rel'];
  484. }
  485. if (!empty($image['attributes']['title'])) {
  486. $image['link']['attributes']['title'] = $image['attributes']['title'];
  487. }
  488. }
  489. // Theme the output and return.
  490. return theme('image_resize_filter_image', array('image' => $image, 'settings' => $settings));
  491. }
  492. /**
  493. * Generate a themed image tag based on an image array.
  494. *
  495. * @param $image
  496. * An array containing image information and properties.
  497. * @param $settings
  498. * Settings for the input filter.
  499. */
  500. function theme_image_resize_filter_image($variables) {
  501. $image = $variables['image'];
  502. $settings = $variables['settings'];
  503. $output = '<img' . drupal_attributes($image['attributes']) . ' />';
  504. if ($image['link']) {
  505. $output = '<a'. drupal_attributes($image['link']['attributes']) . '>' . $output . '</a>';
  506. }
  507. return $output;
  508. }
  509. /**
  510. * A short-cut function to delete temporary remote images.
  511. */
  512. function image_resize_filter_delete_temp_file($source, $uri) {
  513. if ($source == 'remote' && is_file($uri)) {
  514. @unlink($uri);
  515. }
  516. }
  517. /**
  518. * Delete all generated image when the original file is removed.
  519. */
  520. function image_resize_filter_delete_derivatives($original_uri) {
  521. // First delete all derivatives in the saved file location.
  522. $path_info = image_resize_filter_pathinfo($original_uri);
  523. $basename = $path_info['filename'];
  524. $extension = $path_info['extension'];
  525. $directory = str_replace($path_info['scheme'] . '://', $path_info['scheme'] . '://resize/', $path_info['dirname']);
  526. // Delete all the derivatives.
  527. $files = file_scan_directory($directory, '/' . preg_quote($basename, '/') . '-[0-9]+[x][0-9]+\.' . preg_quote($extension, '/') . '/', array('callback' => 'file_unmanaged_delete'));
  528. // Then work up the directories and delete any empty ones.
  529. $folders = explode('/', $directory);
  530. $directories = array();
  531. $current_directory = '';
  532. foreach ($folders as $folder) {
  533. $current_directory .= $folder . '/';
  534. $directories[] = $current_directory;
  535. }
  536. foreach (array_reverse($directories) as $directory) {
  537. if ($directory == ($path_info['scheme'] . '://')) {
  538. break;
  539. }
  540. $directory_files = file_scan_directory($directory, '/.*/');
  541. if (empty($directory_files)) {
  542. @rmdir($directory);
  543. }
  544. else {
  545. break;
  546. }
  547. }
  548. }
  549. /**
  550. * Delete the entire set of cached images.
  551. */
  552. function image_resize_filter_delete_all() {
  553. foreach (file_get_stream_wrappers() as $scheme => $stream_wrapper) {
  554. $directory = $scheme . '://resize';
  555. file_unmanaged_delete_recursive($directory);
  556. }
  557. cache_clear_all('*', 'cache_filter');
  558. }
  559. /**
  560. * Utility function to return path information.
  561. */
  562. function image_resize_filter_pathinfo($uri) {
  563. $info = pathinfo($uri);
  564. $info['extension'] = substr($uri, strrpos($uri, '.') + 1);
  565. $info['basename'] = basename($uri);
  566. $info['filename'] = basename($uri, '.' . $info['extension']);
  567. $info['scheme'] = file_uri_scheme($uri);
  568. if (empty($info['scheme'])) {
  569. foreach (file_get_stream_wrappers() as $scheme => $stream_wrapper) {
  570. $scheme_base_path = file_stream_wrapper_get_instance_by_scheme($scheme)->getDirectoryPath();
  571. $matches = array();
  572. if (preg_match('/^' . preg_quote($scheme_base_path, '/') . '\/?(.*)/', $info['dirname'], $matches)) {
  573. $info['scheme'] = $scheme;
  574. $info['dirname'] = $scheme . '://' . $matches[1];
  575. break;
  576. }
  577. }
  578. }
  579. return $info;
  580. }