* porting to Drupal 6 * @author Heshan Wanigasooriya * @author Glen Marianko Twitter@demoforum * @todo */ /** * Implementation of hook_help(). */ function video_image_help($path, $arg) { switch ($path) { case 'admin/modules#description': return t('Enable thumbnail support for video module.'); } } /** * Implementation of hook_menu() */ function video_image_menu() { $items = array(); $may_cache=true; if ($may_cache) { $items['admin/settings/video/image'] = array( 'title' => 'Video image', 'description' => 'Administer video_image module settings', 'page callback' => 'drupal_get_form', 'page arguments' => array('video_image_admin_settings'), 'access arguments' => array('administer site configuration'), 'type' => MENU_NORMAL_ITEM, ); } return $items; } /** * Implementation of hook_perm */ //GMM: define positions in perm array to reference later (without misspellings :) define('OVERRIDE_IMAGE', 0); function video_image_perm() { $array = array('override autothumbnailing using uploaded image'); return $array; } /** * Settings form */ function video_image_admin_settings() { if (module_exists('video_upload') && variable_get('video_image_auto_thumbnail', 0)) { $upload_weight = db_result(db_query("SELECT weight FROM {system} WHERE name='video_upload'")); db_query("UPDATE {system} SET weight=".($upload_weight+1)." WHERE name='video_image'"); } $form = array(); $form['video_image_publish_thumbnail'] = array( '#type' => 'checkbox', '#title' => t('Publish the video thumbnails'), '#description' => t('Checking this value will cause the video thumbnail image nodes to be published and therefore could show up in blocks. Usually, this is not what you want because then you could end up with both the thumbnail node and the video node showing up and since there is no way to link the image node to the video node, this is not desirable. However, with this unchecked, the administrator will end up with a lot of unpublished nodes.'), '#default_value' => _video_image_publish_thumbnails(), ); $form['video_image_promote_thumbnail'] = array( '#type' => 'checkbox', '#title' => t('Promote the thumbnails to the front page'), '#default_value' => _video_image_promote_thumbnails(), ); $form['autothumb'] = array( '#type' => 'fieldset', '#title' => t('Automatic video thumbnailing'), '#collapsible' => TRUE, '#collapsed' => FALSE, '#description' => t('This feature requires the \'video_ffmpeg_helper\' module'), ); $auto_thumb_disable = !module_exists('video_ffmpeg_helper'); $form['autothumb']['video_image_auto_thumbnail'] = array( '#type' => 'checkbox', '#title' => t('Auto thumbnail for videos'), '#description' => t('If set up correctly, this will auto-generate a thumbnail for each video created.'), '#default_value' => variable_get('video_image_auto_thumbnail', false) && !$auto_thumb_disable, '#disabled' => $auto_thumb_disable, ); /* $form['autothumb']['video_image_auto_thumbnail_only'] = array( '#type' => 'checkbox', '#title' => t('Use auto-thumbnailer exclusively for video images'), '#description' => t('If checked, this will disable the file upload box for the user-supplied thumbnail. Only check this if you have checked to be sure that auto-thumbnailing works. Auto thumbnail must be selected for this to be enabled.'), '#default_value' => variable_get('video_image_auto_thumbnail_only', false), '#disabled' => !variable_get('video_image_auto_thumbnail', false), ); */ $form['autothumb']['video_image_auto_thumbnail_debug'] = array( '#type' => 'checkbox', '#title' => t('Debug automatic thumbnail process'), '#default_value' => variable_get('video_image_auto_thumbnail_debug', false), '#description' => t('Automatic thumbnailing of videos is dependent on the actual video types. Some video types may not be able to be automatically thumbnailed. Setting this option will allow messages to be show when posting and editing of videos that can be automatically thumbnailed.'), '#disabled' => $auto_thumb_disable, ); return system_settings_form($form); } function video_image_admin_settings_validate($form, &$form_state) { if (module_exists('video_ffmpeg_helper')) { if ($form_state['values']['video_image_auto_thumbnail']) { $path_ok = _video_ffmpeg_helper_check_exe_path(); if (!$path_ok) { drupal_set_message(t('The path for \'ffmpeg\' is not valid. Please check settings on the !ffmpeg_settings_page', array('!ffmpeg_settings_page' => l(t('Video ffmpeg helper settings page'), 'admin/settings/video/ffmpeg_helper'))), 'error'); } } } } /** * Return true if the video support authothumbnailing */ function video_image_is_autothumbable($node) { $info = video_get_type_info($node->vtype); $has_hook = module_hook('video_' . $node->vtype, 'v_auto_thumbnail'); return $has_hook && isset($info[$node->vtype]['#autothumbable']) && $info[$node->vtype]['#autothumbable']; } /** * Implementation of hook_form_alter() */ function video_image_form_alter(&$form, &$form_state, $form_id) { if($form_id == 'video_node_form') { $node = $form['#node']; $value = ($node->new_image) ? '#value' : '#default_value'; $form['iid'] = array('#type' => 'hidden', $value => $node->iid); if (!is_array($node->tempimage['fids'])) { $fids = array('_original' => 0); foreach (_image_get_sizes() as $size) { $fids[$size['label']] = 0; } $node->tempimage['fids'] = $fids; } $form['tempimage']['#tree'] = true; foreach ($node->tempimage['fids'] as $label => $fid) { $form['tempimage']['fids'][$label] = array('#type' => 'hidden', $value => $fid); } $auto_thumbable = video_image_is_autothumbable($node); //GMM: fix permission misspelling $perm=video_image_perm(); if(!$auto_thumbable || user_access($perm[OVERRIDE_IMAGE])) { // let's be sure that image directories are ready if (function_exists('_image_check_settings')) { _image_check_settings(); } // needed for uploading $form['#attributes'] = array("enctype" => "multipart/form-data"); $form['image'] = array('#type' => 'fieldset', '#title' => t('Image thumbnails'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => -17, '#description' => t('Upload an image to be used as the thumbnail for this video.')); $form['image']['image'] = array('#type' => 'file', '#title' => t('Image')); } if ($node->nid && $auto_thumbable) { $form['regenerate_thumbnail'] = array('#type' => 'checkbox', '#title' => t('Auto regenerate thumbnail'), '#default_value' => 0); } } } /** * Implementation of hook_nodeapi() */ function video_image_nodeapi(&$node, $op, $teaser) { //print_r($op); //exit; if($node->type == 'video') { switch ($op) { case 'load': //exit; return _video_image_load($node); case 'prepare': //exit; _video_image_prepare($node); break; case 'presave': //exit; _video_image_submit($node); break; case 'view': //exit; _video_image_view($node, $teaser); break; case 'delete': //exit; _video_image_delete($node); break; } } } /** * Renders thumbnail node with a link to the video node to be used on video teasers. * * @param $image * object with image node information * @param $video * the video node associated with image * * @return * string of content to display */ function theme_video_image_teaser($image, $video) { $image_html = NULL; if($image != NULL && $image->type == 'image') { $image_html = image_display($image, 'thumbnail', array('class' => 'video_image_teaser')); } else if($image_node == NULL && $video->serial_data['image_teaser']){ // only for backward compatibility $image_html = theme('image', $video->serial_data['image_teaser'], $video->title, $video->title, array('class' => 'video_image_teaser'), FALSE); } if($image) { //Create a link with an image in it. $output .= l($image_html, "node/$video->nid", array('html' => TRUE)); $output .= '
'; } return $output; } /* nodeapi split out hooks */ function _video_image_load(&$node) { $output['iid'] = $node->serial_data['iid']; return $output; } function _video_image_prepare(&$node) { // let's check that we have a valid image //print_r($node); //exit; if (count($_POST)) { $validators = array( 'file_validate_is_image' => array() ); $field_name = file_save_upload('image', $validators); if (!$field_name && video_image_is_autothumbable($node)) { _video_image_thumbnail_debug(t('video_image_nodeapi: prepare: ready to thumbnail video')); $field_name = module_invoke('video_' . $node->vtype, 'v_auto_thumbnail', $node); if ($field_name === false && count($_POST)) { drupal_set_message(t('The thumbnailing process of your video failed for some reason. Video thumbnail will not be available.'), 'error'); } } } if ($field_name) { $node->tempimage = _video_image_temp_image_store($field_name); $node->new_image = TRUE; } else if (is_array($_POST['tempimage']) && ($_POST['op'] == 'Preview' || $_POST['op'] == 'Submit')) { $node->tempimage = (array)_video_image_temp_image_load(array_values($_POST['tempimage']['fids'])); } } function _video_image_submit(&$node) { //print_r($node); //exit; // ############# PREPARE ####################### // ------------- MOVED ------------------------- if (count($_POST)) { $validators = array( 'file_validate_is_image' => array() ); $field_name = file_save_upload('image', $validators); if (!$field_name && video_image_is_autothumbable($node)) { _video_image_thumbnail_debug(t('video_image_nodeapi: prepare: ready to thumbnail video')); $field_name = module_invoke('video_' . $node->vtype, 'v_auto_thumbnail', $node); if ($field_name === false && count($_POST)) { drupal_set_message(t('The thumbnailing process of your video failed for some reason. Video thumbnail will not be available.'), 'error'); } } } if ($field_name) { /* if($node->iid){ // Remove all the existing images. $result = db_query("SELECT f.fid, f.filepath FROM {image} i INNER JOIN {files} f ON i.fid = f.fid WHERE i.nid = %d", $node->iid); while ($file = db_fetch_object($result)) { file_delete(file_create_path($file->filepath)); db_query("DELETE FROM {files} WHERE fid = %d", $file->fid); } //db_query("DELETE FROM {image} WHERE nid = %d", $node->iid); } * */ $node->tempimage = _video_image_temp_image_store($field_name); $node->new_image = TRUE; //print_r($node); //die; } else if (is_array($_POST['tempimage']) && ($_POST['op'] == 'Preview' || $_POST['op'] == 'Submit')) { //print_r($node); //exit; $node->tempimage = (array)_video_image_temp_image_load(array_values($_POST['tempimage']['fids'])); } // ########################################################################### if ($node->regenerate_thumbnail) { _video_image_regenerate_thumbnail($node); } if (is_array($node->tempimage['fids']) && $node->tempimage['fids']['_original']) { $image = _video_image_temp_image_load(array_values($node->tempimage['fids'])); //print_r($image); //exit; //GMM: This DELETE query causes the uploaded video file to disappear // especially when using ffmpeg to convert the video - no need to kill the // tempimage, since its status is 0 in {files} Drupal cron will nix it //db_query("DELETE FROM {files} WHERE fid in (%s)", // implode(',', array_values($node->tempimage['fids']))); // initialize standard node fields //print_r($image); //exit; $image->uid = $node->uid; $image->created = time(); $image->title = t('Video thumbnail for !title', array('!title' => $node->title)); $image = node_submit($image); $image->uid = $node->uid; $image->status = _video_image_publish_thumbnails(); $image->promote = _video_image_promote_thumbnails(); // This is a messages hack (we don't want to see what happens under the covers) _video_image_pause_messages(true); if ($node->iid) { $oldimage = node_load($node->iid); $oldimage->images = $image->images; // delete the old images? //GMM: Fix to use the proper variable to trigger current Image module to rebuild derivative images // Tested against Image 6.x-1.0-aplha4 // $oldimage->new_image = 1; $oldimage->new_file = true; node_save($oldimage); $node->iid = $oldimage->nid; $node->serial_data['iid'] = $node->iid; } else { node_save($image); //print_r($image); //exit; //image_insert($image); $node->iid = $image->nid; // store the iid into the serial_data $node->serial_data['iid'] = $node->iid; //print_r($node); //die; // needed to set the correct status and promote values even if the user does not have enough permissions. Is there a better solution??? // db_query('UPDATE {node} SET status = %d, promote = %d WHERE nid = %d AND vid = %d', _video_image_publish_thumbnails(), _video_image_promote_thumbnails(), $image->nid, $image->vid); } _video_image_pause_messages(); } else { $node->serial_data['iid'] = $node->iid; } } function _video_image_view(&$node, $teaser) { if (is_array($node->tempimage['fids']) && $node->tempimage['fids']['_original']) { $image = _video_image_temp_image_load(array_values($node->tempimage['fids'])); } else if ($node->iid) { $image = node_load($node->iid); } else { $image = NULL; // this is for backward compatibility } if($teaser) { $node->content['video_image_thumbnail'] = array('#value' => theme('video_image_teaser', $image, $node)); } } function _video_image_regenerate_thumbnail(&$node) { $field_name = module_invoke('video_' . $node->vtype, 'v_auto_thumbnail', $node); if ($field_name) { $node->tempimage = _video_image_temp_image_store($field_name); $node->new_image = TRUE; } } function _video_image_delete(&$node) { _video_image_pause_messages(true); node_delete(array('nid' => $node->iid)); _video_image_pause_messages(); } /* At times, when doing sub-node processing (creating/deleting thumbnail nodes) * we don't really want to show all the messages through to the end user or it * gets a little bit confusing (image created messages when creating a video) * so we suppress the messages with this procedure. */ function _video_image_pause_messages($snapshot = false) { static $messages = null; if ($snapshot) { $messages = drupal_get_messages(); } else if (is_array($messages)) { $_SESSION['messages'] = $messages; $messages = null; } } /* debugging framework for troublesome thumbnailing */ function _video_image_thumbnail_debug($msg) { static $debug = NULL; if ($debug == NULL) { $debug = variable_get('video_image_auto_thumbnail_debug', false); } if ($debug) { $t = debug_backtrace(); $l = array_shift($t); drupal_set_message(basename($l['file'], '.module').': '.$msg); } } function _video_image_temp_image_store(&$file) { $image = new stdClass(); $image->images[IMAGE_ORIGINAL] = $file->filepath; $image->images =_image_build_derivatives($image); $image->type = 'image'; $image->uid = 1; $image->created = time(); $image->title = t('video image thumbnail'); // We're good to go. $image->rebuild_images = FALSE; $image->new_file = TRUE; //print_r($image); //exit; if ($image->images) { node_validate($image); if (!form_get_errors()) { // save the images in the files table foreach ($image->images as $l => $f) { $info = image_get_info($f); $file->fid = db_last_insert_id('files','fid'); if($l=="_original"){ db_query("INSERT INTO {files} (fid, filename, filepath, filemime, filesize) VALUES (%d, '%s', '%s', '%s', '%s')", $fid, "video_image_temp.$l", $f, $info['mime_type'], $info['file_size']); } $image->fids[$l] = $file->fid; } } } //print_r($image); //exit; return (array)$image; } /* Create a fake node object that acts like an image node * by looking up each file in the array $fids and loading * them into the images array. */ function _video_image_temp_image_load($fids) { $image = new stdClass(); $image->type = 'image'; $image->new_file = 1; $fids = implode(',', $fids); $results = db_query("SELECT fid, filename, filepath FROM {files} WHERE fid IN (%s)", $fids); while ($file = db_fetch_object($results)) { $label = substr($file->filename, 17); $image->images[$label] = $file->filepath; $image->fids[$label] = $file->fid; } return $image; } /* If the user has set a promote preference, use that, otherwise return * if 'promote' is set in the drupal content type settings * * @return * Returns whether we should promote thumbnails or not */ function _video_image_promote_thumbnails() { $settings_override = variable_get('video_image_promote_thumbnail', NULL); if ($settings_override === NULL) { $node_options = variable_get('node_options_image', array('status', 'promote')); return in_array('promote', $node_options); } return $settings_override; } /* If the user has set a publish preference, use that, otherwise return * if 'status' is set in the drupal content type settings * * @return * Returns whether we should publish thumbnails or not */ function _video_image_publish_thumbnails() { $settings_override = variable_get('video_image_publish_thumbnail', NULL); if ($settings_override === NULL) { $node_options = variable_get('node_options_image', array('status', 'promote')); return in_array('status', $node_options); } return $settings_override; } /** * Create an image file object from a given image url */ function _video_image_get_thumb_file_object($thumbnail_url, $id) { if($thumbnail_url && $thumbnail_url != '' && $image = image_gd_open($thumbnail_url, 'jpeg')) { // save image to temp directory for processing $location = file_directory_temp() .'/'. $id .'.jpg'; image_gd_close($image, $location, 'jpeg'); // get info and build a file object $filepath = file_create_path($location, file_directory_temp()); $info = image_get_info($filepath); $file = new stdClass(); $file->filepath = realpath($filepath); $file->filename = basename($file->filepath); $file->filesize = $info['file_size']; $file->filemime = $info['mime_type']; return $file; } return null; } /** * Implementation of hook_theme(). */ function video_image_theme() { return array( 'video_image_teaser' => array( 'arguments' => array('image' => NULL,'video' => NULL), ), ); }