'Teaser Thumb', 'description' => 'Add thumbnail image to teaser', 'page callback' => 'drupal_get_form', 'page arguments' => array('teaserthumb_admin_form'), 'access arguments' => array('administer site configuration'), 'type' => MENU_NORMAL_ITEM, ); return $items; } //------------------------------------------------------------------------------ /** * Implementation of hook_theme() */ function teaserthumb_theme($existing, $type, $theme, $path) { return array('teaserthumb_admin_form' => array( 'render element' => 'form', ), ); } //------------------------------------------------------------------------------ /** * Admin settings form (module configuration page) */ function teaserthumb_admin_form($form, &$form_state) { $presets = image_style_options(FALSE); $form['teaserthumb_valid_node_types'] = array( '#type' => 'checkboxes', '#title' => t('Valid node types'), '#options' => node_type_get_names(), '#default_value' => variable_get('teaserthumb_valid_node_types', array()), '#description' => t('Select the node types you want to create teaser thumbnail'), ); $form['teaserthumb_image_style'] = array( '#type' => 'select', '#title' => t('Thumbnail image style preset'), '#default_value' => variable_get('teaserthumb_image_style', 'thumbnail'), '#description' => t('Select an image module style preset to be used for generating thumbnail image.'), '#options' => $presets, ); $form['teaserthumb_image_align'] = array( '#type' => 'select', '#title' => t('Thumbnail image tag alignment'), '#default_value' => variable_get('teaserthumb_image_align', 0), '#description' => t('Select the "align" attribute of the img tag for the thumbnail image.'), '#options' => array(t('None'), t('Left'), t('Right')), ); $form['teaserthumb_min_width'] = array( '#type' => 'textfield', '#title' => t('Thumbnail image mininum width'), '#default_value' => variable_get('teaserthumb_min_width', ''), '#description' => t('Minimum width of an image to be used as a thumbnail. Leave this field blank for no minimum.'), '#maxlength' => 10, '#size' => 10, ); $form['teaserthumb_min_height'] = array( '#type' => 'textfield', '#title' => t('Thumbnail image mininum height'), '#default_value' => variable_get('teaserthumb_min_height', ''), '#description' => t('Minimum height of an image to be used as a thumbnail. Leave this field blank for no minimum.'), '#maxlength' => 10, '#size' => 10, ); $form['teaserthumb_position'] = array( '#type' => 'radios', '#title' => t('Thumbnail position'), '#default_value' => variable_get('teaserthumb_position', 1), '#description' => t('Select where to put the thumbnail image'), '#options' => array( t('Disabled (Thumbnail is not displayed)'), t('Before the teaser body'), t('After the teaser body'), ), ); // create form for the draggable table $form['priority'] = array('#tree' => TRUE); $source_names = _teaserthumb_get_source_names(); $priorities = variable_get('teaserthumb_priorities', _teaserthumb_default_priorities()); foreach ($priorities as $priority) { $id = $priority['id']; $form['priority'][$id] = array( 'source' => array( '#type' => 'item', '#markup' => $source_names[$id], ), 'status' => array( '#type' => 'checkbox', '#default_value' => $priority['status'], ), 'weight' => array( '#type' => 'weight', '#default_value' => $priority['weight'], '#attributes' => array('class' => array('priority-weight')), ), ); } _teaserthumb_debugout("--------\$form['priority']---------"); _teaserthumb_debugout($form['priority']); $form['submit'] = array( '#type' => 'submit', '#value' => t('Save changes'), ); return $form; } //------------------------------------------------------------------------------ /** * Theme function for admin form */ function theme_teaserthumb_admin_form($variables) { $form = $variables['form']; $rows = array(); $header = array( t('Image source'), t('Enable status'), t('Weight'), ); $attributes = array( 'id' => 'priority-table', ); foreach(element_children($form['priority']) as $id) { $rows[] = array( 'data' => array( drupal_render($form['priority'][$id]['source']), drupal_render($form['priority'][$id]['status']), drupal_render($form['priority'][$id]['weight']), ), 'class' => array('draggable') ); } $info = t('This table determines from where the original image for the teaser thumbnail is obtained.'); $output = drupal_render($form['teaserthumb_valid_node_types']); $output .= '' . t('Priority of image source') . ''; $output .= '
' . $info . '
'; $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => $attributes)); $output .= drupal_render($form['common']); $output .= drupal_render_children($form); drupal_add_tabledrag('priority-table', 'order', 'sibling', 'priority-weight'); return $output; } //------------------------------------------------------------------------------ /** * Admin form submit handler */ function teaserthumb_admin_form_submit($form, &$form_state) { $priorities = array(); foreach ($form_state['values']['priority'] as $id => $priority) { $priorities[] = array( 'id' => $id, 'status' => $priority['status'], 'weight' => $priority['weight'], ); } if (!empty($priorities)) { // sort priority based on the weight usort($priorities, '_teaserthumb_arraysort'); } // save the settings variable_set('teaserthumb_valid_node_types', $form_state['values']['teaserthumb_valid_node_types']); variable_set('teaserthumb_priorities', $priorities); variable_set('teaserthumb_image_style', $form_state['values']['teaserthumb_image_style']); variable_set('teaserthumb_image_align', $form_state['values']['teaserthumb_image_align']); variable_set('teaserthumb_min_width', $form_state['values']['teaserthumb_min_width']); variable_set('teaserthumb_min_height', $form_state['values']['teaserthumb_min_height']); variable_set('teaserthumb_position', $form_state['values']['teaserthumb_position']); } function _teaserthumb_arraysort($a, $b) { return ($a['weight'] < $b['weight'])? -1: 1; } //------------------------------------------------------------------------------ /** * Implements hook_node_view() */ function teaserthumb_node_view($node, $view_mode, $langcode) { // This module works only with teaser if($view_mode != 'teaser') { _teaserthumb_debugout("NOP: view_mode is not teaser"); return; } // check if this node type is a target or not $valid_node_types = variable_get('teaserthumb_valid_node_types', array()); if (empty($valid_node_types[$node->type])) { _teaserthumb_debugout("NOP: this node type (" . $node->type . ") is not a target"); return; } $nid = $node->nid; _teaserthumb_debugout(" "); _teaserthumb_debugout("-----------------------------------------------------------"); _teaserthumb_debugout(" hook_node_view (nid=" . $nid . ", type=" . $node->type . ") "); _teaserthumb_debugout("-----------------------------------------------------------"); // _teaserthumb_debugout($node); // Load options $image_style = variable_get('teaserthumb_image_style', 'thumbnail'); $position = variable_get('teaserthumb_position', 1); $priorities = variable_get('teaserthumb_priorities', _teaserthumb_default_priorities()); $image_align = variable_get('teaserthumb_image_align', 0); $align = _teaserthumb_get_align_attribute($image_align); $img_url = ''; $loopend = FALSE; foreach ($priorities as $priority) { $id = $priority['id']; if ($priority['status']) { switch($id) { case TT_SOURCE_IMAGEFIELD: //---------------------------------------------------------------- // get image associated with the node (image_field) //---------------------------------------------------------------- if ($img_url = _teaserthumb_get_thumbnail_from_imagefield($node)) { $loopend = TRUE; } break; case TT_SOURCE_BODY: //---------------------------------------------------------------- // get image from the node body //---------------------------------------------------------------- if ($img_url = _teaserthumb_get_thumbnail_from_body($node)) { $loopend = TRUE; } break; case TT_SOURCE_TAXONOMY: //---------------------------------------------------------------- // get image associated with taxonomy used by the node //---------------------------------------------------------------- if ($img_url = _teaserthumb_get_thumbnail_from_taxonomy($node)) { $loopend = TRUE; } break; } // we found image to be used, let's exit the loop if ($loopend) break; } } _teaserthumb_debugout("----\$img_url----"); _teaserthumb_debugout($img_url); if ($img_url) { // e.g: public://images/sample01.jpg // NOTE: // image_style_path() --> does NOT cause styled image to be created // image_style_url() --> cause styled image to be created // // $new_path = image_style_path($image_style, $img_url); // _teaserthumb_debugout("----\$new_path----"); // _teaserthumb_debugout($new_path); $new_url = image_style_url($image_style, $img_url); // NOTE: $new_url is absolute URL. // e.g: http://www.example.com/sites/default/files/styles/thumbnail/public/images/sample01.jpg?itok=XXXXXXX _teaserthumb_debugout("----\$new_url----"); _teaserthumb_debugout($new_url); /*-------- Extra (works without this section) ------*/ // convert absolute > relative URL $host = "http://" . $_SERVER['HTTP_HOST']; $new_url = str_replace($host, '', $new_url); _teaserthumb_debugout("----\$new_url (relative)----"); _teaserthumb_debugout($new_url); /*--------------------------------------------------*/ if (!empty($align)) { $variables = array('path' => $new_url, 'attributes' => array('align' => $align, 'class' => array('teaserthumb'))); } else { $variables = array('path' => $new_url); } // theming support $img_tag = theme('image', $variables); // add link to the node $img_tag = '' . $img_tag . ''; _teaserthumb_debugout("----\$img_tag----"); _teaserthumb_debugout($img_tag); } if (!empty($img_tag)) { // Now construct HTML markup and insert it if($position) { // insert thumbnail image tag with a link to the node // $html = '' // . $img_tag . ''; $node->content['teaserthumbnail'] = array( '#markup' => $img_tag, '#weight' => ($position == 1)? -50: 50, ); } } // update node teaser body (no media tags) $body = ''; if(isset($node->content['body'][0]['#markup'])) { $body = $node->content['body'][0]['#markup']; // teaser body } _teaserthumb_debugout("----\$body before _teaserthumb_strip_media----"); _teaserthumb_debugout($body); $body = _teaserthumb_strip_media($body); // remove media tags _teaserthumb_debugout("----\$body after _teaserthumb_strip_media----"); _teaserthumb_debugout($body); $node->content['body'][0]['#markup'] = $body; return; } //------------------------------------------------------------------------------ // // Internal functions // //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ function _teaserthumb_get_node_full_body($node) { // get the node body of proper language (current or undefined) $body = field_get_items('node', $node, 'body'); // get full body $full_body = isset($body[0]['safe_value'])? $body[0]['safe_value'] : check_markup($body[0]['value'], $body[0]['format']); return $full_body; } //------------------------------------------------------------------------------ function _teaserthumb_get_source_names() { return array( TT_SOURCE_IMAGEFIELD => t('Image from node associated image field'), TT_SOURCE_BODY => t('Image from node body'), TT_SOURCE_TAXONOMY => t('Image associated with taxonomy term'), ); } //------------------------------------------------------------------------------ function _teaserthumb_get_align_attribute($align) { switch($align) { case 0: return ''; case 1: return 'left'; case 2: return 'right'; } return ''; } //------------------------------------------------------------------------------ function _teaserthumb_default_priorities() { $source_names = _teaserthumb_get_source_names(); return array( array( 'id' => TT_SOURCE_IMAGEFIELD, 'source' => $source_names[TT_SOURCE_IMAGEFIELD], 'status' => 1, 'weight' => -10, ), array( 'id' => TT_SOURCE_BODY, 'source' => $source_names[TT_SOURCE_BODY], 'status' => 1, 'weight' => -9, ), array( 'id' => TT_SOURCE_TAXONOMY, 'source' => $source_names[TT_SOURCE_TAXONOMY], 'status' => 1, 'weight' => -8, ), ); } //------------------------------------------------------------------------------ function _teaserthumb_get_thumbnail_from_taxonomy($node) { $tids = array(); $tags = field_get_items('node', $node, 'field_tags'); if (empty($tags)) { return ''; } foreach($tags as $index => $tag) { if (!empty($tag['taxonomy_term'])) { $term = $tag['taxonomy_term']; _teaserthumb_debugout("[" . $index . "] name=" . $term->name . ", tid=" . $term->tid . ", vid=" . $term->vid); $tids[] = $term->tid; } } // the node has no taxonomy terms if(empty($tids)) return ''; _teaserthumb_debugout("------tids------"); _teaserthumb_debugout($tids); $img_url = ''; $files = array(); $query = db_select('file_managed', 'fm'); $query->innerJoin('file_usage', 'fu', 'fm.fid = fu.fid'); $query->fields('fm'); $query->fields('fu', array('id')); $query->condition('fu.id', $tids, 'IN'); $query->condition('fm.status', 1); $files = $query->execute(); if (!empty($files)) { _teaserthumb_debugout("------teaserthumb_get_node_taxonomy_image------"); foreach ($files as $file) { $image_info = image_get_info($file->uri); if ($image_info) { // this is image file $img_urls[$file->id] = $file->uri; _teaserthumb_debugout("Node nid:". $node->nid . " found taxonomy image: " . $file->uri. " for tid=" . $file->id); } } _teaserthumb_debugout("----img_urls----"); _teaserthumb_debugout($img_urls); // pick the image by the order of the terms user input at tag field foreach ($tids as $tid) { if (isset($img_urls[$tid])) { $img_url = $img_urls[$tid]; if (_teaserthumb_validate_image_size($img_url)) { break; // we need only the first good image found! } else { // NG $img_url = ''; } } } } // $img_url - stream URI of the original image // e.g: public://images/scaled/aaa.jpg _teaserthumb_debugout("----returning img_url: " . $img_url . "----"); return $img_url; } //------------------------------------------------------------------------------ function _teaserthumb_get_thumbnail_from_imagefield($node) { $img_url = ''; $files = array(); $query = db_select('file_managed', 'fm'); $query->innerJoin('file_usage', 'fu', 'fm.fid = fu.fid'); $query->fields('fm'); $query->fields('fu', array('id')); $query->condition('fu.id', $node->nid); $query->condition('fm.status', 1); $files = $query->execute(); if (!empty($files)) { foreach ($files as $file) { $image_info = image_get_info($file->uri); if ($image_info) { // this is image file $img_url = $file->uri; if (_teaserthumb_validate_image_size($img_url)) { break; // we need only the first good image found! } else { // NG $img_url = ''; } } } } // $img_url - stream URI of the original image // e.g: public://images/scaled/aaa.jpg return $img_url; } //------------------------------------------------------------------------------ function _teaserthumb_get_thumbnail_from_body($node) { global $base_url; $img_url = ''; // obtain node teaser body and full body if(!isset($node->content['body'][0]['#markup'])) { return ''; // no body } else { $full_body = _teaserthumb_get_node_full_body($node); } // remove program code section so that we do not pick up any img tag inside $full_body = preg_replace('/.*?<\/pre>/ims', '', $full_body); $full_body = preg_replace('/.*?<\/code>/ims', '', $full_body); $full_body = preg_replace('/.*?<\/script>/ims', '', $full_body); // search img tag in node's full body (not teaser body) if ($cnt = preg_match_all('//imsu', $full_body, $matches)) { // found at least one img tag // // $matches[0] is an array of entire matched strings // $matches[0][0] => '' // $matches[0][1] => '' // $matches[0][2] => '' // $matches[1] is an array of attributes before src= // $matches[1][0] => ' ' // $matches[1][1] => ' alt="sample" ' // $matches[1][0] => ' title="mytitle" alt="sample-ccc"' // $matches[2] is an array of matches img src // $matches[2][0] => '/sites/default/files/images/aaa.jpg' // $matches[2][1] => '/sites/default/files/images/bbb.jpg' // $matches[2][2] => '/sites/default/files/images/ccc.jpg' // $matches[3] is an array of attributes after src="" // $matches[3][0] => ' /' // $matches[3][1] => ' width="1024" height="600" /' // $matches[3][1] => ' rel="lightbox" /' $tags = $matches[0]; $srcs = $matches[2]; // skip the image which already has a style // // 1. convert 1st image to thumbnail // 2. remove all tag // 3. remove empty anchor tag // 4. add thumbnail of the 1st image to the top of the body with a link to the node for($i = 0 ; $i < $cnt ; $i++) { $tag = $tags[$i]; $src = $srcs[$i]; // skip already styled image /*--- do we need this? -- if(preg_match('/\/files\/styles/i', $src)) { _teaserthumb_debugout("Skip alread styled image, src is: " . $src); continue; } ------------*/ $file_public_path = variable_get('file_public_path', conf_path() . '/files'); $file_public_url = $base_url . '/'. $file_public_path; _teaserthumb_debugout("src: " . $src); _teaserthumb_debugout("file_public_path: " . $file_public_path); _teaserthumb_debugout("file_public_url: " . $file_public_url); // check if this is a local file or not $url = parse_url($src); if (!empty($url['scheme'])) { // absolute URL if (strstr($src, $file_public_url)) { _teaserthumb_debugout("OK: it is local file under Drupal public file dir"); // OK, it is local file under Drupal public file directory $getimage_path = $src; } else { _teaserthumb_debugout("NG: it is either remote file or local file outside of Drupal public file dir --> skip!"); // 1. remote file // 2. local file but not under Drupal public file directory // -----> skip it continue; } } else { _teaserthumb_debugout("Neutral: it is relative path"); // relative URL } // Remove hostname and base URL from src // // From: http://www.example.com/drupal/sites/default/files/image/aaa.jpg // To: /sites/default/files/image/aaa.jpg // // Note: $base_url: http://www.example.com/drupal (no trailing slash!) // $file_public_path // $src = str_replace($base_url, '', $src); $file_public_path = variable_get('file_public_path', conf_path() . '/files'); _teaserthumb_debugout("file_public_path: " . $file_public_path); $img_url = str_replace($file_public_path, 'public:/', $src); $img_url = preg_replace('/^\/*/', '', $img_url); // $img_url - stream URI of the original image // e.g: public://images/scaled/aaa.jpg if (_teaserthumb_validate_image_size($img_url)) { break; // we need only the first good image found! } else { // NG $img_url = ''; } } } return $img_url; } //------------------------------------------------------------------------------ function _teaserthumb_validate_image_size($img_url) { $min_width = variable_get('teaserthumb_min_width', ''); $min_height = variable_get('teaserthumb_min_height', ''); $image_info = image_get_info($img_url); if(!$image_info) { _teaserthumb_debugout("NG: image_get_info(". $img_url . ") failed --> skip!"); return FALSE; } _teaserthumb_debugout("image_get_info(". $img_url . ") succeeded"); _teaserthumb_debugout("min_width: " . $min_width . ", min_height: " . $min_height); _teaserthumb_debugout($image_info); if (!empty($min_width)) { if ($image_info['width'] < $min_width) { _teaserthumb_debugout("NG! width is too small"); return FALSE; } } if (!empty($min_height)) { if ($image_info['height'] < $min_height) { _teaserthumb_debugout("NG! height is too small"); return FALSE; } } _teaserthumb_debugout("OK! passed the min width/height check!!"); return TRUE; // OK! can be used for a thumbnail } //------------------------------------------------------------------------------ function _teaserthumb_strip_media($string) { // Remove all img tags $img_pattern = "/]+src=\"[^\"]+\"[^>]*>/i"; $string = preg_replace($img_pattern, '', $string); // Remove all object blocks (video, flash) $object_pattern = "/\\n\\r\\t]+<\/object>/smi"; $string = preg_replace($object_pattern, '', $string); // Remove empty anchors $string = preg_replace('/<\/a>/ims', '', $string); return $string; } //------------------------------------------------------------------------------ function _teaserthumb_debugout($str) { return; // for production $outfile = "/tmp/debug.txt"; if(is_array($str) || is_object($str)) { error_log(print_r($str, TRUE) . "\n", 3, $outfile); } else { error_log($str . "\n", 3, $outfile); } }