image.module

Tracking 6.x-1.x branch
  1. drupal
    1. 5 contributions/image/image.module
    2. 6 contributions/image/image.module
    3. 7 drupal/modules/image/image.module
    4. 8 drupal/core/modules/image/image.module

Functions & methods

NameDescription
image_accessImplementation of hook_access().
image_blockImplementation of hook_block.
image_content_extra_fieldsImplementation of hook_content_extra_fields().
image_create_node_fromFunction to other modules to use to create image nodes.
image_cronImplements hook_cron. (deletes old temp images)
image_deleteImplementation of hook_delete().
image_displayCreate an <img> tag for an image.
image_fetchFetches an image file, allows "shorthand" image urls such of the form: image/view/$nid/$label (e.g. image/view/25/thumbnail or image/view/14)
image_file_downloadImplementation of hook_file_download().
image_formImplementation of hook_form().
image_form_add_thumbnail
image_form_submit
image_form_validate
image_get_derivative_sizesDetermine which sizes of derivative images need to be built for this image.
image_get_latestFetch the latest N image(s) - optionally from a given term.
image_get_randomFetch a random N image(s) - optionally from a given term.
image_get_sizesHelper function to return the defined sizes (or proper defaults).
image_helpImplementation of hook_help().
image_insertImplementation of hook_insert().
image_linkImplementation of hook_link.
image_loadImplementation of hook_load().
image_menuImplementation of hook_menu
image_node_infoImplementation of hook_node_info
image_node_operationsImplementation of hook_node_operations().
image_operations_rebuild
image_permImplementation of hook_perm
image_themeImplementation of hook_theme().
image_updateImplementation of hook_update().
image_viewImplementation of hook_view
image_views_apiImplementation of hook_views_api().
theme_image_block_latestTheme a latest block
theme_image_block_randomTheme a random block
theme_image_bodyTheme a body
theme_image_displayTheme an img tag for displaying the image.
theme_image_teaserTheme a teaser
_image_build_derivativesGenerate image derivatives.
_image_build_derivatives_if_neededRebuild derivatives if needed. Helper function for image_load().
_image_check_settingsVerify the image module and toolkit settings.
_image_filenameCreates an image filename.
_image_file_removeRemove image file if no other node references it.
_image_get_sizesHelper function to preserve backwards compatibility. This has been deprecated in favor of image_get_sizes().
_image_insertMoves temporary (working) images to the final directory and stores relevant information in the files table
_image_is_required_sizeIs a given size a built-in, required size?

Constants

NameDescription
IMAGE_LINK_HIDDEN
IMAGE_LINK_NEW
IMAGE_LINK_SHOWN
IMAGE_ORIGINAL
IMAGE_PREVIEW
IMAGE_THUMBNAIL

File

View source
  1. <?php
  2. define('IMAGE_ORIGINAL', '_original');
  3. define('IMAGE_PREVIEW', 'preview');
  4. define('IMAGE_THUMBNAIL', 'thumbnail');
  5. define('IMAGE_LINK_HIDDEN', 0);
  6. define('IMAGE_LINK_SHOWN', 1);
  7. define('IMAGE_LINK_NEW', 2);
  8. /**
  9. * Implementation of hook_help().
  10. */
  11. function image_help($path, $arg) {
  12. switch ($path) {
  13. case 'admin/help#image':
  14. $output = '<p>' . t('The Image module is used to create and administer images for your site. Each image is stored as a post, with thumbnails of the original generated automatically. There are two default derivative image sizes, "thumbnail" and "preview". The "thumbnail" size is shown as preview image in posts and when browsing image galleries. The "preview" size is the default size when viewing an image node page.') . '</p>';
  15. $output .= '<p>' . t('The settings page for Image module allows the image directory and the image sizes to be configured.') . '</p>';
  16. $output .= '<p>' . t('Image module ships with contributed modules. Their settings can be accessed from the image settings page.') . '</p>';
  17. $output .= '<ul>';
  18. $output .= '<li>' . t('Image attach is used to add an existing or new image to a node. The selected image will show up in a predefined spot on the selected node.') . '</li>';
  19. $output .= '<li>' . t('Image gallery is used to organize and display images in galleries. The list tab allows users to edit existing image gallery names, descriptions, parents and relative position, known as a weight. The add galleries tab allows you to create a new image gallery defining name, description, parent and weight. If the <a href="@views-url">Views module</a> is installed, then the Image gallery module settings are mostly replaced by settings of the view.', array('@views-url' => 'http://drupal.org/project/views')) . '</li>';
  20. $output .= '<li>' . t('Image import is used to import batches of images. The administration page lets you define the folder from which images will be imported.') . '</li>';
  21. $output .= '<li>' . t('The separate <a href="@img-assist-url">Image assist module</a> can be installed to upload and insert images into posts.', array('@img-assist-url' => 'http://drupal.org/project/img_assist')) . '</li>';
  22. $output .= '</ul>';
  23. $output .= '<p>' . t('You can:') . '</p>';
  24. $output .= '<ul>';
  25. $output .= '<li>' . t('Configure image sizes and file directories at <a href="@image-settings-url">Administer &raquo; Site configuration &raquo; Image</a>.', array('@image-settings-url' => url('admin/settings/image'))) . '</li>';
  26. $output .= '</ul>';
  27. $output .= '<p>' . t('For more information, see the online handbook entry for <a href="@image-url">Image module</a>.', array('@image-url' => 'http://drupal.org/handbook/modules/image')) . '</p>';
  28. return $output;
  29. }
  30. }
  31. /**
  32. * Implementation of hook_theme().
  33. */
  34. function image_theme() {
  35. return array(
  36. 'image_settings_sizes_form' => array(
  37. 'arguments' => array('form' => NULL),
  38. ),
  39. 'image_teaser' => array(
  40. 'arguments' => array('node' => NULL, 'size' => IMAGE_THUMBNAIL),
  41. ),
  42. 'image_body' => array(
  43. 'arguments' => array('node' => NULL, 'size' => IMAGE_PREVIEW),
  44. ),
  45. 'image_block_random' => array(
  46. 'arguments' => array('images' => NULL, 'size' => IMAGE_THUMBNAIL),
  47. ),
  48. 'image_block_latest' => array(
  49. 'arguments' => array('images' => NULL, 'size' => IMAGE_THUMBNAIL),
  50. ),
  51. 'image_display' => array(
  52. 'arguments' => array(
  53. 'node' => NULL,
  54. 'label' => NULL,
  55. 'url' => NULL,
  56. 'attributes' => NULL,
  57. ),
  58. ),
  59. );
  60. }
  61. /**
  62. * Implementation of hook_node_info
  63. */
  64. function image_node_info() {
  65. return array(
  66. 'image' => array(
  67. 'name' => t('Image'),
  68. 'module' => 'image',
  69. 'description' => t('An image (with thumbnail). This is ideal for publishing photographs or screenshots.'),
  70. ),
  71. );
  72. }
  73. /**
  74. * Implementation of hook_perm
  75. */
  76. function image_perm() {
  77. return array('view original images', 'create images', 'edit own images', 'edit any images', 'delete own images', 'delete any images');
  78. }
  79. /**
  80. * Implementation of hook_access().
  81. */
  82. function image_access($op, $node, $account) {
  83. switch ($op) {
  84. case 'create':
  85. if (user_access('create images', $account)) {
  86. return TRUE;
  87. }
  88. break;
  89. case 'update':
  90. if (user_access('edit any images', $account) || ($account->uid == $node->uid && user_access('edit own images', $account))) {
  91. return TRUE;
  92. }
  93. break;
  94. case 'delete':
  95. if (user_access('delete any images', $account) || ($account->uid == $node->uid && user_access('delete own images', $account))) {
  96. return TRUE;
  97. }
  98. break;
  99. }
  100. }
  101. /**
  102. * Implementation of hook_menu
  103. */
  104. function image_menu() {
  105. $items = array();
  106. $items['image/view'] = array(
  107. 'title' => 'image',
  108. 'access arguments' => array('access content'),
  109. 'type' => MENU_CALLBACK,
  110. 'page callback' => 'image_fetch',
  111. );
  112. $items['admin/settings/image'] = array(
  113. 'title' => 'Images',
  114. 'description' => 'Configure the location of image files and image sizes. Also, if enabled, configure image attachments and options for image galleries and image imports.',
  115. 'page callback' => 'drupal_get_form',
  116. 'page arguments' => array('image_admin_settings'),
  117. 'access arguments' => array('administer site configuration'),
  118. 'type' => MENU_NORMAL_ITEM,
  119. 'file' => 'image.admin.inc',
  120. );
  121. $items['admin/settings/image/nodes'] = array(
  122. 'title' => 'Files and sizes',
  123. 'description' => 'Configure the location of image files and image sizes.',
  124. 'access arguments' => array('administer site configuration'),
  125. 'type' => MENU_DEFAULT_LOCAL_TASK,
  126. 'weight' => '-10',
  127. );
  128. return $items;
  129. }
  130. /**
  131. * Implements hook_cron. (deletes old temp images)
  132. */
  133. function image_cron() {
  134. $path = file_directory_path() . '/' . variable_get('image_default_path', 'images') . '/temp';
  135. $files = file_scan_directory(file_create_path($path), '.*');
  136. foreach ($files as $file => $info) {
  137. if (time() - filemtime($file) > 60 * 60 * 6) {
  138. file_delete($file);
  139. }
  140. }
  141. }
  142. /**
  143. * Implementation of hook_node_operations().
  144. */
  145. function image_node_operations() {
  146. $operations = array(
  147. 'rebuild_thumbs' => array(
  148. 'label' => t('Rebuild derivative images'),
  149. 'callback' => 'image_operations_rebuild',
  150. ),
  151. );
  152. return $operations;
  153. }
  154. function image_operations_rebuild($nids) {
  155. foreach ($nids as $nid) {
  156. if ($node = node_load($nid)) {
  157. if ($node->type == 'image') {
  158. $node->rebuild_images = TRUE;
  159. image_update($node);
  160. }
  161. }
  162. }
  163. }
  164. /**
  165. * Implementation of hook_file_download().
  166. *
  167. * Note that in Drupal 5, the upload.module's hook_file_download() checks its
  168. * permissions for all files in the {files} table. We store our file
  169. * information in {files} if private files transfers are selected and the
  170. * upload.module is enabled, users will the 'view uploaded files' permission to
  171. * view images.
  172. */
  173. function image_file_download($filename) {
  174. $filepath = file_create_path($filename);
  175. $result = db_query("SELECT i.nid, f.filemime, f.filesize FROM {image} i INNER JOIN {files} f ON i.fid = f.fid WHERE f.filepath = '%s'", $filepath);
  176. if ($file = db_fetch_object($result)) {
  177. $node = node_load(array('type' => 'image', 'nid' => $file->nid));
  178. if (node_access('view', $node)) {
  179. // The user either needs to have 'view original images' permission or
  180. // the path must be listed for something other than the node's original
  181. // size. This will be the case when the orignal is smaller than a
  182. // derivative size.
  183. $images = (array) $node->images;
  184. unset($images[IMAGE_ORIGINAL]);
  185. if (user_access('view original images') || in_array($filepath, $images)) {
  186. return array(
  187. 'Content-Type: ' . mime_header_encode($file->filemime),
  188. 'Content-Length: ' . (int) $file->filesize,
  189. );
  190. }
  191. }
  192. return -1;
  193. }
  194. }
  195. /**
  196. * Implementation of hook_link.
  197. */
  198. function image_link($type, $node, $main = 0) {
  199. $links = array();
  200. if ($type == 'node' && $node->type == 'image' && !$main) {
  201. $request = isset($_GET['size']) ? $_GET['size'] : IMAGE_PREVIEW;
  202. foreach (image_get_sizes() as $key => $size) {
  203. if ($size['link']) {
  204. // For smaller images some derivative images may not have been created.
  205. // The thumbnail and preview images will be equal to the original images
  206. // but other sizes will not be set.
  207. if (isset($node->images[$key]) && $node->images[$key] != $node->images[$request]) {
  208. if ($size['link'] == IMAGE_LINK_NEW) {
  209. $links['image_size_' . $key] = array(
  210. 'title' => t($size['label']),
  211. 'href' => "image/view/{$node->nid}/$key",
  212. 'attributes' => array('target' => '_blank'),
  213. );
  214. }
  215. else {
  216. $links['image_size_' . $key] = array(
  217. 'title' => t($size['label']),
  218. 'href' => 'node/' . $node->nid,
  219. 'query' => 'size=' . urlencode($key),
  220. );
  221. }
  222. }
  223. }
  224. }
  225. if (!user_access('view original images')) {
  226. unset($links['image_size_' . IMAGE_ORIGINAL]);
  227. }
  228. }
  229. return $links;
  230. }
  231. /**
  232. * Implementation of hook_block.
  233. *
  234. * Offers 2 blocks: latest image and random image
  235. */
  236. function image_block($op = 'list', $delta = 0, $edit = array()) {
  237. switch ($op) {
  238. case 'list':
  239. $block[0]['info'] = t('Latest image');
  240. $block[1]['info'] = t('Random image');
  241. return $block;
  242. case 'configure':
  243. $form['number_images'] = array(
  244. '#type' => 'select',
  245. '#title' => t('Number of images to display'),
  246. '#options' => drupal_map_assoc(range(1, 99)),
  247. '#default_value' => variable_get("image_block_{$delta}_number_images", 1),
  248. );
  249. return $form;
  250. case 'view':
  251. if (user_access('access content')) {
  252. switch ($delta) {
  253. case 0:
  254. $images = image_get_latest(variable_get("image_block_{$delta}_number_images", 1));
  255. $block['subject'] = t('Latest image');
  256. $block['content'] = theme('image_block_latest', $images, IMAGE_THUMBNAIL);
  257. break;
  258. case 1:
  259. $images = image_get_random(variable_get("image_block_{$delta}_number_images", 1));
  260. $block['subject'] = t('Random image');
  261. $block['content'] = theme('image_block_random', $images, IMAGE_THUMBNAIL);
  262. break;
  263. }
  264. }
  265. return $block;
  266. case 'save':
  267. variable_set("image_block_{$delta}_number_images", $edit['number_images']);
  268. break;
  269. }
  270. }
  271. function image_form_add_thumbnail($form, &$form_state) {
  272. if ($form_state['values']['images'][IMAGE_THUMBNAIL]) {
  273. $node = (object)($form_state['values']);
  274. $form['#title'] = t('Thumbnail');
  275. $form['#value'] = image_display($node, IMAGE_THUMBNAIL);
  276. }
  277. return $form;
  278. }
  279. /**
  280. * Implementation of hook_form().
  281. */
  282. function image_form(&$node, $form_state) {
  283. _image_check_settings();
  284. if (!$_POST && !empty($_SESSION['image_upload'])) {
  285. unset($_SESSION['image_upload']);
  286. }
  287. $type = node_get_types('type', $node);
  288. $form['#validate'][] = 'image_form_validate';
  289. $form['#submit'][] = 'image_form_submit';
  290. $form['title'] = array(
  291. '#type' => 'textfield',
  292. '#title' => check_plain($type->title_label),
  293. '#size' => 60,
  294. '#maxlength' => 128,
  295. '#required' => TRUE,
  296. '#default_value' => $node->title,
  297. );
  298. $form['images']['#tree'] = TRUE;
  299. foreach (image_get_sizes() as $key => $size) {
  300. $form['images'][$key] = array(
  301. '#type' => 'value',
  302. '#value' => isset($node->images[$key]) ? $node->images[$key] : '',
  303. );
  304. }
  305. $form['new_file'] = array(
  306. '#type' => 'value',
  307. '#default_value' => isset($node->new_file) ? $node->new_file : FALSE,
  308. );
  309. $form['#attributes'] = array('enctype' => 'multipart/form-data');
  310. $form['image'] = array(
  311. '#prefix' => '<div class="image-field-wrapper">',
  312. '#suffix' => '</div>',
  313. );
  314. $form['image']['thumbnail'] = array(
  315. '#type' => 'item',
  316. '#after_build' => array('image_form_add_thumbnail'),
  317. );
  318. $form['image']['image'] = array(
  319. '#type' => 'file',
  320. '#title' => t('Image'),
  321. '#size' => 40,
  322. '#description' => t('Select an image to upload.'),
  323. );
  324. $form['image']['rebuild_images'] = array(
  325. '#type' => 'checkbox',
  326. '#title' => t('Rebuild derivative images'),
  327. '#default_value' => FALSE,
  328. '#description' => t('Check this to rebuild the derivative images for this node.'),
  329. '#access' => (!isset($node->nid) ? FALSE : TRUE),
  330. );
  331. if ($type->has_body) {
  332. $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count);
  333. }
  334. return $form;
  335. }
  336. function image_form_validate($form, &$form_state) {
  337. // Avoid blocking node deletion with missing image.
  338. if ($form_state['values']['op'] == t('Delete')) {
  339. return;
  340. }
  341. // Validators for file_save_upload().
  342. $validators = array(
  343. 'file_validate_is_image' => array(),
  344. );
  345. // New image uploads need to be saved in images/temp in order to be viewable
  346. // during node preview.
  347. $temporary_file_path = file_create_path(file_directory_path() . '/' . variable_get('image_default_path', 'images') . '/temp');
  348. if ($file = file_save_upload('image', $validators, $temporary_file_path)) {
  349. // Resize the original.
  350. $image_info = image_get_info($file->filepath);
  351. $aspect_ratio = $image_info['height'] / $image_info['width'];
  352. $original_size = image_get_sizes(IMAGE_ORIGINAL, $aspect_ratio);
  353. if (!empty($original_size['width']) && !empty($original_size['height'])) {
  354. $result = image_scale($file->filepath, $file->filepath, $original_size['width'], $original_size['height']);
  355. if ($result) {
  356. clearstatcache();
  357. $file->filesize = filesize($file->filepath);
  358. drupal_set_message(t('The original image was resized to fit within the maximum allowed resolution of %width x %height pixels.', array('%width' => $original_size['width'], '%height' => $original_size['height'])));
  359. }
  360. }
  361. // Check the file size limit.
  362. if ($file->filesize > variable_get('image_max_upload_size', 800) * 1024) {
  363. form_set_error('image', t('The image you uploaded was too big. You are only allowed upload files less than %max_size but your file was %file_size.', array('%max_size' => format_size(variable_get('image_max_upload_size', 800) * 1024), '%file_size' => format_size($file->filesize))));
  364. file_delete($file->filepath);
  365. return;
  366. }
  367. // We're good to go.
  368. $form_state['values']['images'][IMAGE_ORIGINAL] = $file->filepath;
  369. $form_state['values']['rebuild_images'] = FALSE;
  370. $form_state['values']['new_file'] = TRUE;
  371. // Call hook to allow other modules to modify the original image.
  372. module_invoke_all('image_alter', $form_state['values'], $form_state['values']['images'][IMAGE_ORIGINAL], IMAGE_ORIGINAL);
  373. $form_state['values']['images'] = _image_build_derivatives((object) $form_state['values'], TRUE);
  374. // Store the new file into the session.
  375. $_SESSION['image_upload'] = $form_state['values']['images'];
  376. }
  377. elseif (empty($form_state['values']['images'][IMAGE_ORIGINAL])) {
  378. if (empty($_SESSION['image_upload'])) {
  379. form_set_error('image', t('You must upload an image.'));
  380. }
  381. }
  382. }
  383. function image_form_submit($form, &$form_state) {
  384. if (!empty($_SESSION['image_upload'])) {
  385. $form_state['values']['images'] = $_SESSION['image_upload'];
  386. $form_state['values']['new_file'] = TRUE;
  387. unset($_SESSION['image_upload']);
  388. }
  389. }
  390. /**
  391. * Implementation of hook_view
  392. */
  393. function image_view($node, $teaser = 0, $page = 0) {
  394. $sizes = image_get_sizes();
  395. $size = IMAGE_PREVIEW;
  396. if (isset($_GET['size'])) {
  397. // Invalid size specified.
  398. if (!isset($sizes[$_GET['size']])) {
  399. drupal_goto("node/$node->nid");
  400. }
  401. $size = $_GET['size'];
  402. // Not allowed to view the original.
  403. if ($size == IMAGE_ORIGINAL && !user_access('view original images')) {
  404. drupal_goto("node/$node->nid");
  405. }
  406. }
  407. $node = node_prepare($node, $teaser);
  408. $node->content['image'] = array(
  409. '#value' => theme($teaser ? 'image_teaser' : 'image_body', $node, $size),
  410. '#weight' => 0,
  411. );
  412. return $node;
  413. }
  414. /**
  415. * Implementation of hook_load().
  416. */
  417. function image_load(&$node) {
  418. $result = db_query("SELECT i.image_size, f.filepath FROM {image} i INNER JOIN {files} f ON i.fid = f.fid WHERE i.nid = %d", $node->nid);
  419. $node->images = array();
  420. while ($file = db_fetch_object($result)) {
  421. $node->images[$file->image_size] = file_create_path($file->filepath);
  422. }
  423. $original_path = (isset($node->images[IMAGE_ORIGINAL]) ? $node->images[IMAGE_ORIGINAL] : NULL);
  424. if (empty($original_path)) {
  425. // There's no original image, we're in trouble...
  426. return;
  427. }
  428. if ((arg(0) != 'batch') && (strpos($_GET['q'], 'admin/content/node') === FALSE)) {
  429. _image_build_derivatives_if_needed($node);
  430. }
  431. }
  432. /**
  433. * Rebuild derivatives if needed. Helper function for image_load().
  434. */
  435. function _image_build_derivatives_if_needed(&$node) {
  436. $node->rebuild_images = FALSE;
  437. // Figure out which sizes should have been generated.
  438. $all_sizes = image_get_sizes();
  439. unset($all_sizes[IMAGE_ORIGINAL]);
  440. $needed_sizes = array_keys(image_get_derivative_sizes($node->images[IMAGE_ORIGINAL]));
  441. $unneeded_sizes = array_diff(array_keys($all_sizes), $needed_sizes);
  442. // Derivative sizes that are larger than the original get set to the
  443. // original.
  444. foreach ($unneeded_sizes as $key) {
  445. if (empty($node->images[$key])) {
  446. $node->images[$key] = $node->images[IMAGE_ORIGINAL];
  447. }
  448. else {
  449. // Need to remove an extra derivative image in the database.
  450. $node->rebuild_images = TRUE;
  451. }
  452. }
  453. // Check that the derivative images are present and current.
  454. foreach ($needed_sizes as $key) {
  455. // If the file is missing or created after the last change to the sizes,
  456. // rebuild the derivatives.
  457. if (empty($node->images[$key]) || !file_exists($node->images[$key])) {
  458. $node->rebuild_images = TRUE;
  459. }
  460. // Derivative image had a timestamp that predates last changes to image
  461. // size settings, so it needs to be rebuilt.
  462. elseif (filemtime($node->images[$key]) < variable_get('image_updated', 0)) {
  463. $node->rebuild_images = TRUE;
  464. }
  465. }
  466. // Correct any problems with the derivative images.
  467. if ($node->rebuild_images) {
  468. // Possibly TODO: calling a hook implementation here isn't very elegant.
  469. // but the code there is tangled and it works, so maybe leave it ;)
  470. image_update($node);
  471. watchdog('image', 'Derivative images were regenerated for %title.', array('%title' => $node->title), WATCHDOG_INFO, l(t('view'), 'node/' . $node->nid));
  472. }
  473. }
  474. /**
  475. * Implementation of hook_insert().
  476. */
  477. function image_insert($node) {
  478. // If a new image node contains no new file, but has a translation source,
  479. // insert all images from the source for this node.
  480. if (empty($node->new_file) && !empty($node->translation_source)) {
  481. db_query("INSERT INTO {image} (nid, fid, image_size) SELECT %d, fid, image_size FROM {image} WHERE nid = %d", $node->nid, $node->translation_source->nid);
  482. return;
  483. }
  484. // Derivative images that aren't needed are set to the original file. Make
  485. // note of the current path before calling _image_insert() because if it's
  486. // in the temp directory it'll be moved. We'll need it later to determine
  487. // which derivative images need to be saved with _image_insert().
  488. $original_path = $node->images[IMAGE_ORIGINAL];
  489. // Save the original first so that it if it's moved the derivatives are
  490. // placed in the correct directory.
  491. _image_insert($node, IMAGE_ORIGINAL, $original_path);
  492. $sizes = image_get_derivative_sizes($node->images[IMAGE_ORIGINAL]);
  493. foreach ($sizes as $key => $size_info) {
  494. if (!empty($node->images[$key]) && $node->images[$key] != $original_path) {
  495. _image_insert($node, $key, $node->images[$key]);
  496. }
  497. }
  498. }
  499. /**
  500. * Implementation of hook_update().
  501. *
  502. * Take $node by reference so we can use this to save the node after
  503. * rebuilding derivatives.
  504. */
  505. function image_update(&$node) {
  506. if (!empty($node->new_file) || !empty($node->rebuild_images)) {
  507. // Derivative images that aren't needed are set to the original file. Make
  508. // note of the current path before calling _image_insert() because if it's
  509. // in the temp directory it'll be moved. We'll need it later to determine
  510. // which derivative images need to be saved with _image_insert().
  511. $original_path = $node->images[IMAGE_ORIGINAL];
  512. if (!empty($node->new_file)) {
  513. // The derivative images were built during image_prepare() or
  514. // image_create_node_from() so all we need to do is remove all the old,
  515. // existing images.
  516. // Remove all the existing images.
  517. $result = db_query("SELECT f.fid, f.filepath FROM {image} i INNER JOIN {files} f ON i.fid = f.fid WHERE i.nid = %d", $node->nid);
  518. while ($file = db_fetch_object($result)) {
  519. db_query("DELETE FROM {image} WHERE nid = %d AND fid = %d", $node->nid, $file->fid);
  520. _image_file_remove($file);
  521. }
  522. // Save the original first so that it if it's moved the derivatives are
  523. // placed in the correct directory.
  524. _image_insert($node, IMAGE_ORIGINAL, $original_path);
  525. }
  526. else if (!empty($node->rebuild_images)) {
  527. // Find the original image.
  528. $original_file = db_fetch_object(db_query("SELECT i.fid, f.filepath FROM {image} i INNER JOIN {files} f ON i.fid = f.fid WHERE i.nid = %d AND i.image_size = '%s'", $node->nid, IMAGE_ORIGINAL));
  529. // Delete all but the original image.
  530. $result = db_query("SELECT i.fid, f.filepath FROM {image} i INNER JOIN {files} f ON i.fid = f.fid WHERE i.nid = %d AND f.fid <> %d", $node->nid, $original_file->fid);
  531. while ($file = db_fetch_object($result)) {
  532. db_query("DELETE FROM {image} WHERE nid = %d AND fid = %d", $node->nid, $file->fid);
  533. // Beware of derivative images that have the same path as the original.
  534. if ($file->filepath != $original_file->filepath) {
  535. _image_file_remove($file);
  536. }
  537. }
  538. $node->images = _image_build_derivatives($node, FALSE);
  539. // Prevent multiple rebuilds.
  540. $node->rebuild_images = FALSE;
  541. }
  542. $sizes = image_get_derivative_sizes($node->images[IMAGE_ORIGINAL]);
  543. foreach ($sizes as $key => $size_info) {
  544. if (!empty($node->images[$key]) && $node->images[$key] != $original_path) {
  545. _image_insert($node, $key, $node->images[$key]);
  546. }
  547. }
  548. }
  549. }
  550. /**
  551. * Implementation of hook_delete().
  552. */
  553. function image_delete($node) {
  554. $result = db_query('SELECT i.fid, f.filepath FROM {image} i INNER JOIN {files} f ON i.fid = f.fid WHERE i.nid = %d', $node->nid);
  555. while ($file = db_fetch_object($result)) {
  556. db_query("DELETE FROM {image} WHERE nid = %d AND fid = %d", $node->nid, $file->fid);
  557. _image_file_remove($file);
  558. }
  559. }
  560. /**
  561. * Create an <img> tag for an image.
  562. */
  563. function image_display(&$node, $label = IMAGE_PREVIEW, $attributes = array()) {
  564. if (empty($node->images[$label])) {
  565. return;
  566. }
  567. $image_info = image_get_info(file_create_path($node->images[$label]));
  568. $attributes['class'] = "image image-$label " . (isset($attributes['class']) ? $attributes['class'] : "");
  569. // Only output width/height attributes if image_get_info() was able to detect
  570. // the image dimensions, since certain browsers interpret an empty attribute
  571. // value as zero.
  572. if (!empty($image_info['width'])) {
  573. $attributes['width'] = $image_info['width'];
  574. }
  575. if (!empty($image_info['height'])) {
  576. $attributes['height'] = $image_info['height'];
  577. }
  578. return theme('image_display', $node, $label, file_create_url($node->images[$label]), $attributes);
  579. }
  580. /**
  581. * Fetches an image file, allows "shorthand" image urls such of the form:
  582. * image/view/$nid/$label
  583. * (e.g. image/view/25/thumbnail or image/view/14)
  584. */
  585. function image_fetch($nid = 0, $size = IMAGE_PREVIEW) {
  586. if ($size == IMAGE_ORIGINAL && !user_access('view original images')) {
  587. return drupal_access_denied();
  588. }
  589. if (isset($nid)) {
  590. $node = node_load(array('type' => 'image', 'nid' => $nid));
  591. if ($node) {
  592. if (!node_access('view', $node)) {
  593. return drupal_access_denied();
  594. }
  595. if (isset($node->images[$size])) {
  596. $file = $node->images[$size];
  597. if (file_exists(file_create_path($file))) {
  598. $headers = module_invoke_all('file_download', $file);
  599. if ($headers == -1) {
  600. return drupal_access_denied();
  601. }
  602. file_transfer($file, $headers);
  603. }
  604. }
  605. }
  606. }
  607. return drupal_not_found();
  608. }
  609. /**
  610. * Theme a teaser
  611. */
  612. function theme_image_teaser($node, $size) {
  613. return l(image_display($node, IMAGE_THUMBNAIL), 'node/' . $node->nid, array('html' => TRUE));
  614. }
  615. /**
  616. * Theme a body
  617. */
  618. function theme_image_body($node, $size) {
  619. return image_display($node, $size);
  620. }
  621. /**
  622. * Theme an img tag for displaying the image.
  623. */
  624. function theme_image_display($node, $label, $url, $attributes) {
  625. $title = isset($attributes['title']) ? $attributes['title'] : $node->title;
  626. $alt = isset($attributes['alt']) ? $attributes['alt'] : $node->title;
  627. // Remove alt and title from $attributes, otherwise they get added to the img tag twice.
  628. unset($attributes['title']);
  629. unset($attributes['alt']);
  630. return theme('image', $url, $alt, $title, $attributes, FALSE);
  631. }
  632. /**
  633. * Theme a random block
  634. */
  635. function theme_image_block_random($images, $size) {
  636. $output = '';
  637. foreach ($images as $image) {
  638. $output .= l(image_display($image, $size), 'node/' . $image->nid, array('html' => TRUE));
  639. }
  640. return $output;
  641. }
  642. /**
  643. * Theme a latest block
  644. */
  645. function theme_image_block_latest($images, $size) {
  646. $output = '';
  647. foreach ($images as $image) {
  648. $output .= l(image_display($image, $size), 'node/' . $image->nid, array('html' => TRUE));
  649. }
  650. return $output;
  651. }
  652. /**
  653. * Fetch a random N image(s) - optionally from a given term.
  654. */
  655. function image_get_random($count = 1, $tid = 0) {
  656. if ($tid != 0) {
  657. $result = db_query_range(db_rewrite_sql("SELECT DISTINCT(n.nid), RAND() AS rand FROM {term_node} tn LEFT JOIN {node} n ON n.nid = tn.nid WHERE n.type='image' AND n.status = 1 AND tn.tid = %d ORDER BY rand"), $tid, 0, $count);
  658. }
  659. else {
  660. $result = db_query_range(db_rewrite_sql("SELECT DISTINCT(n.nid), RAND() AS rand FROM {node} n WHERE n.type = 'image' AND n.status = 1 ORDER BY rand"), 0, $count);
  661. }
  662. $output = array();
  663. while ($nid = db_fetch_object($result)) {
  664. $output[] = node_load(array('nid' => $nid->nid));
  665. }
  666. return $output;
  667. }
  668. /**
  669. * Fetch the latest N image(s) - optionally from a given term.
  670. */
  671. function image_get_latest($count = 1, $tid = 0) {
  672. if ($tid != 0) {
  673. $result = db_query_range(db_rewrite_sql("SELECT n.nid FROM {term_node} tn LEFT JOIN {node} n ON n.nid=tn.nid WHERE n.type='image' AND n.status=1 AND tn.tid=%d ORDER BY n.changed DESC"), $tid, 0, $count);
  674. }
  675. else {
  676. $result = db_query_range(db_rewrite_sql("SELECT n.nid FROM {node} n WHERE n.type = 'image' AND n.status = 1 ORDER BY n.changed DESC"), 0, $count);
  677. }
  678. $output = array();
  679. while ($nid = db_fetch_object($result)) {
  680. $output[] = node_load(array('nid' => $nid->nid));
  681. }
  682. return $output;
  683. }
  684. /**
  685. * Verify the image module and toolkit settings.
  686. */
  687. function _image_check_settings() {
  688. // File paths
  689. $image_path = file_create_path(file_directory_path() . '/' . variable_get('image_default_path', 'images'));
  690. $temp_path = $image_path . '/temp';
  691. if (!file_check_directory($image_path, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS, 'image_default_path')) {
  692. return FALSE;
  693. }
  694. if (!file_check_directory($temp_path, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS, 'image_default_path')) {
  695. return FALSE;
  696. }
  697. // Sanity check : make sure we've got a working toolkit
  698. if (!image_get_toolkit()) {
  699. drupal_set_message(t('No image toolkit is currently enabled. Without one the image module will not be able to resize your images. You can select one from the <a href="@link">image toolkit settings page</a>.', array('@link' => url('admin/settings/image-toolkit'))), 'error');
  700. return FALSE;
  701. }
  702. return TRUE;
  703. }
  704. /**
  705. * Determine which sizes of derivative images need to be built for this image.
  706. *
  707. * @param $image_path
  708. * String file path to the image.
  709. *
  710. * @return
  711. * Returns a subset of image_get_sizes()'s results depending on what
  712. * derivative images are needed.
  713. */
  714. function image_get_derivative_sizes($image_path) {
  715. $sizes = array();
  716. // Can't do much if we can't read the image.
  717. if (!$image_info = image_get_info($image_path)) {
  718. return $sizes;
  719. }
  720. $all_sizes = image_get_sizes(NULL, $image_info['height'] / $image_info['width']);
  721. foreach ($all_sizes as $key => $size) {
  722. // We don't want to include the original.
  723. if ($key == IMAGE_ORIGINAL) {
  724. continue;
  725. }
  726. // If the original isn't bigger than the requested size then there's no
  727. // need to resize it.
  728. if ($image_info['width'] > $size['width'] || $image_info['height'] > $size['height']) {
  729. $sizes[$key] = $size;
  730. }
  731. }
  732. return $sizes;
  733. }
  734. /**
  735. * Generate image derivatives.
  736. *
  737. * @param $node
  738. * The node.
  739. * @param $temp
  740. * Boolean indicating if the derivatives should be saved to the temp
  741. * directory.
  742. *
  743. * @return
  744. * New array of images for the node.
  745. */
  746. function _image_build_derivatives($node, $temp = FALSE) {
  747. $original_path = file_create_path($node->images[IMAGE_ORIGINAL]);
  748. // Figure out which sizes we need to generate.
  749. $all_sizes = image_get_sizes();
  750. $needed_sizes = image_get_derivative_sizes($original_path);
  751. $unneeded_sizes = array_diff(array_keys($all_sizes), array_keys($needed_sizes));
  752. // Images that don't need a derivative image get set to the original.
  753. $images[IMAGE_ORIGINAL] = $original_path;
  754. foreach ($unneeded_sizes as $key) {
  755. $images[$key] = $original_path;
  756. }
  757. // Resize for the necessary sizes.
  758. $image_info = image_get_info($original_path);
  759. foreach ($needed_sizes as $key => $size) {
  760. $destination = _image_filename($original_path, $key, $temp);
  761. $status = FALSE;
  762. switch ($size['operation']) {
  763. // Depending on the operation, the image will be scaled or resized & cropped
  764. case 'scale':
  765. $status = image_scale($original_path, $destination, $size['width'], $size['height']);
  766. break;
  767. case 'scale_crop':
  768. $status = image_scale_and_crop($original_path, $destination, $size['width'], $size['height']);
  769. break;
  770. }
  771. if (!$status) {
  772. drupal_set_message(t('Unable to create scaled %label image.', array('%label' => $size['label'])), 'error');
  773. return FALSE;
  774. }
  775. // Set standard file permissions for webserver-generated files
  776. @chmod($destination, 0664);
  777. $images[$key] = $destination;
  778. module_invoke_all('image_alter', $node, $destination, $key);
  779. }
  780. return $images;
  781. }
  782. /**
  783. * Creates an image filename.
  784. *
  785. * @param $filepath
  786. * The full path and filename of the original image file,relative to Drupal
  787. * root, eg 'sites/default/files/images/myimage.jpg'.
  788. *
  789. * @return
  790. * A full path and filename with derivative image label inserted if required.
  791. */
  792. function _image_filename($filepath, $label = IMAGE_ORIGINAL, $temp = FALSE) {
  793. // Get default path for a new file.
  794. $path = file_directory_path() . '/' . variable_get('image_default_path', 'images');
  795. if ($temp) {
  796. $path .= '/temp';
  797. }
  798. $original_path = dirname($filepath);
  799. $filename = basename($filepath);
  800. if ($label && ($label != IMAGE_ORIGINAL)) {
  801. // Keep resized images in the same path, where original is (does not
  802. // apply to temporary files, these still use the default path).
  803. if (!$temp && $original_path != '.') {
  804. $path = $original_path;
  805. }
  806. // Insert the resized name in non-original images.
  807. $pos = strrpos($filename, '.');
  808. if ($pos === FALSE) {
  809. // The file had no extension - which happens in really old image.module
  810. // versions, so figure out the extension.
  811. $image_info = image_get_info(file_create_path($path . '/' . $filename));
  812. $filename = $filename . '.' . $label . '.' . $image_info['extension'];
  813. }
  814. else {
  815. $filename = substr($filename, 0, $pos) . '.' . $label . substr($filename, $pos);
  816. }
  817. }
  818. return file_create_path($path . '/' . $filename);
  819. }
  820. /**
  821. * Helper function to return the defined sizes (or proper defaults).
  822. *
  823. * @param $size
  824. * An optional string to return only the image size with the specified key.
  825. * @param $aspect_ratio
  826. * Float value with the ratio of image height / width. If a size has only one
  827. * dimension provided this will be used to compute the other.
  828. *
  829. * @return
  830. * An associative array with width, height, and label fields for the size.
  831. * If a $size parameter was specified and it cannot be found FALSE will be
  832. * returned.
  833. */
  834. function image_get_sizes($size = NULL, $aspect_ratio = NULL) {
  835. $defaults = array(
  836. IMAGE_ORIGINAL => array('width' => '', 'height' => '', 'label' => t('Original'), 'operation' => 'scale', 'link' => IMAGE_LINK_SHOWN),
  837. IMAGE_THUMBNAIL => array('width' => 100, 'height' => 100, 'label' => t('Thumbnail'), 'operation' => 'scale', 'link' => IMAGE_LINK_SHOWN),
  838. IMAGE_PREVIEW => array('width' => 640, 'height' => 640, 'label' => t('Preview'), 'operation' => 'scale', 'link' => IMAGE_LINK_SHOWN),
  839. );
  840. $sizes = array();
  841. foreach (variable_get('image_sizes', $defaults) as $key => $val) {
  842. // Only return sizes with a label.
  843. if (!empty($val['label'])) {
  844. // For a size with only one dimension specified, compute the other
  845. // dimension based on an aspect ratio.
  846. if ($aspect_ratio && (empty($val['width']) || empty($val['height']))) {
  847. if (empty($val['height']) && !empty($val['width'])) {
  848. $val['height'] = (int)round($val['width'] * $aspect_ratio);
  849. }
  850. elseif (empty($val['width']) && !empty($val['height'])) {
  851. $val['width'] = (int)round($val['height'] / $aspect_ratio);
  852. }
  853. }
  854. $sizes[$key] = $val;
  855. }
  856. }
  857. // If they requested a specific size return only that.
  858. if (isset($size)) {
  859. // Only return an array if it's available.
  860. return isset($sizes[$size]) ? $sizes[$size] : FALSE;
  861. }
  862. return $sizes;
  863. }
  864. /**
  865. * Helper function to preserve backwards compatibility. This has been
  866. * deprecated in favor of image_get_sizes().
  867. *
  868. * @TODO: Remove this in a future version.
  869. */
  870. function _image_get_sizes($size = NULL, $aspect_ratio = NULL) {
  871. return image_get_sizes($size, $aspect_ratio);
  872. }
  873. /**
  874. * Is a given size a built-in, required size?
  875. *
  876. * @param $size
  877. * One of the keys in the array returned by image_get_sizes().
  878. *
  879. * @return boolean
  880. */
  881. function _image_is_required_size($size) {
  882. return in_array($size, array(IMAGE_THUMBNAIL, IMAGE_PREVIEW, IMAGE_ORIGINAL));
  883. }
  884. /**
  885. * Moves temporary (working) images to the final directory and stores
  886. * relevant information in the files table
  887. */
  888. function _image_insert(&$node, $size, $image_path) {
  889. $original_path = $node->images[IMAGE_ORIGINAL];
  890. if (file_move($image_path, _image_filename($original_path, $size))) {
  891. // Update the node to reflect the actual filename, it may have been changed
  892. // if a file of the same name already existed.
  893. $node->images[$size] = $image_path;
  894. $image_info = image_get_info($image_path);
  895. $file = array(
  896. 'uid' => $node->uid,
  897. 'filename' => $size,
  898. 'filepath' => $image_path,
  899. 'filemime' => $image_info['mime_type'],
  900. 'filesize' => $image_info['file_size'],
  901. 'status' => FILE_STATUS_PERMANENT,
  902. 'timestamp' => time(),
  903. );
  904. drupal_write_record('files', $file);
  905. $image = array(
  906. 'fid' => $file['fid'],
  907. 'nid' => $node->nid,
  908. 'image_size' => $size,
  909. );
  910. drupal_write_record('image', $image);
  911. }
  912. }
  913. /**
  914. * Remove image file if no other node references it.
  915. *
  916. * @param $file
  917. * An object representing a table row from {files}.
  918. */
  919. function _image_file_remove($file) {
  920. if (!db_result(db_query("SELECT COUNT(*) FROM {image} WHERE fid = %d", $file->fid))) {
  921. file_delete(file_create_path($file->filepath));
  922. db_query('DELETE FROM {files} WHERE fid = %d', $file->fid);
  923. }
  924. }
  925. /**
  926. * Function to other modules to use to create image nodes.
  927. *
  928. * @param $filepath
  929. * String filepath of an image file. Note that this file will be moved into
  930. * the image module's images directory.
  931. * @param $title
  932. * String to be used as the node's title. If this is ommitted the filename
  933. * will be used.
  934. * @param $body
  935. * String to be used as the node's body.
  936. * @param $taxonomy
  937. * Taxonomy terms to assign to the node if the taxonomy.module is installed.
  938. * @param $keep_original
  939. * Boolean to indicate whether the original file should be deleted
  940. *
  941. * @return
  942. * A node object if the node is created successfully or FALSE on error.
  943. */
  944. function image_create_node_from($filepath, $title = NULL, $body = '', $taxonomy = NULL, $keep_original = FALSE) {
  945. global $user;
  946. if (!user_access('create images')) {
  947. return FALSE;
  948. }
  949. // Ensure it's a valid image.
  950. if (!$image_info = image_get_info($filepath)) {
  951. return FALSE;
  952. }
  953. // Ensure the file is within our size bounds.
  954. if ($image_info['file_size'] > variable_get('image_max_upload_size', 800) * 1024) {
  955. form_set_error('', t('The image you uploaded was too big. You are only allowed upload files less than %max_size but your file was %file_size.', array('%max_size' => format_size(variable_get('image_max_upload_size', 800) * 1024), '%file_size' => format_size($image_info['file_size']))), 'warning');
  956. return FALSE;
  957. }
  958. // Make sure we can copy the file into our temp directory.
  959. $original_path = $filepath;
  960. if (!file_copy($filepath, _image_filename($filepath, IMAGE_ORIGINAL, TRUE))) {
  961. return FALSE;
  962. }
  963. // Resize the original image.
  964. $aspect_ratio = $image_info['height'] / $image_info['width'];
  965. $size = image_get_sizes(IMAGE_ORIGINAL, $aspect_ratio);
  966. if (!empty($size['width']) && !empty($size['height'])) {
  967. image_scale($filepath, $filepath, $size['width'], $size['height']);
  968. }
  969. // Build the node.
  970. $node = new stdClass();
  971. $node->type = 'image';
  972. $node->uid = $user->uid;
  973. $node->name = $user->name;
  974. $node->title = isset($title) ? $title : basename($filepath);
  975. $node->body = $body;
  976. // Set the node's defaults... (copied this from node and comment.module)
  977. $node_options = variable_get('node_options_'. $node->type, array('status', 'promote'));
  978. $node->status = in_array('status', $node_options);
  979. $node->promote = in_array('promote', $node_options);
  980. if (module_exists('comment')) {
  981. $node->comment = variable_get("comment_$node->type", COMMENT_NODE_READ_WRITE);
  982. }
  983. if (module_exists('taxonomy')) {
  984. $node->taxonomy = $taxonomy;
  985. }
  986. $node->new_file = TRUE;
  987. $node->images[IMAGE_ORIGINAL] = $filepath;
  988. // Save the node.
  989. $node = node_submit($node);
  990. node_save($node);
  991. // By default, remove the original image now that the import has completed.
  992. if ($keep_original !== TRUE) {
  993. file_delete($original_path);
  994. }
  995. return $node;
  996. }
  997. /**
  998. * Implementation of hook_views_api().
  999. */
  1000. function image_views_api() {
  1001. return array(
  1002. 'api' => 2,
  1003. 'path' => drupal_get_path('module', 'image') . '/views',
  1004. );
  1005. }
  1006. /**
  1007. * Implementation of hook_content_extra_fields().
  1008. *
  1009. * Lets CCK expose the image weight in the node content.
  1010. */
  1011. function image_content_extra_fields($type_name) {
  1012. if ($type_name == 'image') {
  1013. $extra['image'] = array(
  1014. 'label' => t('Image'),
  1015. 'description' => t('Image display.'),
  1016. 'weight' => 0,
  1017. );
  1018. return $extra;
  1019. }
  1020. }