'Plupload upload page',
'page callback' => 'plup_upload_page',
'access callback' => 'plup_upload_access',
'access arguments' => array('access content', 'allow plupload'),
'type' => MENU_CALLBACK
);
$items['plupload/%'] = array(
'title' => 'Plupload upload page',
'page callback' => 'plup_upload_page',
'access callback' => 'plup_upload_access',
'access arguments' => array('access content', 'allow plupload'),
'type' => MENU_CALLBACK
);
return $items;
}
/**
* Verifies the token for this request.
*/
function plup_upload_access() {
foreach(func_get_args() as $permission) {
if (!user_access($permission)) {
return FALSE;
}
}
return !empty($_REQUEST['plupload_token']) && drupal_valid_token($_REQUEST['plupload_token'], 'plupload-handle-uploads');
}
/**
* _permission
*/
function plup_permission() {
return array(
'allow plupload' => array(
'title' => t('Allow Plupload'),
'description' => t('Allow user to upload files via Plupload.'),
),
);
}
/**
* _library
*/
function plup_library() {
$module = drupal_get_path('module', 'plup');
$plupload = libraries_get_path('plupload');
$lib['plupload'] = array(
'title' => 'Plupload',
'website' => 'http://www.plupload.com',
'version' => '1.5.1.1',
'js' => array(
"$plupload/js/plupload.full.js" => array(),
"$module/plup.js" => array(),
),
'css' => array(
"$module/plup.css" => array(
'type' => 'file',
'media' => 'screen',
),
),
'dependencies' => array(
array('system', 'ui.progressbar'),
array('system', 'ui.sortable')
),
);
return $lib;
}
/**
* _element_info
*/
function plup_element_info() {
$plupload = libraries_get_path('plupload');
$types['plupload_file'] = array(
'#input' => TRUE,
'#title' => NULL,
'#process' => array('plup_process_element'),
'#value_callback' => 'plup_value_element',
'#element_validate' => array('plup_validate_element'),
'#pre_render' => array('plup_pre_render_element'),
'#default_value' => NULL,
'#required' => FALSE,
'#autocomplete_path' => FALSE,
'#theme_wrappers' => array('form_element'),
'#theme' => 'plup_plupload',
'#upload_location' => NULL,
'#info' => array(),
'#attached' => array(
'library' => array(
array('plup', 'plupload')
)
),
'#plup' => array(
'container' => NULL,
'browse_button' => NULL,
'upload' => NULL,
'runtimes' => 'html5,gears,flash,silverlight,browserplus,html4',
'max_file_size' => '512MB',
'url' => url('plupload', array('query' => array('plupload_token' => drupal_get_token('plupload-handle-uploads')))),
'filters' => array(),
'chunk_size' => '512K',
'unique_names' => TRUE,
'flash_swf_url' => base_path() ."$plupload/js/plupload.flash.swf",
'silverlight_xap_url' => base_path() ."$plupload/js/plupload.silverlight.xap",
'drop_element' => NULL,
'multipart' => FALSE,
'dragdrop' => TRUE,
'multiple_queues' => TRUE,
'urlstream_upload' => FALSE,
'image_style' => 'thumbnail',
'image_style_path' => '',
'max_files' => -1
),
'#plup_override' => array()
);
return $types;
}
/**
* Value callback needed for removing all items.
*/
function plup_value_element(&$element, $input = FALSE, $form_state = NULL) {
// Default state - no new data
if ($input === FALSE) {
return NULL;
}
// Field was emptied - user deleted all files
if (is_null($input)) {
return array(array('fid' => 0));
}
// Field has new data
return $input;
}
/**
* Process callback to set JS settings before Plupload init.
*/
function plup_process_element($element, &$form_state, $form) {
$element['#default_value'] = isset($element['#value']) ? $element['#value'] : $element['#default_value'];
$element['#plup']['container'] = $element['#id'];
$element['#plup']['browse_button'] = $element['#id'] .'-plup-select';
$element['#plup']['upload'] = $element['#id'] .'-plup-upload';
$element['#plup']['drop_element'] = $element['#id'] .'-plup-filelist';
$element['#plup']['name'] = $element['#name'];
$element['#plup'] = array_merge($element['#plup'], $element['#plup_override']);
$files = variable_get('file_public_path', 'sites/default/files');
$element['#plup']['image_style_path'] = base_path() . $files .'/styles/'. $element['#plup']['image_style'] .'/temporary/';
$element['#attached']['js'][] = array(
'data' => array('plup' => array($element['#name'] => $element['#plup'])),
'type' => 'setting',
);
return $element;
}
/**
* Pre-render callback to load existing items.
*/
function plup_pre_render_element($element) {
if (isset($element['#default_value']) && !empty($element['#default_value'])) {
foreach ($element['#default_value'] AS $delta => $item) {
$element['#default_value'][$delta] = array_merge($item, (array) file_load($item['fid']));
}
}
return $element;
}
/**
* Element validation callback.
*/
function plup_validate_element($element, &$form_state, $form) {
$item = reset($element['#value']);
if ($element['#required'] == TRUE && $item['fid'] == 0 && count($element['#value']) == 1) {
form_error($element, t("@field field is required.", array('@field' => $element['#title'])));
}
$cardinality = isset($element['#plup_override']['max_files']) ? $element['#plup_override']['max_files'] : $element['#plup']['max_files'];
if ($cardinality > 0 && count($element['#value']) > $cardinality) {
form_error($element, t("Only !num items are allowed.", array('!num' => $cardinality)));
}
}
/**
* _theme
*/
function plup_theme() {
return array(
'plup_plupload' => array(
'render element' => 'element',
),
'plup_items' => array(
'render element' => 'element'
)
);
}
/**
* Theme Plupload widget.
*/
function theme_plup_plupload($variables) {
$element = $variables['element'];
$attributes = array();
if (isset($element['#id'])) {
$attributes['id'] = $element['#id'];
}
if (!empty($element['#attributes']['class'])) {
$attributes['class'] = (array) $element['#attributes']['class'];
}
$attributes['class'][] = 'plupload';
$hasTitle = (bool) (isset($element['#plup_override']['title_field']) && $element['#plup_override']['title_field'] == 1);
$hasAlt = (bool) (isset($element['#plup_override']['alt_field']) && $element['#plup_override']['alt_field'] == 1);
if ($hasTitle) {
$attributes['class'][] = 'has-title';
}
if ($hasAlt) {
$attributes['class'][] = 'has-alt';
}
$output = '
';
$output .= '
';
$output .= '
';
$output .= theme('plup_items', array('element' => $element));
$output .= ' ';
$output .= '
'. t('Drag files here') .'
'. implode("\n", $element['#info']) .'
';
$output .= '
';
$output .= '
';
$output .= '
';
return $output;
}
/**
* Theme Plupload items within widget.
*/
function theme_plup_items($vars) {
$element = &$vars['element'];
if (isset($element['#default_value']) && !empty($element['#default_value'])) {
$items = &$element['#default_value'];
} else {
return '';
}
$output = '';
foreach ($items AS $delta => $item) {
// If user deleted all items I'll get array('fid' => 0)
if ($item['fid'] > 0) {
$hasTitle = (bool) (isset($element['#plup_override']['title_field']) && $element['#plup_override']['title_field'] == 1);
$hasAlt = (bool) (isset($element['#plup_override']['alt_field']) && $element['#plup_override']['alt_field'] == 1);
$name = $element['#name'] .'['. $delta .']';
$output .= '';
$output .= ''. theme('image_style', array('style_name' => $element['#plup']['image_style'], 'path' => $item['uri'], 'title' => $item['filename'])) .'
';
$output .= ' ';
if ($hasTitle) {
$output .= ' ';
}
if ($hasAlt) {
$output .= ' ';
}
$output .= ' ';
$output .= ' ';
if (isset($item['rename'])) {
$output .= ' ';
}
$output .= ' ';
}
}
return $output;
}
/**
* Plupload's upload function.
*/
function plup_upload_page() {
drupal_add_http_header('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT', TRUE);
drupal_add_http_header('Last-Modified', gmdate("D, d M Y H:i:s") . ' GMT', TRUE);
drupal_add_http_header('Cache-Control', 'no-store, no-cache, must-revalidate post-check=0, pre-check=0', TRUE);
drupal_add_http_header('Pragma', 'no-cache', TRUE);
// Settings
$targetDir = $GLOBALS['conf']['file_temporary_path'] .'/';
// 5 minutes execution time
@set_time_limit(5 * 60);
// Uncomment this one to fake upload time
// usleep(5000);
// Get parameters
$chunk = isset($_REQUEST['chunk']) ? $_REQUEST['chunk'] : 0;
$chunks = isset($_REQUEST['chunks']) ? $_REQUEST['chunks'] : 0;
$filename = isset($_REQUEST['name']) ? $_REQUEST['name'] : '';
// Clean the fileName for security reasons
$extensions = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp';
$filename = file_munge_filename($filename, $extensions, FALSE);
// Make sure the fileName is unique but only if chunking is disabled
if ($chunks < 2 && file_exists($targetDir . DIRECTORY_SEPARATOR . $filename)) {
$ext = strrpos($filename, '.');
$filename_a = substr($filename, 0, $ext);
$filename_b = substr($filename, $ext);
$count = 1;
while (file_exists($targetDir . DIRECTORY_SEPARATOR . $filename_a . '_' . $count . $filename_b))
$count++;
$filename = $filename_a . '_' . $count . $filename_b;
}
// Look for the content type header
if (isset($_SERVER['HTTP_CONTENT_TYPE'])) {
$contentType = $_SERVER['HTTP_CONTENT_TYPE'];
}
if (isset($_SERVER['CONTENT_TYPE'])) {
$contentType = $_SERVER['CONTENT_TYPE'];
}
// Handle non multipart uploads older WebKit versions didn't support multipart in HTML5
if (strpos($contentType, 'multipart') !== false) {
if (isset($_FILES['file']['tmp_name']) && is_uploaded_file($_FILES['file']['tmp_name'])) {
// Open temp file
$out = fopen($targetDir . DIRECTORY_SEPARATOR . $filename, $chunk == 0 ? 'wb' : 'ab');
if ($out) {
// Read binary input stream and append it to temp file
$in = fopen($_FILES['file']['tmp_name'], 'rb');
if ($in) {
while ($buff = fread($in, 4096))
fwrite($out, $buff);
} else {
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
}
fclose($in);
fclose($out);
@unlink($_FILES['file']['tmp_name']);
} else {
die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
}
} else {
die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}');
}
} else {
// Open temp file
$out = fopen($targetDir . DIRECTORY_SEPARATOR . $filename, $chunk == 0 ? 'wb' : 'ab');
if ($out) {
// Read binary input stream and append it to temp file
$in = fopen("php://input", 'rb');
if ($in) {
while ($buff = fread($in, 4096)) {
fwrite($out, $buff);
}
} else {
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
}
fclose($in);
fclose($out);
} else {
die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
}
}
if ((isset($_GET['chunk']) && ($_GET['chunk'] + 1) == $_GET['chunks']) || (!isset($_GET['chunk']))) {
$source = 'temporary://'. $filename;
$file = new stdClass();
$file->uid = $GLOBALS['user']->uid;
$file->status = 0;
$file->filename = $filename;
$file->uri = $source;
$file->filemime = file_get_mimetype($file->filename);
$file->filesize = filesize($source);
// Save url argument(instance_id) so we can validate file by instance settings
$file->arg = arg(1);
$e = FALSE;
// Here we validate file
drupal_alter('plup_file_validation', $file, $e);
if ($e === FALSE) {
$return = file_save($file);
} else {
file_unmanaged_delete($file->uri);
$return = (object) array('error_message' => $e);
}
drupal_json_output($return);
}
}
/**
* Default validation function.
* In most cases Pluplaod will be used for images so we use image-specific validation.
* Non-image files has no support right now.
*/
function plup_plup_file_validation_alter(&$file, &$e) {
if (!isset($file->arg) || (isset($file->arg) && is_numeric($file->arg))) {
$instance = db_select('field_config_instance','i')->fields('i', array('data'))->condition('id', $file->arg)->execute()->fetchField();
$instance = unserialize($instance);
$settings = $instance['settings'];
// Check if file is image
$passImage = file_validate_is_image($file);
if (!empty($passImage)) {
$e = reset($passImage);
return;
}
// Check if file has allowed extension
$passExt = file_validate_extensions($file, $settings['file_extensions']);
if (!empty($passExt)) {
$e = reset($passExt);
return;
}
// Check if file doesn't exceed the maximal allowed size
$passSize = file_validate_size($file, parse_size($settings['max_filesize']), 0);
if (!empty($passSize)) {
$e = reset($passSize);
return;
}
// Check if file name isn't too long
$passLength = file_validate_name_length($file);
if (!empty($passLength)) {
$e = reset($passLength);
return;
}
// Check if file meet the resolution restrictions
$passRes = file_validate_image_resolution($file, $settings['max_resolution'], $settings['min_resolution']);
if (!empty($passRes)) {
$e = reset($passRes);
return;
}
}
}
/*********************************************
* FIELD WIDGET
********************************************/
/**
* _field_widget_info
*/
function plup_field_widget_info() {
return array(
'image_plupload' => array(
'label' => t('Plupload'),
'field types' => array('image'),
'behaviors' => array(
'multiple values' => FIELD_BEHAVIOR_CUSTOM,
'default value' => FIELD_BEHAVIOR_NONE,
),
),
);
}
/**
* _field_widget_settings_form
*/
function plup_field_widget_settings_form($field, $instance) {
$styles = array();
foreach (image_styles() as $name => $style) {
$styles[$name] = $style['name'];
}
$form['image_style'] = array(
'#type' => 'select',
'#title' => t('Image style'),
'#default_value' => isset($instance['widget']['settings']['image_style']) ? $instance['widget']['settings']['image_style'] : 'thumbnail',
'#options' => $styles
);
return $form;
}
/**
* _field_widget_form
*/
function plup_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
$parents = array_merge(array('values', $element['#field_name'], $element['#language']), $element['#field_parents']);
$state_data = drupal_array_get_nested_value($form_state, $parents);
if (isset($state_data)) {
$default_values = $state_data;
} else if (isset($items)) {
$default_values = $items;
} else {
$default_values = array();
}
// We inform user about restrictions
$info = array();
if ($field['cardinality'] > 0) {
$info[] = ''. t("You can upload up to !num files.", array('!num' => ''. $field['cardinality'] .' ')) .'
';
}
if (isset($instance['settings']['max_filesize']) && !empty($instance['settings']['max_filesize'])) {
$info[] = ''. t("Maximal file size: !size", array('!size' => ''. $instance['settings']['max_filesize'] .' ')) .'
';
}
$info[] = ''. t("Allowed files types: !types.", array('!types' => ''. $instance['settings']['file_extensions'] .' ')) .'
';
$max = $instance['settings']['max_resolution'];
$min = $instance['settings']['min_resolution'];
if ($min && $max && $min == $max) {
$info[] = ''. t('Images must be exactly !size pixels.', array('!size' => '' . $max . ' ')) .'
';
}
elseif ($min && $max) {
$info[] = ''. t('Images must be between !min and !max pixels.', array('!min' => '' . $min . ' ', '!max' => '' . $max . ' ')) .'
';
}
elseif ($min) {
$info[] = ''. t('Images must be larger than !min pixels.', array('!min' => '' . $min . ' ')) .'
';
}
elseif ($max) {
$info[] = ''. t('Images must be smaller than !max pixels.', array('!max' => '' . $max . ' ')) .'
';
}
$images = array(
'#type' => 'plupload_file',
'#default_value' => $default_values,
'#info' => $info
);
// Allowed file types(extensions) needs to be set here
$ext = new stdClass();
$ext->title = 'Allowed extensions'; // This won't show up anywhere so no t()
$ext->extensions = (isset($instance['settings']['file_extensions']) && !empty($instance['settings']['file_extensions'])) ? strtr($instance['settings']['file_extensions'], ' ', ',') : 'jpg,png,gif';
$images['#plup_override']['filters'] = array($ext);
// Maximal file size
if (isset($instance['settings']['max_filesize']) && !empty($instance['settings']['max_filesize'])) {
$images['#plup_override']['max_file_size'] = $instance['settings']['max_filesize'];
}
// URL callback for Plupload library has to be altered so we can get instance ID for later validation
$images['#plup_override']['url'] = url('plupload/'. $instance['id'], array('query' => array('plupload_token' => drupal_get_token('plupload-handle-uploads'))));
// We set image style to present uploaded images in Plupload widget
$images['#plup_override']['image_style'] = isset($instance['widget']['settings']['image_style']) ? $instance['widget']['settings']['image_style'] : 'thumbnail';
// We set if we want to enable title and alt fields for images
$images['#plup_override']['alt_field'] = (int) $instance['settings']['alt_field'];
$images['#plup_override']['title_field'] = (int) $instance['settings']['title_field'];
// We set the maximum files user can upload
$images['#plup_override']['max_files'] = (int) $field['cardinality'];
$element += $images;
return $element;
}
/**
* _field_attach_presave
* Somehow Drupal 7 doesn't have proper field hooks so we have to use
* this big global hook so we can move files to their desiganted folder
* because image module won't pick on #upload_location attribute(don't know why yet).
* We also rename files back to their original name.
*/
function plup_field_attach_presave($entity_type, $entity) {
$entityInfo = entity_get_info($entity_type);
$bundleKey = (isset($entityInfo['entity keys']['bundle']) && !empty($entityInfo['entity keys']['bundle'])) ? $entityInfo['entity keys']['bundle'] : $entity_type;
$bundle = isset($entity->{$bundleKey}) ? $entity->{$bundleKey} : $entity_type;
$instances = field_info_instances();
$entityFields = $instances[$entity_type][$bundle];
if (!is_array($entityFields) || empty($entityFields)) {
return;
}
foreach ($entityFields AS $field_name => $instance) {
if ($instance['widget']['type'] == 'image_plupload' && $instance['widget']['module'] == 'plup') {
$items = field_get_items($entity_type, $entity, $field_name);
if ($items && $items[0]['fid'] > 0) {
$field = field_info_field($instance['field_name']);
$destination = file_field_widget_uri($field, $instance);
foreach ($items AS $delta => $item) {
$file = file_load($item['fid']);
// Filefield paths integration
if (module_exists('filefield_paths') && isset($item['rename'])) {
$file->origname = $item['rename'];
file_save($file);
} else {
$fileName = isset($item['rename']) ? $item['rename'] : $file->filename;
$dir = isset($instance['settings']['file_directory']) ? $instance['settings']['file_directory'] .'/' : '';
$location = token_replace($field['settings']['uri_scheme'] .'://'. $dir);
$filePath = $location . $fileName;
if ($file->uri !== $filePath) {
file_prepare_directory($location, FILE_CREATE_DIRECTORY);
file_move($file, $filePath);
}
}
}
}
}
}
}