'Image FUpload',
'page callback' => 'image_fupload_imagefield_node_create',
'access arguments' => array('mass upload images'),
'type' => MENU_CALLBACK,
'file' => 'field_file.inc',
'file path' => drupal_get_path('module', 'filefield'),
);
return $items;
}
function image_fupload_imagefield_theme() {
return array(
// imagefield_widget form element type theme function.
'image_fupload_imagefield_widget' => array(
'arguments' => array('element' => null),
'file' => 'image_fupload_imagefield_widget.inc',
),
// theme an imagefield field item. It calls imagefied_image with the proper item properties as arguments.
'image_fupload_imagefield_item' => array(
'arguments' => array('item' => null),
),
// use to generate a preview (admin view) of an imagefield item for use in field item forms
// and filefield widgets. It is invoked by filefield_widget_process.
'image_fupload_imagefield_widget_preview' => array(
'arguments' => array('item' => null),
),
// theme function for the field item elements. allows you to place children within the context
// of the parent.
'image_fupload_imagefield_widget_item' => array(
'arguments' => array('element' => null),
),
);
}
/**
* Implementation of hook_form_alter() registry.
**/
function image_fupload_imagefield_form_alter(&$form, $form_state, $form_id) {
global $user;
// load available image node types & field information
$image_node_types = variable_get('image_node_types', array());
$node_type = substr($form_id, 0, (strlen($form_id) - 10)); // get node type
// examine whether this node type is configured as image fupload handler (basic check = performance)
if (image_node_type_load($node_type, TRUE)) {
// get field information
$field_name = $image_node_types[$node_type]['fieldname'];
$field = content_fields($field_name, $node_type);
// check whether image fupload widget is still chosen as widget and permission things
if ($field['widget']['type'] == "image_fupload_imagefield_widget" && arg(3) != "noflash" && arg(3) != "list_imagefields" && user_access('mass upload images')) {
// load needed JS & CSS - Files
$module_path = drupal_get_path('module', 'image_fupload');
drupal_add_js($module_path .'/swfupload/swfupload.js', 'module');
drupal_add_js($module_path .'/swfupload/swfupload.queue.js', 'module');
drupal_add_js($module_path .'/swfupload/fileprogress.js', 'module');
drupal_add_js($module_path .'/swfupload/handlers.js', 'module');
// provide a link for further steps (preview list with editing) if permitted
$redirect_url = '';
if (user_access('edit captions')) {
$redirect_url = url('node/add/' .str_replace("_", "-", $node_type) .'/list_imagefields');
}
// number of images allowed to be uploaded
switch ($field['multiple']) {
case "1":
$upload_limit_count = 100; // unlimited
break;
case "0":
$upload_limit_count = 1;
break;
default:
$upload_limit_count = $field['multiple'];
}
// don't observe number of fields in single storage mode
if ($field['widget']['fupload_mode'] == "single")
$upload_limit_count = 100; // unlimited
$fupload_widget_weight = $field['widget']['weight'];
$file_max_size = parse_size($field['widget']['max_filesize_per_file']); // maximal allowed file size (bytes)
drupal_add_js(theme('swfupload_settings', base_path() . $module_path, url('fupload/flash'), round($file_max_size/1024), ("*." .str_replace(" ", "; *.", $field['widget']['file_extensions'])), (!empty($user->sid) ? $user->sid : session_id()), $upload_limit_count, $node_type, $field_name, (!empty($field['required']) ? "true" : "false"), $field['widget']['fupload_mode'], $redirect_url), 'inline');
drupal_add_css($module_path .'/image_fupload-style.css', 'module', 'all', FALSE);
// unvisible elements, will be written in cache
$form['node_type'] = array('#type' => 'value', '#value' => $node_type);
$form['field_name'] = array('#type' => 'value', '#value' => $field_name);
// is it a new node?
if (!isset($form['#node']->nid)) {
// new node; remove our imagefield and its validation, also in cache
unset($form[$field_name], $form['#field_info'][$field_name], $form['#validate'][array_search('filefield_node_form_validate', $form['#validate'])]);
// hide submit button; will be clicked via JS if needed
$form['buttons']['submit']['#attributes'] = array('style' => 'display:none;');
// we have to split up storage modes
if ($field['widget']['fupload_mode'] == "single") {
// one node per image - mode
if (user_access('edit captions')) { // user allowed to use preview list?
// delete fields which will be shown later in preview list; cache this
form_fields_destroy($form, $field['widget']['fupload_previewlist_field_settings']);
form_set_cache($form['form_build_id'], $form, $form_state); // save changed data in cache, include removed fields for image preview list --> no validation
unset($form['buttons']['preview']);
} else {
// basic form manipulations (normal mode)
unset($form['title']);
form_set_cache($form['form_build_id'], $form, $form_state); // save data in cache, don't include some unsets
unset($form['buttons']['preview']); // only make them unvisible
}
} else {
// multiple images per node - mode ...puhh...
if (user_access('edit captions')) { // user allowed to use preview list?
// delete fields which will be shown later in preview list; cache this
form_fields_destroy($form, $field['widget']['fupload_previewlist_field_settings']);
form_set_cache($form['form_build_id'], $form, $form_state); // save changed data in cache, include removed fields for image preview list --> no validation
unset($form['buttons']['preview']);
} else {
// basic form manipulations (normal mode)
form_set_cache($form['form_build_id'], $form, $form_state); // save data in cache, don't include some unsets
unset($form['buttons']['preview']); // only make them unvisible
}
}
} else {
// edit already existing node
// ... already done by following form manipulations
}
// standard image fupload form; will be shown in the most cases (not for existing node in single mode)
if (isset($form['#node']->nid) && $field['widget']['fupload_mode'] == "single") {
// don't create fupload form
} else {
$form['fupload'] = array(
'#weight' => $fupload_widget_weight - 0.1,
);
$form['fupload']['message'] = array(
'#value' => '
',
'#prefix' => '', // needed to scroll to error and status messages
'#weight' => -7,
);
$form['fupload']['image'] = array(
'#type' => 'fieldset',
'#title' => t('Images'),
'#value' => '' .t('Click the "Select Images" icon on the left below to begin.') .'',
'#weight' => -6,
'#attributes' => array('class' => 'flash', 'id' => 'fsUploadProgress'),
'#collapsible' => FALSE,
'#collapsed' => FALSE,
);
$form['fupload']['upload_info'] = array(
'#value' => t('0 files uploaded.') . ' ',
'#prefix' => '
',
'#suffix' => '
',
'#weight' => -5,
);
// Drupal 6 Bug: can't use "normal" buttons, only submit buttons are possible => workaround
$form['fupload']['upload_buttons'] = array(
'#prefix' => '
',
'#value' => ' ',
'#suffix' => '
',
'#weight' => -4,
);
$form['fupload']['upload_buttons']['node_create'] = array(
'#type' => 'submit',
'#value' => t('Process queued images'),
'#weight' => -3,
'#ahah' => array(
'path' => 'fupload/js/imagefield',
'event' => 'click',
'method' => 'replace',
'wrapper' => 'jsstatus',
'progress' => array('type' => 'bar', 'message' => t('Images in queue are processed...')),
),
);
$form['fupload']['upload_buttons']['delete_queue'] = array(
'#type' => 'submit',
'#value' => t('Delete queued images'),
'#weight' => -2,
'#ahah' => array(
'path' => 'fupload/js/deletequeue/' .$field_name,
'event' => 'click',
'method' => 'append',
'wrapper' => 'jsstatus',
),
);
if (user_access('edit captions') && !isset($form['#node']->nid)) {
$form['fupload']['upload_buttons']['next_step'] = array(
'#value' => '',
'#weight' => -1,
);
}
if (isset($form['#node']->nid)) {
$form['fupload']['upload_info']['#value'] .= t('Queued files will be uploaded by clicking the "Upload images" button.');
$form['fupload']['upload_buttons']['submit'] = array(
'#value' => '',
'#weight' => -2,
);
} else {
$form['fupload']['upload_info']['#value'] .= t('Queued files will be uploaded by clicking the "Save" button at the bottom of this page.');
$form['buttons']['fupload_submit'] = array(
'#value' => '',
'#weight' => 19,
'#submit' => array('node_form_submit'),
);
}
//it seems we don't need it any more, but before, some testing
//if (!isset($form['#node']->nid) && !isset($form_state['redirect']))
//$form['#redirect'] = FALSE; // Important that $_POST is not empty after browser submit
}
}
}
}
function image_fupload_imagefield_node_create() {
global $user;
// Get some POST Variables
$form_build_id = $_POST['form_build_id'];
$form_id = $_POST['form_id'];
if (isset($form_build_id) && isset($form_id)) {
$form_error = 0;
$message = ''; // message which will be returned to the user via JavaScript
$redirect_url = ''; // updated redirect url
// Load the form from the Form API cache and check if valid
$form_state = array('rebuild' => TRUE, 'values' => $_POST); // rebuild option needed to prevent that "_image_node_form_submit" gets executed by drupal_process_form
if (!($form = form_get_cache($form_build_id, $form_state))) {
// code based on upload.module (15/08/2008)
form_set_error('form_token', t('Validation error, please try again. If this error persists, please contact the site administrator.'));
$output = theme('status_messages');
drupal_json(array('status' => TRUE, 'data' => $output));
exit();
}
// some basic variables
$field_name = $form['field_name']['#value']; // name of imagefield
$node_type = $form['node_type']['#value']; // image node type
$field = content_fields($field_name, $node_type); // imagefield widget
// include node.pages.inc for some needed functions (node_form_submit_build_node)
module_load_include('inc', 'node', 'node.pages');
// Remove images which couldn't be processed completly (--> mostly because of memory excaustion) --> physical removal done by cron
db_query("UPDATE {files} SET filename = '%s' WHERE uid = %d AND status = %d AND filename = '%s'", image_fupload_image_status($field_name, IMAGE_PROCESSED), $user->uid, FILE_STATUS_TEMPORARY, image_fupload_image_status($field_name, IMAGE_HALFPROCESSED));
// Some form manipulations, if some fields are not sent
$form['#post'] = $_POST;
$form['#post']['form_id'] = $form['form_id']['#value'];
if (!isset($form['title'])) {
// Default Value; Title will be overwritten in the next steps
$form['#post']['title'] = 'Image';
// New generated fields
$form['title'] = array('#type' => 'textfield', '#title' => 'title2change', '#default_value' => ''); // Needed that validation is successful
}
// if we are editing already existing node, remove nid to prevent errors because node was changed before
if (isset($form['nid']['#value'])) {
$form['gallery_nid']['#value'] = $form['nid']['#value'];
unset($form['nid']['#value'], $form_state['values']['nid']);
}
// Form validation
drupal_process_form($form_id, $form, $form_state);
if (!form_get_errors()) {
// prepare array for queuing images for imagefield (multiple images per node)
$imagefield_files = array();
// validation passed
$result = db_query_range("SELECT * FROM {files} WHERE uid = %d AND status = %d AND filename = '%s'", $user->uid, FILE_STATUS_TEMPORARY, image_fupload_image_status($field_name, IMAGE_UNMACHINED), 0, 3);
while ($image = db_fetch_object($result)) {
$img_cache[] = $image; // cache our images
// Set status flag on image (work on picture can begin); if there are problems with this image, it will be kicked next time
db_query("UPDATE {files} SET filename = '%s' WHERE fid = %d", image_fupload_image_status($field_name, IMAGE_HALFPROCESSED), $image->fid);
// Create a filename out of the given image information; used a theme function so that it can be customised; mapping new title only if title field is disabled for preview list
if ($form['title']['#title'] == "title2change") {
$form['title']['#value'] = theme('fupload_create_filename', $image, $field['widget']['fupload_title_replacements']);
$form_state['values']['title'] = $form['title']['#value'];
}
// import our file (uploaded in queue); code from [#292904] (thx to drewish) (D6 24/08/2008)
$validators = array_merge(filefield_widget_upload_validators($field), imagefield_widget_upload_validators($field));
$files_path = _fupload_import_widget_files_directory($field);
//drupal_set_message($files_path);
// split up storage modes
if ($field['widget']['fupload_mode'] == "single") {
// one node per image - mode
if ($file = field_file_save_file($image->filepath, $validators, $files_path)) {
// prepare node; code partially taken from function "node_form_submit" (D6 24/08/2008 node.pages.inc)
$node = node_form_submit_build_node($form, $form_state);
$insert = empty($node->nid);
// add new files to node
$node->$field_name = array(0 => $file);
// save node
node_save($node);
// report to watchdog
$node_link = l(t('view'), 'node/'. $node->nid);
$watchdog_args = array('@type' => $node->type, '%title' => $node->title);
$t_args = array('@type' => node_get_types('name', $node), '%title' => $node->title);
if ($insert) {
watchdog('content', '@type: added %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link);
drupal_set_message(t('@type %title has been created.', $t_args));
}
else {
watchdog('content', '@type: updated %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link);
drupal_set_message(t('@type %title has been updated.', $t_args));
}
if ($node->nid) {
$form_state['nid'] = $node->nid;
// file inserting successful =) Prevent that same image is processed twice; deleted later by cron
db_query("UPDATE {files} SET filename = '%s' WHERE fid = %d", image_fupload_image_status($field_name, IMAGE_PROCESSED), $image->fid);
// mark file for preview list if necessary
if (user_access('edit captions'))
db_query("INSERT INTO {fupload_previewlist} (fieldname, uid, nid, fid, created) VALUES ('%s', %d, %d, %d, %d)", $field_name, $user->uid, $node->nid, $file['fid'], time());
} else {
// In the unlikely case something went wrong on save, the node will be
// rebuilt and node form redisplayed the same way as in preview.
drupal_set_message(t('The post could not be saved.'), 'error');
}
}
} else {
// multiple images per node - mode
// create gallery node if it doesn't exist
if (!isset($form['gallery_nid']['#value'])) {
$node = _fupload_node_save($form, $form_state, $form_build_id);
} else {
$node->nid = $form['gallery_nid']['#value'];
// add removed node specific; in case that form is cached again
$form['nid']['#value'] = $node->nid;
$form_state['values']['nid'] = $node->nid;
}
// add images to node
// add validation to check if some space is left for the file in our node
$validators['image_fupload_validate_total_filesize'] = array($node->nid, $field_name, parse_size($field['widget']['max_filesize_per_node']));
if ($file = field_file_save_file($image->filepath, $validators, $files_path)) {
// add images to queue to be merged into one node later --> better performance
$imagefield_files[] = $file;
drupal_set_message(t('Image %title added.', array('%title' => $file['filename'])));
// file inserting successful =) Prevent that same image is processed twice; deleted later by cron
db_query("UPDATE {files} SET filename = '%s' WHERE fid = %d", image_fupload_image_status($field_name, IMAGE_PROCESSED), $image->fid);
// mark file for preview list if necessary
if (user_access('edit captions'))
db_query("INSERT INTO {fupload_previewlist} (fieldname, uid, nid, fid, created) VALUES ('%s', %d, %d, %d, %d)", $field_name, $user->uid, $node->nid, $file['fid'], time());
} else {
// some validators couldn't be satisfied --> empty queue
$form_error = 2;
}
}
}
// add queued images to node gallery (multiple storage mode)
if (count($imagefield_files)) {
$node = node_load($node->nid);
// add files to node
//$node->$field_name = array_merge((is_array($node->$field_name) ? $node->$field_name : array()), $imagefield_files);
$node->$field_name = array_merge($node->$field_name, $imagefield_files);
node_save($node);
}
// build redirect url: multiple mode and no access to preview page
if (isset($form['gallery_nid']['#value']) && !user_access('edit captions')) {
$redirect_url = 'node/' .$form['gallery_nid']['#value'];
}
// if alternative redirect url is provided, replace existing one
if (!empty($field['widget']['fupload_previewlist_redirecturl']) && !user_access('edit captions'))
$redirect_url = $field['widget']['fupload_previewlist_redirecturl'];
// send updated redirect url via json_decode
if (!empty($redirect_url) && $form_error == 0)
$message .= '';
// examine how many images are left in queue and inform JS by sending a hidden element
$result = db_fetch_object(db_query("SELECT COUNT(*) AS img_count FROM {files} WHERE uid = %d AND status = %d AND filename = '%s'", $user->uid, FILE_STATUS_TEMPORARY, image_fupload_image_status($field_name, IMAGE_UNMACHINED)));
$message .= '';
} else {
// error in received form (for example a required field was not filled) or during process; inform JS => user
if ($form_error != 2) // special case: some validators of imagefield weren't satisfied
$form_error = 1;
$message .= '';
}
// Inform JS about errors
$message .= '';
$message .= theme('status_messages'); // Theme all messages
drupal_json(array('status' => TRUE, 'data' => $message));
} else {
drupal_json(array('status' => FALSE, 'data' => t('Error: No or wrong POST Data')));
}
}
/**
* CCK related stuff
*/
/**
* Implementation of hook_elements().
*/
function image_fupload_imagefield_elements() {
$elements = array();
$elements_imagefield = module_invoke('imagefield', 'elements');
$elements['image_fupload_imagefield_widget'] = $elements_imagefield['imagefield_widget'];
return $elements;
}
/**
* Implementation of CCK's hook_widget_info().
*/
function image_fupload_imagefield_widget_info() {
return array(
'image_fupload_imagefield_widget' => array(
'label' => t('Image FUpload'),
'field types' => array('image', 'filefield'),
'multiple values' => CONTENT_HANDLE_CORE,
'callbacks' => array('default value' => CONTENT_CALLBACK_CUSTOM),
'description' => t('An edit widget for image files being uploaded via Image FUpload, including a preview of the image and editing options.'),
),
);
}
/**
* Implementation of CCK's hook_widget_settings().
*/
function image_fupload_imagefield_widget_settings($op, $widget) {
module_load_include('inc', 'image_fupload_imagefield', 'image_fupload_imagefield_widget');
switch ($op) {
case 'form':
return image_fupload_imagefield_widget_settings_form($widget);
case 'validate':
return image_fupload_imagefield_widget_settings_validate($widget);
case 'save':
return image_fupload_imagefield_widget_settings_save($widget);
}
}
/**
* Implementation of hook_widget().
*
* Assign default properties to item and delegate to filefield.
*/
function image_fupload_imagefield_widget($form, $form_state, $field, $items, $delta = 0) {
$element = module_invoke('imagefield', 'widget', $form, $form_state, $field, $items, $delta);
return $element;
}
/**
* Interact on changes to CCK ImageFields.
*/
function image_fupload_imagefield_content_fieldapi($op, $field, $new_instance = NULL) {
$image_node_types = variable_get('image_node_types', array());
switch ($op) {
case "delete instance":
// check if a image fupload related field is deleted
if ($image_node_types[$field['type_name']]['fieldname'] == $field['field_name'])
_fupload_imagepreview_settings('delete', $field['type_name']); // remove settings in variable image_node_types
break;
}
}
/**
* Removing a given list of fields (ckk, node, imagefield, taxonomy) of form
* Validation for these fields will also be deactivated
*/
function form_fields_destroy(&$form, $fields_to_remove = array()) {
// collect information about fields being used by image preview list, if needed
$fields = array();
foreach ($fields_to_remove as $key) {
$elements = explode("_", $key, 2);
if (!isset($fields[$elements[0]]) && $fields_to_remove[$key] != FALSE)
$fields[$elements[0]] = array();
if ($fields_to_remove[$key] != FALSE)
array_push($fields[$elements[0]], $elements[1]);
}
// any node specific fields which should be removed ..argg
if (isset($fields['node'])) {
if (in_array("title", $fields['node']))
unset($form['title']);
if (in_array("description", $fields['node']))
unset($form['body_field']);
}
if (isset($fields['taxonomy'])) {
// remove taxonomy items if necessary
for ($i = 0; $i < count($fields['taxonomy']); $i++) {
unset($form['taxonomy'][$fields['taxonomy'][$i]]);
unset($form['taxonomy']['tags'][$fields['taxonomy'][$i]]);
if (!count($form['taxonomy']['tags']))
unset($form['taxonomy']['tags']);
}
// still anything left to render?
if (count(taxonomy_get_vocabularies($form['#node']->type)) == count($fields['taxonomy']))
unset($form['taxonomy']);
}
if (isset($fields['cck'])) {
// remove cck fields if necessary
for ($i = 0; $i < count($fields['cck']);$i++) {
unset($form[$fields['cck'][$i]]);
unset($form['#field_info'][$fields['cck'][$i]]);
}
if (!count($form['#field_info']))
unset($form['#field_info']);
}
}
/**
* Theming related things
*/
function theme_image_fupload_imagefield_item($item) {
return theme('imagefield_item', $item);
}
function theme_image_fupload_imagefield_widget_preview($item = null) {
return theme('imagefield_widget_preview', $item);
}
function theme_image_fupload_imagefield_widget_item($element) {
return theme('imagefield_widget_item', $element);
}
/**
* Determine the widget's files directory [#292904] (great thanks to drewish)
*
* @param $field CCK field
* @return files directory path.
*/
function _fupload_import_widget_files_directory($field) {
$widget_file_path = $field['widget']['file_path'];
if (module_exists('token')) {
global $user;
$widget_file_path = token_replace($widget_file_path, 'user', $user);
}
return file_directory_path() .'/'. $widget_file_path;
}
/*
* Build a node from $form and save it to database
*/
function _fupload_node_save(&$form, $form_state, $form_build_id) {
$node = node_form_submit_build_node($form, $form_state);
$insert = empty($node->nid);
// save node
node_save($node);
// report to watchdog
$node_link = l(t('view'), 'node/'. $node->nid);
$watchdog_args = array('@type' => $node->type, '%title' => $node->title);
$t_args = array('@type' => node_get_types('name', $node), '%title' => $node->title);
if ($insert) {
watchdog('content', '@type: added %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link);
drupal_set_message(t('@type %title has been created.', $t_args));
}
if ($node->nid) {
// will be saved in cache so that only one gallery node will be created
$form['gallery_nid']['#value'] = $node->nid;
$form_state['values']['gallery_nid'] = $node->nid;
// save form to cache
form_set_cache($form_build_id, $form, $form_state);
return $node;
} else {
// In the unlikely case something went wrong on save, the node will be
// rebuilt and node form redisplayed the same way as in preview.
drupal_set_message(t('The post could not be saved.'), 'error');
return FALSE;
}
}
/*
* Validation function
* Checkes if filesize sum of node doesn't exceed the limits
*/
function image_fupload_validate_total_filesize($file, $nid, $field_name, $node_size = 0) {
global $user;
$errors = array();
$total_size = 0;
if ($node_size && $user->uid != 1) {
$result = db_query("SELECT f.filesize FROM {content_%s} AS t JOIN {files} AS f ON t.%s_fid = f.fid WHERE t.nid = %d", $field_name, $field_name, $nid);
while ($image = db_fetch_object($result)) {
$total_size += $image->filesize;
}
if (($total_size + $file->filesize) > $node_size)
$errors[] = t('The new total filesize of this node, %tsize, would exceed field settings of %msize.', array('%tsize' => format_size($total_size + $file->filesize), '%msize' => format_size($node_size)));
}
return $errors;
}