taxonomy_image.module

Tracking 5.x-1.x branch
  1. drupal
    1. 5 contributions/taxonomy_image/taxonomy_image.module
    2. 6 contributions/taxonomy_image/taxonomy_image.module

taxonomy_image.module Simple module for providing an association between taxonomy terms and images. Written by Jeremy Andrews <jeremy@kerneltrap.org>, May 2004.

Functions & methods

NameDescription
taxonomy_image_admin
taxonomy_image_admin_form
taxonomy_image_admin_settingsAdministration Page
taxonomy_image_delete
taxonomy_image_displayFunction to display the image associated with the given term id. An html <img> tag will be returned if an image is found. The link format can be modified with the tags parameter.
taxonomy_image_file_download
taxonomy_image_form_alter
taxonomy_image_get_objectFunction to get an image object with more useful data for custom formatting.
taxonomy_image_get_term
taxonomy_image_get_urlFunction to get a url for an image, for use in css, html or other client-side code.
taxonomy_image_handler_arg_taximg
taxonomy_image_handler_field_alltermsDisplay all the terms for a given vocabulary
taxonomy_image_helpImplementation of hook_help.
taxonomy_image_menuImplementation of hook_menu.
taxonomy_image_overview
taxonomy_image_parse_tagsFunction to parse tags (HTML) string.
taxonomy_image_permImplementation of hook_perm.
taxonomy_image_resizeFunction to resize the image.
taxonomy_image_taxonomyImplementation of hook_taxonomy().
taxonomy_image_userImplementation of hook_user().
taxonomy_image_views_arguments
taxonomy_image_views_handlerHandler for Taxonomy Image views field.
taxonomy_image_views_handler_linkHandler for Taxonomy Image views field as link.
taxonomy_image_views_tables
taxonomy_image_views_tables_alterThis module is a small helper module to fix an issue with a view.
_taxonomy_image_exists
_taxonomy_image_presetsHelper function for Imagecache presets.

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * taxonomy_image.module
  5. * Simple module for providing an association between taxonomy terms and images.
  6. * Written by Jeremy Andrews <jeremy@kerneltrap.org>, May 2004.
  7. */
  8. /**
  9. * Function to display the image associated with the given term id.
  10. * An html <img> tag will be returned if an image is found. The link
  11. * format can be modified with the tags parameter.
  12. * @see taxonomy_image_get_object()
  13. * @see taxonomy_image_get_url()
  14. *
  15. * @param
  16. * $tid - the term id.
  17. * @param
  18. * $tags - optional HTML attributes to include in the link.
  19. *
  20. * @return
  21. * An html img link.
  22. */
  23. function taxonomy_image_display($tid, $tags = NULL, $profile = NULL, $overrides = array()) {
  24. global $user;
  25. if (user_access('access taxonomy images') && empty($user->taxonomy_image_disable_images)) {
  26. // Get our controlling variables.
  27. $wrapper = variable_get('taxonomy_image_wrapper', FALSE);
  28. $imagecache_preset = variable_get('taxonomy_image_imagecache_preset', 'ORIGINAL');
  29. $recursive = variable_get('taxonomy_image_recursive', 0);
  30. $resize = variable_get('taxonomy_image_resize', 0);
  31. $width = variable_get('taxonomy_image_width', '');
  32. $height = variable_get('taxonomy_image_height', '');
  33. // Any overrides specified?
  34. if ($overrides) {
  35. extract($overrides, EXTR_IF_EXISTS);
  36. }
  37. // do lookup, return full display path
  38. $image = taxonomy_image_get_object($tid, $recursive);
  39. // Do we have an image?
  40. if (isset($image->path)) {
  41. if (module_exists('imagecache') && $image->type_extension != 'swf') {
  42. $preset = $profile ? $profile : $imagecache_preset;
  43. }
  44. else {
  45. $preset = 'ORIGINAL';
  46. }
  47. // Handle our own image resizing?
  48. if ($preset == 'ORIGINAL') {
  49. taxonomy_image_resize($image, $resize, $width, $height);
  50. }
  51. $current = $image;
  52. // Build the link title based on admin choice.
  53. if ($current->description && !variable_get('taxonomy_image_link_title', 0)) {
  54. $current->title = strip_tags($current->description);
  55. }
  56. else {
  57. $current->title = strip_tags($current->name);
  58. }
  59. // Have to dump double quotes for attribute.
  60. $current->title = htmlspecialchars(strip_tags($current->title), ENT_COMPAT);
  61. $my_attrs = array(
  62. 'width' => $current->width,
  63. 'height' => $current->height,
  64. );
  65. // $tag was originally an HTML attribute string. It should now be a standard attributes array.
  66. // If the caller provided the same key, this will force me to use those.
  67. if (is_array($tags)) {
  68. $attributes = array_merge($my_attrs, $tags);
  69. }
  70. else {
  71. // Handle the old string format.
  72. $attributes = array_merge($my_attrs, taxonomy_image_parse_tags($tags));
  73. }
  74. // Handle special image types.
  75. switch ($current->type_extension) {
  76. case 'swf':
  77. $attributes['type'] = $current->mime;
  78. $attributes['data'] = $current->url;
  79. $attributes['title'] = $current->title;
  80. $return_url = '<object '. drupal_attributes($attributes) .'>Your browser does not support Flash objects.</object>';
  81. break;
  82. default:
  83. if ($preset == 'ORIGINAL') {
  84. $return_url = theme('image', $current->url, $current->name, $current->title, $attributes, FALSE);
  85. }
  86. else {
  87. // Make sure the attributes don't try to override the preset.
  88. unset($attributes['width'], $attributes['height']);
  89. $return_url = theme('imagecache', $preset, $current->path, $current->name, $current->title, $attributes);
  90. }
  91. }
  92. if ($wrapper) {
  93. $return_url = '<div class="taxonomy_image_wrapper">'. $return_url .'</div>';
  94. }
  95. return $return_url;
  96. }
  97. }
  98. return '';
  99. }
  100. /**
  101. * Function to resize the image.
  102. */
  103. function taxonomy_image_resize($image, $resize, $width = NULL, $height = NULL) {
  104. switch ($resize) {
  105. case 3: // Exact.
  106. if ($width) {
  107. $image->width = $width;
  108. }
  109. if ($height) {
  110. $image->height = $height;
  111. }
  112. break;
  113. case 2: // Not less than.
  114. if ($width && ($width > $image->width)) {
  115. $width_scale = $image->width / $width;
  116. }
  117. if ($height && ($height > $image->height)) {
  118. $height_scale = $image->height / $height;
  119. }
  120. if ($height_scale || $width_scale) {
  121. if ($width_scale && $height_scale) {
  122. $scale = min($width_scale, $height_scale);
  123. }
  124. else {
  125. $scale = $width_scale ? $width_scale : $height_scale;
  126. }
  127. $image->height = round($image->height / $scale);
  128. $image->width = round($image->width / $scale);
  129. }
  130. break;
  131. case 1: // Not greater than.
  132. if ($width && ($width < $image->width)) {
  133. $width_scale = $image->width / $width;
  134. }
  135. if ($height && ($height < $image->height)) {
  136. $height_scale = $image->height / $height;
  137. }
  138. if ($height_scale || $width_scale) {
  139. $scale = max($width_scale, $height_scale);
  140. $image->height = round($image->height / $scale);
  141. $image->width = round($image->width / $scale);
  142. }
  143. break;
  144. case 0: // Disabled.
  145. $image->height = '';
  146. $image->width = '';
  147. break;
  148. }
  149. }
  150. /**
  151. * Function to parse tags (HTML) string.
  152. */
  153. function taxonomy_image_parse_tags($tags) {
  154. $attrs = array();
  155. $arr = explode(' ', $tags);
  156. foreach ($arr as $tag) {
  157. if (!empty($tag)) { // Ignore extra blanks.
  158. $key_len = strpos($tag, '=');
  159. $attrs[drupal_substr($tag, 0, $key_len)] = trim(drupal_substr($tag, $key_len + 1), '\'"');
  160. }
  161. }
  162. return $attrs;
  163. }
  164. /**
  165. * Function to get a url for an image, for use in css, html or other client-side code.
  166. * @param
  167. * $tid - the term id
  168. * @return
  169. * The url string.
  170. */
  171. function taxonomy_image_get_url($tid) {
  172. $image = taxonomy_image_get_object($tid);
  173. return $image->url;
  174. }
  175. /**
  176. * Function to get an image object with more useful data for custom formatting.
  177. * @param
  178. * $tid - the term tid.
  179. * @return
  180. * image object.
  181. */
  182. function taxonomy_image_get_object($tid, $recursive = NULL) {
  183. global $user;
  184. static $image = array();
  185. if (!$recursive) {
  186. $recursive = variable_get('taxonomy_image_recursive', 0);
  187. }
  188. // Themes may call with a string of terms, skip it. Also skip if invalid tid.
  189. if (!is_numeric($tid) || $tid < 1) {
  190. return NULL;
  191. }
  192. // Should we be here?
  193. if (!user_access('access taxonomy images') || !empty($user->taxonomy_image_disable_images)) {
  194. return NULL;
  195. }
  196. // Is the data statically cached?
  197. if (isset($image[$tid])) {
  198. return $image[$tid];
  199. }
  200. else {
  201. $cache_obj = cache_get("taxonomy_image:$tid", 'cache_tax_image');
  202. if ($cache_obj) {
  203. $image[$tid] = unserialize($cache_obj->data);
  204. return $image[$tid];
  205. }
  206. // Not cached, so go build it.
  207. if ($image[$tid] = db_fetch_object(db_query('SELECT i.path, d.name, d.description FROM {term_image} i INNER JOIN {term_data} d USING(tid) WHERE i.tid=%d', $tid))) {
  208. $image[$tid]->url = file_create_url($image[$tid]->path);
  209. }
  210. elseif ($recursive) {
  211. // Walk up the taxonomy hierarchy and look for an image.
  212. $orig = $tid;
  213. while ($parent = db_fetch_object(db_query('SELECT t.tid FROM {term_hierarchy} h, {term_data} t WHERE h.parent=t.tid AND h.tid=%d ORDER BY weight, name', $tid))) {
  214. return $image[$orig] = taxonomy_image_get_object($parent->tid, $recursive);
  215. }
  216. }
  217. }
  218. // Get more properties if we had an image.
  219. $image[$tid]->tid = $tid;
  220. // If there was no image, but there is a default, fake it.
  221. if (!isset($image[$tid]->path) && ($default = variable_get('taxonomy_image_default', NULL))) {
  222. $image[$tid]->path = $default;
  223. $term = taxonomy_get_term($tid);
  224. $image[$tid]->name = $term->name;
  225. $image[$tid]->description = $term->description;
  226. $image[$tid]->url = file_create_url($image[$tid]->path);
  227. }
  228. if (!empty($image[$tid]->path)) {
  229. $img = getimagesize($image[$tid]->path);
  230. // Make sure it worked.
  231. if (!$img) {
  232. return NULL;
  233. }
  234. $exts = array(1 => 'gif', 'jpeg', 'png', 'swf', 'psd', 'bmp',
  235. 'tiff', 'tiff', 'jpc', 'jp2', 'jpf', 'jb2', 'swc',
  236. 'aiff', 'wbmp', 'xbm');
  237. $image[$tid]->width = $img[0];
  238. $image[$tid]->height = $img[1];
  239. $image[$tid]->type = $img[2];
  240. $image[$tid]->type_extension = $exts[$img[2]];
  241. $image[$tid]->tags = $img[3];
  242. $image[$tid]->bits = isset($img['bits']) ? $img['bits'] : NULL;
  243. $image[$tid]->channels = isset($img['channels']) ? $img['channels'] : NULL;
  244. $image[$tid]->mime = isset($img['mime']) ? $img['mime'] : NULL;
  245. $image[$tid]->term = drupal_get_path_alias(taxonomy_term_path(taxonomy_get_term($tid)));
  246. $image[$tid]->img = '<img src="'. $image[$tid]->url
  247. .'" '. $image[$tid]->tags
  248. .'alt="'. check_plain($image[$tid]->name) .'" '
  249. .'title="'. check_plain(($image[$tid]->description ? $image[$tid]->description : $image[$tid]->name)) .'" '
  250. .'>';
  251. }
  252. cache_set("taxonomy_image:$tid", 'cache_tax_image', serialize($image[$tid]));
  253. return $image[$tid];
  254. }
  255. // Standard Drupal functions.
  256. /**
  257. * Implementation of hook_perm.
  258. */
  259. function taxonomy_image_perm() {
  260. return array('access taxonomy images', 'administer taxonomy images', 'can disable taxonomy images');
  261. }
  262. /**
  263. * Implementation of hook_help.
  264. */
  265. function taxonomy_image_help($section) {
  266. switch ($section) {
  267. case 'admin/content/taxonomy/taxonomy_image':
  268. if (user_access('administer taxonomy images')) {
  269. $settings = '<p>'. l(t('Taxonomy Image settings'), 'admin/settings/taxonomy_image') .'</p>';
  270. }
  271. else {
  272. $settings = NULL;
  273. }
  274. return t('The taxonomy_image module allows site administrators to associate images with category (taxonomy) terms. Once defined, this association allows Drupal themes to display images with site content. For example, this module might be used to display a penguin with content about Linux, and a cheeseburger with content about junk food. To modify or delete an existing image or to upload a new image, click "edit image" on the overview page or use the term edit page. To learn more about how to create vocabularies and terms, review the <a href="!taxonomy-help">taxonomy help page</a>.', array('!taxonomy-help' => url('admin/help/taxonomy'))) . $settings;
  275. case 'admin/help#taxonomy_image':
  276. $output = t('<h3>Introduction</h3>
  277. <p>The taxonomy_image module allows site administrators to associate images with category terms. Once defined, this association allows Drupal sites to display images with site content. For example, the taxonomy_image module might be used to display a penguin with content about Linux, and a cheeseburger with content about junk food.</p>
  278. <p>The module allows both a one-to-one term-to-image relationship, and a many-to-one terms-to-image relationship.</p>
  279. <p>The taxonomy_image module requires that the taxonomy module also be enabled. The Categories module is not currently supported.</p>
  280. <h3>Configuration</h3>
  281. <h4>Uploading images</h4>
  282. <p>With the taxonomy_image module enabled, images can be uploaded and associated with category terms at "Administer >> Content management >> Categories". On that page you will find links to tables containing all your vocabularies and terms. Next to each term is a link titled "edit term" which you can click to upload an image for that term. After clicking that link, you will be you will find the image management section at the bottom of that page. Using the "browse" button you can select your image then click "Save".
  283. <p>Continue this process to upload appropriate images for your category terms. Note that by default images will be displayed at the size they were uploaded. Alternatively, you can go to "Administer >> Site configuration >> Taxonomy_image" to set the display height and/or width of all taxonomy images.</p>
  284. <h4>Permissions</h4>
  285. <p>For your users to be able to view the images you have uploaded, you will need to give them the necessary permissions. Only users with the "access taxonomy images" permission will see images. If you wish to give your users the ability to disable the images, also give them the "can disable taxonomy images" permission. A third permission, "administer taxonomy images", controls which users are allowed to configure taxonomy images.</p>
  286. <h4>Recursive image display</h4>
  287. <p>Taxonomy is a very powerful tool. One of its features is providing the ability to create hierarchical vocabularies, with which you can build a tree of terms. It is possible that an entire tree of terms, or even just a branch of terms, are all about a similar subject and should all be associated with the same image. By going to "Administer >> Site configuration >> Taxonomy_image", you can enable "Recursive image display". With this option enabled, you only need to configure an image for the parent term, and all children will automatically inheret the same image (unless they are manually configured to display an alternate image).</p>
  288. <h3>Displaying images</h3>
  289. <p>To actually display images, you will have to make a call to taxonomy_image_display(). [There are some included add-on modules that will do this for you.] When calling this function, you will need to pass in the taxonomy term for which an image should be displayed. For example, from your theme\'s "_node" function you might do the following:
  290. <pre>
  291. foreach (taxonomy_node_get_terms($node->nid) as $term) {
  292. if ($image = taxonomy_image_display($term->tid)) {
  293. $output .= \"$image\";
  294. }
  295. </pre>
  296. <p>Taxonomy_image_display uses "theme(\'image\',..." so you may override the display in your theme layer.</p>
  297. ');
  298. if (module_exists('views')) {
  299. $output .= t('<h3>Views</h3><p>Views support has been added, so you can use the field "Taxonomy Image" in your view definition.</p>');
  300. }
  301. if (module_exists('imagecache')) {
  302. $output .= t('<h3>Imagecache</h3>
  303. <p>With imagecache enabled, you may use Imagecache profiles instead of image height/width. You can set a default profile on the settings page, or use it manually with taxonomy_image_display(), like this:</p>
  304. <pre>
  305. taxonomy_image_display($term->tid, NULL, \'PROFILE_NAME\');
  306. </pre>
  307. <p>set <em>PROFILE_NAME</em> to whatever profile you want to use.</p>'
  308. );
  309. }
  310. if (module_exists('taxonomy_image_node_display')
  311. || module_exists('taxonomy_image_link_alter')) {
  312. $output .= t('<h3>Add-on Feature Modules</h3>');
  313. }
  314. if (module_exists('taxonomy_image_node_display')) {
  315. $output .= t('<h4>Taxonomy Image Node Display</h4><p>With this add-on feature, Taxonomy Image will automatically include all available term-associated images into the display of selected node types. The weight option determines where in the output the image will be positioned. Another option specifies whether or not to add a "taxonomy/term/nn" link to each image. Configuration is part of the usual Taxonomy Image settings page.</p>');
  316. }
  317. if (module_exists('taxonomy_image_link_alter')) {
  318. $output .= t('<h4>Taxonomy Image Link Alter</h4><p>With this add-on feature, Taxonomy Image will automatically modify the standard taxonomy term display to use the term\'s image, if it has one. Configuration is part of the usual Taxonomy Image settings page.</p>');
  319. }
  320. if (module_exists('taxonomy_image_blocks')) {
  321. $output .= t('<h4>Taxonomy Image Blocks</h4><p>This add-on feature provides a block in which the terms and images are shown on full node display. Configuration is part of the usual block settings page.</p>');
  322. }
  323. return $output;
  324. }
  325. }
  326. /**
  327. * Implementation of hook_menu.
  328. */
  329. function taxonomy_image_menu($may_cache) {
  330. $items = array();
  331. if ($may_cache) {
  332. $items[]= array(
  333. 'path' => 'admin/content/taxonomy/taxonomy_image',
  334. 'title' => t('Images'),
  335. 'callback' => 'taxonomy_image_overview',
  336. 'access' => user_access('administer taxonomy images'),
  337. 'description' => t('An overview of taxonomy images'),
  338. 'type' => MENU_LOCAL_TASK,
  339. );
  340. // Admin Settings
  341. $items[] = array(
  342. 'path' => 'admin/settings/taxonomy_image',
  343. 'title' => t('Taxonomy Image'),
  344. 'callback' => 'taxonomy_image_admin_settings',
  345. 'access' => user_access('administer site configuration'),
  346. 'description' => t('Global configuration of taxonomy image functionality.'),
  347. 'type' => MENU_NORMAL_ITEM,
  348. );
  349. $items[] = array(
  350. 'path' => 'admin/settings/taxonomy_image/general',
  351. 'title' => t('General'),
  352. 'callback' => 'drupal_get_form',
  353. 'callback arguments' => array('taxonomy_image_admin_form'),
  354. 'access' => user_access('administer site configuration'),
  355. 'type' => MENU_DEFAULT_LOCAL_TASK,
  356. 'weight' => -1,
  357. );
  358. }
  359. return $items;
  360. }
  361. /**
  362. * Implementation of hook_user().
  363. */
  364. function taxonomy_image_user($type, $edit, $user, $category='account') {
  365. switch ($type) {
  366. case 'form':
  367. if (user_access('can disable taxonomy images') && $category == 'account') {
  368. $form['content_images'] = array(
  369. '#type' => 'fieldset',
  370. '#title' => t('Content images'),
  371. );
  372. $form['content_images']['taxonomy_image_disable_images'] = array(
  373. '#type' => 'checkbox',
  374. '#title' => t('Disable images'),
  375. '#return_value' => 1,
  376. '#default_value' => !empty($user->taxonomy_image_disable_images),
  377. '#description' => variable_get('taxonomy_image_disable', t('Check this box to disable the display of content images.')),
  378. );
  379. return $form;
  380. }
  381. break;
  382. }
  383. }
  384. /**
  385. * Administration Page
  386. */
  387. function taxonomy_image_admin_settings() {
  388. return drupal_get_form('taxonomy_image_admin_form');
  389. }
  390. function taxonomy_image_admin_form() {
  391. $error = NULL;
  392. if (!file_check_directory(file_create_path(variable_get('taxonomy_image_path', 'category_pictures')), FILE_CREATE_DIRECTORY)) {
  393. $error = theme('error', t('The picture directory does not exist, or is not writable.'));
  394. }
  395. drupal_add_css(drupal_get_path('module', 'taxonomy_image') .'/taxonomy_image.css');
  396. $files_path = variable_get('file_directory_path', 'files') .'/';
  397. $form['general'] = array(
  398. '#type' => 'fieldset',
  399. '#title' => t('General Options'),
  400. '#collapsible' => TRUE,
  401. '#collapsed' => FALSE,
  402. '#weight' => -6,
  403. );
  404. $form['general']['taxonomy_image_path'] = array(
  405. '#type' => 'textfield',
  406. '#title' => t('Picture image path'),
  407. '#default_value' => variable_get('taxonomy_image_path', 'category_pictures'),
  408. '#size' => 60,
  409. '#maxlength' => 255,
  410. '#field_prefix' => '<strong>'. $files_path .'</strong>',
  411. '#description' => t('Subdirectory in the directory "%dir" where category pictures will be stored. Lower case is recommended. ', array('%dir' => $files_path)) . $error,
  412. );
  413. $form['general']['taxonomy_image_disable'] = array(
  414. '#type' => 'textarea',
  415. '#rows' => 1,
  416. '#title' => t('User "Disable" box description'),
  417. '#default_value' => variable_get('taxonomy_image_disable', t('Check this box to disable the display of content images.')),
  418. '#description' => t('This allows you to describe the ability to disable Taxonomy Image display in a manner that your users will understand.'),
  419. );
  420. $form['general']['taxonomy_image_link_title'] = array(
  421. '#type' => 'radios',
  422. '#options' => array(t('Term description'), t('Term name')),
  423. '#title' => t('Link title'),
  424. '#default_value' => variable_get('taxonomy_image_link_title', 0),
  425. '#description' => t('This indicates which information to use for the link title (tool tip). If you choose "description" and one is not available, the title will be used.'),
  426. '#prefix' => '<div class="taxonomy_image_radios">',
  427. '#suffix' => '</div>',
  428. );
  429. $form['general']['taxonomy_image_default'] = array(
  430. '#type' => 'textfield',
  431. '#title' => t('Default image'),
  432. '#default_value' => variable_get('taxonomy_image_default', NULL),
  433. '#description' => t('If you specify a default image and the requested term has no image, the default will be used. You must enter the entire image path.'),
  434. '#size' => 80,
  435. '#maxlength' => 255,
  436. );
  437. $form['resizing'] = array(
  438. '#type' => 'fieldset',
  439. '#title' => t('Resizing Options'),
  440. '#collapsible' => TRUE,
  441. '#collapsed' => FALSE,
  442. '#weight' => -4,
  443. );
  444. $ic_exists = module_exists('imagecache');
  445. if ($ic_exists) {
  446. $form['resizing']['ic'] = array(
  447. '#type' => 'fieldset',
  448. '#title' => t('Resizing Options - Imagecache'),
  449. '#collapsible' => TRUE,
  450. '#collapsed' => FALSE,
  451. '#weight' => -3,
  452. );
  453. $form['resizing']['ic']['taxonomy_image_imagecache_preset'] = array(
  454. '#type' => 'radios',
  455. '#title' => t('Imagecache Preset'),
  456. '#description' => t('The Imagecache module is available. If you select one of these presets, or provide one in your call, then Taxonomy Image will use Imagecache to process the image and ignore the following settings. "ORIGINAL" effectively disables Imagecache and uses the following settings.'),
  457. '#options' => drupal_map_assoc(_taxonomy_image_presets()),
  458. '#default_value' => variable_get('taxonomy_image_imagecache_preset', NULL),
  459. '#prefix' => '<div class="taxonomy_image_radios">',
  460. '#suffix' => '</div>',
  461. );
  462. }
  463. $form['resizing']['ti'] = array(
  464. '#type' => 'fieldset',
  465. '#title' => t('Resizing Options - Taxonomy Image'),
  466. '#collapsible' => TRUE,
  467. '#collapsed' => $ic_exists,
  468. '#weight' => -2,
  469. );
  470. $form['resizing']['ti']['taxonomy_image_resize'] = array(
  471. '#type' => 'radios',
  472. '#title' => t('Constraint'),
  473. '#default_value' => variable_get('taxonomy_image_resize', 0),
  474. '#options' => array(3 => 'Exact', 2 => 'Not less than', 1 => 'Not greater than', 0 => 'Disabled'),
  475. '#description' => t('This option allows you to control the size of pictures displayed by this module. If set to "disabled," pictures will not be resized, i.e. displayed exactly as they are uploaded. If set to "not greater than," pictures larger than the specified size will be scaled down. If set to "not less than," pictures smaller than the specified size will be scaled up. If set to "exact," pictures will be resized to exactly the specified dimension(s).'),
  476. '#prefix' => '<div class="taxonomy_image_radios">',
  477. '#suffix' => '</div>',
  478. );
  479. $form['resizing']['ti']['taxonomy_image_height'] = array(
  480. '#type' => 'textfield',
  481. '#title' => t('Picture height'),
  482. '#default_value' => variable_get('taxonomy_image_height', ''),
  483. '#size' => 5,
  484. '#maxlength' => 6,
  485. '#field_suffix' => '<em>'. t('pixels') .'</em>',
  486. '#description' => t('Specify a height in pixels. If left blank or set to 0 this field is ignored.'),
  487. );
  488. $form['resizing']['ti']['taxonomy_image_width'] = array(
  489. '#type' => 'textfield',
  490. '#title' => t('Picture width'),
  491. '#default_value' => variable_get('taxonomy_image_width', ''),
  492. '#size' => 5,
  493. '#maxlength' => 6,
  494. '#field_suffix' => '<em>'. t('pixels') .'</em>',
  495. '#description' => t('Specify a width in pixels. If left blank or set to 0 this field is ignored.'),
  496. );
  497. $form['advanced'] = array(
  498. '#type' => 'fieldset',
  499. '#title' => t('Advanced'),
  500. '#collapsible' => TRUE,
  501. '#collapsed' => TRUE,
  502. '#weight' => -3,
  503. );
  504. if ($ic_exists) {
  505. $form['advanced']['taxonomy_image_admin_preset'] = array(
  506. '#type' => 'radios',
  507. '#title' => t('Admin Preset'),
  508. '#description' => t('This setting is for the images shown in the Admin page. "ORIGINAL" causes the standard non-Imagcache resizing option to be used.'),
  509. '#options' => drupal_map_assoc(_taxonomy_image_presets()),
  510. '#default_value' => variable_get('taxonomy_image_admin_preset', 'ORIGINAL'),
  511. '#prefix' => '<div class="taxonomy_image_radios">',
  512. '#suffix' => '</div>',
  513. );
  514. }
  515. $form['advanced']['taxonomy_image_wrapper'] = array(
  516. '#type' => 'checkbox',
  517. '#title' => t('Add wrapper div tag'),
  518. '#default_value' => variable_get('taxonomy_image_wrapper', FALSE),
  519. '#description' => t('This option indicates whether to add a "&lt;div&gt;" tag to the image. This may be useful in lists where the images are not of the same width.'),
  520. );
  521. $form['advanced']['taxonomy_image_recursive'] = array(
  522. '#type' => 'radios',
  523. '#title' => t('Recursive image display'),
  524. '#default_value' => variable_get('taxonomy_image_recursive', 0),
  525. '#options' => array(0 => 'Disabled', 1 => 'Enabled'),
  526. '#description' => t("When enabled, taxonomy_image_display() will recursively search for an image to display, starting with the passed in term, then trying the term's parents. This functionality is only useful if you have defined hierarchical taxonomies, and multiple terms within a tree will share the same image. If this doesn't mean anything to you, leave this option disabled."),
  527. '#prefix' => '<div class="taxonomy_image_radios">',
  528. '#suffix' => '</div>',
  529. );
  530. return system_settings_form($form);
  531. }
  532. /**
  533. * Helper function for Imagecache presets.
  534. */
  535. function _taxonomy_image_presets() {
  536. if (module_exists('imagecache')) {
  537. if (function_exists('_imagecache_get_presets')) {
  538. $presets = _imagecache_get_presets();
  539. }
  540. elseif (function_exists('imagecache_presets')) {
  541. $ic_presets = imagecache_presets();
  542. $presets = array();
  543. foreach ($ic_presets as $preset_id => $preset_info) {
  544. $presets[$preset_info['presetid']] = $preset_info['presetname'];
  545. }
  546. }
  547. else {
  548. drupal_set_message(t('Unrecognized Imagecache API.'), 'error');
  549. return FALSE;
  550. }
  551. $presets[0] = 'ORIGINAL';
  552. sort($presets);
  553. return $presets;
  554. }
  555. else {
  556. return FALSE;
  557. }
  558. }
  559. function taxonomy_image_views_tables() {
  560. $tables['term_image'] = array(
  561. 'name' => 'term_image',
  562. 'join' => array(
  563. 'left' => array(
  564. 'table' => 'term_node',
  565. 'field' => 'tid',
  566. ),
  567. 'right' => array(
  568. 'field' => 'tid'
  569. )
  570. ),
  571. 'fields' => array(
  572. 'tid' => array(
  573. 'name' => t('Taxonomy Image: Image'),
  574. 'help' => t('The image associated with the node\'s taxonomy term.'),
  575. 'handler' => array(
  576. 'taxonomy_image_views_handler' => t('Image'),
  577. 'taxonomy_image_views_handler_link' => t('Image as Link'),
  578. )
  579. )
  580. ),
  581. );
  582. if (module_exists('imagecache')) {
  583. $tables['term_image']['fields']['tid']['option']['#options'] = _taxonomy_image_presets();
  584. $tables['term_image']['fields']['tid']['option']['#type'] = 'select';
  585. }
  586. return $tables;
  587. }
  588. function taxonomy_image_views_arguments() {
  589. $arguments = array(
  590. 'taximage' => array(
  591. 'name' => t('Taxonomy: Term Image'),
  592. 'handler' => 'taxonomy_image_handler_arg_taximg',
  593. 'option' => 'string',
  594. 'help' => t('The argument will filter by a taxonomy term image.'),
  595. ),
  596. );
  597. return $arguments;
  598. }
  599. /**
  600. * Handler for Taxonomy Image views field.
  601. */
  602. function taxonomy_image_views_handler($fieldinfo, $fielddata, $value, $data) {
  603. if (empty($fielddata['options'])) {
  604. return taxonomy_image_display($value, NULL, NULL, array('wrapper' => FALSE));
  605. }
  606. else {
  607. $profile = imagecache_preset($fielddata['options']);
  608. $profile_name = $profile['presetname'];
  609. $img = taxonomy_image_display($value, $data->name, $profile_name, array('wrapper' => FALSE));
  610. return $img;
  611. }
  612. }
  613. /**
  614. * Handler for Taxonomy Image views field as link.
  615. */
  616. function taxonomy_image_views_handler_link($fieldinfo, $fielddata, $value, $data) {
  617. if (empty($fielddata['options'])) {
  618. $img = taxonomy_image_display($value, $data->name, NULL, array('wrapper' => FALSE));
  619. $img = l($img, drupal_get_path_alias(taxonomy_term_path(taxonomy_get_term($value))), array(), NULL, NULL, FALSE, TRUE);
  620. return $img;
  621. }
  622. else {
  623. $profile = imagecache_preset($fielddata['options']);
  624. $profile_name = $profile['presetname'];
  625. $img = taxonomy_image_display($value, $data->name, $profile_name, array('wrapper' => FALSE));
  626. $img = l($img, drupal_get_path_alias(taxonomy_term_path(taxonomy_get_term($value))), array(), NULL, NULL, FALSE, TRUE);
  627. return $img;
  628. }
  629. }
  630. function taxonomy_image_handler_arg_taximg($op, &$query, $argtype, $arg = '') {
  631. switch($op) {
  632. case 'summary':
  633. $query->ensure_table('term_data');
  634. $query->add_field('name', 'term_data');
  635. $query->add_field('weight', 'term_data');
  636. $query->add_field('tid', 'term_data');
  637. $fieldinfo['field'] = "term_data.name";
  638. return $fieldinfo;
  639. case 'sort':
  640. $query->add_orderby('term_data', 'weight', $argtype);
  641. $query->add_orderby('term_data', 'name', $argtype);
  642. break;
  643. case 'filter':
  644. $query->ensure_table('term_data');
  645. $query->add_where('term_data.tid = %d', $arg);
  646. $query->set_distinct();
  647. break;
  648. case 'link':
  649. $tid = intval($query->tid);
  650. if ($tid) {
  651. $img = taxonomy_image_display($tid, NULL, NULL, array('wrapper' => FALSE));
  652. }
  653. else {
  654. $img = t('Uncategorized');
  655. }
  656. return l($img, "$arg/" . $tid, array(), NULL, NULL, FALSE, TRUE);
  657. case 'title':
  658. $result = db_query("SELECT name FROM {term_data} WHERE tid = %d", $query);
  659. $term = db_fetch_object($result);
  660. return $term->name ? check_plain($term->name) : t('Uncategorized');
  661. }
  662. }
  663. /**
  664. * This module is a small helper module to fix an issue with a view.
  665. */
  666. function taxonomy_image_views_tables_alter(&$data) {
  667. if (module_exists('taxonomy_image_link_alter')) {
  668. $vocabularies = taxonomy_get_vocabularies();
  669. foreach ($vocabularies as $voc) {
  670. $data["term_node_$voc->vid"] = array(
  671. 'name' => 'term_node',
  672. 'provider' => 'internal',
  673. 'join' => array(
  674. 'left' => array(
  675. 'table' => 'node',
  676. 'field' => 'nid'
  677. ),
  678. 'right' => array(
  679. 'field' => 'nid'
  680. )
  681. ),
  682. 'fields' => array(
  683. 'name' => array(
  684. 'name' => t('Taxonomy: Terms for @voc-name', array('@voc-name' => $voc->name)),
  685. 'sortable' => FALSE,
  686. 'help' => t('This will display all taxonomy terms associated with the node that are members of %voc-name. Note that this causes one extra query per row displayed, and might have a minor performance impact.', array('%voc-name' => $voc->name)),
  687. 'handler' => 'taxonomy_image_handler_field_allterms',
  688. 'vocabulary' => $voc->vid,
  689. 'notafield' => TRUE,
  690. 'option' => array(
  691. '#type' => 'select',
  692. '#options' => array(
  693. 'link' => 'As links',
  694. 'nolink' => 'Without links',
  695. 'image' => 'As Image',
  696. 'imagenolink' => 'As Image NO links'
  697. ),
  698. ),
  699. ),
  700. ),
  701. 'filters' => array(
  702. 'tid' => array(
  703. 'name' => t('Taxonomy: Terms for @voc-name', array('@voc-name' => $voc->name)),
  704. 'value' => views_taxonomy_form($voc),
  705. //views does handle this as array regardless what we state here due to the operators
  706. 'value-type' => 'array',
  707. 'tags' => $voc->tags,
  708. 'operator' => 'views_handler_operator_andor',
  709. 'handler' => 'views_handler_filter_tid_custom',
  710. 'option' => 'string',
  711. 'vocabulary' => $voc->vid,
  712. 'help' => t("Only terms associated with %voc-name will appear in the select box for this filter. When filtering by taxonomy term you may specify the 'depth' as an option. Please see the taxonomy help for more information.", array('%voc-name' => $voc->name)),
  713. ),
  714. )
  715. );
  716. }
  717. }
  718. }
  719. /**
  720. * Display all the terms for a given vocabulary
  721. */
  722. function taxonomy_image_handler_field_allterms($fieldinfo, $fielddata, $value, $data) {
  723. if ($fieldinfo['vocabulary']) {
  724. $terms = taxonomy_node_get_terms_by_vocabulary($data->nid, $fieldinfo['vocabulary']);
  725. }
  726. else {
  727. $terms = taxonomy_node_get_terms($data->nid);
  728. }
  729. switch ($fielddata['options']) {
  730. case 'nolink':
  731. $links = array();
  732. foreach ($terms as $tid => $term) {
  733. $links[] = check_plain($term->name);
  734. }
  735. $links = !empty($links) ? implode(' | ', $links) : '';
  736. break;
  737. case 'image':
  738. // load node to get type which is needed by taxonomy_image_link_alter to check that the type is activated to replace links by images.
  739. $node = node_load($data->nid);
  740. // copy terms, but should not be needed
  741. $node->taxonomy = $terms;
  742. // taxonomy_image_link_alter will hook on the taxonomy_link call
  743. $links = theme('links', taxonomy_link('taxonomy terms', $node));
  744. break;
  745. case 'imagenolink':
  746. $links = array();
  747. foreach ($terms as $tid => $term) {
  748. $links[] = taxonomy_image_display($tid, NULL, NULL, array('wrapper' => FALSE));
  749. }
  750. $links = implode(' ', $links);
  751. break;
  752. default:
  753. $node = new stdClass();
  754. $node->taxonomy = $terms;
  755. $links = theme('links', taxonomy_link('taxonomy terms', $node));
  756. }
  757. return $links;
  758. }
  759. function taxonomy_image_file_download($file) {
  760. if (user_access('access taxonomy images')) {
  761. $path = file_create_path($file);
  762. if (function_exists('mime_content_type')) {
  763. if ($type = mime_content_type($path))
  764. return array("Content-type: $type");
  765. }
  766. list($width, $height, $type, $attr) = getimagesize($path);
  767. $types = array(
  768. IMAGETYPE_GIF => 'image/gif',
  769. IMAGETYPE_JPEG => 'image/jpeg',
  770. IMAGETYPE_PNG => 'image/png',
  771. IMAGETYPE_SWF => 'application/x-shockwave-flash',
  772. IMAGETYPE_PSD => 'image/psd',
  773. IMAGETYPE_BMP => 'image/bmp',
  774. IMAGETYPE_TIFF_II => 'image/tiff',
  775. IMAGETYPE_TIFF_MM => 'image/tiff',
  776. IMAGETYPE_JPC => 'application/octet-stream',
  777. IMAGETYPE_JP2 => 'image/jp2',
  778. IMAGETYPE_JPX => 'application/octet-stream',
  779. IMAGETYPE_JB2 => 'application/octet-stream',
  780. IMAGETYPE_SWC => 'application/x-shockwave-flash',
  781. IMAGETYPE_IFF => 'image/iff',
  782. IMAGETYPE_WBMP => 'image/vnd.wap.wbmp',
  783. IMAGETYPE_XBM => 'image/xbm'
  784. );
  785. if (isset($types[$type])) {
  786. return array('Content-type: '. $types[$type]);
  787. }
  788. }
  789. }
  790. // Taxonomy_image specific functions.
  791. function taxonomy_image_admin() {
  792. global $form_values;
  793. $op = $_POST['op'];
  794. $tid = $_POST['tid'];
  795. // TODO: Use menus, not arg()
  796. if (empty($op)) {
  797. $op = arg(3);
  798. }
  799. switch ($op) {
  800. case 'image':
  801. if (arg(4) == 'add' || arg(4) == 'edit') {
  802. $output = drupal_get_form('taxonomy_image_form', (array)(taxonomy_image_get_term(arg(5))));
  803. break;
  804. }
  805. $output = taxonomy_image_overview();
  806. break;
  807. case t('Save'):
  808. $output = taxonomy_image_save($tid);
  809. $output = taxonomy_image_overview();
  810. break;
  811. case t('Delete'):
  812. $output = taxonomy_image_delete($tid);
  813. $output = taxonomy_image_overview();
  814. break;
  815. default:
  816. $output = taxonomy_image_overview();
  817. }
  818. print theme('page', $output);
  819. }
  820. function taxonomy_image_overview() {
  821. $dest = drupal_get_destination();
  822. $output = '';
  823. if (variable_get('taxonomy_image_recursive', 0)) {
  824. $output .= '<h4>'. t('Recursively displaying images.') .'</h4>';
  825. }
  826. $admin_preset = variable_get('taxonomy_image_admin_preset', NULL);
  827. $header = array(
  828. t('Name'),
  829. t('Node Types'),
  830. t('Image'),
  831. t('Op'),
  832. );
  833. $attrs = array('class' => 'taxonomy_image_overview');
  834. $vocabularies = taxonomy_get_vocabularies();
  835. foreach ($vocabularies as $vocabulary) {
  836. $types = array();
  837. $rows = array();
  838. $types = $vocabulary->nodes;
  839. $rows[] = array(
  840. array('data' => '<strong>'. check_plain($vocabulary->name) .'</strong>', 'valign' => 'top', 'class' => 'title'),
  841. array('data' => implode(', ', $types), 'align' => 'center', 'valign' => 'middle'),
  842. '',
  843. '',
  844. );
  845. $tree = taxonomy_get_tree($vocabulary->vid);
  846. if ($tree) {
  847. foreach ($tree as $term) {
  848. $data = str_repeat('--', $term->depth) . ($term->depth > 0 ? ' ' : NULL) . check_plain($term->name);
  849. $exists = taxonomy_image_display($term->tid, array(), $admin_preset);
  850. $op = l(t('edit term'), 'admin/content/taxonomy/edit/term/'. $term->tid, array(), $dest);
  851. // Use taxonomy_image_display() instead of _taxonomy_image_exists() in
  852. // case image display recursion is enabled...
  853. $image = $exists ? $exists : ('<em>'. t('none') .'</em>');
  854. $rows[] = array(
  855. array('data' => $data, 'colspan' => 2),
  856. array('data' => $image, 'align' => 'center'),
  857. $op,
  858. );
  859. }
  860. }
  861. $output .= '<br/>'. theme('table', $header, $rows, $attrs);
  862. }
  863. return $output;
  864. }
  865. function taxonomy_image_get_term($tid) {
  866. return db_fetch_object(db_query('SELECT d.name, d.description, d.tid, i.path FROM {term_data} d LEFT JOIN {term_image} i USING(tid) WHERE d.tid = %d', $tid));
  867. }
  868. function taxonomy_image_form_alter($form_id, &$form) {
  869. switch ($form_id) {
  870. case 'taxonomy_form_term':
  871. $preview = @taxonomy_image_display($form['tid']['#value']);
  872. $form['#attributes'] = array('enctype' => 'multipart/form-data');
  873. $form['taxonomy_image'] = array(
  874. '#type' => 'fieldset',
  875. '#title' => t('Term Image'),
  876. '#collapsible' => TRUE,
  877. '#collapsed' => empty($preview),
  878. );
  879. if ($preview) {
  880. $form['taxonomy_image']['current_image'] = array(
  881. '#type' => 'fieldset',
  882. '#title' => t('Current Image'),
  883. );
  884. $form['taxonomy_image']['current_image']['image'] = array(
  885. '#value' => $preview,
  886. );
  887. $form['taxonomy_image']['current_image']['current_image_delete'] = array(
  888. '#type' => 'checkbox',
  889. '#title' => t('Delete'),
  890. '#prefix' => '<div class="taxonomy_image_checkboxes">',
  891. '#suffix' => '</div>',
  892. );
  893. }
  894. $form['taxonomy_image']['new_image']['path'] = array(
  895. '#type' => 'file',
  896. '#title' => t('Upload image'),
  897. '#size' => 40,
  898. '#description' => t("The image file you wish to associate this term."),
  899. );
  900. $form['submit']['#weight'] = 10;
  901. $form['delete']['#weight'] = 10;
  902. break;
  903. }
  904. return $form;
  905. }
  906. /**
  907. * Implementation of hook_taxonomy().
  908. */
  909. function taxonomy_image_taxonomy($op, $type, $form_values = NULL) {
  910. $directory = file_create_path(variable_get('taxonomy_image_path', 'category_pictures'));
  911. file_check_directory($directory, FILE_CREATE_DIRECTORY);
  912. // We're only interested in term changes.
  913. if ($type != 'term') {
  914. return;
  915. }
  916. $tid = $form_values['tid'];
  917. switch ($op) {
  918. case 'insert':
  919. case 'update':
  920. // Delete the cached version.
  921. cache_clear_all("taxonomy_image:$tid", 'cache_tax_image');
  922. // Did they mark it to delete?
  923. if (!empty($form_values['current_image_delete'])) {
  924. taxonomy_image_delete($tid);
  925. }
  926. $is_current_image = db_result(db_query('SELECT COUNT(tid) FROM {term_image} WHERE tid=%d', $tid));
  927. if ($file = file_save_upload('path', $directory)) {
  928. if ($is_current_image) {
  929. // Delete old image before saving the new one.
  930. taxonomy_image_delete($tid);
  931. }
  932. $insert = db_query("INSERT INTO {term_image} (tid, path) VALUES ('%d', '%s')", $tid, $file->filepath);
  933. if ($insert == FALSE) {
  934. $message = theme('error', t('Database insert failed. [tid = !tid, path = @path.', array('!tid' => $tid, '@path' => $file->filepath)));
  935. }
  936. else {
  937. $message = t('Image uploaded as @name.', array('@name' => $file->filepath));
  938. }
  939. }
  940. else if (!file_check_directory($directory)) {
  941. // we know what's wrong, so generate a more useful error message
  942. $message = theme('error', t('The category picture directory "%dir" does not exist, or is not writable.', array('%dir' => variable_get('file_directory_path', 'files') .'/'. variable_get('taxonomy_image_path', 'category_pictures'))));
  943. }
  944. else {
  945. $message = theme('error', t('Image upload failed.'));
  946. }
  947. return drupal_set_message($message);
  948. case 'delete':
  949. taxonomy_image_delete($tid);
  950. return;
  951. }
  952. }
  953. function taxonomy_image_delete($tid) {
  954. $old_path = db_result(db_query('SELECT path FROM {term_image} WHERE tid=%d', $tid));
  955. $how_many = db_result(db_query("SELECT COUNT(path) FROM {term_image} WHERE path='%s'", $old_path));
  956. $taxonomy_image_path = file_directory_path() .'/'. variable_get('taxonomy_image_path', 'category_pictures');
  957. if ((drupal_substr($old_path, 0, drupal_strlen($taxonomy_image_path)) != $taxonomy_image_path)) {
  958. // This file is not one of our own, don't actually delete it.
  959. $file_del_ok = TRUE;
  960. $db_del_ok = TRUE;
  961. }
  962. else {
  963. if ($how_many == 1) {
  964. // This is the only term using this file, so it is safe to delete it.
  965. $file_del_ok = db_query("DELETE FROM {files} WHERE filepath='%s'", $old_path);
  966. }
  967. else {
  968. // Pretend we deleted it.
  969. $file_del_ok = TRUE;
  970. drupal_set_message(t('Not deleted from the files table because it is use on !count other terms.', array('!count' => $how_many - 1)));
  971. }
  972. }
  973. $db_del_ok = db_query('DELETE FROM {term_image} WHERE tid=%d', $tid);
  974. if ($file_del_ok && $db_del_ok) {
  975. drupal_set_message(t('@name image removed.', array('@name' => $old_path)));
  976. }
  977. else {
  978. drupal_set_message(t('Image delete failed. File: !file, Db: !db.',
  979. array('!file' => ($file_del_ok ? 'yes' : 'no'), '!db' => ($db_del_ok ? 'yes' : 'no'))));
  980. }
  981. return;
  982. }
  983. function _taxonomy_image_exists($tid) {
  984. if (db_fetch_object(db_query('SELECT path FROM {term_image} WHERE tid=%d', $tid))) {
  985. return TRUE;
  986. }
  987. return FALSE;
  988. }