empty the filter cache 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'))); case 'img_assist/template': return '
%image
%caption
'; } } /** * Implementation of hook_theme(). */ function img_assist_theme() { return array( 'img_assist_inline' => array( 'arguments' => array('node' => NULL, 'size' => NULL, 'attributes' => NULL), ), 'img_assist_filter' => array( 'arguments' => array('text' => NULL), ), 'img_assist_popup' => array( 'arguments' => array('content' => NULL, 'attributes' => NULL), ), 'img_assist_page' => array( 'arguments' => array('content' => NULL, 'attributes' => NULL), ), 'img_assist_legacy' => array(), ); } /** * Implementation of hook_menu(). */ function img_assist_menu() { $items['img_assist/cache/clear'] = array( 'title' => 'Empty cache', 'page callback' => 'img_assist_cache_clear', 'access arguments' => array('administer site configuration'), 'type' => MENU_CALLBACK, 'file' => 'img_assist.admin.inc', ); $items['img_assist/load'] = array( 'title' => 'Image assist', 'page callback' => 'img_assist_loader', 'access arguments' => array('access img_assist'), 'type' => MENU_CALLBACK, ); // Page callbacks called internally by img_assist/load. $items['img_assist/header'] = array( 'title' => 'Image assist header', 'page callback' => 'img_assist_header', 'page arguments' => array(2), 'access arguments' => array('access img_assist'), 'type' => MENU_CALLBACK, ); $items['img_assist/thumbs'] = array( 'title' => 'Image assist thumbnails', 'page callback' => 'img_assist_thumbs', 'access arguments' => array('access img_assist'), 'type' => MENU_CALLBACK, ); $items['img_assist/upload'] = array( 'title' => 'Image assist upload', 'page callback' => 'img_assist_upload', 'access arguments' => array('access img_assist'), 'type' => MENU_CALLBACK, ); $items['img_assist/properties'] = array( 'title' => 'Image assist properties', 'page callback' => 'img_assist_properties', 'access arguments' => array('access img_assist'), 'type' => MENU_CALLBACK, ); // Popup images page. $items['img_assist/popup'] = array( 'title' => 'Popup image', 'page callback' => 'img_assist_popup', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); // Insert callback (only for inserting HTML, not filter tag). $items['img_assist/insert_html'] = array( 'title' => 'Insert callback', 'page callback' => 'img_assist_insert_html', 'access arguments' => array('access img_assist'), 'type' => MENU_CALLBACK, ); $items['admin/settings/img_assist'] = array( 'title' => 'Image assist', 'description' => 'Change settings for the Image assist module.', 'page callback' => 'drupal_get_form', 'page arguments' => array('img_assist_admin_settings'), 'access arguments' => array('administer site configuration'), 'file' => 'img_assist.admin.inc', ); return $items; } /** * Implementation of hook_init(). */ function img_assist_init() { $path = drupal_get_path('module', 'img_assist'); if (arg(0) == 'img_assist') { // Suppress Administration menu. module_invoke('admin_menu', 'suppress'); drupal_add_css($path .'/img_assist_popup.css', 'module', 'all', FALSE); } else { drupal_add_js($path .'/img_assist.js'); if (variable_get('img_assist_page_styling', 'yes') == 'yes') { drupal_add_css($path .'/img_assist.css'); } } } /** * Implementation of hook_perm(). */ function img_assist_perm() { return array('access img_assist', 'access all images', 'access advanced options', 'use original size'); } /** * Implementation of hook_elements(). */ function img_assist_elements() { $type['textarea'] = array( '#process' => 'img_assist_textarea' ); return $type; } /** * Add JavaScript settings for generating the image link underneath textareas. */ function img_assist_textarea($element) { static $initialized = FALSE; if (!user_access('access img_assist')) { return $element; } $link = variable_get('img_assist_link', 'icon'); if ($link == 'icon' || $link == 'text') { if (_img_assist_textarea_match($element['#id']) && _img_assist_page_match() && !strstr($_GET['q'], 'img_assist')) { if (!$initialized) { // Add settings. $settings['link'] = $link; if ($link == 'icon') { $settings['icon'] = drupal_get_path('module', 'img_assist') .'/add-image.jpg'; } drupal_add_js(array('img_assist' => $settings), 'setting'); $initialized = TRUE; } // Attach behavior. // @todo Some browsers do not support underscores in CSS classes. if (!isset($element['#attributes']['class'])) { $element['#attributes']['class'] = 'img_assist'; } else { $element['#attributes']['class'] .= ' img_assist'; } } } return $element; } /** * Implementation of hook_block(). * * Generates a block that references the other places the current image is used. * The block is only visible when looking at the full view of an image. */ function img_assist_block($op = 'list', $delta = 0) { if ($op == 'list') { $blocks[0]['info'] = t('Image reference'); return $blocks; } else if ($op == 'view') { switch ($delta) { case 0: // Since blocks aren't passed node objects (which makes sense) we need // to determine if we are viewing a node and grab its nid. if (arg(0) == 'node' && is_numeric(arg(1))) { $block['subject'] = t('This image appears in...'); $block['content'] = img_assist_get_references(arg(1)); return $block; } break; } } } /** * Implementation of hook_filter(). */ function img_assist_filter($op, $delta = 0, $format = -1, $text = '') { switch ($op) { case 'list': return array(0 => t('Inline images')); case 'description': return t('Add images to your posts with Image assist.'); // case 'no cache': // return TRUE; case 'process': $processed = FALSE; foreach (img_assist_get_macros($text) as $unexpanded_macro => $macro) { $expanded_macro = img_assist_render_image($macro); $text = str_replace($unexpanded_macro, $expanded_macro, $text); $processed = TRUE; } return $processed ? theme('img_assist_filter', $text) : $text; default: return $text; } } /** * Implementation of hook_filter_tips(). */ function img_assist_filter_tips($delta, $format, $long = FALSE) { return t('Images can be added to this post.'); } /** * Implementation of hook_nodeapi(). * * - Clear input filter cache. * - Keep track of where images are used. * - Catch nids of recently uploaded images. */ function img_assist_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { switch ($op) { case 'update': if ($node->type == 'image') { // Clear the input filter cache to force all node content to be rebuilt. // This is to make sure all image paths are up to date. cache_clear_all(NULL, 'cache_filter'); } // break is intentionally left out. case 'insert': // Update the image map. img_assist_map_save($node); break; case 'delete': img_assist_map_delete($node); break; } } /** * @defgroup img_assist_pages Image Assist Pages * @{ * All but img_assist_loader() are in frames. */ /** * Output main img_assist interface HTML. * * @todo Remove hard-coded TinyMCE integration. */ function img_assist_loader() { $path = drupal_get_path('module', 'img_assist'); $caller = arg(2) ? arg(2) : 'textarea'; drupal_add_js($path . '/img_assist_popup.js'); if (module_exists('wysiwyg') && ($editor = wysiwyg_get_editor($caller))) { if ($editor['name'] == 'tinymce') { drupal_add_js($editor['library path'] . '/tiny_mce_popup.js'); } } else { $caller = 'textarea'; } drupal_add_js($path . '/img_assist_' . $caller . '.js'); $output = ''."\n"; $output .= "\n"; $output .= "\n"; $output .= ''. t('Add image') ."\n"; $output .= drupal_get_js(); $output .= ''; $output .= "\n\n"; $output .= '' . "\n"; $output .= '' . "\n"; $output .= '' . "\n"; $output .= "\n"; $output .= "\n"; echo $output; exit; } /** * Fetch the number of nodes for terms of given vocabulary ids. * * Note that this does not count nodes of subterms, as opposed to * taxonomy_term_count_nodes(). * * @param $vids * An array of taxonomy vocabulary ids. * * @return * An array keyed by term ids with the count of nodes for each term. */ function _img_assist_term_count_nodes($vids) { $count = array(); $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)); while ($term = db_fetch_object($result)) { $count[$term->tid] = $term->count; } return $count; } function img_assist_header($mode) { // Mode may be 'uploading', 'properties' or 'browse'. $output = drupal_get_form('img_assist_header_form', $mode); echo theme('img_assist_page', $output, array('id' => 'img_assist_header', 'onload' => 'parent.initHeader();', 'class' => 'img_assist')); exit; } function img_assist_header_form(&$form_state, $mode) { global $user; // Upload image. if ($mode == 'uploading') { $form[] = array('#value' => '
'); $form[] = array('#value' => ''. t('Upload: ') .''. t('Fill in the form below to upload a new image.')); $form[] = array('#value' => '
'); $form['startover'] = array( '#type' => 'button', '#value' => t('Start Over'), '#button_type' => 'button', '#attributes' => array('onclick' => 'parent.onClickStartOver();'), ); $form[] = array('#value' => '
'); } elseif ($mode == 'properties') { $form[] = array('#value' => '
'); $form[] = array('#value' => ''. t('Properties: ') .''. t('Change how the image is displayed.')); $form[] = array('#value' => '
'); $form['startover'] = array( '#type' => 'button', '#value' => t('Start Over'), '#button_type' => 'button', '#attributes' => array('onclick' => 'parent.onClickStartOver()'), ); $form[] = array('#value' => '
'); } // Browse images. else { $form[] = array('#value' => '
'); $form[] = array('#value' => ''. t('Browse Images: ') .''); $views = variable_get('img_assist_views', drupal_map_assoc(array('img_assist_browser'))); foreach ($views as $view_name => $view_title) { if ($view_name == 'img_assist_browser') { // @todo execute() performs a full query, we just need the count here. // Get my images and count. $myimages = views_get_view('img_assist_browser'); $myimages->set_arguments(array($user->uid)); $myimages->get_total_rows = TRUE; // Required for overridden default view. $myimages->execute(); $options['img_assist_browser/' . $user->uid] = t('My Images') ." ($myimages->total_rows)"; // Get all images and count. if (user_access('access all images')) { $allimages = views_get_view('img_assist_browser'); $allimages->set_arguments(array('all')); $allimages->get_total_rows = TRUE; // Required for overridden default view. $allimages->execute(); $options['img_assist_browser/all'] = t('All Images') ." ($allimages->total_rows)"; } // Get category list. if (module_exists('taxonomy') && $vocabs = variable_get('img_assist_vocabs', array())) { $term_counts = _img_assist_term_count_nodes($vocabs); // Get all images or only user's images depending on permissions. $user_arg = (user_access('access all images')) ? 'all' : $user->uid; foreach ($vocabs as $vid) { $vocab = taxonomy_vocabulary_load($vid); $terms = taxonomy_get_tree($vid); if ($terms) { foreach ($terms as $key => $value) { $tid = $value->tid; $name = $value->name; $count = (isset($term_counts[$tid]) ? $term_counts[$tid] : 0); $options[$vocab->name]["img_assist_browser/$user_arg/" . $tid] = "$name ($count)"; } } } } } else { $view = views_get_view($view_name); $view->execute(); $view_title = ($view->get_title() == '') ? $view_name : $view->get_title(); $options[$view_name] = "$view_title ($view->total_rows)"; } } $form['browse'] = array( '#type' => 'select', '#default_value' => 'img_assist_browser/' . $user->uid, '#options' => $options, '#attributes' => array('onchange' => 'parent.onChangeBrowseBy(this)'), ); if (user_access('create images')) { $form['upload'] = array( '#type' => 'button', '#prefix' => ' '. t('or') .' ', '#value' => t('Upload'), '#suffix' => ' '. t('a new image'), '#button_type' => 'button', '#attributes' => array('onclick' => 'parent.onClickUpload()'), ); } $form[] = array('#value' => '
'); $form['cancel'] = array( '#type' => 'button', '#value' => t('Cancel'), '#button_type' => 'button', '#attributes' => array('onclick' => 'parent.cancelAction()'), ); $form[] = array('#value' => '
'); } return $form; } /** * Interface for adding images. Uses the regular image node form. */ function img_assist_upload() { global $user; module_load_include('inc', 'node', 'node.pages'); if (module_exists('image') && user_access('create images')) { // On other img_assist_pages I've added the javascript using the body onload // attribute, but for this page will also need the collapse functions and // setting the body onload interferes with this. To solve this, I'm forced // use the $(document).ready call. I should probably switch all the pages to // this format to be more Drupal friendly, but at the same time I don't know // if it really matters. If a user doesn't have Javascript, she can't use // img_assist at all. $output = "\n"; // Define an empty node and fetch an image node form $node = array('uid' => $user->uid, 'name' => $user->name, 'type' => 'image'); $output .= drupal_get_form('image_node_form', $node); } else { if (!module_exists('image')) { $output = t('The image module must be enabled to use Image assist.'); } else { $output = t('Your account does not have image uploading privileges.'); } } echo theme('img_assist_page', $output, array('id' => 'img_assist_upload', 'class' => 'img_assist')); exit; } /** * Implementation of hook_form_alter(). * * Reroute submit button callback to our own handler to be able to redirect * the user after saving the node. * * @see img_assist_node_form_submit() */ function img_assist_form_alter(&$form, &$form_state, $form_id) { if ($form_id == 'image_node_form' && arg(0) == 'img_assist') { $form['buttons']['submit']['#submit'] = array('img_assist_node_form_submit'); } // If a view uses exposed filters in the image browser, we need to modify #action. elseif ($form_id == 'views_exposed_form' && arg(0) == 'img_assist') { $form['#action'] = url($_GET['q']); } } /** * Submit callback for image_node_form. */ function img_assist_node_form_submit($form, &$form_state) { // Execute regular node submit handler. node_form_submit($form, $form_state); if ($form_state['nid']) { // Send to different url. $form_state['redirect'] = 'img_assist/properties/'. $form_state['nid']; } } /** * Load the thumbnail display pane. * * Loads a View displaying the thumbnails. The view name is picked from the * third path argument and any remaining arguments are used as arguments to the * view. * Module developers may add options to img_assist_header_form using * hook_form_alter() if they wish to use custom views and/or arguments. */ function img_assist_thumbs() { global $user; if (module_exists('image') && module_exists('views')) { $view_name = ((arg(2) != '') ? arg(2) : 'img_assist_browser'); // Get view arguments from path. $args = explode('/', $_GET['q']); $args = array_slice($args, 3); // Check sanity and permissions for the 'img_assist_browser' view. if ($view_name == 'img_assist_browser') { if (empty($args[0]) || !user_access('access all images')) { $args[0] = $user->uid; } if (isset($args[1])) { $args[1] = (int) $args[1]; } } $view = views_get_view($view_name); if ($view) { $view_output = $view->execute_display(NULL, $args); if (empty($view_output)) { $output = t('No images were found. Please upload a new image or browse images by a different category.'); } else { $output = $view_output; } } else { $output = t('Error: The specified view was not found.'); } } else { $output = t('The Image and Views modules must be enabled to use Image assist.'); } echo theme('img_assist_page', $output, array('id' => 'img_assist_thumbs', 'onload' => 'parent.initThumbs();', 'class' => 'img_assist')); exit; } /** * Load the image properties pane. */ function img_assist_properties() { $nid = arg(2); // Update is put into a hidden field so the javascript can see it. $update = (arg(3)) ? 1 : 0; if (is_numeric($nid) && ($node = node_load($nid)) && $node->type == 'image' && node_access('view', $node)) { $output = drupal_get_form('img_assist_properties_form', $node, $update); } else { $output = t('Image ID not found'); } echo theme('img_assist_page', $output, array('id' => 'img_assist_properties', 'onload' => 'parent.initProperties();', 'class' => 'img_assist')); exit; } /** * Convert a node field value to text for usage in a textfield. */ function img_assist_sanitize($text) { return check_plain(trim(preg_replace("/[\r\n]+/", ' ', strip_tags($text)))); } /** * Construct the image properties form. */ function img_assist_properties_form($form_state, $node, $update) { require_once drupal_get_path('module', 'img_assist') .'/includes/img_assist.token.inc'; $image_info = image_get_info(file_create_path($node->images[IMAGE_ORIGINAL])); $image_info['aspect_ratio'] = $image_info['height'] / $image_info['width']; // Select (or generate) a preview image. $img_assist_create_derivatives = variable_get('img_assist_create_derivatives', array()); if (!empty($img_assist_create_derivatives['properties'])) { $properties_size['label'] = t('Properties'); $properties_size['key'] = 'img_assist_properties'; $properties_size['width'] = 200; $properties_size['height'] = 200; } else { $properties_size['key'] = IMAGE_THUMBNAIL; } $properties_image = img_assist_display($node, $properties_size); // Get actual image size. $properties_size = image_get_info(file_create_path($node->images[$properties_size['key']])); // Create an array of image derivative choices // // The name for each option is actually the size in pixels, not the derivative // name. This is necessary so that // - the Javascript that process this page and inserts code to your textarea // or editor will know the size to make the image placeholder (in a WYSIWYG // editor) // - the code that processes the img_assist filter tags can work with standard // sizes and custom sizes in the same way. // The WYSIWYG placeholder, however, is the most important reason to keep the // img_assist tags this way. This way users can even resize images in the // editor, and if they aren't allow to create custom sizes the filter will // pick the existing derivative that is closest to the size of the WYSIWYG // placeholder. For users not familiar with pixel sizes or names like // 'thumbnail' and 'preview', this is a nice visual way to size images. // The size selection dropdown could even be hidden using the stylesheet, // making the insertion of images even more of a visual process. And for // administrators and those with the proper permissions, images don't have to // snap to the nearest standard size. You can create any arbitrary size you // choose. $max_size = explode('x', variable_get('img_assist_max_size', '640x640')); foreach (image_get_sizes(NULL, $image_info['aspect_ratio']) as $key => $size) { // Sizes are strings, may contain '' for 0; convert to integers. settype($size['width'], 'int'); settype($size['height'], 'int'); $added_to_derivatives = FALSE; if ($key == IMAGE_ORIGINAL) { if (user_access('use original size') && $image_info['width'] <= $max_size[0] && $image_info['height'] <= $max_size[1]) { $derivatives[$image_info['width'] .'x'. $image_info['height']] = $size['label']; $added_to_derivatives = TRUE; } } elseif ($size['width'] <= $max_size[0] && $size['height'] <= $max_size[1]) { $derivatives[$size['width'] .'x'. $size['height']] = $size['label']; $added_to_derivatives = TRUE; } if (!$added_to_derivatives) { // The thumbnail option will be shown even if it is larger than the // maximum size. if ($key == IMAGE_THUMBNAIL) { $derivatives[$size['width'] .'x'. $size['height']] = $size['label']; } } } // Add a choice for 'other' if the user has the proper permission to create // custom sizes. if (!empty($img_assist_create_derivatives['custom_advanced']) && user_access('access advanced options')) { $derivatives['other'] = t('Other'); } // Use 'preview' size by default. $default_size = image_get_info(file_create_path($node->images[IMAGE_PREVIEW])); $default_width = $default_size['width']; $default_height = $default_size['height']; // Calculate the aspect ratio to keep in a hidden field // // When 'other' is chosen, the custom size will always follow the aspect ratio. // It doesn't really matter what this value is here because the actual custom // image will be created when the filter tag is processed. The size, of course, // is a bounding box. If it a user stretches an image placeholder out of // proportion in the WYSIWYG editor, the image will never be out of proportion // on the processed page. $aspect_ratio = ($default_height > 0 ? round($default_width / $default_height, 6) : 1); // Create the form. $form[] = array('#value' => '
'); $form[] = array('#value' => ''); $form[] = array('#value' => '
'); $form[] = array('#value' => $properties_image); $form[] = array('#value' => ''. check_plain($node->title) .''); // Image node properties fieldset. $form['properties'] = array('#type' => 'fieldset', '#title' => t('Image properties')); $form['properties'][] = array('#value' => '
'. t('Size') .':
'. strtr('@widthx@height px', array('@width' => $image_info['width'], '@height' => $image_info['height'])) .'
'); $rendered = node_build_content($node); foreach (array_filter(variable_get('img_assist_display_properties', array())) as $field) { if (isset($node->content[$field])) { $form['properties'][] = array('#value' => drupal_render($node->content[$field])); } } $form[] = array('#value' => '
'); $token_installed = module_exists('token'); if (variable_get('img_assist_load_title', 1)) { $title = img_assist_sanitize($token_installed ? token_replace(variable_get('img_assist_title_pattern', '[title]'), 'node', $node) : $node->title); } if (variable_get('img_assist_load_description', 1)) { $description = img_assist_sanitize($token_installed ? token_replace(variable_get('img_assist_description_pattern', '[body]'), 'node', $node) : $node->body); } $form['title'] = array( '#type' => 'textfield', '#title' => t('Title (optional)'), '#default_value' => isset($title) ? $title : '', '#size' => 50, '#maxlength' => 255, '#description' => NULL, '#attributes' => array('onblur' => 'parent.updateCaption()'), ); $form['desc'] = array( '#type' => 'textfield', '#title' => t('Description (optional)'), '#default_value' => isset($description) ? $description : '', '#size' => 50, '#maxlength' => 255, '#description' => NULL, '#attributes' => array('onblur' => 'parent.updateCaption()'), ); // Size. $form[] = array('#value' => '
'); $form[] = array('#value' => '
'); $form[] = array('#value' => ''); $form['size_label'] = array( '#type' => 'select', '#default_value' => variable_get('img_assist_default_label', '100x100'), '#options' => $derivatives, '#attributes' => array('onchange' => 'parent.onChangeSizeLabel()'), ); $form[] = array('#value' => '
'); $form['width'] = array( '#type' => 'textfield', '#default_value' => $default_width, '#size' => 4, '#maxlength' => 4, '#attributes' => array('onblur' => 'parent.onChangeWidth()'), ); $form[] = array('#value' => ' x '); $form['height'] = array( '#type' => 'textfield', '#default_value' => $default_height, '#size' => 4, '#maxlength' => 4, '#attributes' => array('onblur' => 'parent.onChangeHeight()'), ); $form[] = array('#value' => '
'); $form[] = array('#value' => '
'); // Alignment. $form['align'] = array( '#type' => 'select', '#title' => t('Alignment'), '#default_value' => variable_get('img_assist_default_alignment', 'left'), '#options' => array('left' => t('left'), 'right' => t('right'), 'none' => t('none'), 'center' => t('center')), '#prefix' => '
', '#suffix' => '
', ); $form[] = array('#value' => '
'); // Link. if (user_access('access advanced options')) { $form[] = array('#value' => ''); } else { $form['link'] = array( '#type' => 'hidden', '#value' => variable_get('img_assist_default_link_behavior', 'none'), ); $form['url'] = array( '#type' => 'hidden', '#value' => variable_get('img_assist_default_link_url', 'http://'), ); $form['link_options_visible'] = array( '#type' => 'hidden', '#value' => 0, ); } // Default link url is needed for JS to indicate if an url has been entered. $form['default_url'] = array( '#type' => 'hidden', '#value' => variable_get('img_assist_default_link_url', 'http://'), ); // Insert Mode (HTML or Filter Tag). if (user_access('access advanced options')) { $form[] = array('#value' => '
'); $form['insertmode'] = array( '#type' => 'select', '#title' => t('Insert mode'), '#default_value' => variable_get('img_assist_default_insert_mode', 'filtertag'), '#options' => array('filtertag' => t('Filter Tag'), 'html' => t('HTML Code')), ); $form[] = array('#value' => '
'); } else { $form['insertmode'] = array( '#type' => 'hidden', '#value' => variable_get('img_assist_default_insert_mode', 'filtertag'), ); } // Hidden Fields. $form['nid'] = array( '#type' => 'hidden', '#value' => $node->nid, ); $form['update'] = array( '#type' => 'hidden', '#value' => $update, ); $form['aspect'] = array( '#type' => 'hidden', '#value' => $aspect_ratio, ); // Buttons. $form['buttons'] = array( '#prefix' => '
', '#suffix' => '
', ); $form['#attributes']['onsubmit'] = 'return parent.insertImage();'; $form['buttons']['insert'] = array( '#type' => 'submit', '#value' => ($update) ? t('Update') : t('Insert'), '#attributes' => array('style' => 'float: left;'), ); $form['buttons']['cancel'] = array( '#type' => 'button', '#value' => t('Cancel'), '#button_type' => 'button', '#attributes' => array('onclick' => 'return parent.cancelAction();', 'style' => 'float: right;'), ); $form[] = array('#value' => '
'); $form['#attributes']['name'] = 'img_assist'; return $form; } function img_assist_properties_form_validate($form, &$form_state) { $html = img_assist_render_image($form_state['values']); img_assist_set_htmlcode($html); drupal_goto('img_assist/insert_html'); } /** * Store image tag or HTML in session. * * Used for saving HTML code so it can be inserted instead of the filter tags. * * @param string $htmlcode * A filter tag or HTML code. If omitted, session variable is emptied. * * @return string * A previously stored value in the user session. */ function img_assist_set_htmlcode($htmlcode = NULL) { if (isset($htmlcode)) { $_SESSION['htmlcode'] = urlencode($htmlcode); } else { $html = urldecode($_SESSION['htmlcode']); $_SESSION['htmlcode'] = ''; return $html; } } function img_assist_insert_html() { $output = drupal_get_form('img_assist_insert_html_form'); echo theme('img_assist_page', $output, array('id' => 'img_assist_insert_html', 'onload' => 'parent.insertImage();', 'class' => 'img_assist')); } function img_assist_insert_html_form() { $htmlcode = img_assist_set_htmlcode(); $form[] = array( '#id' => 'finalhtmlcode', '#type' => 'hidden', '#value' => $htmlcode, ); $form['insertmode'] = array( '#type' => 'hidden', '#value' => 'html2', ); return $form; } /** * @} End of "defgroup img_assist_pages". */ /** * @defgroup img_assist_image Image Assist Image Generation * @{ * Functions used in image.module vs. img_assist.module (simplified): * * image.module: * - image_display() * - is called for galleries, image nodes, image blocks, etc * (everytime in image is shown) * - returns * - can be passed a specific standard size only * - may call _image_build_derivatives() * - calls theme_image() to create the tag * _image_build_derivatives() * - rebuilds all standard image sizes for a particular node * * img_assist.module: (more complicated, but more flexible) * - image_display() * - is called for thumbnails browsing, inline images, etc (everytime in image is shown) * - returns * - can be passed EITHER a specific standard size only OR a custom size * - may call _image_build_derivatives() * - calls theme_image() to create the tag * _image_build_derivatives() * - rebuilds only a specfic image size (standard or custom size) */ /** * Create an IMG tag for an image. * * This is nearly identical to image_display, but * - it uses a more efficient regenerate images routine * - the size attribute can be a custom size OR a standard size */ function img_assist_display(&$node, $size = NULL, $attributes = array()) { // Custom size should include values for label, width, and height. if (is_array($size) && !empty($size['key']) && !empty($size['width']) && !empty($size['height'])) { $label = $size['key']; } // Standard size. elseif ($size) { // Size can be an array without the width and/or height. if (is_array($size)) { // Size is no longer an array. $size = $size['key']; } $label = $size; } // Assign preview size if no size specified. else { $label = IMAGE_THUMBNAIL; } // Regenerate images if necessary. $regen = FALSE; if (!isset($node->images[$label])) { $regen = TRUE; } elseif (!is_file(file_create_path($node->images[$label]))) { $regen = TRUE; } elseif (filemtime(file_create_path($node->images[$label])) < variable_get('image_updated', 0)) { $regen = TRUE; } else { // If $size is not an array, try to find the corresponding predefined size. // _image_build_derivatives() blindly assigns the *original* image file to // all derivative image sizes that are smaller than the original image size. // Without re-assigning the actual derivative size definition, img_assist // would assume that this derivative size does not exist, delete the // *original* file and subsequently fail to generate derivative images. // Also, when one predefined size has changed, the derivative sizes need to // be updated. if (!is_array($size)) { foreach (image_get_sizes() as $std_size) { if (isset($std_size['key']) && $std_size['key'] == $label) { $size = $std_size; break; } } } if (is_array($size)) { $info = image_get_info(file_create_path($node->images[$label])); if (($info['width'] != $size['width']) && ($info['height'] != $size['height'])) { $regen = TRUE; } } } if ($regen) { _img_assist_build_derivatives($node, $size); } // For custom size images, add a consistent CSS class, JavaScripts can rely on. $attributes = array(); if (is_array($size) && isset($size['key']) && strpos($size['key'], 'img_assist_custom') === 0) { $attributes['class'] = 'image-img_assist_custom'; } return image_display($node, $label, $attributes); } /** * Generate a image derivative * * @see _image_build_derivatives() in image.module * * @param $node * @param $size * An array containing the keys 'label', 'width', 'height'. */ function _img_assist_build_derivatives(&$node, $size = NULL) { // Verify the image module and toolkit settings. if (!_image_check_settings()) { return FALSE; } $info = image_get_info(file_create_path($node->images[IMAGE_ORIGINAL])); // Custom size. if (is_array($size) && !empty($size['key']) && !empty($size['width']) && !empty($size['height'])) { $sizes = array($size['key'] => $size); } // Standard size. elseif ($size) { // Size can be an array without the width and/or height. if (is_array($size)) { $size = $size['key']; } $sizes = image_get_sizes(); $sizes = array($size => $sizes[$size]); } // No size given: rebuild derivatives for all standard sizes. else { $sizes = image_get_sizes(); } foreach ($sizes as $key => $size) { $size['key'] = $key; _img_assist_remove($node, $size); if (is_array($size) && ($size['label']) && ($size['width']) && ($size['height'])) { if ($info['width'] > $size['width'] || $info['height'] > $size['height']) { $source = file_create_path($node->images[IMAGE_ORIGINAL]); $destination = _image_filename(basename($source), $key, FALSE); $destination_path = file_create_path($destination); if (!image_scale($source, $destination_path, $size['width'], $size['height'])) { drupal_set_message(t('Unable to create %label image', array('%label' => $size['label'])), 'error'); } else { // Set default file permissions for webserver-generated files. @chmod($destination_path, 0664); $node->images[$key] = $destination; _image_insert($node, $key, $destination_path); } } else { $node->images[$key] = $node->images[IMAGE_ORIGINAL]; } } } } function _img_assist_remove($node, $size) { $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']); while ($file = db_fetch_object($result)) { // Never delete original image. if ($file->filepath != $node->images[IMAGE_ORIGINAL]) { // Delete image file. file_delete(file_create_path($file->filepath)); // Delete file reference in database. db_query("DELETE FROM {files} WHERE fid = %d AND filename = '%s'", $file->fid, $size['key']); db_query("DELETE FROM {image} WHERE nid = %d AND fid = '%d'", $node->nid, $file->fid); } } } /** * Return image HTML. */ function img_assist_render_image($attributes = array()) { global $user; if ($attributes['nid']) { $node = node_load($attributes['nid']); // Get image size. $width = (int) $attributes['width']; $height = (int) $attributes['height']; $default_sizes = image_get_sizes(); if ($width || $height) { // Check to ensure that the dimensions don't exceed the max set in the // img_assist settings. $max_size = explode('x', variable_get('img_assist_max_size', '640x640')); $width = ($width <= $max_size[0]) ? $width : $max_size[0]; $height = ($height <= $max_size[1]) ? $height : $max_size[1]; // Get width and height of original size. $original_size = image_get_info(file_create_path($node->images[IMAGE_ORIGINAL])); if ($width == $original_size['width'] && $height == $original_size['height']) { // Nothing to process, this is the original image size. $closest_std_size = IMAGE_ORIGINAL; $create_custom = FALSE; } else { // Get width and height of preview size. $preview_size = image_get_info(file_create_path($node->images[IMAGE_PREVIEW])); $preview_width = $preview_size['width']; $preview_height = $preview_size['height']; if ($preview_width && $preview_height) { // Get aspect ratio from preview image dimensions. $aspect_ratio = round($preview_width / $preview_height, 6); // Get new width and height for this inline image. // If height is either left out or larger than width then // Width is controlling factor. if (!$height || ($width && round($width / $aspect_ratio) <= $height)) { $height = round($width / $aspect_ratio); } // Else, width is either left out or larger than height so // Height is controlling factor. else { $width = round($height * $aspect_ratio); } // Find out whether the given width/height is the same as (or extremely // close to) a default image derivative size; if so, we will use the // default size instead of generating a custom image. $diag_size_new = sqrt(pow($width, 2) + pow($height, 2)); $closest_difference = 9999; foreach ($default_sizes as $key => $stdsize) { $width_std = $stdsize['width']; $height_std = $stdsize['height']; // Diagonal size calculations require a width or height. if (!$height_std && !$width_std) { // For the original image, we can fall back to actual dimensions // though. In fact, IMAGE_ORIGINAL can either have maximum // dimensions (so aspect ratio based calculations below will apply) // or the real dimensions (which must be considered as valid // "derivative size"). // Note that this annuls the 'use original size' user permission. if ($key !== IMAGE_ORIGINAL) { continue; } else { $width_std = $original_size['width']; $height_std = $original_size['height']; } } // Calculate default width and height based on aspect ratio. // Width is controlling factor. if (!$height_std || ($width_std && round($width_std / $aspect_ratio) <= $height_std)) { $height_std = round($width_std / $aspect_ratio); } // Height is controlling factor. else { $width_std = round($height_std * $aspect_ratio); } // Calculate diagonal size of this default size. $diag_size_std = sqrt(pow($width_std, 2) + pow($height_std, 2)); $difference = abs($diag_size_new - $diag_size_std); if ($difference < $closest_difference) { $closest_std_size = $key; $closest_difference = $difference; } } // If, for any reason, no default image derivative size has a width or // height, fall back to IMAGE_THUMBNAIL. if (!isset($closest_std_size)) { $closest_std_size = IMAGE_THUMBNAIL; } if ($closest_difference < 3) { $create_custom = FALSE; } else { $img_assist_create_derivatives = variable_get('img_assist_create_derivatives', array()); // If all users are allowed to create custom sized images. if ($img_assist_create_derivatives['custom_all']) { $create_custom = TRUE; } elseif ($img_assist_create_derivatives['custom_advanced']) { // Note: The following line is NOT the right way to do this. // The user acount passed to user_access() should be the user who // CREATED this node, not the CURRENT user. I'm not sure how to // get the user who created the node, because this function // doesn't have access to the node object. I could probably figure // out some hack, but I think I'm going to completely rethink the // 'img_assist_create_derivatives' option. When I started it this // method made sense, but the more I've worked on this, the more // confusing it gets. if (user_access('access advanced options', $user)) { $create_custom = TRUE; } else { $create_custom = TRUE; } } else { $create_custom = FALSE; } } } } if ($create_custom) { $size['label'] = t('Custom'); // Add width and height to make it possible to have multiple custom // sizes of the same image. $size['key'] = 'img_assist_custom-' . $width . 'x' . $height; $size['width'] = $width; $size['height'] = $height; } else { $size['key'] = $closest_std_size; } } // Default to thumbnail if width and height is missing. else { $size = $default_sizes[IMAGE_THUMBNAIL]; $size['key'] = IMAGE_THUMBNAIL; } return theme('img_assist_inline', $node, $size, $attributes); } // Legacy img_assist filter tag. elseif ($attributes['fid']) { $img = img_assist_load_image($attributes['fid'], FALSE); $image = array_shift($img); $width = $attributes['width'] ? $attributes['width'] : $image->width; $height = $attributes['height'] ? $attributes['height'] : $image->height; $src = file_create_url($image->filepath); $class = $attributes['class'] ? $attributes['class'] : 'image'; $img_template = theme('img_assist_legacy'); $img_template = strtr($img_template, array('%caption' => $attributes['caption'], '%node-link' => url('node/'. $image->nid), '%nid' => $image->nid, '%img-link' => $image->filepath, '%alt' => check_plain($attributes['alt']), '%width' => $width, '%height' => $height, '%src' => $src, '%image-class' => $class)); return $img_template; } } function img_assist_popup() { $nid = arg(2); if (is_numeric($nid) && ($node = node_load($nid)) && $node->type == 'image' && node_access('view', $node)) { drupal_set_title(check_plain($node->title)); $size = variable_get('img_assist_popup_label', IMAGE_PREVIEW); $size = array('key' => $size); $content = img_assist_display($node, $size); $attributes = array('id' => 'img_assist_popup'); echo theme('img_assist_popup', $content, $attributes); exit; } else { return drupal_access_denied(); } } /** * @} End of "defgroup img_assist_image". */ /** * @defgroup img_assist_reference Image Assist Image Referencing Routines * @{ */ /** * Update the map table * * Look for any images linked in this content and keep a reference of them. */ function img_assist_map_save($node) { $content = $node->body; // If CCK is used, image macros can be found in fields other than the body. // Get all the fields that use text filtering and extract their content: if (function_exists('content_types')) { $type = content_types($node->type); if (!empty($type['fields'])) { foreach ($type['fields'] as $field) { // Distinguish between plain text fields and filtered fields: if (!empty($field['text_processing'])) { if (count($node->{$field['field_name']})) { foreach ($node->{$field['field_name']} as $field_instance) { $content .= $field_instance['value']; } } } } } } // Get all the macros from the content: $macros = (array)img_assist_get_macros($content); // Save the image references: db_query('DELETE FROM {img_assist_map} WHERE nid = %d', $node->nid); $nids = array(); foreach ($macros as $m) { if (!isset($nids[$m['nid']]) && is_numeric($m['nid'])) { db_query('INSERT INTO {img_assist_map} (nid, iid) VALUES(%d, %d)', $node->nid, $m['nid']); $nids[$m['nid']] = $m['nid']; } } } /** * Delete references to a non-existant node. * * If a node is being deleted update the map table. The node can either be an * image node or a node containing one ore more images. Note: nodes that link * to image nodes that are deleted will still be broken. */ function img_assist_map_delete($node) { db_query('DELETE FROM {img_assist_map} WHERE nid = %d OR iid = %d', $node->nid, $node->nid); } /** * Load the image map for a given nid. */ function img_assist_map_load($nid) { $imagemap = array(); $result = db_query('SELECT * FROM {image} i INNER JOIN {img_assist_map} iam ON i.nid = iam.iid WHERE i.nid = %d', $nid); while ($data = db_fetch_object($result)) { $imagemap[] = $data->nid; } return $imagemap; } /** * Return a list of node links for a given nid. */ function img_assist_get_references($nid, $limit = 10) { $and_clause = array(); $images = img_assist_map_load($nid); foreach ($images as $id) { $and_clause[] = 'n.nid = '. $id; } $and_clause = implode(' OR ', $and_clause); if ($images) { return node_title_list(db_query_range(db_rewrite_sql("SELECT n.nid, n.title FROM {node} n WHERE n.status = 1 AND ($and_clause) ORDER BY n.nid DESC"), 0, (int) $limit)); } } /** * @} End of "defgroup img_assist_reference". */ /** * @defgroup img_assist_macro Image Assist Filter macro parsing * @{ */ /** * Return all img_assist macros as an array. */ function img_assist_get_macros($text) { $m = array(); preg_match_all('/ \[ ( [^\[\]]+ )* \] /x', $text, $matches); // Don't process duplicates. $tag_match = (array) array_unique($matches[1]); foreach ($tag_match as $macro) { $current_macro = '['. $macro .']'; $param = array_map('trim', explode('|', $macro)); // The first macro param is assumed to be the function name. $func_name = array_shift($param); if ($func_name == 'img_assist') { $vars = array(); foreach ($param as $p) { $pos = strpos($p, '='); $varname = trim(substr($p, 0, $pos)); $varvalue = substr($p, $pos + 1); $vars[$varname] = trim($varvalue); } // The full unaltered filter string is the key for the array of filter // attributes. $m[$current_macro] = $vars; } } return $m; } /** * Determine if img_assist can render the current page. * @see block_list(). * * @return * TRUE if can render, FALSE if not allowed. */ function _img_assist_page_match() { $must_match = variable_get('img_assist_paths_type', 2); if ($must_match == 2) { return TRUE; } else { $paths = variable_get('img_assist_paths', "node/*\ncomment/*"); $path = drupal_get_path_alias($_GET['q']); $regexp = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\($|\|)/'), array('|', '.*', '\1'. variable_get('site_frontpage', 'node') .'\2'), preg_quote($paths, '/')) .')$/'; $match = preg_match($regexp, $path); return $match != $must_match; } } /** * Determine if img_assist can render the current textarea. * @see block_list(). * * @return * TRUE if can render, FALSE if not allowed. */ function _img_assist_textarea_match($formid) { $must_match = variable_get('img_assist_textareas_type', 2); if ($must_match == 2) { return TRUE; } else { $formids = variable_get('img_assist_textareas', "edit-body\nedit-comment"); $regexp = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/'), array('|', '.*'), preg_quote($formids, '/')) .')$/'; // Compare with the form id. $page_match = preg_match($regexp, $formid); // When $must_match has a value of 0, img_assist is displayed on // all pages except those listed in img_assist_textareas. When set to 1, it // is displayed only on those textareas listed in img_assist_textareas. $page_match = !($must_match xor $page_match); return $page_match; } } /** * @} End of "defgroup img_assist_macro". */ /** * @defgroup img_assist_theme Image Assist Theme functions * @{ * * @ingroup themeable */ function theme_img_assist_inline($node, $size, $attributes) { $caption = ''; if ($attributes['title'] && $attributes['desc']) { $caption = ''. $attributes['title'] .': '. $attributes['desc']; } elseif ($attributes['title']) { $caption = ''. $attributes['title'] .''; } elseif ($attributes['desc']) { $caption = $attributes['desc']; } // Change the node title because img_assist_display() uses the node title for // alt and title. $node->title = strip_tags($caption); $img_tag = img_assist_display($node, $size); // Always define an alignment class, even if it is 'none'. $output = ''; $link = $attributes['link']; $url = ''; // Backwards compatibility: Also parse link/url in the format link=url,foo. if (strpos($link, ',') !== FALSE) { list($link, $url) = explode(',', $link, 2); } elseif (isset($attributes['url'])) { $url = $attributes['url']; } if ($link == 'node') { $output .= l($img_tag, 'node/'. $node->nid, array('html' => TRUE)); } elseif ($link == 'popup') { $popup_size = variable_get('img_assist_popup_label', IMAGE_PREVIEW); $info = image_get_info(file_create_path($node->images[$popup_size])); $width = $info['width']; $height = $info['height']; $popup_url = file_create_url($node->images[variable_get('img_assist_popup_label', IMAGE_PREVIEW)]); $output .= l($img_tag, $popup_url, array('attributes' => array('onclick' => "launch_popup({$node->nid}, {$width}, {$height}); return false;", 'target' => '_blank'), 'html' =>TRUE)); } elseif ($link == 'url') { $output .= l($img_tag, $url, array('html' => TRUE)); } else { $output .= $img_tag; } if ($caption) { if ($attributes['align'] != 'center') { $info = image_get_info(file_create_path($node->images[$size['key']])); // Reduce the caption width slightly so the variable width of the text // doesn't ever exceed image width. $width = $info['width'] - 2; $output .= ''. $caption .''; } else { $output .= ''. $caption .''; } } $output .= ''; return $output; } function theme_img_assist_filter($text) { // The div tag added to the end of each node is necessary to clear the // floating properties of inline images immediately after a node's content. return $text .'
'; } function theme_img_assist_popup($content, $attributes = NULL) { $title = drupal_get_title(); $output = '' . "\n"; $output .= "\n"; $output .= "\n"; $output .= ''. $title ."\n"; $output .= drupal_get_html_head(); $output .= drupal_get_css(); $output .= "\n"; $output .= '\n"; $output .= "\n"; $output .= l($content, '', array('attributes' => array('onclick' => 'javascript:window.close();'), 'html' => TRUE)); $output .= "\n"; $output .= ''; $output .= ''; return $output; } function theme_img_assist_page($content, $attributes = NULL) { $title = drupal_get_title(); $output = ''."\n"; $output .= ''."\n"; $output .= "\n"; $output .= ''. $title ."\n"; // Note on CSS files from Benjamin Shell: // Stylesheets are a problem with image assist. Image assist works great as a // TinyMCE plugin, so I want it to LOOK like a TinyMCE plugin. However, it's // not always a TinyMCE plugin, so then it should like a themed Drupal page. // Advanced users will be able to customize everything, even TinyMCE, so I'm // more concerned about everyone else. TinyMCE looks great out-of-the-box so I // want image assist to look great as well. My solution to this problem is as // follows: // If this image assist window was loaded from TinyMCE, then include the // TinyMCE popups_css file (configurable with the initialization string on the // page that loaded TinyMCE). Otherwise, load drupal.css and the theme's // styles. This still leaves out sites that allow users to use the TinyMCE // plugin AND the Add Image link (visibility of this link is now a setting). // However, on my site I turned off the text link since I use TinyMCE. I think // it would confuse users to have an Add Images link AND a button on the // TinyMCE toolbar. // // Note that in both cases the img_assist.css file is loaded last. This // provides a way to make style changes to img_assist independently of how it // was loaded. $output .= drupal_get_html_head(); $output .= drupal_get_js(); $output .= "\n\n"; // Ensure that img_assist.js is imported last. $path = drupal_get_path('module', 'img_assist') .'/img_assist_popup.css'; $output .= "\n"; $output .= "\n"; $output .= '\n"; // Do not display status/error messages in popup header frame. if (!isset($attributes['id']) || $attributes['id'] != 'img_assist_header') { $output .= theme('status_messages'); } $output .= "\n"; $output .= $content; $output .= "\n"; $output .= ''; $output .= ''; return $output; } /** * @} End of "defgroup img_assist_theme". */ /** * Implementation of hook_wysiwyg_include_directory(). */ function img_assist_wysiwyg_include_directory($type) { switch ($type) { case 'plugins': return $type; } } /** * Implementation of hook_views_api(). */ function img_assist_views_api() { return array( 'api' => 2, 'path' => drupal_get_path('module', 'img_assist') .'/includes', ); } /** * @defgroup img_assist_legacy Image Assist Legacy functions * @{ * Used for backwards compatibility with original img_assist module. */ /** * Load an image from the database. */ function img_assist_load_image($id, $derivatives = TRUE) { $node = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title, i.*, f.* FROM {image} i, {node} n INNER JOIN {files} f ON f.fid = i.fid WHERE f.nid = n.nid AND f.fid = %d'), $id)); $node->filepath = file_create_path($node->filepath); if (!$derivatives) { $dim = getimagesize($node->filepath, $info); $node->width = $dim[0]; $node->height = $dim[1]; $image[$node->filename] = $node; } else { $image_module_image = FALSE; if (function_exists('image_get_sizes')) { foreach (image_get_sizes() as $size) { if ($size['label'] == $node->filename) { $image_module_image = TRUE; $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, r.teaser, i.*, f.* FROM {image} i, {node} n INNER JOIN {node_revisions} r ON r.nid = n.nid AND r.vid = n.nid INNER JOIN {files} f ON f.fid = i.fid WHERE i.nid = n.nid AND n.nid = %d'), $node->nid); while ($node = db_fetch_object($result)) { $node->filepath = file_create_path($node->filepath); $dim = getimagesize($node->filepath, $info); $node->width = $dim[0]; $node->height = $dim[1]; $image[$node->filename] = $node; } break; } } } if (!$image_module_image) { $dim = getimagesize($node->filepath, $info); $node->width = $dim[0]; $node->height = $dim[1]; $image[IMAGE_THUMBNAIL] = $node; $image[IMAGE_ORIGINAL] = $node; } } return $image; } /** * Attach the thumbnail metadata to the image object. * * Unfortunately we have to query the database since the thumbnail can be named * something entirely different from the original image. */ function _img_assist_get_thumbnail(&$image) { static $thumbs = array(); if ($thumbs[$image->nid] === NULL) { $thumbpath = file_create_path(db_result(db_query("SELECT f.filepath FROM {image} i INNER JOIN {files} f ON f.fid = i.fid WHERE i.nid = %d AND f.filename = '%s'", $image->nid, IMAGE_THUMBNAIL))); // In old versions of image.module thumbs were named 'thumb_filename.ext'. if (!file_exists($thumbpath)) { $pos = strrpos($image->filepath, '/') + 1; $thumbpath = file_create_path(substr($image->filepath, 0, $pos) .'thumb_'. substr($image->filepath, $pos)); } $img->thumbpath = is_file($thumbpath) && preg_match('|^'. variable_get('file_directory_path', 'files') .'\/'. variable_get('image_default_path', 'images') .'\/|', $image->filepath) ? $thumbpath : $image->filepath; $dim = getimagesize($img->thumbpath, $info); $img->thumbwidth = $dim[0]; $img->thumbheight = $dim[1]; $thumbs[$image->nid] = $img; } if ($thumbs[$image->nid]) { foreach ($thumbs[$image->nid] as $key => $value) { $image->$key = $value; } } } function theme_img_assist_legacy() { return '
%alt
%caption
'; } /** * @} End of "defgroup img_assist_legacy". */