'); /** * Initializes the module. Called only when needed * * @return * Nothing */ function _img_boot() { static $loaded; if(!$loaded) { $loaded = TRUE; drupal_add_css(drupal_get_path('module', 'img') .'/img.css', 'module'); global $img_thumbnails_directory; $img_thumbnails = rtrim(variable_get('img_thumbnails_directory', 'img_thumbnails'), '\\/'); $img_thumbnails_directory = file_create_path($img_thumbnails); if(!file_check_directory($img_thumbnails_directory, TRUE)) { drupal_set_message(t('The %dir is not writeable' , array('%dir' => $img_thumbnails_directory)), 'error'); } } } /** * Compose a file object from the id [img:id] * * @param &$node * The node to which the files belong * @param $id * The 'id' from the '[img:id]' * * @return * The file object on success, else NULL */ function img_fileobj(&$node, $id) { if(is_numeric($id)) { $n = 0; foreach($node->files as $file) { if(++$n == $id) { return (object)$file; } } } else { foreach($node->files as $file) { if($file->filename == $id) { return (object)$file; } } } return NULL; } /** * Implementation of hook_theme(). */ function img_theme() { return array( 'img_box' => array( 'arguments' => array('title', 'image', 'additional_box_class', 'width', 'title_position'), ), ); } /** * Theme the image box * * @param $title * The title to display * @param $image * The image to be themed enclosed in a (optionaly) and img html tags * @param $additional_box_class * 'img_left' or 'img_right' * @param $width * The width of the image * @param $title_position * The title class. Also control the placement * Possible values: 'top', 'bottom' or anything else for no title * * @return * A themed box containing the title and image */ function theme_img_box($title, $image, $additional_box_class, $width, $title_position) { $image = '
' . $image . '
'; // hack for empty titles if(!isset($title) || trim($title) == '') { $title_position = 'none'; } switch($title_position) { case 'top': $title = '
' . $title . '
'; $content = $title . $image; break; case 'bottom': $title = '
' . $title . '
'; $content = $image . $title; break; default: $content = $image; break; } return '
' . $content . '
'; } /** * Helper funtion. Parse the captured part of a [img:(.*)] tag. * * @param $node_type * The type of the node to which the img tag belongs * @param $img_tag * The captured part * @return * The list of (index (number or filename), 'img_css', 'img_titles'). If the tag don't contain these the defaults are used. */ function img_parse_img_tag($node_type, $img_tag) { $attributes = preg_split('/\s/', $img_tag); // set the defaults $index = array_shift($attributes); $img_css = variable_get('img_css_' . $node_type, variable_get('img_css', 'float_left')); $img_titles = variable_get('img_titles_' . $node_type, variable_get('img_titles', 'bottom')); foreach($attributes as $attribute) { list($item, $value) = split('=', $attribute); if(isset($item) && isset($value)) { switch($item) { case 'align': $img_css = $value; break; case 'title': $img_titles = $value; break; default: break; } } } return array($index, $img_css, $img_titles); } /** * Generate an url for the thumbnail. * Thumbnails will look like number1_number1.jpeg where number1 is the node nid and number2 is the file id. * * @param $nid * The node nid * @param &$file * The file object * * @return * The path to the thumbnail */ function img_thumbnail_path($nid, &$file) { global $img_thumbnails_directory; $ext = substr($file->filemime, strrpos($file->filemime, '/') + 1); $path = $img_thumbnails_directory . '/' . $nid . '_' . $file->fid . '.' . $ext; return file_create_path($path); } /** * Get the thumbnail size for a node type * * @param $type * The node type ('story', 'page', 'blog', ...) * * @return * The thumbnail size setting */ function img_get_thumbnail_size($type) { return variable_get('img_dim_' . $type, variable_get('img_dim', '500')); } /** * Get the 'img_teaser_threshold' variable ('The maximum number of thumbnails displayed by teasers') * * @param $type * The node type ('story', 'page', 'blog', ...) * * @return * The thumbnail size setting */ function img_get_teaser_threshold($type) { return variable_get('img_teaser_threshold_' . $type, variable_get('img_teaser_threshold', 10)); } /** * If it is necessary create a thumbnail for a file object. * * @param &$node * The node to which the file belongs * @param &$file * The file object * * @return * The existing or the newly generated thumblail's path or the original filepath on error */ function img_create_thumbnail(&$node, &$file) { $thumbnail_size = img_get_thumbnail_size($node->type); // does the node exists? if(!is_numeric($node->nid)) { return file_create_url($file->filepath); } $img_thumbnail_path = img_thumbnail_path($node->nid, $file); // does the thumbnail exist? if(is_file($img_thumbnail_path)) { return file_create_url($img_thumbnail_path); } // create the thumbnail if(image_scale($file->filepath , $img_thumbnail_path , $thumbnail_size , $thumbnail_size)) { // maybe the thumbnails are manually deleted $row = db_fetch_object(db_query(' SELECT * FROM {img_thumbnails} WHERE nid = %d AND fid = %d', $node->nid, $file->fid)); if(!$row) { db_query(' INSERT INTO {img_thumbnails} (nid, fid, filemime) VALUES (%d, %d, \'%s\')', $node->nid, $file->fid, $file->filemime); } return file_create_url($img_thumbnail_path); } else { // should never happen return file_create_url($file->filepath); } } /** * Create all thumbnails for the given node * * @param &$node * The node to which the thumbnails belong * * @return * Nothing */ function img_create_thumbnails(&$node) { $width = variable_get('img_dim', '500'); if(is_array($node->files)) { foreach($node->files as $file) { $file = (object)$file; img_create_thumbnail($node, $file); } } } /** * Delete the thumbnails for the given node * * @param &$node * The node object * * @return * Nothing */ function img_delete_thumbnails(&$node) { $res = db_query(' SELECT fid, filemime FROM {img_thumbnails} WHERE nid = %d ', $node->nid); while($row = db_fetch_object($res)) { file_delete(img_thumbnail_path($node->nid, $row)); } db_query(' DELETE FROM {img_thumbnails} where nid = %d', $node->nid); } /** * Pseudo-filter to substitute [img:xx] tags with the xx-th image * * @param &$node * The node containing the [img:xx] tags * @param &$content * The node's content to be modified * * @return * Nothing, but modified &$content */ function img_substitute_tags(&$node, &$content) { if(preg_match_all("/\[(img|inline|file|attachment):(.*)\]/Ui", $content, $match)) { // add 'lightbox' like attributes first $attributes = array(); $thumbnail_size = img_get_thumbnail_size($node->type); $img_clear = ''; $link_class = variable_get('img_link_class', 'none'); switch($link_class) { case 'thickbox': $attributes['attributes']['class'] = $link_class; $attributes['attributes']['rel'] = 'img_' . ($node->nid? $node->nid: time()); // 'insert' has no $node->nid break; case 'lightbox': $attributes['attributes']['rel'] = 'lightbox[' . ($node->nid? $node->nid: time()) . ']'; // 'insert' has no $node->nid break; default: break; } foreach($match[2] as $key => $value) { $additional_box_class = ''; list($index, $img_css, $img_titles) = img_parse_img_tag($node->type, $value); // the defaults could be 'top_...', 'bottom_...' too switch($img_css) { case 'left': case 'float_left': case 'bottom_left': $additional_box_class = 'img_left'; $img_clear = IMG_CLEAR; break; case 'right': case 'float_right': case 'bottom_right': $additional_box_class = 'img_right'; $img_clear = IMG_CLEAR; break; default: break; } $file = img_fileobj($node, $index); if($file->fid) { $sizes = image_get_info($file->filepath); if($sizes) { $img_attributes = array('class' => 'img'); if($thumbnail_size < $sizes['width'] || $thumbnail_size < $sizes['height']) { if($sizes['width'] > $sizes['height']) { $img_attributes['width'] = $thumbnail_size . 'px'; } else { $img_attributes['height'] = $thumbnail_size . 'px'; } $img = theme("image", img_create_thumbnail($node, $file) , $file->description , $file->description , $img_attributes , FALSE); $attributes['attributes']['title'] = $file->description; $attributes['html'] = TRUE; if($thumbnail_sizes = image_get_info(img_thumbnail_path($node->nid, $file))) { $thumbnail_width = $thumbnail_sizes['width']; } $image = l($img, file_create_url($file->filepath), $attributes); } else { // need no thumbnail $img_attributes['width'] = $sizes['width'] . 'px'; $thumbnail_width = $sizes['width']; $image = theme("image", file_create_url($file->filepath) , $file->description , $file->description , $img_attributes , FALSE); } // if the image is centered we need an additional div $div_start = $div_end = ''; if($img_css == 'center' || $img_css == 'bottom_center') { $div_start = '
'; $div_end = '
'; } else if($img_css != 'float_left' && $img_css != 'float_right') { $div_start = '
'; $div_end = IMG_CLEAR . '
'; } $replace[] = $div_start . theme("img_box", $file->description , $image , $additional_box_class , $thumbnail_width , $img_titles) . $div_end; } else { // not an image, display a download link $replace[] = l($file->description, file_create_url($file->filepath) , array('title' => t('Download: %name (%size)' , array('%name' => $file->filename, '%size' => format_size($file->filesize))))); } } else { $replace[] = 'NOT FOUND: ' . $value . ''; } $matches[] = $match[0][$key]; } $content = str_replace($matches, $replace, $content) . $img_clear; } } /** * Generate all thumbnails from the uploaded files * * @param &$node * The node to which the thumbnails will be generated * @param $img_css * The img_css get from the 'Display mode' * @param &$img_clear * For floated images it will be filled with a clear div (IMG_CLEAR) * @param $is_teaser * Is this a teaser or not? * @param &$need_read_more_link * Will be set to TRUE if a read more is needed when $is_teaser is TRUE * * @return * a content containing all themed thumbnails or FALSE if there is no thumbnail to display */ function img_get_images(&$node, $img_css, &$img_clear, $is_teaser, &$need_read_more_link) { $output = ''; $images_found = 0; $need_read_more_link = FALSE; if(is_array($node->files)) { $thumbnail_size = img_get_thumbnail_size($node->type); $teaser_threshold = img_get_teaser_threshold($node->type); // add 'lightbox' like attributes first $attributes = array(); $link_class = variable_get('img_link_class', 'none'); switch($link_class) { case 'thickbox': $attributes['attributes']['class'] = 'thickbox'; $attributes['attributes']['rel'] = 'img_' . ($node->nid? $node->nid: time()); // 'insert' has no $node->nid break; case 'lightbox': $attributes['attributes']['rel'] = 'lightbox[' . ($node->nid? $node->nid: time()) . ']'; // 'insert' has no $node->nid break; default: break; } $additional_box_class = ''; $is_vertical = ($node->img_valign == 1); // a big div containing all images $container_start = ''; $container_end = ''; switch($img_css) { case 'left': case 'float_left': case 'bottom_left': $additional_box_class = 'img_left'; $img_clear = IMG_CLEAR; break; case 'center': case 'bottom_center': $is_vertical = true; break; case 'right': case 'float_right': case 'bottom_right': $additional_box_class = 'img_right'; $img_clear = IMG_CLEAR; break; } if($is_vertical) { if($img_css == 'float_left') { $container_start = '
'; $container_end = '
'; } else if($img_css == 'float_right') { $container_start = '
'; $container_end = '
'; } } else { if($img_css != 'float_left' && $img_css != 'float_right' ) { $container_start = '
'; $container_end = IMG_CLEAR . '
'; } } $output .= $container_start; foreach($node->files as $file) { $file = (object)$file; if($file->fid && ($sizes = image_get_info($file->filepath))) { $images_found++; if($is_teaser && $images_found > $teaser_threshold) { $need_read_more_link = TRUE; break; } $img_attributes = array('class' => 'img'); $thumbnail_width = $thumbnail_size; if($thumbnail_size < $sizes['width'] || $thumbnail_size < $sizes['height']) { if($sizes['width'] >= $sizes['height']) { $img_attributes['width'] = $thumbnail_size . 'px'; } else { $img_attributes['height'] = $thumbnail_size . 'px'; } $img = theme("image",img_create_thumbnail($node, $file) , $file->description , $file->description , $img_attributes , FALSE); $attributes['attributes']['title'] = $file->description; $attributes['html'] = TRUE; if($thumbnail_sizes = image_get_info(img_thumbnail_path($node->nid, $file))) { $thumbnail_width = $thumbnail_sizes['width']; } $image = l($img, file_create_url($file->filepath), $attributes); } else { // need no thumbnail $img_attributes['width'] = $sizes['width'] . 'px'; $thumbnail_width = $sizes['width']; $image = theme("image", file_create_url($file->filepath) , $file->description , $file->description , $img_attributes , FALSE); } $image = theme("img_box", $file->description , $image , $additional_box_class , $thumbnail_width , $node->img_titles); // if images are vertically aligned or centered we need an additional div // div containing a single image $div_start = $div_end = ''; if($img_css == 'center' || $img_css == 'bottom_center') { $div_start = '
'; $div_end = '
'; } else if($is_vertical) { $div_start = '
'; $div_end = IMG_CLEAR . '
'; } $output .= $div_start . $image . $div_end; } } $output .= $container_end; } if(!$images_found) { return FALSE; } return $output; } /** * Insert into a form the img elements * * @param &$form * The form to be modified * @param $img_css * 'Display mode' * @param $img_valign * 'Align images vertically' * @param $img_titles * 'Position of titles' * * @return * Nothing */ function _img_form_alter(&$form, $img_css, $img_valign, $img_titles) { $options = array(); $basic_top = t('Basic - top'); $basic_bottom = t('Basic - bottom'); $advanced = t('Advanced'); $options[$basic_top]['left'] = t('Left aligned'); $options[$basic_top]['float_left'] = t('Left floated'); $options[$basic_top]['center'] = t('Centered'); $options[$basic_top]['right'] = t('Right aligned'); $options[$basic_top]['float_right'] = t('Right floated'); $options[$basic_bottom]['bottom_left'] = t('Left aligned'); $options[$basic_bottom]['bottom_center'] = t('Centered'); $options[$basic_bottom]['bottom_right'] = t('Right aligned'); $options[$advanced]['none'] = t('No images'); $options[$advanced]['advanced'] = t('[img:xx] tags'); $titles_options = array(); $titles_options['none'] = t('No titles'); $titles_options['top'] = t('Above images'); $titles_options['bottom'] = t('Below images'); if(!isset($form['img_fieldset'])) { $form['img_fieldset'] = array( '#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => TRUE, '#title' => t('Images'), ); } $form['img_fieldset']['img_css'] = array( '#type' => 'select', '#title' => t('Display mode'), '#description' => t('Select the way how the attached images are inserted into this post') . '
' . t('For advanced mode use [img:xx align=yy title=zz] where ') . '
' . t('align and title are optional'), '#required' => FALSE, '#options' => $options, '#default_value' => $img_css, ); $form['img_fieldset']['img_valign'] = array( '#type' => 'checkbox', '#title' => t('Align images vertically'), '#required' => FALSE, '#default_value' => $img_valign, ); $form['img_fieldset']['img_titles'] = array( '#type' => 'select', '#title' => t('Position of titles'), '#required' => FALSE, '#options' => $titles_options, '#default_value' => $img_titles, ); } /** * Validator function for 'img_dim' and 'img_teaser_threshold'. */ function img_validate_img_form($form, &$form_state) { if(!is_numeric($form_state['values']['img_dim']) || $form_state['values']['img_dim'] <= 0) { form_set_error('img_dim', t('You must enter a positive number.')); } else if(!is_numeric($form_state['values']['img_teaser_threshold']) || $form_state['values']['img_teaser_threshold'] <= 0) { form_set_error('img_teaser_threshold', t('You must enter a positive number.')); } } /** * Implementation of hook_form_alter() */ function img_form_alter(&$form, $form_state, $form_id) { if ($form_id == 'node_type_form' && isset($form['identity']['type'])) { $form['img_fieldset'] = array( '#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => TRUE, '#title' => t('Img module'), ); $form['img_fieldset']['img_disable'] = array( '#type' => 'checkbox', '#title' => t('Disable images'), '#default_value' => variable_get('img_disable_' . $form['#node_type']->type, FALSE), '#description' => t('Don\'t show images for this node type'), ); $form['img_fieldset']['img_dim'] = array( '#type' => 'textfield', '#title' => t('Thumbnail size'), '#size' => 10, '#maxlength' => 10, '#required' => TRUE, '#default_value' => img_get_thumbnail_size($form['#node_type']->type), '#description' => t('Choose the size of thumbnails. Existing thumbnails are not resized.'), ); $form['img_fieldset']['img_teaser_threshold'] = array( '#type' => 'textfield', '#title' => t('Teaser threshold'), '#size' => 10, '#maxlength' => 10, '#required' => TRUE, '#default_value' => img_get_teaser_threshold($form['#node_type']->type), '#description' => t('The maximum number of thumbnails displayed by teasers.'), ); $form['#validate'][] = 'img_validate_img_form'; _img_form_alter($form , variable_get('img_css_' . $form['#node_type']->type, variable_get('img_css', 'float_left')) , variable_get('img_valign_' . $form['#node_type']->type, variable_get('img_valign', 0)) , variable_get('img_titles_' . $form['#node_type']->type, variable_get('img_titles', 'bottom'))); } if(isset($form['type']) && isset($form['#node'])) { $node = $form['#node']; // display the form on the upload enabled nodes only if($form['type']['#value'] . '_node_form' == $form_id && variable_get("upload_$node->type", TRUE) && !variable_get('img_disable_' . $node->type, FALSE)) { $node = $form['#node']; _img_form_alter($form , isset($node->img_css)? $node->img_css: variable_get('img_css_' . $node->type, variable_get('img_css', 'float_left')) , isset($node->img_valign)? $node->img_valign: variable_get('img_valign_' . $node->type, variable_get('img_valign', 0)) , isset($node->img_titles)? $node->img_titles: variable_get('img_titles_' . $node->type, variable_get('img_titles', 'bottom'))); } } } /** * Implementation of hook_nodeapi(). */ function img_nodeapi(&$node, $op, $a3, $a4) { // return if images are disabled for this node type if(variable_get('img_disable_' . $node->type, FALSE)) { return; } _img_boot(); switch($op) { case 'view': if(isset($node->files) && count($node->files)) { $img_css = $node->img_css? $node->img_css: 'advanced'; if($img_css != 'none') { $is_teaser = (bool)$a3; if($img_css == 'advanced') { img_substitute_tags($node, $node->content['body']['#value']); } else { $img_clear = ''; $need_read_more_link = FALSE; if(($images = img_get_images($node, $img_css, $img_clear, $is_teaser, $need_read_more_link)) !== FALSE) { $top = !in_array($img_css, array('bottom_left', 'bottom_center', 'bottom_right')); if($top) { $node->content['body']['#value'] = $images . $node->content['body']['#value'] . $img_clear; } else { $node->content['body']['#value'] .= $images . $img_clear; } } if($need_read_more_link) { $node->readmore = TRUE; } } } } break; case 'rss item': if(isset($node->files) && count($node->files)) { $img_css = $node->img_css? $node->img_css: 'advanced'; if($img_css != 'none') { // hack for rss feeds switch(variable_get('feed_item_length', 'teaser')) { case 'fulltext': $node->body = $node->content['body']['#value']; break; case 'teaser': $node->teaser = $node->content['body']['#value']; break; default: break; } } } break; case 'load': return db_fetch_array(db_query(' SELECT img_css, img_valign, img_titles FROM {img} WHERE nid = %d', $node->nid)); break; case 'insert': db_query(' INSERT INTO {img} (nid, img_css, img_valign, img_titles) VALUES (%d, \'%s\', %d, \'%s\')', $node->nid, $node->img_css, $node->img_valign, $node->img_titles); img_create_thumbnails($node); break; case 'update': // compatibility with existing nodes if(db_fetch_object(db_query('SELECT * FROM {img} WHERE nid = %d', $node->nid))) { db_query(' UPDATE {img} SET img_css = \'%s\', img_valign = %d, img_titles = \'%s\' WHERE nid = %d', $node->img_css, $node->img_valign, $node->img_titles, $node->nid); img_delete_thumbnails($node); img_create_thumbnails($node); } else { db_query(' INSERT INTO {img} (nid, img_css, img_valign, img_titles) VALUES (%d, \'%s\', %d, \'%s\')', $node->nid, $node->img_css, $node->img_valign, $node->img_titles); img_create_thumbnails($node); } break; case 'delete': db_query(' DELETE FROM {img} WHERE nid = %d', $node->nid); img_delete_thumbnails($node); break; } } /** * Implementation of hook_node_type(). */ function img_node_type($op, $info) { switch($op) { case 'delete': variable_del('img_disable_' . $info->type); variable_del('img_dim_' . $info->type); break; } } /** * The administration form */ function img_admin_settings() { $form = array(); $form['img_admin_fieldset'] = array( '#type' => 'fieldset', '#collapsible' => TRUE, '#title' => t('Thumbnails'), ); $form['img_admin_fieldset']['img_dim'] = array( '#type' => 'textfield', '#title' => t('Thumbnail size'), '#size' => 10, '#maxlength' => 10, '#required' => TRUE, '#default_value' => variable_get('img_dim', '500'), '#description' => t('Choose the size of thumbnails. Existing thumbnails are not resized.'), ); $form['img_admin_fieldset']['img_teaser_threshold'] = array( '#type' => 'textfield', '#title' => t('Teaser threshold'), '#size' => 10, '#maxlength' => 10, '#required' => TRUE, '#default_value' => variable_get('img_teaser_threshold', 10), '#description' => t('The maximum number of thumbnails displayed by teasers.'), ); $form['img_admin_fieldset']['img_thumbnails_directory'] = array( '#type' => 'textfield', '#title' => t('Image thumbnails directory'), '#size' => 20, '#maxlength' => 128, '#required' => TRUE, '#default_value' => variable_get('img_thumbnails_directory', 'img_thumbnails'), '#description' => t('A subdirectory of files/ where the thumbnails will be placed.
Reserve this directory for the img module.'), ); $form['img_admin_fieldset']['img_link_class'] = array( '#type' => 'select', '#title' => t('Image browser module'), '#required' => FALSE, '#options' => array( 'none' => t('none'), 'thickbox' => t('thickbox'), 'lightbox' => t('lightbox'), ), '#default_value' => variable_get('img_link_class', 'none'), '#description' => t('Select the image browser module. Install it first if it wasn\'t already installed.'), ); $form['#validate'][] = 'img_validate_img_form';; _img_form_alter($form , variable_get('img_css', 'float_left') , variable_get('img_valign', 0) , variable_get('img_titles', 'bottom')); $form['img_fieldset']['#title'] = t('Images global settings'); $form['img_fieldset']['#description'] = t('You can override these settings from the node type settings forms'); $form['img_fieldset']['#collapsed'] = FALSE; return system_settings_form($form); } /** * The void main(void) */ function img_menu() { $items = array(); $items['admin/content/img'] = array( 'title' => 'Img', 'description' => 'Display images in posts', 'page callback' => 'drupal_get_form', 'page arguments' => array('img_admin_settings'), 'access' => array('administer site configuration'), 'type' => MENU_NORMAL_ITEM, ); return $items; } ?>