/[drupal]/contributions/modules/img_assist/img_assist.module
ViewVC logotype

Contents of /contributions/modules/img_assist/img_assist.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.107 - (show annotations) (download) (as text)
Thu Aug 13 19:32:44 2009 UTC (3 months, 2 weeks ago) by sun
Branch: MAIN
CVS Tags: HEAD
Changes since 1.106: +3 -364 lines
File MIME type: text/x-php
#546876 by sun: Moved administrative page callbacks into include file; removed obsolete/stale legacy img_assist_load_images() function.
1 <?php
2 // $Id: img_assist.module,v 1.106 2009/08/03 21:47:25 sun Exp $
3
4 /**
5 * @file
6 * Image Assist module
7 *
8 * Implements a javascript-driven user interface to upload images to a Drupal
9 * site and select a previously uploaded image for displaying inline in a
10 * content.
11 */
12
13 /**
14 * Implementation of hook_help().
15 */
16 function img_assist_help($path, $arg) {
17 switch ($path) {
18 case 'admin/settings/img_assist':
19 return t('If this site was moved or is planned to move to another domain or sub-directory, it might be needed to <a href="!empty-cache">empty the filter cache</a> to correct image paths that are pointing to the old address. Note that this will only work for images that have been inserted using filter tags.', array('!empty-cache' => url('img_assist/cache/clear')));
20
21 case 'img_assist/template':
22 return '<div class="%image-class"><a href="%link">%image</a><div class="caption">%caption</div></div>';
23 }
24 }
25
26 /**
27 * Implementation of hook_theme().
28 */
29 function img_assist_theme() {
30 return array(
31 'img_assist_inline' => array(
32 'arguments' => array('node' => NULL, 'size' => NULL, 'attributes' => NULL),
33 ),
34 'img_assist_filter' => array(
35 'arguments' => array('text' => NULL),
36 ),
37 'img_assist_popup' => array(
38 'arguments' => array('content' => NULL, 'attributes' => NULL),
39 ),
40 'img_assist_page' => array(
41 'arguments' => array('content' => NULL, 'attributes' => NULL),
42 ),
43 'img_assist_legacy' => array(),
44 );
45 }
46
47 /**
48 * Implementation of hook_menu().
49 */
50 function img_assist_menu() {
51 $items['img_assist/cache/clear'] = array(
52 'title' => 'Empty cache',
53 'page callback' => 'img_assist_cache_clear',
54 'access arguments' => array('administer site configuration'),
55 'type' => MENU_CALLBACK,
56 'file' => 'img_assist.admin.inc',
57 );
58 $items['img_assist/load'] = array(
59 'title' => 'Image assist',
60 'page callback' => 'img_assist_loader',
61 'access arguments' => array('access img_assist'),
62 'type' => MENU_CALLBACK,
63 );
64 // Page callbacks called internally by img_assist/load.
65 $items['img_assist/header'] = array(
66 'title' => 'Image assist header',
67 'page callback' => 'img_assist_header',
68 'page arguments' => array(2),
69 'access arguments' => array('access img_assist'),
70 'type' => MENU_CALLBACK,
71 );
72 $items['img_assist/thumbs'] = array(
73 'title' => 'Image assist thumbnails',
74 'page callback' => 'img_assist_thumbs',
75 'access arguments' => array('access img_assist'),
76 'type' => MENU_CALLBACK,
77 );
78 $items['img_assist/upload'] = array(
79 'title' => 'Image assist upload',
80 'page callback' => 'img_assist_upload',
81 'access arguments' => array('access img_assist'),
82 'type' => MENU_CALLBACK,
83 );
84 $items['img_assist/properties'] = array(
85 'title' => 'Image assist properties',
86 'page callback' => 'img_assist_properties',
87 'access arguments' => array('access img_assist'),
88 'type' => MENU_CALLBACK,
89 );
90 // Popup images page.
91 $items['img_assist/popup'] = array(
92 'title' => 'Popup image',
93 'page callback' => 'img_assist_popup',
94 'access arguments' => array('access content'),
95 'type' => MENU_CALLBACK,
96 );
97 // Insert callback (only for inserting HTML, not filter tag).
98 $items['img_assist/insert_html'] = array(
99 'title' => 'Insert callback',
100 'page callback' => 'img_assist_insert_html',
101 'access arguments' => array('access img_assist'),
102 'type' => MENU_CALLBACK,
103 );
104 $items['admin/settings/img_assist'] = array(
105 'title' => 'Image assist',
106 'description' => 'Change settings for the Image assist module.',
107 'page callback' => 'drupal_get_form',
108 'page arguments' => array('img_assist_admin_settings'),
109 'access arguments' => array('administer site configuration'),
110 'file' => 'img_assist.admin.inc',
111 );
112 return $items;
113 }
114
115 /**
116 * Implementation of hook_init().
117 */
118 function img_assist_init() {
119 $path = drupal_get_path('module', 'img_assist');
120 if (arg(0) == 'img_assist') {
121 // Suppress Administration menu.
122 module_invoke('admin_menu', 'suppress');
123
124 drupal_add_css($path .'/img_assist_popup.css', 'module', 'all', FALSE);
125 }
126 else {
127 drupal_add_js($path .'/img_assist.js');
128 if (variable_get('img_assist_page_styling', 'yes') == 'yes') {
129 drupal_add_css($path .'/img_assist.css');
130 }
131 }
132 }
133
134 /**
135 * Implementation of hook_perm().
136 */
137 function img_assist_perm() {
138 return array('access img_assist', 'access all images', 'access advanced options', 'use original size');
139 }
140
141 /**
142 * Implementation of hook_elements().
143 */
144 function img_assist_elements() {
145 $type['textarea'] = array(
146 '#process' => 'img_assist_textarea'
147 );
148 return $type;
149 }
150
151 /**
152 * Add JavaScript settings for generating the image link underneath textareas.
153 */
154 function img_assist_textarea($element) {
155 static $initialized = FALSE;
156
157 if (!user_access('access img_assist')) {
158 return $element;
159 }
160 $link = variable_get('img_assist_link', 'icon');
161 if ($link == 'icon' || $link == 'text') {
162 if (_img_assist_textarea_match($element['#id']) && _img_assist_page_match() && !strstr($_GET['q'], 'img_assist')) {
163 if (!$initialized) {
164 // Add settings.
165 $settings['link'] = $link;
166 if ($link == 'icon') {
167 $settings['icon'] = drupal_get_path('module', 'img_assist') .'/add-image.jpg';
168 }
169 drupal_add_js(array('img_assist' => $settings), 'setting');
170 $initialized = TRUE;
171 }
172
173 // Attach behavior.
174 // @todo Some browsers do not support underscores in CSS classes.
175 if (!isset($element['#attributes']['class'])) {
176 $element['#attributes']['class'] = 'img_assist';
177 }
178 else {
179 $element['#attributes']['class'] .= ' img_assist';
180 }
181 }
182 }
183 return $element;
184 }
185
186 /**
187 * Implementation of hook_block().
188 *
189 * Generates a block that references the other places the current image is used.
190 * The block is only visible when looking at the full view of an image.
191 */
192 function img_assist_block($op = 'list', $delta = 0) {
193 if ($op == 'list') {
194 $blocks[0]['info'] = t('Image reference');
195 return $blocks;
196 }
197 else if ($op == 'view') {
198 switch ($delta) {
199 case 0:
200 // Since blocks aren't passed node objects (which makes sense) we need
201 // to determine if we are viewing a node and grab its nid.
202 if (arg(0) == 'node' && is_numeric(arg(1))) {
203 $block['subject'] = t('This image appears in...');
204 $block['content'] = img_assist_get_references(arg(1));
205 return $block;
206 }
207 break;
208 }
209 }
210 }
211
212 /**
213 * Implementation of hook_filter().
214 */
215 function img_assist_filter($op, $delta = 0, $format = -1, $text = '') {
216 switch ($op) {
217 case 'list':
218 return array(0 => t('Inline images'));
219
220 case 'description':
221 return t('Add images to your posts with Image assist.');
222
223 // case 'no cache':
224 // return TRUE;
225
226 case 'process':
227 $processed = FALSE;
228 foreach (img_assist_get_macros($text) as $unexpanded_macro => $macro) {
229 $expanded_macro = img_assist_render_image($macro);
230 $text = str_replace($unexpanded_macro, $expanded_macro, $text);
231 $processed = TRUE;
232 }
233 return $processed ? theme('img_assist_filter', $text) : $text;
234
235 default:
236 return $text;
237 }
238 }
239
240 /**
241 * Implementation of hook_filter_tips().
242 */
243 function img_assist_filter_tips($delta, $format, $long = FALSE) {
244 return t('Images can be added to this post.');
245 }
246
247 /**
248 * Implementation of hook_nodeapi().
249 *
250 * - Clear input filter cache.
251 * - Keep track of where images are used.
252 * - Catch nids of recently uploaded images.
253 */
254 function img_assist_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
255 switch ($op) {
256 case 'update':
257 if ($node->type == 'image') {
258 // Clear the input filter cache to force all node content to be rebuilt.
259 // This is to make sure all image paths are up to date.
260 cache_clear_all(NULL, 'cache_filter');
261 }
262 // break is intentionally left out.
263 case 'insert':
264 // Update the image map.
265 img_assist_map_save($node);
266 break;
267
268 case 'delete':
269 img_assist_map_delete($node);
270 break;
271 }
272 }
273
274 /**
275 * @defgroup img_assist_pages Image Assist Pages
276 * @{
277 * All but img_assist_loader() are in frames.
278 */
279
280 /**
281 * Output main img_assist interface HTML.
282 *
283 * @todo Remove hard-coded TinyMCE integration.
284 */
285 function img_assist_loader() {
286 $path = drupal_get_path('module', 'img_assist');
287 $caller = arg(2) ? arg(2) : 'textarea';
288
289 drupal_add_js($path . '/img_assist_popup.js');
290 if (module_exists('wysiwyg') && ($editor = wysiwyg_get_editor($caller))) {
291 if ($editor['name'] == 'tinymce') {
292 drupal_add_js($editor['library path'] . '/tiny_mce_popup.js');
293 }
294 }
295 else {
296 $caller = 'textarea';
297 }
298 drupal_add_js($path . '/img_assist_' . $caller . '.js');
299
300 $output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN">'."\n";
301 $output .= "<html>\n";
302 $output .= "<head>\n";
303 $output .= '<title>'. t('Add image') ."</title>\n";
304 $output .= drupal_get_js();
305 $output .= "</head>\n\n";
306
307 $output .= '<frameset rows="38, *" onload="initLoader()" frameborder="0" border="0" framespacing="0">' . "\n";
308 $output .= '<frame name="img_assist_header" src="" class="img_assist_header" noresize="noresize" />' . "\n";
309 $output .= '<frame name="img_assist_main" src="" class="img_assist_main" noresize="noresize" />' . "\n";
310 $output .= "</frameset>\n";
311
312 $output .= "</html>\n";
313 echo $output;
314 exit;
315 }
316
317 /**
318 * Fetch the number of nodes for terms of given vocabulary ids.
319 *
320 * Note that this does not count nodes of subterms, as opposed to
321 * taxonomy_term_count_nodes().
322 *
323 * @param $vids
324 * An array of taxonomy vocabulary ids.
325 *
326 * @return
327 * An array keyed by term ids with the count of nodes for each term.
328 */
329 function _img_assist_term_count_nodes($vids) {
330 $count = array();
331 $result = db_query(db_rewrite_sql('SELECT t.tid, COUNT(n.nid) AS count FROM {term_node} t INNER JOIN {node} n ON t.vid = n.vid INNER JOIN {term_data} td ON td.tid = t.tid WHERE n.status = 1 AND td.vid IN (' . db_placeholders($vids) . ') GROUP BY t.tid'), implode(',', $vids));
332 while ($term = db_fetch_object($result)) {
333 $count[$term->tid] = $term->count;
334 }
335 return $count;
336 }
337
338 function img_assist_header($mode) {
339 // Mode may be 'uploading', 'properties' or 'browse'.
340 $output = drupal_get_form('img_assist_header_form', $mode);
341 echo theme('img_assist_page', $output, array('id' => 'img_assist_header', 'onload' => 'parent.initHeader();', 'class' => 'img_assist'));
342 exit;
343 }
344
345 function img_assist_header_form(&$form_state, $mode) {
346 global $user;
347
348 // Upload image.
349 if ($mode == 'uploading') {
350 $form[] = array('#value' => '<div id="header-uploading">');
351 $form[] = array('#value' => '<strong>'. t('Upload: ') .'</strong>'. t('Fill in the form below to upload a new image.'));
352 $form[] = array('#value' => '</div><div id="header-startover">');
353 $form['startover'] = array(
354 '#type' => 'button',
355 '#value' => t('Start Over'),
356 '#button_type' => 'button',
357 '#attributes' => array('onclick' => 'parent.onClickStartOver();'),
358 );
359 $form[] = array('#value' => '</div>');
360 }
361 elseif ($mode == 'properties') {
362 $form[] = array('#value' => '<div id="header-properties">');
363 $form[] = array('#value' => '<strong>'. t('Properties: ') .'</strong>'. t('Change how the image is displayed.'));
364 $form[] = array('#value' => '</div><div id="header-startover">');
365 $form['startover'] = array(
366 '#type' => 'button',
367 '#value' => t('Start Over'),
368 '#button_type' => 'button',
369 '#attributes' => array('onclick' => 'parent.onClickStartOver()'),
370 );
371 $form[] = array('#value' => '</div>');
372 }
373 // Browse images.
374 else {
375 $form[] = array('#value' => '<div id="header-browse">');
376 $form[] = array('#value' => '<strong>'. t('Browse Images: ') .'</strong>');
377
378 $views = variable_get('img_assist_views', drupal_map_assoc(array('img_assist_browser')));
379 foreach ($views as $view_name => $view_title) {
380 if ($view_name == 'img_assist_browser') {
381 // @todo execute() performs a full query, we just need the count here.
382 // Get my images and count.
383 $myimages = views_get_view('img_assist_browser');
384 $myimages->set_arguments(array($user->uid));
385 $myimages->get_total_rows = TRUE; // Required for overridden default view.
386 $myimages->execute();
387 $options['img_assist_browser/' . $user->uid] = t('My Images') ." ($myimages->total_rows)";
388
389 // Get all images and count.
390 if (user_access('access all images')) {
391 $allimages = views_get_view('img_assist_browser');
392 $allimages->set_arguments(array('all'));
393 $allimages->get_total_rows = TRUE; // Required for overridden default view.
394 $allimages->execute();
395 $options['img_assist_browser/all'] = t('All Images') ." ($allimages->total_rows)";
396 }
397 // Get category list.
398 if (module_exists('taxonomy') && $vocabs = variable_get('img_assist_vocabs', array())) {
399 $term_counts = _img_assist_term_count_nodes($vocabs);
400 // Get all images or only user's images depending on permissions.
401 $user_arg = (user_access('access all images')) ? 'all' : $user->uid;
402 foreach ($vocabs as $vid) {
403 $vocab = taxonomy_vocabulary_load($vid);
404 $terms = taxonomy_get_tree($vid);
405 if ($terms) {
406 foreach ($terms as $key => $value) {
407 $tid = $value->tid;
408 $name = $value->name;
409 $count = (isset($term_counts[$tid]) ? $term_counts[$tid] : 0);
410 $options[$vocab->name]["img_assist_browser/$user_arg/" . $tid] = "$name ($count)";
411 }
412 }
413 }
414 }
415 }
416 else {
417 $view = views_get_view($view_name);
418 $view->execute();
419 $view_title = ($view->get_title() == '') ? $view_name : $view->get_title();
420 $options[$view_name] = "$view_title ($view->total_rows)";
421 }
422 }
423 $form['browse'] = array(
424 '#type' => 'select',
425 '#default_value' => 'img_assist_browser/' . $user->uid,
426 '#options' => $options,
427 '#attributes' => array('onchange' => 'parent.onChangeBrowseBy(this)'),
428 );
429 if (user_access('create images')) {
430 $form['upload'] = array(
431 '#type' => 'button',
432 '#prefix' => ' '. t('or') .' ',
433 '#value' => t('Upload'),
434 '#suffix' => ' '. t('a new image'),
435 '#button_type' => 'button',
436 '#attributes' => array('onclick' => 'parent.onClickUpload()'),
437 );
438 }
439 $form[] = array('#value' => '</div><div id="header-cancel">');
440 $form['cancel'] = array(
441 '#type' => 'button',
442 '#value' => t('Cancel'),
443 '#button_type' => 'button',
444 '#attributes' => array('onclick' => 'parent.cancelAction()'),
445 );
446 $form[] = array('#value' => '</div>');
447 }
448 return $form;
449 }
450
451 /**
452 * Interface for adding images. Uses the regular image node form.
453 */
454 function img_assist_upload() {
455 global $user;
456 module_load_include('inc', 'node', 'node.pages');
457
458 if (module_exists('image') && user_access('create images')) {
459 // On other img_assist_pages I've added the javascript using the body onload
460 // attribute, but for this page will also need the collapse functions and
461 // setting the body onload interferes with this. To solve this, I'm forced
462 // use the $(document).ready call. I should probably switch all the pages to
463 // this format to be more Drupal friendly, but at the same time I don't know
464 // if it really matters. If a user doesn't have Javascript, she can't use
465 // img_assist at all.
466 $output = "<script type=\"text/javascript\"><!-- \n";
467 $output .= " if (Drupal.jsEnabled) { \n";
468 $output .= " $(document).ready(parent.initUpload);\n";
469 $output .= " } \n";
470 $output .= "--></script>\n";
471
472 // Define an empty node and fetch an image node form
473 $node = array('uid' => $user->uid, 'name' => $user->name, 'type' => 'image');
474 $output .= drupal_get_form('image_node_form', $node);
475 }
476 else {
477 if (!module_exists('image')) {
478 $output = t('The image module must be enabled to use Image assist.');
479 }
480 else {
481 $output = t('Your account does not have image uploading privileges.');
482 }
483 }
484 echo theme('img_assist_page', $output, array('id' => 'img_assist_upload', 'class' => 'img_assist'));
485 exit;
486 }
487
488 /**
489 * Implementation of hook_form_alter().
490 *
491 * Reroute submit button callback to our own handler to be able to redirect
492 * the user after saving the node.
493 *
494 * @see img_assist_node_form_submit()
495 */
496 function img_assist_form_alter(&$form, &$form_state, $form_id) {
497 if ($form_id == 'image_node_form' && arg(0) == 'img_assist') {
498 $form['buttons']['submit']['#submit'] = array('img_assist_node_form_submit');
499 }
500 // If a view uses exposed filters in the image browser, we need to modify #action.
501 elseif ($form_id == 'views_exposed_form' && arg(0) == 'img_assist') {
502 $form['#action'] = url($_GET['q']);
503 }
504 }
505
506 /**
507 * Submit callback for image_node_form.
508 */
509 function img_assist_node_form_submit($form, &$form_state) {
510 // Execute regular node submit handler.
511 node_form_submit($form, $form_state);
512
513 if ($form_state['nid']) {
514 // Send to different url.
515 $form_state['redirect'] = 'img_assist/properties/'. $form_state['nid'];
516 }
517 }
518
519 /**
520 * Load the thumbnail display pane.
521 *
522 * Loads a View displaying the thumbnails. The view name is picked from the
523 * third path argument and any remaining arguments are used as arguments to the
524 * view.
525 * Module developers may add options to img_assist_header_form using
526 * hook_form_alter() if they wish to use custom views and/or arguments.
527 */
528 function img_assist_thumbs() {
529 global $user;
530
531 if (module_exists('image') && module_exists('views')) {
532 $view_name = ((arg(2) != '') ? arg(2) : 'img_assist_browser');
533
534 // Get view arguments from path.
535 $args = explode('/', $_GET['q']);
536 $args = array_slice($args, 3);
537
538 // Check sanity and permissions for the 'img_assist_browser' view.
539 if ($view_name == 'img_assist_browser') {
540 if (empty($args[0]) || !user_access('access all images')) {
541 $args[0] = $user->uid;
542 }
543 if (isset($args[1])) {
544 $args[1] = (int) $args[1];
545 }
546 }
547
548 $view = views_get_view($view_name);
549 if ($view) {
550 $view_output = $view->execute_display(NULL, $args);
551 if (empty($view_output)) {
552 $output = t('No images were found. Please upload a new image or browse images by a different category.');
553 }
554 else {
555 $output = $view_output;
556 }
557 }
558 else {
559 $output = t('Error: The specified view was not found.');
560 }
561 }
562 else {
563 $output = t('The Image and Views modules must be enabled to use Image assist.');
564 }
565 echo theme('img_assist_page', $output, array('id' => 'img_assist_thumbs', 'onload' => 'parent.initThumbs();', 'class' => 'img_assist'));
566 exit;
567 }
568
569 /**
570 * Load the image properties pane.
571 */
572 function img_assist_properties() {
573 $nid = arg(2);
574 // Update is put into a hidden field so the javascript can see it.
575 $update = (arg(3)) ? 1 : 0;
576
577 if (is_numeric($nid) && ($node = node_load($nid)) && $node->type == 'image' && node_access('view', $node)) {
578 $output = drupal_get_form('img_assist_properties_form', $node, $update);
579 }
580 else {
581 $output = t('Image ID not found');
582 }
583
584 echo theme('img_assist_page', $output, array('id' => 'img_assist_properties', 'onload' => 'parent.initProperties();', 'class' => 'img_assist'));
585 exit;
586 }
587
588 /**
589 * Convert a node field value to text for usage in a textfield.
590 */
591 function img_assist_sanitize($text) {
592 return check_plain(trim(preg_replace("/[\r\n]+/", ' ', strip_tags($text))));
593 }
594
595 /**
596 * Construct the image properties form.
597 */
598 function img_assist_properties_form($form_state, $node, $update) {
599 require_once drupal_get_path('module', 'img_assist') .'/includes/img_assist.token.inc';
600
601 $image_info = image_get_info(file_create_path($node->images[IMAGE_ORIGINAL]));
602 $image_info['aspect_ratio'] = $image_info['height'] / $image_info['width'];
603
604 // Select (or generate) a preview image.
605 $img_assist_create_derivatives = variable_get('img_assist_create_derivatives', array());
606 if (!empty($img_assist_create_derivatives['properties'])) {
607 $properties_size['label'] = t('Properties');
608 $properties_size['key'] = 'img_assist_properties';
609 $properties_size['width'] = 200;
610 $properties_size['height'] = 200;
611 }
612 else {
613 $properties_size['key'] = IMAGE_THUMBNAIL;
614 }
615 $properties_image = img_assist_display($node, $properties_size);
616 // Get actual image size.
617 $properties_size = image_get_info(file_create_path($node->images[$properties_size['key']]));
618
619 // Create an array of image derivative choices
620 //
621 // The name for each option is actually the size in pixels, not the derivative
622 // name. This is necessary so that
623 // - the Javascript that process this page and inserts code to your textarea
624 // or editor will know the size to make the image placeholder (in a WYSIWYG
625 // editor)
626 // - the code that processes the img_assist filter tags can work with standard
627 // sizes and custom sizes in the same way.
628 // The WYSIWYG placeholder, however, is the most important reason to keep the
629 // img_assist tags this way. This way users can even resize images in the
630 // editor, and if they aren't allow to create custom sizes the filter will
631 // pick the existing derivative that is closest to the size of the WYSIWYG
632 // placeholder. For users not familiar with pixel sizes or names like
633 // 'thumbnail' and 'preview', this is a nice visual way to size images.
634 // The size selection dropdown could even be hidden using the stylesheet,
635 // making the insertion of images even more of a visual process. And for
636 // administrators and those with the proper permissions, images don't have to
637 // snap to the nearest standard size. You can create any arbitrary size you
638 // choose.
639 $max_size = explode('x', variable_get('img_assist_max_size', '640x640'));
640
641 foreach (image_get_sizes(NULL, $image_info['aspect_ratio']) as $key => $size) {
642 // Sizes are strings, may contain '' for 0; convert to integers.
643 settype($size['width'], 'int');
644 settype($size['height'], 'int');
645
646 $added_to_derivatives = FALSE;
647 if ($key == IMAGE_ORIGINAL) {
648 if (user_access('use original size') && $image_info['width'] <= $max_size[0] && $image_info['height'] <= $max_size[1]) {
649 $derivatives[$image_info['width'] .'x'. $image_info['height']] = $size['label'];
650 $added_to_derivatives = TRUE;
651 }
652 }
653 elseif ($size['width'] <= $max_size[0] && $size['height'] <= $max_size[1]) {
654 $derivatives[$size['width'] .'x'. $size['height']] = $size['label'];
655 $added_to_derivatives = TRUE;
656 }
657
658 if (!$added_to_derivatives) {
659 // The thumbnail option will be shown even if it is larger than the
660 // maximum size.
661 if ($key == IMAGE_THUMBNAIL) {
662 $derivatives[$size['width'] .'x'. $size['height']] = $size['label'];
663 }
664 }
665 }
666
667 // Add a choice for 'other' if the user has the proper permission to create
668 // custom sizes.
669 if (!empty($img_assist_create_derivatives['custom_advanced']) && user_access('access advanced options')) {
670 $derivatives['other'] = t('Other');
671 }
672
673 // Use 'preview' size by default.
674 $default_size = image_get_info(file_create_path($node->images[IMAGE_PREVIEW]));
675 $default_width = $default_size['width'];
676 $default_height = $default_size['height'];
677
678 // Calculate the aspect ratio to keep in a hidden field
679 //
680 // When 'other' is chosen, the custom size will always follow the aspect ratio.
681 // It doesn't really matter what this value is here because the actual custom
682 // image will be created when the filter tag is processed. The size, of course,
683 // is a bounding box. If it a user stretches an image placeholder out of
684 // proportion in the WYSIWYG editor, the image will never be out of proportion
685 // on the processed page.
686 $aspect_ratio = ($default_height > 0 ? round($default_width / $default_height, 6) : 1);
687
688 // Create the form.
689 $form[] = array('#value' => '<div id="properties">');
690 $form[] = array('#value' => '<table width="100%" border="0" cellspacing="0" cellpadding="0">');
691 $form[] = array('#value' => '<tr><td valign="top" rowspan="3" id="preview">');
692 $form[] = array('#value' => $properties_image);
693 $form[] = array('#value' => '<span id="caption" style="width: '. $properties_size['width'] .'px;">'. check_plain($node->title) .'</span>');
694
695 // Image node properties fieldset.
696 $form['properties'] = array('#type' => 'fieldset', '#title' => t('Image properties'));
697 $form['properties'][] = array('#value' => '<div class="field field-type-text"><div class="field-label">'. t('Size') .': </div><div class="field-items"><div class="field-item">'. strtr('@widthx@height px', array('@width' => $image_info['width'], '@height' => $image_info['height'])) .'</div></div></div>');
698 $rendered = node_build_content($node);
699 foreach (array_filter(variable_get('img_assist_display_properties', array())) as $field) {
700 if (isset($node->content[$field])) {
701 $form['properties'][] = array('#value' => drupal_render($node->content[$field]));
702 }
703 }
704
705 $form[] = array('#value' => '</td><td width="100%" colspan="2">');
706
707 $token_installed = module_exists('token');
708 if (variable_get('img_assist_load_title', 1)) {
709 $title = img_assist_sanitize($token_installed ? token_replace(variable_get('img_assist_title_pattern', '[title]'), 'node', $node) : $node->title);
710 }
711 if (variable_get('img_assist_load_description', 1)) {
712 $description = img_assist_sanitize($token_installed ? token_replace(variable_get('img_assist_description_pattern', '[body]'), 'node', $node) : $node->body);
713 }
714 $form['title'] = array(
715 '#type' => 'textfield',
716 '#title' => t('Title (optional)'),
717 '#default_value' => isset($title) ? $title : '',
718 '#size' => 50,
719 '#maxlength' => 255,
720 '#description' => NULL,
721 '#attributes' => array('onblur' => 'parent.updateCaption()'),
722 );
723 $form['desc'] = array(
724 '#type' => 'textfield',
725 '#title' => t('Description (optional)'),
726 '#default_value' => isset($description) ? $description : '',
727 '#size' => 50,
728 '#maxlength' => 255,
729 '#description' => NULL,
730 '#attributes' => array('onblur' => 'parent.updateCaption()'),
731 );
732
733 // Size.
734 $form[] = array('#value' => '</td></tr><tr><td width="90%">');
735 $form[] = array('#value' => '<div class="form-item" id="edit-size">');
736 $form[] = array('#value' => '<label for="edit-size-label">' . t('Size: (max @maxsize)', array('@maxsize' => variable_get('img_assist_max_size', '640x640'))) .'</label>');
737 $form['size_label'] = array(
738 '#type' => 'select',
739 '#default_value' => variable_get('img_assist_default_label', '100x100'),
740 '#options' => $derivatives,
741 '#attributes' => array('onchange' => 'parent.onChangeSizeLabel()'),
742 );
743 $form[] = array('#value' => '<div class="form-item" id="size-other">');
744 $form['width'] = array(
745 '#type' => 'textfield',
746 '#default_value' => $default_width,
747 '#size' => 4,
748 '#maxlength' => 4,
749 '#attributes' => array('onblur' => 'parent.onChangeWidth()'),
750 );
751 $form[] = array('#value' => ' x ');
752 $form['height'] = array(
753 '#type' => 'textfield',
754 '#default_value' => $default_height,
755 '#size' => 4,
756 '#maxlength' => 4,
757 '#attributes' => array('onblur' => 'parent.onChangeHeight()'),
758 );
759 $form[] = array('#value' => '</div></div>');
760 $form[] = array('#value' => '</td><td>');
761
762 // Alignment.
763 $form['align'] = array(
764 '#type' => 'select',
765 '#title' => t('Alignment'),
766 '#default_value' => variable_get('img_assist_default_alignment', 'left'),
767 '#options' => array('left' => t('left'), 'right' => t('right'), 'none' => t('none'), 'center' => t('center')),
768 '#prefix' => '<div id="alignment">',
769 '#suffix' => '</div>',
770 );
771 $form[] = array('#value' => '</td></tr><tr><td colspan="2">');
772
773 // Link.
774 if (user_access('access advanced options')) {
775 $form[] = array('#value' => '<div class="form-item" id="link-group">');
776 $form['link'] = array(
777 '#type' => 'select',
778 '#title' => t('Link'),
779 '#default_value' => variable_get('img_assist_default_link_behavior', 'none'),
780 '#options' => array('none' => t('Not a link'), 'node' => t('Link to image page'), 'popup' => t('Open in popup window'), 'url' => t('Go to URL')),
781 '#attributes' => array('onchange' => 'parent.onChangeLink()'),
782 );
783 $form['url'] = array(
784 '#type' => 'textfield',
785 '#default_value' => variable_get('img_assist_default_link_url', 'http://'),
786 '#size' => 25,
787 '#maxlength' => 255,
788 '#description' => NULL,
789 );
790 $form['link_options_visible'] = array(
791 '#type' => 'hidden',
792 '#value' => 1,
793 );
794 $form[] = array('#value' => '</div>');
795 }
796 else {
797 $form['link'] = array(
798 '#type' => 'hidden',
799 '#value' => variable_get('img_assist_default_link_behavior', 'none'),
800 );
801 $form['url'] = array(
802 '#type' => 'hidden',
803 '#value' => variable_get('img_assist_default_link_url', 'http://'),
804 );
805 $form['link_options_visible'] = array(
806 '#type' => 'hidden',
807 '#value' => 0,
808 );
809 }
810 // Default link url is needed for JS to indicate if an url has been entered.
811 $form['default_url'] = array(
812 '#type' => 'hidden',
813 '#value' => variable_get('img_assist_default_link_url', 'http://'),
814 );
815
816 // Insert Mode (HTML or Filter Tag).
817 if (user_access('access advanced options')) {
818 $form[] = array('#value' => '<div id="insertmode">');
819 $form['insertmode'] = array(
820 '#type' => 'select',
821 '#title' => t('Insert mode'),
822 '#default_value' => variable_get('img_assist_default_insert_mode', 'filtertag'),
823 '#options' => array('filtertag' => t('Filter Tag'), 'html' => t('HTML Code')),
824 );
825 $form[] = array('#value' => '</div>');
826 }
827 else {
828 $form['insertmode'] = array(
829 '#type' => 'hidden',
830 '#value' => variable_get('img_assist_default_insert_mode', 'filtertag'),
831 );
832 }
833
834 // Hidden Fields.
835 $form['nid'] = array(
836 '#type' => 'hidden',
837 '#value' => $node->nid,
838 );
839 $form['update'] = array(
840 '#type' => 'hidden',
841 '#value' => $update,
842 );
843 $form['aspect'] = array(
844 '#type' => 'hidden',
845 '#value' => $aspect_ratio,
846 );
847
848 // Buttons.
849 $form['buttons'] = array(
850 '#prefix' => '<div id="buttons">',
851 '#suffix' => '</div>',
852 );
853 $form['#attributes']['onsubmit'] = 'return parent.insertImage();';
854 $form['buttons']['insert'] = array(
855 '#type' => 'submit',
856 '#value' => ($update) ? t('Update') : t('Insert'),
857 '#attributes' => array('style' => 'float: left;'),
858 );
859 $form['buttons']['cancel'] = array(
860 '#type' => 'button',
861 '#value' => t('Cancel'),
862 '#button_type' => 'button',
863 '#attributes' => array('onclick' => 'return parent.cancelAction();', 'style' => 'float: right;'),
864 );
865
866 $form[] = array('#value' => '</td></tr></table></div>');
867
868 $form['#attributes']['name'] = 'img_assist';
869 return $form;
870 }
871
872 function img_assist_properties_form_validate($form, &$form_state) {
873 $html = img_assist_render_image($form_state['values']);
874 img_assist_set_htmlcode($html);
875 drupal_goto('img_assist/insert_html');
876 }
877
878 /**
879 * Store image tag or HTML in session.
880 *
881 * Used for saving HTML code so it can be inserted instead of the filter tags.
882 *
883 * @param string $htmlcode
884 * A filter tag or HTML code. If omitted, session variable is emptied.
885 *
886 * @return string
887 * A previously stored value in the user session.
888 */
889 function img_assist_set_htmlcode($htmlcode = NULL) {
890 if (isset($htmlcode)) {
891 $_SESSION['htmlcode'] = urlencode($htmlcode);
892 }
893 else {
894 $html = urldecode($_SESSION['htmlcode']);
895 $_SESSION['htmlcode'] = '';
896 return $html;
897 }
898 }
899
900 function img_assist_insert_html() {
901 $output = drupal_get_form('img_assist_insert_html_form');
902 echo theme('img_assist_page', $output, array('id' => 'img_assist_insert_html', 'onload' => 'parent.insertImage();', 'class' => 'img_assist'));
903 }
904
905 function img_assist_insert_html_form() {
906 $htmlcode = img_assist_set_htmlcode();
907 $form[] = array(
908 '#id' => 'finalhtmlcode',
909 '#type' => 'hidden',
910 '#value' => $htmlcode,
911 );
912
913 $form['insertmode'] = array(
914 '#type' => 'hidden',
915 '#value' => 'html2',
916 );
917
918 return $form;
919 }
920
921 /**
922 * @} End of "defgroup img_assist_pages".
923 */
924
925 /**
926 * @defgroup img_assist_image Image Assist Image Generation
927 * @{
928 * Functions used in image.module vs. img_assist.module (simplified):
929 *
930 * image.module:
931 * - image_display()
932 * - is called for galleries, image nodes, image blocks, etc
933 * (everytime in image is shown)
934 * - returns <span ...><img ...></span>
935 * - can be passed a specific standard size only
936 * - may call _image_build_derivatives()
937 * - calls theme_image() to create the <img> tag
938 * _image_build_derivatives()
939 * - rebuilds all standard image sizes for a particular node
940 *
941 * img_assist.module: (more complicated, but more flexible)
942 * - image_display()
943 * - is called for thumbnails browsing, inline images, etc (everytime in image is shown)
944 * - returns <span ...><img ...></span>
945 * - can be passed EITHER a specific standard size only OR a custom size
946 * - may call _image_build_derivatives()
947 * - calls theme_image() to create the <img> tag
948 * _image_build_derivatives()
949 * - rebuilds only a specfic image size (standard or custom size)
950 */
951
952 /**
953 * Create an IMG tag for an image.
954 *
955 * This is nearly identical to image_display, but
956 * - it uses a more efficient regenerate images routine
957 * - the size attribute can be a custom size OR a standard size
958 */
959 function img_assist_display(&$node, $size = NULL, $attributes = array()) {
960 // Custom size should include values for label, width, and height.
961 if (is_array($size) && !empty($size['key']) && !empty($size['width']) && !empty($size['height'])) {
962 $label = $size['key'];
963 }
964 // Standard size.
965 elseif ($size) {
966 // Size can be an array without the width and/or height.
967 if (is_array($size)) {
968 // Size is no longer an array.
969 $size = $size['key'];
970 }
971 $label = $size;
972 }
973 // Assign preview size if no size specified.
974 else {
975 $label = IMAGE_THUMBNAIL;
976 }
977
978 // Regenerate images if necessary.
979 $regen = FALSE;
980 if (!isset($node->images[$label])) {
981 $regen = TRUE;
982 }
983 elseif (!is_file(file_create_path($node->images[$label]))) {
984 $regen = TRUE;
985 }
986 elseif (filemtime(file_create_path($node->images[$label])) < variable_get('image_updated', 0)) {
987 $regen = TRUE;
988 }
989 else {
990 // If $size is not an array, try to find the corresponding predefined size.
991 // _image_build_derivatives() blindly assigns the *original* image file to
992 // all derivative image sizes that are smaller than the original image size.
993 // Without re-assigning the actual derivative size definition, img_assist
994 // would assume that this derivative size does not exist, delete the
995 // *original* file and subsequently fail to generate derivative images.
996 // Also, when one predefined size has changed, the derivative sizes need to
997 // be updated.
998 if (!is_array($size)) {
999 foreach (image_get_sizes() as $std_size) {
1000 if (isset($std_size['key']) && $std_size['key'] == $label) {
1001 $size = $std_size;
1002 break;
1003 }
1004 }
1005 }
1006 if (is_array($size)) {
1007 $info = image_get_info(file_create_path($node->images[$label]));
1008 if (($info['width'] != $size['width']) && ($info['height'] != $size['height'])) {
1009 $regen = TRUE;
1010 }
1011 }
1012 }
1013
1014 if ($regen) {
1015 _img_assist_build_derivatives($node, $size);
1016 }
1017
1018 return image_display($node, $label);
1019 }
1020
1021 /**
1022 * Generate a image derivative
1023 *
1024 * @see _image_build_derivatives() in image.module
1025 *
1026 * @param $node
1027 * @param $size
1028 * An array containing the keys 'label', 'width', 'height'.
1029 */
1030 function _img_assist_build_derivatives(&$node, $size = NULL) {
1031 // Verify the image module and toolkit settings.
1032 if (!_image_check_settings()) {
1033 return FALSE;
1034 }
1035 $info = image_get_info(file_create_path($node->images[IMAGE_ORIGINAL]));
1036
1037 // Custom size.
1038 if (is_array($size) && !empty($size['key']) && !empty($size['width']) && !empty($size['height'])) {
1039 $sizes = array($size['key'] => $size);
1040 }
1041 // Standard size.
1042 elseif ($size) {
1043 // Size can be an array without the width and/or height.
1044 if (is_array($size)) {
1045 $size = $size['key'];
1046 }
1047 $sizes = image_get_sizes();
1048 $sizes = array($size => $sizes[$size]);
1049 }
1050 // No size given: rebuild derivatives for all standard sizes.
1051 else {
1052 $sizes = image_get_sizes();
1053 }
1054
1055 foreach ($sizes as $key => $size) {
1056 $size['key'] = $key;
1057 _img_assist_remove($node, $size);
1058
1059 if (is_array($size) && ($size['label']) && ($size['width']) && ($size['height'])) {
1060 if ($info['width'] > $size['width'] || $info['height'] > $size['height']) {
1061 $source = file_create_path($node->images[IMAGE_ORIGINAL]);
1062 $destination = _image_filename(basename($source), $key, FALSE);
1063 $destination_path = file_create_path($destination);
1064 if (!image_scale($source, $destination_path, $size['width'], $size['height'])) {
1065 drupal_set_message(t('Unable to create %label image', array('%label' => $size['label'])), 'error');
1066 }
1067 else {
1068 // Set default file permissions for webserver-generated files.
1069 @chmod($destination_path, 0664);
1070 $node->images[$key] = $destination;
1071 _image_insert($node, $key, $destination_path);
1072 }
1073 }
1074 else {
1075 $node->images[$key] = $node->images[IMAGE_ORIGINAL];
1076 }
1077 }
1078 }
1079 }
1080
1081 function _img_assist_remove($node, $size) {
1082 $result = db_query("SELECT * FROM {files} f INNER JOIN {image} i ON f.fid = i.fid WHERE i.nid = %d AND f.filename = '%s'", $node->nid, $size['key']);
1083 while ($file = db_fetch_object($result)) {
1084 // Never delete original image.
1085 if ($file->filepath != $node->images[IMAGE_ORIGINAL]) {
1086 // Delete image file.
1087 file_delete(file_create_path($file->filepath));
1088 // Delete file reference in database.
1089 db_query("DELETE FROM {files} WHERE fid = %d AND filename = '%s'", $file->fid, $size['key']);
1090 db_query("DELETE FROM {image} WHERE nid = %d AND fid = '%d'", $node->nid, $file->fid);
1091 }
1092 }
1093 }
1094
1095 /**
1096 * Return image HTML.
1097 */
1098 function img_assist_render_image($attributes = array()) {
1099 global $user;
1100
1101 if ($attributes['nid']) {
1102 $node = node_load($attributes['nid']);
1103
1104 // Get image size.
1105 $width = (int) $attributes['width'];
1106 $height = (int) $attributes['height'];
1107 $default_sizes = image_get_sizes();
1108 if ($width || $height) {
1109 // Check to ensure that the dimensions don't exceed the max set in the