- Patch #115267 by drewish, dopry et al: simplified file uploads code, improved file API, centralized file validation, implemented quotas and fixed file previews.
parent
3568709803
commit
4fd54aabc5
|
@ -48,6 +48,9 @@ Drupal 6.0, xxxx-xx-xx (development version)
|
|||
* Tags are now automatically closed at the end of the teaser.
|
||||
- Performance:
|
||||
* Made it easier to conditionally load include files.
|
||||
- File handling improvements:
|
||||
* Entries in the files table are now keyed to a user, and not a node.
|
||||
* Added re-usable validation functions to check for uploaded file sizes, extensions, and image resolution.
|
||||
|
||||
Drupal 5.0, 2007-01-15
|
||||
----------------------
|
||||
|
|
|
@ -1053,7 +1053,7 @@ function language_list($field = 'language', $reset = FALSE) {
|
|||
|
||||
/**
|
||||
* Default language used on the site
|
||||
*
|
||||
*
|
||||
* @param $property
|
||||
* Optional property of the language object to return
|
||||
*/
|
||||
|
|
|
@ -20,6 +20,18 @@ define('FILE_EXISTS_RENAME', 0);
|
|||
define('FILE_EXISTS_REPLACE', 1);
|
||||
define('FILE_EXISTS_ERROR', 2);
|
||||
|
||||
/**
|
||||
* A files status can be one of two values: temorary or permanent. The status
|
||||
* for each file Drupal manages is stored in the {files} tables. If the status
|
||||
* is temporary Drupal's file garbage collection will delete the file and
|
||||
* remove it from the files table after a set period of time.
|
||||
*
|
||||
* If you wish to add custom statuses for use by contrib modules please expand as
|
||||
* binary flags and consider the first 8 bits reserved. (0,1,2,4,8,16,32,64,128)
|
||||
*/
|
||||
define('FILE_STATUS_TEMPORARY', 0);
|
||||
define('FILE_STATUS_PERMANENT', 1);
|
||||
|
||||
/**
|
||||
* Create the download path to a file.
|
||||
*
|
||||
|
@ -152,115 +164,6 @@ function file_check_path(&$path) {
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if $source is a valid file upload. If so, move the file to Drupal's tmp dir
|
||||
* and return it as an object.
|
||||
*
|
||||
* The use of SESSION['file_uploads'] should probably be externalized to upload.module
|
||||
*
|
||||
* @todo Rename file_check_upload to file_prepare upload.
|
||||
* @todo Refactor or merge file_save_upload.
|
||||
* @todo Extenalize SESSION['file_uploads'] to modules.
|
||||
*
|
||||
* @param $source An upload source (the name of the upload form item), or a file
|
||||
* @return FALSE for an invalid file or upload. A file object for valid uploads/files.
|
||||
*
|
||||
*/
|
||||
|
||||
function file_check_upload($source = 'upload') {
|
||||
// Cache for uploaded files. Since the data in _FILES is modified
|
||||
// by this function, we cache the result.
|
||||
static $upload_cache;
|
||||
|
||||
// Test source to see if it is an object.
|
||||
if (is_object($source)) {
|
||||
|
||||
// Validate the file path if an object was passed in instead of
|
||||
// an upload key.
|
||||
if (is_file($source->filepath)) {
|
||||
return $source;
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Return cached objects without processing since the file will have
|
||||
// already been processed and the paths in _FILES will be invalid.
|
||||
if (isset($upload_cache[$source])) {
|
||||
return $upload_cache[$source];
|
||||
}
|
||||
|
||||
// If a file was uploaded, process it.
|
||||
if (isset($_FILES["files"]) && $_FILES["files"]["name"][$source] && is_uploaded_file($_FILES["files"]["tmp_name"][$source])) {
|
||||
|
||||
// Check for file upload errors and return FALSE if a
|
||||
// lower level system error occurred.
|
||||
switch ($_FILES["files"]["error"][$source]) {
|
||||
|
||||
// @see http://php.net/manual/en/features.file-upload.errors.php
|
||||
case UPLOAD_ERR_OK:
|
||||
break;
|
||||
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
drupal_set_message(t('The file %file could not be saved, because it exceeds the maximum allowed size for uploads.', array('%file' => $source)), 'error');
|
||||
return 0;
|
||||
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
drupal_set_message(t('The file %file could not be saved, because the upload did not complete.', array('%file' => $source)), 'error');
|
||||
return 0;
|
||||
|
||||
// Unknown error
|
||||
default:
|
||||
drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $source)), 'error');
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Begin building file object.
|
||||
$file = new stdClass();
|
||||
$file->filename = trim(basename($_FILES["files"]["name"][$source]), '.');
|
||||
|
||||
// Create temporary name/path for newly uploaded files.
|
||||
$file->filepath = tempnam(file_directory_temp(), 'tmp_');
|
||||
|
||||
$file->filemime = $_FILES["files"]["type"][$source];
|
||||
|
||||
// Rename potentially executable files, to help prevent exploits.
|
||||
if (preg_match('/\.(php|pl|py|cgi|asp|js)$/i', $file->filename) && (substr($file->filename, -4) != '.txt')) {
|
||||
$file->filemime = 'text/plain';
|
||||
$file->filepath .= '.txt';
|
||||
$file->filename .= '.txt';
|
||||
}
|
||||
|
||||
// Move uploaded files from php's upload_tmp_dir to Drupal's file temp.
|
||||
// This overcomes open_basedir restrictions for future file operations.
|
||||
if (!move_uploaded_file($_FILES["files"]["tmp_name"][$source], $file->filepath)) {
|
||||
drupal_set_message(t('File upload error. Could not move uploaded file.'));
|
||||
watchdog('file', 'Upload Error. Could not move uploaded file (%file) to destination (%destination).', array('%file' => $_FILES["files"]["tmp_name"][$source], '%destination' => $file->filepath));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$file->filesize = $_FILES["files"]["size"][$source];
|
||||
$file->source = $source;
|
||||
|
||||
// Add processed file to the cache.
|
||||
$upload_cache[$source] = $file;
|
||||
return $file;
|
||||
}
|
||||
|
||||
else {
|
||||
// In case of previews return previous file object.
|
||||
if (isset($_SESSION['file_uploads']) && file_exists($_SESSION['file_uploads'][$source]->filepath)) {
|
||||
return $_SESSION['file_uploads'][$source];
|
||||
}
|
||||
}
|
||||
// If nothing was done, return FALSE.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a file is really located inside $directory. Should be used to make
|
||||
* sure a file specified is really located within the directory to prevent
|
||||
|
@ -347,29 +250,9 @@ function file_copy(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME) {
|
|||
// to copy it if they are. In fact copying the file will most likely result in
|
||||
// a 0 byte file. Which is bad. Real bad.
|
||||
if ($source != realpath($dest)) {
|
||||
if (file_exists($dest)) {
|
||||
switch ($replace) {
|
||||
case FILE_EXISTS_RENAME:
|
||||
// Destination file already exists and we can't replace is so we try and
|
||||
// and find a new filename.
|
||||
if ($pos = strrpos($basename, '.')) {
|
||||
$name = substr($basename, 0, $pos);
|
||||
$ext = substr($basename, $pos);
|
||||
}
|
||||
else {
|
||||
$name = $basename;
|
||||
}
|
||||
|
||||
$counter = 0;
|
||||
do {
|
||||
$dest = $directory .'/'. $name .'_'. $counter++ . $ext;
|
||||
} while (file_exists($dest));
|
||||
break;
|
||||
|
||||
case FILE_EXISTS_ERROR:
|
||||
drupal_set_message(t('The selected file %file could not be copied, because a file by that name already exists in the destination.', array('%file' => $source)), 'error');
|
||||
return 0;
|
||||
}
|
||||
if (!$dest = file_destination($dest, $replace)) {
|
||||
drupal_set_message(t('The selected file %file could not be copied, because a file by that name already exists in the destination.', array('%file' => $source)), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!@copy($source, $dest)) {
|
||||
|
@ -378,7 +261,9 @@ function file_copy(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME) {
|
|||
}
|
||||
|
||||
// Give everyone read access so that FTP'd users or
|
||||
// non-webserver users can see/read these files.
|
||||
// non-webserver users can see/read these files,
|
||||
// and give group write permissions so group memebers
|
||||
// can alter files uploaded by the webserver.
|
||||
@chmod($dest, 0664);
|
||||
}
|
||||
|
||||
|
@ -394,6 +279,36 @@ function file_copy(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME) {
|
|||
return 1; // Everything went ok.
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the destination path for a file depending on how replacement of
|
||||
* existing files should be handled.
|
||||
*
|
||||
* @param $destination A string specifying the desired path.
|
||||
* @param $replace Replace behavior when the destination file already exists.
|
||||
* - FILE_EXISTS_REPLACE - Replace the existing file
|
||||
* - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
|
||||
* unique
|
||||
* - FILE_EXISTS_ERROR - Do nothing and return FALSE.
|
||||
* @return The destination file path or FALSE if the file already exists and
|
||||
* FILE_EXISTS_ERROR was specified.
|
||||
*/
|
||||
function file_destination($destination, $replace) {
|
||||
if (file_exists($destination)) {
|
||||
switch ($replace) {
|
||||
case FILE_EXISTS_RENAME:
|
||||
$basename = basename($destination);
|
||||
$directory = dirname($destination);
|
||||
$destination = file_create_filename($basename, $directory);
|
||||
break;
|
||||
|
||||
case FILE_EXISTS_ERROR:
|
||||
drupal_set_message(t('The selected file %file could not be copied, because a file by that name already exists in the destination.', array('%file' => $source)), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return $destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a file to a new location.
|
||||
* - Checks if $source and $dest are valid and readable/writable.
|
||||
|
@ -413,7 +328,6 @@ function file_copy(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME) {
|
|||
* @return True for success, FALSE for failure.
|
||||
*/
|
||||
function file_move(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME) {
|
||||
|
||||
$path_original = is_object($source) ? $source->filepath : $source;
|
||||
|
||||
if (file_copy($source, $dest, $replace)) {
|
||||
|
@ -427,6 +341,59 @@ function file_move(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Munge the filename as needed for security purposes. For instance the file
|
||||
* name "exploit.php.pps" would become "exploit.php_.pps".
|
||||
*
|
||||
* @param $filename The name of a file to modify.
|
||||
* @param $extensions A space separated list of extensions that should not
|
||||
* be altered.
|
||||
* @param $alerts Whether alerts (watchdog, drupal_set_message()) should be
|
||||
* displayed.
|
||||
* @return $filename The potentially modified $filename.
|
||||
*/
|
||||
function file_munge_filename($filename, $extensions, $alerts = TRUE) {
|
||||
$original = $filename;
|
||||
|
||||
// Allow potentially insecure uploads for very savvy users and admin
|
||||
if (!variable_get('allow_insecure_uploads', 0)) {
|
||||
$whitelist = array_unique(explode(' ', trim($extensions)));
|
||||
|
||||
// Split the filename up by periods. The first part becomes the basename
|
||||
// the last part the final extension.
|
||||
$filename_parts = explode('.', $filename);
|
||||
$new_filename = array_shift($filename_parts); // Remove file basename.
|
||||
$final_extension = array_pop($filename_parts); // Remove final extension.
|
||||
|
||||
// Loop through the middle parts of the name and add an underscore to the
|
||||
// end of each section that could be a file extension but isn't in the list
|
||||
// of allowed extensions.
|
||||
foreach ($filename_parts as $filename_part) {
|
||||
$new_filename .= '.'. $filename_part;
|
||||
if (!in_array($filename_part, $whitelist) && preg_match("/^[a-zA-Z]{2,5}\d?$/", $filename_part)) {
|
||||
$new_filename .= '_';
|
||||
}
|
||||
}
|
||||
$filename = $new_filename .'.'. $final_extension;
|
||||
|
||||
if ($alerts && $original != $filename) {
|
||||
drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $filename)));
|
||||
}
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo the effect of upload_munge_filename().
|
||||
*
|
||||
* @param $filename string filename
|
||||
* @return string
|
||||
*/
|
||||
function file_unmunge_filename($filename) {
|
||||
return str_replace('_.', '.', $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a full file path from a directory and filename. If a file with the
|
||||
* specified name already exists, an alternative will be used.
|
||||
|
@ -461,7 +428,7 @@ function file_create_filename($basename, $directory) {
|
|||
* Delete a file.
|
||||
*
|
||||
* @param $path A string containing a file path.
|
||||
* @return True for success, FALSE for failure.
|
||||
* @return TRUE for success, FALSE for failure.
|
||||
*/
|
||||
function file_delete($path) {
|
||||
if (is_file($path)) {
|
||||
|
@ -469,46 +436,304 @@ function file_delete($path) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the total amount of disk space used by a single user's files, or
|
||||
* the filesystem as a whole.
|
||||
*
|
||||
* @param $uid An optional, user id. A NULL value returns the total space used
|
||||
* by all files.
|
||||
*/
|
||||
function file_space_used($uid = NULL) {
|
||||
if (is_null($uid)) {
|
||||
return db_result(db_query('SELECT SUM(filesize) FROM {files} WHERE uid = %d', $uid));
|
||||
}
|
||||
return db_result(db_query('SELECT SUM(filesize) FROM {files}'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a file upload to a new location. The source file is validated as a
|
||||
* proper upload and handled as such.
|
||||
*
|
||||
* @param $source A string specifying the name of the upload field to save.
|
||||
* This parameter will contain the resulting destination filename in case of
|
||||
* success.
|
||||
* @param $dest A string containing the directory $source should be copied to,
|
||||
* will use the temporary directory in case no other value is set.
|
||||
* @param $replace A boolean, set to TRUE if the destination should be replaced
|
||||
* when in use, but when FALSE append a _X to the filename.
|
||||
* @return An object containing file info or 0 in case of error.
|
||||
* The file will be added to the files table as a temporary file. Temorary files
|
||||
* are periodically cleaned. To make the file permanent file call
|
||||
* file_set_status() to change it's status.
|
||||
*
|
||||
* @param $source
|
||||
* A string specifying the name of the upload field to save.
|
||||
* @param $dest
|
||||
* A string containing the directory $source should be copied to. If this is
|
||||
* not provided, the temporary directory will be used.
|
||||
* @param $validators
|
||||
* An optional, associative array of callback functions used to validate the
|
||||
* file. The keys are function names and the values arrays of callback
|
||||
* parameters which will be passed in after the user and file objects. The
|
||||
* functions should return an array of error messages, an empty array
|
||||
* indicates that the file passed validation. The functions will be called in
|
||||
* the order specified.
|
||||
* @param $replace
|
||||
* A boolean indicating whether an existing file of the same name in the
|
||||
* destination directory should overwritten. A false value will generate a
|
||||
* new, unique filename in the destination directory.
|
||||
* @return
|
||||
* An object containing the file information, or 0 in the event of an error.
|
||||
*/
|
||||
function file_save_upload($source, $dest = FALSE, $replace = FILE_EXISTS_RENAME) {
|
||||
// Make sure $source exists && is valid.
|
||||
if ($file = file_check_upload($source)) {
|
||||
function file_save_upload($source, $validators = array(), $dest = FALSE, $replace = FILE_EXISTS_RENAME) {
|
||||
global $user;
|
||||
static $upload_cache;
|
||||
|
||||
// This should be refactored, file_check_upload has already
|
||||
// moved the file to the temporary folder.
|
||||
// Add in our check of the the file name length.
|
||||
$validators['file_validate_name_length'] = array();
|
||||
|
||||
// Return cached objects without processing since the file will have
|
||||
// already been processed and the paths in _FILES will be invalid.
|
||||
if (isset($upload_cache[$source])) {
|
||||
return $upload_cache[$source];
|
||||
}
|
||||
|
||||
// If a file was uploaded, process it.
|
||||
if (isset($_FILES['files']) && $_FILES['files']['name'][$source] && is_uploaded_file($_FILES['files']['tmp_name'][$source])) {
|
||||
// Check for file upload errors and return FALSE if a
|
||||
// lower level system error occurred.
|
||||
switch ($_FILES['files']['error'][$source]) {
|
||||
// @see http://php.net/manual/en/features.file-upload.errors.php
|
||||
case UPLOAD_ERR_OK:
|
||||
break;
|
||||
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
drupal_set_message(t('The file %file could not be saved, because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $source, '%maxsize' => format_size(file_upload_max_size()))), 'error');
|
||||
return 0;
|
||||
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
drupal_set_message(t('The file %file could not be saved, because the upload did not complete.', array('%file' => $source)), 'error');
|
||||
return 0;
|
||||
|
||||
// Unknown error
|
||||
default:
|
||||
drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $source)), 'error');
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Build the list of non-munged extensions.
|
||||
// @todo: this should not be here. we need to figure out the right place.
|
||||
$extensions = '';
|
||||
foreach ($user->roles as $rid => $name) {
|
||||
$extensions .= ' '. variable_get("upload_extensions_$rid",
|
||||
variable_get('upload_extensions_default', 'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp'));
|
||||
}
|
||||
|
||||
// Begin building file object.
|
||||
$file = new stdClass();
|
||||
$file->filename = file_munge_filename(trim(basename($_FILES['files']['name'][$source]), '.'), $extensions);
|
||||
$file->filepath = $_FILES['files']['tmp_name'][$source];
|
||||
$file->filemime = $_FILES['files']['type'][$source];
|
||||
|
||||
// Rename potentially executable files, to help prevent exploits.
|
||||
if (preg_match('/\.(php|pl|py|cgi|asp|js)$/i', $file->filename) && (substr($file->filename, -4) != '.txt')) {
|
||||
$file->filemime = 'text/plain';
|
||||
$file->filepath .= '.txt';
|
||||
$file->filename .= '.txt';
|
||||
}
|
||||
|
||||
// Create temporary name/path for newly uploaded files.
|
||||
if (!$dest) {
|
||||
$dest = file_directory_temp();
|
||||
$temporary = 1;
|
||||
if (is_file($file->filepath)) {
|
||||
// If this file was uploaded by this user before replace the temporary copy.
|
||||
$replace = FILE_EXISTS_REPLACE;
|
||||
}
|
||||
$dest = file_destination(file_create_path($file->filename), FILE_EXISTS_RENAME);
|
||||
}
|
||||
$file->source = $source;
|
||||
$file->destination = $dest;
|
||||
$file->filesize = $_FILES['files']['size'][$source];
|
||||
|
||||
// Call the validation functions.
|
||||
$errors = array();
|
||||
foreach ($validators as $function => $args) {
|
||||
array_unshift($args, $file);
|
||||
$errors = array_merge($errors, call_user_func_array($function, $args));
|
||||
}
|
||||
|
||||
unset($_SESSION['file_uploads'][is_object($source) ? $source->source : $source]);
|
||||
if (file_move($file, $dest, $replace)) {
|
||||
if ($temporary) {
|
||||
$_SESSION['file_uploads'][is_object($source) ? $source->source : $source] = $file;
|
||||
// Check for validation errors.
|
||||
if (!empty($errors)) {
|
||||
$message = t('The selected file %name could not be uploaded. ', array('%name' => $file->filename));
|
||||
if (count($errors) > 1) {
|
||||
$message .= '<ul><li>'. implode('</li><li>', $errors) .'</li></ul>';
|
||||
}
|
||||
return $file;
|
||||
else {
|
||||
$message .= array_pop($errors);
|
||||
}
|
||||
form_set_error($source, $message);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
|
||||
// Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary directory.
|
||||
// This overcomes open_basedir restrictions for future file operations.
|
||||
$file->filepath = $file->destination;
|
||||
if (!move_uploaded_file($_FILES['files']['tmp_name'][$source], $file->filepath)) {
|
||||
form_set_error($source, t('File upload error. Could not move uploaded file.'));
|
||||
watchdog('file', t('Upload error. Could not move uploaded file %file to destination %destination.', array('%file' => $file->filename, '%destination', $file->filepath)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If we made it this far it's safe to record this file in the database.
|
||||
$file->fid = db_next_id('fid');
|
||||
db_query("INSERT INTO {files} (fid, uid, filename, filepath, filemime, filesize, status, timestamp) VALUES (%d, %d, '%s', '%s', '%s', %d, %d, %d)", $file->fid, $user->uid, $file->filename, $file->filepath, $file->filemime, $file->filesize, FILE_STATUS_TEMPORARY, time());
|
||||
|
||||
// Add file to the cache.
|
||||
$upload_cache[$source] = $file;
|
||||
return $file;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for files with names longer than we can store in the database.
|
||||
*
|
||||
* @param $file
|
||||
* A Drupal file object.
|
||||
* @return
|
||||
* An array. If the file name is too long, it will contain an error message.
|
||||
*/
|
||||
function file_validate_name_length($file) {
|
||||
$errors = array();
|
||||
|
||||
if (strlen($file->filename) > 255) {
|
||||
$errors[] = t('Its name exceeds the 255 characters limit. Please rename the file and try again.');
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the filename ends with an allowed extension. This check is not
|
||||
* enforced for the user #1.
|
||||
*
|
||||
* @param $file
|
||||
* A Drupal file object.
|
||||
* @param $extensions
|
||||
* A string with a space separated
|
||||
* @return
|
||||
* An array. If the file name is too long, it will contain an error message.
|
||||
*/
|
||||
function file_validate_extensions($file, $extensions) {
|
||||
global $user;
|
||||
|
||||
$errors = array();
|
||||
|
||||
// Bypass validation for uid = 1.
|
||||
if ($user->uid != 1) {
|
||||
$regex = '/\.('. ereg_replace(' +', '|', preg_quote($extensions)) .')$/i';
|
||||
if (!preg_match($regex, $file->filename)) {
|
||||
$errors[] = t('Only files with the following extensions are allowed: %files-allowed.', array('%files-allowed' => $extensions));
|
||||
}
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the file's size is below certain limits. This check is not
|
||||
* enforced for the user #1.
|
||||
*
|
||||
* @param $file
|
||||
* A Drupal file object.
|
||||
* @param $file_limit
|
||||
* An integer specifying the maximum file size in bytes. Zero indicates that
|
||||
* no limit should be enforced.
|
||||
* @param $$user_limit
|
||||
* An integer specifying the maximum number of bytes the user is allowed. Zero
|
||||
* indicates that no limit should be enforced.
|
||||
* @return
|
||||
* An array. If the file name is too long, it will contain an error message.
|
||||
*/
|
||||
function file_validate_size($file, $file_limit = 0, $user_limit = 0) {
|
||||
global $user;
|
||||
|
||||
$errors = array();
|
||||
|
||||
// Bypass validation for uid = 1.
|
||||
if ($user->uid != 1) {
|
||||
if ($file_limit && $file->filesize > $file_limit) {
|
||||
$errors[] = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size($file->filesize), '%maxsize' => format_size($file_limit)));
|
||||
}
|
||||
|
||||
$total_size = file_space_used($user->uid) + $file->filesize;
|
||||
if ($user_limit && $total_size > $user_limit) {
|
||||
$errors[] = t('The file is %filesize which would exceed your disk quota of %quota.', array('%filesize' => format_size($file->filesize), '%quota' => format_size($user_limit)));
|
||||
}
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the file is recognized by image_get_info() as an image.
|
||||
*
|
||||
* @param $file
|
||||
* A Drupal file object.
|
||||
* @return
|
||||
* An array. If the file is not an image, it will contain an error message.
|
||||
*/
|
||||
function file_validate_is_image(&$file) {
|
||||
$errors = array();
|
||||
|
||||
$info = image_get_info($file->filepath);
|
||||
if (!$info || empty($info['extension'])) {
|
||||
$errors[] = t('Only JPEG, PNG and GIF images are allowed.');
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the file is an image verify that its dimensions are within the specified
|
||||
* maximum and minimum dimensions. Non-image files will be ignored.
|
||||
*
|
||||
* @param $file
|
||||
* A Drupal file object. This function may resize the file affecting its size.
|
||||
* @param $maximum_dimensions
|
||||
* An optional string in the form WIDTHxHEIGHT e.g. '640x480' or '85x85'. If
|
||||
* an image toolkit is installed the image will be resized down to these
|
||||
* dimensions. A value of 0 indicates no restriction on size, so resizing
|
||||
* will be attempted.
|
||||
* @param $minimum_dimensions
|
||||
* An optional string in the form WIDTHxHEIGHT. This will check that the image
|
||||
* meets a minimum size. A value of 0 indicates no restriction.
|
||||
* @return
|
||||
* An array. If the file is an image and did not meet the requirements, it
|
||||
* will contain an error message.
|
||||
*/
|
||||
function file_validate_image_resolution(&$file, $maximum_dimensions = 0, $minimum_dimensions = 0) {
|
||||
$errors = array();
|
||||
|
||||
// Check first that the file is an image.
|
||||
if ($info = image_get_info($file->filepath)) {
|
||||
if ($maximum_dimensions) {
|
||||
// Check that it is smaller than the given dimensions.
|
||||
list($width, $height) = explode('x', $maximum_dimensions);
|
||||
if ($info['width'] > $width || $info['height'] > $height) {
|
||||
// Try to resize the image to fit the dimensions.
|
||||
if (image_get_toolkit() && image_scale($file->filepath, $file->filepath, $width, $height)) {
|
||||
drupal_set_message(t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', array('%dimensions' => $maximum_dimensions)));
|
||||
|
||||
// Clear the cached filesize and refresh the image information.
|
||||
clearstatcache();
|
||||
$info = image_get_info($file->filepath);
|
||||
$file->filesize = $info['file_size'];
|
||||
}
|
||||
else {
|
||||
$errors[] = t('The image is too large; the maximum dimensions are %dimensions pixels.', array('%dimensions' => $maximum_dimensions));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($minimum_dimensions) {
|
||||
// Check that it is larger than the given dimensions.
|
||||
list($width, $height) = explode('x', $minimum_dimensions);
|
||||
if ($info['width'] < $width || $info['height'] < $maxheight) {
|
||||
$errors[] = t('The image is too small; the minimum dimensions are %dimensions pixels.', array('%dimensions' => $minimum_dimensions));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a string to the specified destination.
|
||||
*
|
||||
|
@ -538,6 +763,22 @@ function file_save_data($data, $dest, $replace = FILE_EXISTS_RENAME) {
|
|||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status of a file.
|
||||
*
|
||||
* @param file A Drupal file object
|
||||
* @param status A status value to set the file to.
|
||||
* @return FALSE on failure, TRUE on success and $file->status will contain the
|
||||
* status.
|
||||
*/
|
||||
function file_set_status(&$file, $status) {
|
||||
if (db_query('UPDATE {files} SET status = %d WHERE fid = %d', $status, $file->fid)) {
|
||||
$file->status = $status;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer file using http to client. Pipes a file through Drupal to the
|
||||
* client.
|
||||
|
@ -578,7 +819,6 @@ function file_transfer($source, $headers) {
|
|||
* returned headers the download will start with the returned headers. If no
|
||||
* modules respond drupal_not_found() will be returned.
|
||||
*/
|
||||
|
||||
function file_download() {
|
||||
// Merge remainder of arguments from GET['q'], into relative file path.
|
||||
$args = func_get_args();
|
||||
|
@ -592,10 +832,10 @@ function file_download() {
|
|||
if (file_exists(file_create_path($filepath))) {
|
||||
$headers = module_invoke_all('file_download', $filepath);
|
||||
if (in_array(-1, $headers)) {
|
||||
return drupal_access_denied();
|
||||
return drupal_access_denied();
|
||||
}
|
||||
if (count($headers)) {
|
||||
file_transfer($filepath, $headers);
|
||||
file_transfer($filepath, $headers);
|
||||
}
|
||||
}
|
||||
return drupal_not_found();
|
||||
|
|
|
@ -612,7 +612,7 @@ function locale_translate_import_form() {
|
|||
*/
|
||||
function locale_translate_import_form_submit($form, &$form_state, $form_values) {
|
||||
// Ensure we have the file uploaded
|
||||
if ($file = file_check_upload('file')) {
|
||||
if ($file = file_save_upload('file')) {
|
||||
|
||||
// Add language, if not yet supported
|
||||
$languages = language_list('language', TRUE);
|
||||
|
|
|
@ -1080,7 +1080,7 @@ function aggregator_page_category() {
|
|||
|
||||
drupal_add_feed(url('aggregator/rss/'. arg(2)), variable_get('site_name', 'Drupal') .' '. t('aggregator - @title', array('@title' => $category->title)));
|
||||
|
||||
return _aggregator_page_list('SELECT i.*, f.title AS ftitle, f.link AS flink FROM {aggregator_category_item} c LEFT JOIN {aggregator_item} i ON c.iid = i.iid LEFT JOIN {aggregator_feed} f ON i.fid = f.fid WHERE cid = '. $category->cid .' ORDER BY timestamp DESC, iid DESC', arg(3));
|
||||
return _aggregator_page_list('SELECT i.*, f.title AS ftitle, f.link AS flink FROM {aggregator_category_item} c LEFT JOIN {aggregator_item} i ON c.iid = i.iid LEFT JOIN {aggregator_feed} f ON i.fid = f.fid WHERE cid = '. $category->cid .' ORDER BY timestamp DESC, i.iid DESC', arg(3));
|
||||
}
|
||||
|
||||
function aggregator_page_list($sql, $header, $categorize) {
|
||||
|
@ -1223,7 +1223,7 @@ function aggregator_page_rss() {
|
|||
$category = db_fetch_object(db_query('SELECT cid, title FROM {aggregator_category} WHERE cid = %d', arg(2)));
|
||||
$url = '/categories/'. $category->cid;
|
||||
$title = ' '. t('in category') .' '. $category->title;
|
||||
$sql = 'SELECT i.*, f.title AS ftitle, f.link AS flink FROM {aggregator_category_item} c LEFT JOIN {aggregator_item} i ON c.iid = i.iid LEFT JOIN {aggregator_feed} f ON i.fid = f.fid WHERE cid = %d ORDER BY timestamp DESC, iid DESC';
|
||||
$sql = 'SELECT i.*, f.title AS ftitle, f.link AS flink FROM {aggregator_category_item} c LEFT JOIN {aggregator_item} i ON c.iid = i.iid LEFT JOIN {aggregator_feed} f ON i.fid = f.fid WHERE cid = %d ORDER BY timestamp DESC, i.iid DESC';
|
||||
$result = db_query_range($sql, $category->cid, 0, variable_get('feed_default_items', 10));
|
||||
}
|
||||
// or, get the default aggregator items
|
||||
|
|
|
@ -366,7 +366,7 @@ function locale_refresh_cache() {
|
|||
* Returns plural form index for a specific number.
|
||||
*
|
||||
* The index is computed from the formula of this language.
|
||||
*
|
||||
*
|
||||
* @param $count
|
||||
* Number to return plural for.
|
||||
* @param $langcode
|
||||
|
@ -378,7 +378,7 @@ function locale_get_plural($count, $langcode = NULL) {
|
|||
static $locale_formula, $plurals = array();
|
||||
|
||||
$langcode = $langcode ? $langcode : $language->language;
|
||||
|
||||
|
||||
if (!isset($plurals[$langcode][$count])) {
|
||||
if (!isset($locale_formula)) {
|
||||
$language_list = language_list();
|
||||
|
|
|
@ -3306,6 +3306,39 @@ function system_update_6021() {
|
|||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update files tables to associate files to a uid by default instead of a nid.
|
||||
* Rename file_revisions to upload since it should only be used by the upload
|
||||
* module used by upload to link files to nodes.
|
||||
*/
|
||||
function system_update_6022() {
|
||||
$ret = array();
|
||||
|
||||
// Rename the nid field to vid, add status and timestamp fields, and indexes.
|
||||
db_drop_index($ret, 'files', 'nid');
|
||||
db_change_field($ret, 'files', 'nid', 'uid', array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0));
|
||||
db_add_field($ret, 'files', 'status', array('type' => 'int', 'not null' => TRUE, 'default' => 0));
|
||||
db_add_field($ret, 'files', 'timestamp', array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0));
|
||||
db_add_index($ret, 'files', 'uid', array('uid'));
|
||||
db_add_index($ret, 'files', 'status', array('status'));
|
||||
db_add_index($ret, 'files', 'timestamp', array('timestamp'));
|
||||
|
||||
// Rename the file_revisions table to upload then add nid column. Since we're
|
||||
// changing the table name we need to drop and re-add the vid index so both
|
||||
// pgsql ends up with the corect index name.
|
||||
db_drop_index($ret, 'file_revisions', 'vid');
|
||||
db_rename_table($ret, 'file_revisions', 'upload');
|
||||
db_add_field($ret, 'upload', 'nid', array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0));
|
||||
db_add_index($ret, 'upload', 'nid', array('nid'));
|
||||
db_add_index($ret, 'upload', 'vid', array('vid'));
|
||||
|
||||
// The nid column was renamed to uid. Use the old nid to find the node's uid.
|
||||
$ret[] = update_sql('UPDATE {files} f JOIN {node} n ON f.uid = n.nid SET f.uid = n.uid');
|
||||
// Use the existing vid to find the nid.
|
||||
$ret[] = update_sql('UPDATE {upload} u JOIN {node_revisions} r ON u.vid = r.vid SET u.nid = r.nid');
|
||||
|
||||
return $ret;
|
||||
}
|
||||
/**
|
||||
* @} End of "defgroup updates-5.x-to-6.x"
|
||||
* The next series of updates should start at 7000.
|
||||
|
|
|
@ -13,6 +13,9 @@ define('DRUPAL_MINIMUM_MYSQL', '4.1.0'); // If using MySQL
|
|||
define('DRUPAL_MINIMUM_PGSQL', '7.4'); // If using PostgreSQL
|
||||
define('DRUPAL_MINIMUM_APACHE', '1.3'); // If using Apache
|
||||
|
||||
// Maximum age of temporary files in seconds.
|
||||
define('DRUPAL_MAXIMUM_TEMP_FILE_AGE', 1440);
|
||||
|
||||
/**
|
||||
* Implementation of hook_help().
|
||||
*/
|
||||
|
@ -83,7 +86,7 @@ function system_theme() {
|
|||
* Implementation of hook_perm().
|
||||
*/
|
||||
function system_perm() {
|
||||
return array('administer site configuration', 'access administration pages', 'select different theme');
|
||||
return array('administer site configuration', 'access administration pages', 'select different theme', 'administer files');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2171,28 +2174,29 @@ function system_theme_settings($key = '') {
|
|||
$form['var'] = array('#type' => 'hidden', '#value' => $var);
|
||||
|
||||
// Check for a new uploaded logo, and use that instead.
|
||||
if ($file = file_check_upload('logo_upload')) {
|
||||
if ($info = image_get_info($file->filepath)) {
|
||||
$parts = pathinfo($file->filename);
|
||||
$filename = ($key) ? str_replace('/', '_', $key) .'_logo.'. $parts['extension'] : 'logo.'. $parts['extension'];
|
||||
if ($file = file_save_upload('logo_upload', array('file_validate_is_image' => array()))) {
|
||||
$parts = pathinfo($file->filename);
|
||||
$filename = ($key) ? str_replace('/', '_', $key) .'_logo.'. $parts['extension'] : 'logo.'. $parts['extension'];
|
||||
|
||||
if ($file = file_save_upload('logo_upload', $filename, 1)) {
|
||||
$_POST['default_logo'] = 0;
|
||||
$_POST['logo_path'] = $file->filepath;
|
||||
$_POST['toggle_logo'] = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
form_set_error('file_upload', t('Only JPEG, PNG and GIF images are allowed to be used as logos.'));
|
||||
// The image was saved using file_save_upload() and was added to the
|
||||
// files table as a temorary file. We'll make a copy and let the garbage
|
||||
// collector delete the original upload.
|
||||
if (file_copy($file, $filename, FILE_EXISTS_REPLACE)) {
|
||||
$_POST['default_logo'] = 0;
|
||||
$_POST['logo_path'] = $file->filepath;
|
||||
$_POST['toggle_logo'] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for a new uploaded favicon, and use that instead.
|
||||
if ($file = file_check_upload('favicon_upload')) {
|
||||
if ($file = file_save_upload('favicon_upload')) {
|
||||
$parts = pathinfo($file->filename);
|
||||
$filename = ($key) ? str_replace('/', '_', $key) .'_favicon.'. $parts['extension'] : 'favicon.'. $parts['extension'];
|
||||
|
||||
if ($file = file_save_upload('favicon_upload', $filename, 1)) {
|
||||
// The image was saved using file_save_upload() and was added to the
|
||||
// files table as a temorary file. We'll make a copy and let the garbage
|
||||
// collector delete the original upload.
|
||||
if (file_copy($file, $filename)) {
|
||||
$_POST['default_favicon'] = 0;
|
||||
$_POST['favicon_path'] = $file->filepath;
|
||||
$_POST['toggle_favicon'] = 1;
|
||||
|
@ -2636,13 +2640,27 @@ function theme_system_admin_by_module($menu_items) {
|
|||
/**
|
||||
* Implementation of hook_cron().
|
||||
*
|
||||
* Remove older rows from flood table
|
||||
* Remove older rows from flood and batch table. Remove old temporary files.
|
||||
*/
|
||||
function system_cron() {
|
||||
// Cleanup the flood
|
||||
// Cleanup the flood.
|
||||
db_query('DELETE FROM {flood} WHERE timestamp < %d', time() - 3600);
|
||||
// Cleanup the batch table
|
||||
// Cleanup the batch table.
|
||||
db_query('DELETE FROM {batch} WHERE timestamp < %d', time() - 864000);
|
||||
|
||||
// Remove temporary files that are older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
|
||||
$result = db_query('SELECT * FROM {files} WHERE status = %s and timestamp < %d', FILE_STATUS_TEMPORARY, time() - DRUPAL_MAXIMUM_TEMP_FILE_AGE);
|
||||
while ($file = db_fetch_object($result)) {
|
||||
if (file_exists($file->filepath)) {
|
||||
// If files that exist cannot be deleted, continue so the database remains
|
||||
// consistant.
|
||||
if (!file_delete($file->filepath)) {
|
||||
watchdog('file system', t('Could not delete temporary file "%path" during garbage collection', array('%path' => $file->filepath)), 'error');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
db_query('DELETE FROM {files} WHERE fid = %d', $file->fid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,28 +32,23 @@ function system_schema() {
|
|||
|
||||
$schema['files'] = array(
|
||||
'fields' => array(
|
||||
'fid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
|
||||
'nid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
|
||||
'filename' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
|
||||
'filepath' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
|
||||
'filemime' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
|
||||
'filesize' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0)
|
||||
'fid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
|
||||
'uid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
|
||||
'filename' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
|
||||
'filepath' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
|
||||
'filemime' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
|
||||
'filesize' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
|
||||
'status' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
|
||||
'timestamp' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
|
||||
),
|
||||
'indexes' => array(
|
||||
'uid' => array('uid'),
|
||||
'status' => array('status'),
|
||||
'timestamp' => array('timestamp'),
|
||||
),
|
||||
'indexes' => array('nid' => array('nid')),
|
||||
'primary key' => array('fid'),
|
||||
);
|
||||
|
||||
$schema['file_revisions'] = array(
|
||||
'fields' => array(
|
||||
'fid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
|
||||
'vid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
|
||||
'description' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
|
||||
'list' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'size' => 'tiny')
|
||||
),
|
||||
'primary key' => array('fid', 'vid'),
|
||||
'indexes' => array('vid' => array('vid')),
|
||||
);
|
||||
|
||||
$schema['flood'] = array(
|
||||
'fields' => array(
|
||||
'fid' => array('type' => 'serial', 'not null' => TRUE),
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* Implementation of hook_install().
|
||||
*/
|
||||
function upload_install() {
|
||||
// Create tables.
|
||||
drupal_install_schema('upload');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_uninstall().
|
||||
*/
|
||||
function upload_uninstall() {
|
||||
// Remove tables.
|
||||
drupal_uninstall_schema('upload');
|
||||
}
|
|
@ -94,28 +94,9 @@ function upload_menu() {
|
|||
}
|
||||
|
||||
function upload_menu_alter(&$items) {
|
||||
$items['system/files']['page callback'] = 'upload_download';
|
||||
$items['system/files']['access arguments'] = array('view uploaded files');
|
||||
}
|
||||
|
||||
function upload_init() {
|
||||
if (arg(0) == 'system' && arg(1) == 'files' && isset($_SESSION['file_previews'])) {
|
||||
$item = menu_get_item('system/files');
|
||||
foreach ($_SESSION['file_previews'] as $fid => $file) {
|
||||
$filename = file_create_filename($file->filename, file_create_path());
|
||||
if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PRIVATE) {
|
||||
// strip file_directory_path() from filename. @see file_create_url
|
||||
if (strpos($filename, file_directory_path()) !== FALSE) {
|
||||
$filename = trim(substr($filename, strlen(file_directory_path())), '\\/');
|
||||
}
|
||||
$filename = 'system/files/'. $filename;
|
||||
}
|
||||
$_SESSION['file_previews'][$fid]->_filename = $filename;
|
||||
menu_set_item($filename, $item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form API callback to validate the upload settings form.
|
||||
*/
|
||||
|
@ -139,11 +120,11 @@ function upload_admin_settings_validate($form, &$form_state, $form_values) {
|
|||
form_set_error('upload_usersize_default', t('The %role file size limit must be a number and greater than zero.', array('%role' => t('default'))));
|
||||
}
|
||||
if ($default_uploadsize > file_upload_max_size()) {
|
||||
form_set_error('upload_uploadsize_default', $exceed_max_msg . $more_info);
|
||||
$more_info = '';
|
||||
form_set_error('upload_uploadsize_default', $exceed_max_msg . $more_info);
|
||||
$more_info = '';
|
||||
}
|
||||
if ($default_uploadsize > $default_usersize) {
|
||||
form_set_error('upload_uploadsize_default', t('The %role maximum file size per upload is greater than the total file size allowed per user', array('%role' => t('default'))));
|
||||
form_set_error('upload_uploadsize_default', t('The %role maximum file size per upload is greater than the total file size allowed per user', array('%role' => t('default'))));
|
||||
}
|
||||
|
||||
foreach ($form_values['roles'] as $rid => $role) {
|
||||
|
@ -157,11 +138,11 @@ function upload_admin_settings_validate($form, &$form_state, $form_values) {
|
|||
form_set_error('upload_usersize_'. $rid, t('The %role file size limit must be a number and greater than zero.', array('%role' => $role)));
|
||||
}
|
||||
if ($uploadsize > file_upload_max_size()) {
|
||||
form_set_error('upload_uploadsize_'. $rid, $exceed_max_msg . $more_info);
|
||||
$more_info = '';
|
||||
form_set_error('upload_uploadsize_'. $rid, $exceed_max_msg . $more_info);
|
||||
$more_info = '';
|
||||
}
|
||||
if ($uploadsize > $usersize) {
|
||||
form_set_error('upload_uploadsize_'. $rid, t('The %role maximum file size per upload is greater than the total file size allowed per user', array('%role' => $role)));
|
||||
form_set_error('upload_uploadsize_'. $rid, t('The %role maximum file size per upload is greater than the total file size allowed per user', array('%role' => $role)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +166,7 @@ function upload_admin_settings() {
|
|||
'#default_value' => variable_get('upload_max_resolution', 0),
|
||||
'#size' => 15,
|
||||
'#maxlength' => 10,
|
||||
'#description' => t('The maximum allowed image size (e.g. 640x480). Set to 0 for no restriction.'),
|
||||
'#description' => t('The maximum allowed image size (e.g. 640x480). Set to 0 for no restriction. If an <a href="!image-toolkit-link">image toolkit</a> is installed, files exceeding this value will be scalled down to fit.', array('!image-toolkit-link' => url('admin/settings/image-toolkit'))),
|
||||
'#field_suffix' => '<kbd>'. t('WIDTHxHEIGHT') .'</kbd>'
|
||||
);
|
||||
$form['settings_general']['upload_list_default'] = array(
|
||||
|
@ -209,8 +190,8 @@ function upload_admin_settings() {
|
|||
'#default_value' => $upload_uploadsize_default,
|
||||
'#size' => 5,
|
||||
'#maxlength' => 5,
|
||||
'#description' => t('The default maximum file size a user can upload.'),
|
||||
'#field_suffix' => t('MB')
|
||||
'#description' => t('The default maximum file size a user can upload. If an image is uploaded and a maximum resolution is set, the size will be checked after the file has been resized.'),
|
||||
'#field_suffix' => t('MB'),
|
||||
);
|
||||
$form['settings_general']['upload_usersize_default'] = array(
|
||||
'#type' => 'textfield',
|
||||
|
@ -219,7 +200,7 @@ function upload_admin_settings() {
|
|||
'#size' => 5,
|
||||
'#maxlength' => 5,
|
||||
'#description' => t('The default maximum size of all files a user can have on the site.'),
|
||||
'#field_suffix' => t('MB')
|
||||
'#field_suffix' => t('MB'),
|
||||
);
|
||||
|
||||
$form['settings_general']['upload_max_size'] = array('#value' => '<p>'. t('Your PHP settings limit the maximum file size per upload to %size.', array('%size' => format_size(file_upload_max_size()))) .'</p>');
|
||||
|
@ -247,7 +228,8 @@ function upload_admin_settings() {
|
|||
'#default_value' => variable_get('upload_uploadsize_'. $rid, $upload_uploadsize_default),
|
||||
'#size' => 5,
|
||||
'#maxlength' => 5,
|
||||
'#description' => t('The maximum size of a file a user can upload (in megabytes).'),
|
||||
'#description' => t('The maximum size of a file a user can upload. If an image is uploaded and a maximum resolution is set, the size will be checked after the file has been resized.'),
|
||||
'#field_suffix' => t('MB'),
|
||||
);
|
||||
$form['settings_role_'. $rid]['upload_usersize_'. $rid] = array(
|
||||
'#type' => 'textfield',
|
||||
|
@ -255,96 +237,117 @@ function upload_admin_settings() {
|
|||
'#default_value' => variable_get('upload_usersize_'. $rid, $upload_usersize_default),
|
||||
'#size' => 5,
|
||||
'#maxlength' => 5,
|
||||
'#description' => t('The maximum size of all files a user can have on the site (in megabytes).'),
|
||||
'#description' => t('The maximum size of all files a user can have on the site.'),
|
||||
'#field_suffix' => t('MB'),
|
||||
);
|
||||
}
|
||||
|
||||
return system_settings_form($form);
|
||||
}
|
||||
|
||||
function upload_download() {
|
||||
foreach ($_SESSION['file_previews'] as $file) {
|
||||
if ($file->_filename == $_GET['q']) {
|
||||
file_transfer($file->filepath, array('Content-Type: '. mime_header_encode($file->filemime), 'Content-Length: '. $file->filesize));
|
||||
}
|
||||
/**
|
||||
* Determine the limitations on files that a given user may upload. The user
|
||||
* may be in multiple roles so we select the most permissive limitations from
|
||||
* all of their roles.
|
||||
*
|
||||
* @param $user
|
||||
* A Drupal user object.
|
||||
* @return
|
||||
* An associative array with the following keys:
|
||||
* 'extensions'
|
||||
* A white space separated string containing all the file extensions this
|
||||
* user may upload.
|
||||
* 'file_size'
|
||||
* The maximum size of a file upload in bytes.
|
||||
* 'user_size'
|
||||
* The total number of bytes for all for a user's files.
|
||||
* 'resolution'
|
||||
* A string specifying the maximum resolution of images.
|
||||
*/
|
||||
function _upload_file_limits($user) {
|
||||
$file_limit = variable_get('upload_uploadsize_default', 1);
|
||||
$user_limit = variable_get('upload_usersize_default', 1);
|
||||
$all_extensions = explode(' ', variable_get('upload_extensions_default', 'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp'));
|
||||
foreach ($user->roles as $rid => $name) {
|
||||
$extensions = variable_get("upload_extensions_$rid", variable_get('upload_extensions_default', 'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp'));
|
||||
$all_extensions = array_merge($all_extensions, explode(' ', $extensions));
|
||||
|
||||
// A zero value indicates no limit, take the least restrictive limit.
|
||||
$file_size = variable_get("upload_uploadsize_$rid", variable_get('upload_uploadsize_default', 1)) * 1024 * 1024;
|
||||
$file_limit = ($file_limit && $file_size) ? max($file_limit, $file_size) : 0;
|
||||
|
||||
$user_size = variable_get("upload_usersize_$rid", variable_get('upload_usersize_default', 1)) * 1024 * 1024;
|
||||
$user_limit = ($user_limit && $user_size) ? max($user_limit, $user_size) : 0;
|
||||
}
|
||||
$all_extensions = implode(' ', array_unique($all_extensions));
|
||||
return array(
|
||||
'extensions' => $all_extensions,
|
||||
'file_size' => $file_limit,
|
||||
'user_size' => $user_limit,
|
||||
'resolution' => variable_get('upload_max_resolution', 0),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_file_download().
|
||||
*/
|
||||
function upload_file_download($file) {
|
||||
if (!user_access('view uploaded files')) {
|
||||
return -1;
|
||||
}
|
||||
$file = file_create_path($file);
|
||||
$result = db_query("SELECT f.* FROM {files} f WHERE filepath = '%s'", $file);
|
||||
$result = db_query("SELECT f.* FROM {files} f INNER JOIN {upload} u ON f.fid = u.uid WHERE filepath = '%s'", $file);
|
||||
if ($file = db_fetch_object($result)) {
|
||||
if (user_access('view uploaded files')) {
|
||||
$node = node_load($file->nid);
|
||||
if (node_access('view', $node)) {
|
||||
$type = mime_header_encode($file->filemime);
|
||||
return array(
|
||||
'Content-Type: '. $type,
|
||||
'Content-Length: '. $file->filesize,
|
||||
);
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
return array(
|
||||
'Content-Type: '. $file->filemime,
|
||||
'Content-Length: '. $file->filesize,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save new uploads and attach them to the node object.
|
||||
* append file_previews to the node object as well.
|
||||
* Save new uploads and store them in the session to be associated to the node
|
||||
* on upload_save.
|
||||
*
|
||||
* @param $node
|
||||
* A node object to associate with uploaded files.
|
||||
*/
|
||||
function _upload_prepare(&$node) {
|
||||
global $user;
|
||||
|
||||
// Clean up old file previews if a post didn't get the user to this page.
|
||||
// i.e. the user left the edit page, because they didn't want to upload anything.
|
||||
if (count($_POST) == 0) {
|
||||
if (!empty($_SESSION['file_previews']) && is_array($_SESSION['file_previews'])) {
|
||||
foreach ($_SESSION['file_previews'] as $fid => $file) {
|
||||
file_delete($file->filepath);
|
||||
}
|
||||
unset($_SESSION['file_previews']);
|
||||
}
|
||||
// Initialize _SESSION['upload_files'] if no post occured.
|
||||
// This clears the variable from old forms and makes sure it
|
||||
// is an array to prevent notices and errors in other parts
|
||||
// of upload.module.
|
||||
if (!$_POST) {
|
||||
$_SESSION['upload_files'] = array();
|
||||
}
|
||||
|
||||
// $_SESSION['file_current_upload'] tracks the fid of the file submitted this page request.
|
||||
// $_SESSION['upload_current_file'] tracks the fid of the file submitted this page request.
|
||||
// form_builder sets the value of file->list to 0 for checkboxes added to a form after
|
||||
// it has been submitted. Since unchecked checkboxes have no return value and do not
|
||||
// get a key in _POST form_builder has no way of knowing the difference between a check
|
||||
// box that wasn't present on the last form build, and a checkbox that is unchecked.
|
||||
unset($_SESSION['upload_current_file']);
|
||||
|
||||
unset($_SESSION['file_current_upload']);
|
||||
$limits = _upload_file_limits($user);
|
||||
$validators = array(
|
||||
'file_validate_extensions' => array($limits['extensions']),
|
||||
'file_validate_image_resolution' => array($limits['resolution']),
|
||||
'file_validate_size' => array($limits['file_size'], $limits['user_size']),
|
||||
);
|
||||
|
||||
global $user;
|
||||
|
||||
// Save new file uploads to tmp dir.
|
||||
if (($file = file_check_upload()) && user_access('upload files')) {
|
||||
|
||||
// Scale image uploads.
|
||||
$file = _upload_image($file);
|
||||
|
||||
$key = 'upload_'. (!isset($_SESSION['file_previews']) ? 0 : count($_SESSION['file_previews']));
|
||||
$file->fid = $key;
|
||||
$file->source = $key;
|
||||
$file->list = variable_get('upload_list_default', 1);
|
||||
$_SESSION['file_previews'][$key] = $file;
|
||||
|
||||
// Store the uploaded fid for this page request in case of submit without
|
||||
// preview or attach. See earlier notes.
|
||||
$_SESSION['file_current_upload'] = $key;
|
||||
// Save new file uploads.
|
||||
if (($user->uid != 1 || user_access('upload files')) && ($file = file_save_upload('upload', $validators))) {
|
||||
$file->list = variable_get('upload_list_default',1);
|
||||
$file->description = $file->filename;
|
||||
$_SESSION['upload_current_file'] = $file->fid;
|
||||
$_SESSION['upload_files'][$file->fid] = $file;
|
||||
}
|
||||
|
||||
// Attach file previews to node object.
|
||||
if (!empty($_SESSION['file_previews']) && is_array($_SESSION['file_previews'])) {
|
||||
foreach ($_SESSION['file_previews'] as $fid => $file) {
|
||||
if ($user->uid != 1) {
|
||||
// Here something.php.pps becomes something.php_.pps
|
||||
$file->filename = upload_munge_filename($file->filename, NULL, 0);
|
||||
$file->description = $file->filename;
|
||||
}
|
||||
// attach session files to node.
|
||||
if (count($_SESSION['upload_files'])) {
|
||||
foreach($_SESSION['upload_files'] as $fid => $file) {
|
||||
$node->files[$fid] = $file;
|
||||
}
|
||||
}
|
||||
|
@ -407,80 +410,6 @@ function upload_form_alter(&$form, $form_state, $form_id) {
|
|||
}
|
||||
}
|
||||
|
||||
function _upload_validate(&$node) {
|
||||
// Accumulator for disk space quotas.
|
||||
$filesize = 0;
|
||||
|
||||
// Check if node->files exists, and if it contains something.
|
||||
if (isset($node->files) && is_array($node->files)) {
|
||||
// Update existing files with form data.
|
||||
foreach ($node->files as $fid => $file) {
|
||||
// Convert file to object for compatibility
|
||||
$file = (object)$file;
|
||||
|
||||
// Validate new uploads.
|
||||
if (strpos($fid, 'upload') !== FALSE && empty($file->remove)) {
|
||||
global $user;
|
||||
|
||||
// Bypass validation for uid = 1.
|
||||
if ($user->uid != 1) {
|
||||
// Update filesize accumulator.
|
||||
$filesize += $file->filesize;
|
||||
|
||||
// Validate file against all users roles.
|
||||
// Only denies an upload when all roles prevent it.
|
||||
|
||||
$total_usersize = upload_space_used($user->uid) + $filesize;
|
||||
$error = array();
|
||||
foreach ($user->roles as $rid => $name) {
|
||||
$extensions = variable_get("upload_extensions_$rid", variable_get('upload_extensions_default', 'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp'));
|
||||
$uploadsize = variable_get("upload_uploadsize_$rid", variable_get('upload_uploadsize_default', 1)) * 1024 * 1024;
|
||||
$usersize = variable_get("upload_usersize_$rid", variable_get('upload_usersize_default', 1)) * 1024 * 1024;
|
||||
|
||||
$regex = '/\.('. ereg_replace(' +', '|', preg_quote($extensions)) .')$/i';
|
||||
|
||||
if (!preg_match($regex, $file->filename)) {
|
||||
$error['extension']++;
|
||||
}
|
||||
|
||||
if ($uploadsize && $file->filesize > $uploadsize) {
|
||||
$error['uploadsize']++;
|
||||
}
|
||||
|
||||
if ($usersize && $total_usersize + $file->filesize > $usersize) {
|
||||
$error['usersize']++;
|
||||
}
|
||||
}
|
||||
|
||||
$user_roles = count($user->roles);
|
||||
$valid = TRUE;
|
||||
if ($error['extension'] == $user_roles) {
|
||||
form_set_error('upload', t('The selected file %name can not be attached to this post, because it is only possible to attach files with the following extensions: %files-allowed.', array('%name' => $file->filename, '%files-allowed' => $extensions)));
|
||||
$valid = FALSE;
|
||||
}
|
||||
elseif ($error['uploadsize'] == $user_roles) {
|
||||
form_set_error('upload', t('The selected file %name can not be attached to this post, because it exceeded the maximum filesize of %maxsize.', array('%name' => $file->filename, '%maxsize' => format_size($uploadsize))));
|
||||
$valid = FALSE;
|
||||
}
|
||||
elseif ($error['usersize'] == $user_roles) {
|
||||
form_set_error('upload', t('The selected file %name can not be attached to this post, because the disk quota of %quota has been reached.', array('%name' => $file->filename, '%quota' => format_size($usersize))));
|
||||
$valid = FALSE;
|
||||
}
|
||||
elseif (strlen($file->filename) > 255) {
|
||||
form_set_error('upload', t('The selected file %name can not be attached to this post, because the filename is too long.', array('%name' => $file->filename)));
|
||||
$valid = FALSE;
|
||||
}
|
||||
|
||||
if (!$valid) {
|
||||
unset($node->files[$fid], $_SESSION['file_previews'][$fid]);
|
||||
file_delete($file->filepath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_nodeapi().
|
||||
*/
|
||||
|
@ -499,10 +428,6 @@ function upload_nodeapi(&$node, $op, $teaser) {
|
|||
_upload_prepare($node);
|
||||
break;
|
||||
|
||||
case 'validate':
|
||||
_upload_validate($node);
|
||||
break;
|
||||
|
||||
case 'view':
|
||||
if (isset($node->files) && user_access('view uploaded files')) {
|
||||
// Add the attachments list to node body with a heavy
|
||||
|
@ -517,31 +442,7 @@ function upload_nodeapi(&$node, $op, $teaser) {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case 'alter':
|
||||
if (isset($node->files) && user_access('view uploaded files')) {
|
||||
// Manipulate so that inline references work in preview
|
||||
if (!variable_get('clean_url', 0)) {
|
||||
$previews = array();
|
||||
foreach ($node->files as $file) {
|
||||
if (strpos($file->fid, 'upload') !== FALSE) {
|
||||
$previews[] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
// URLs to files being previewed are actually Drupal paths. When Clean
|
||||
// URLs are disabled, the two do not match. We perform an automatic
|
||||
// replacement from temporary to permanent URLs. That way, the author
|
||||
// can use the final URL in the body before having actually saved (to
|
||||
// place inline images for example).
|
||||
foreach ($previews as $file) {
|
||||
$old = file_create_filename($file->filename, file_create_path());
|
||||
$new = url($old);
|
||||
$node->body = str_replace($old, $new, $node->body);
|
||||
$node->teaser = str_replace($old, $new, $node->teaser);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'insert':
|
||||
case 'update':
|
||||
if (user_access('upload files')) {
|
||||
|
@ -595,7 +496,7 @@ function theme_upload_attachments($files) {
|
|||
$rows = array();
|
||||
foreach ($files as $file) {
|
||||
$file = (object)$file;
|
||||
if ($file->list && !$file->remove) {
|
||||
if ($file->list && empty($file->remove)) {
|
||||
// Generate valid URL for both existing attachments and preview of new attachments (these have 'upload' in fid)
|
||||
$href = file_create_url((strpos($file->fid, 'upload') === FALSE ? $file->filepath : file_create_filename($file->filename, file_create_path())));
|
||||
$text = $file->description ? $file->description : $file->filename;
|
||||
|
@ -616,7 +517,7 @@ function theme_upload_attachments($files) {
|
|||
* The amount of disk space used by the user in bytes.
|
||||
*/
|
||||
function upload_space_used($uid) {
|
||||
return db_result(db_query('SELECT SUM(filesize) FROM {files} f INNER JOIN {node} n ON f.nid = n.nid WHERE n.uid = %d', $uid));
|
||||
return file_space_used($uid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -626,67 +527,7 @@ function upload_space_used($uid) {
|
|||
* The amount of disk space used by uploaded files in bytes.
|
||||
*/
|
||||
function upload_total_space_used() {
|
||||
return db_result(db_query('SELECT SUM(filesize) FROM {files}'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Munge the filename as needed for security purposes.
|
||||
*
|
||||
* @param $filename
|
||||
* The name of a file to modify.
|
||||
* @param $extensions
|
||||
* A space separated list of valid extensions. If this is blank, we'll use
|
||||
* the admin-defined defaults for the user role from upload_extensions_$rid.
|
||||
* @param $alerts
|
||||
* Whether alerts (watchdog, drupal_set_message()) should be displayed.
|
||||
* @return $filename
|
||||
* The potentially modified $filename.
|
||||
*/
|
||||
function upload_munge_filename($filename, $extensions = NULL, $alerts = 1) {
|
||||
global $user;
|
||||
|
||||
$original = $filename;
|
||||
|
||||
// Allow potentially insecure uploads for very savvy users and admin
|
||||
if (!variable_get('allow_insecure_uploads', 0)) {
|
||||
|
||||
if (!isset($extensions)) {
|
||||
$extensions = '';
|
||||
foreach ($user->roles as $rid => $name) {
|
||||
$extensions .= ' '. variable_get("upload_extensions_$rid", variable_get('upload_extensions_default', 'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$whitelist = array_unique(explode(' ', trim($extensions)));
|
||||
|
||||
$filename_parts = explode('.', $filename);
|
||||
|
||||
$new_filename = array_shift($filename_parts); // Remove file basename.
|
||||
$final_extension = array_pop($filename_parts); // Remove final extension.
|
||||
|
||||
foreach ($filename_parts as $filename_part) {
|
||||
$new_filename .= ".$filename_part";
|
||||
if (!in_array($filename_part, $whitelist) && preg_match("/^[a-zA-Z]{2,5}\d?$/", $filename_part)) {
|
||||
$new_filename .= '_';
|
||||
}
|
||||
}
|
||||
$filename = "$new_filename.$final_extension";
|
||||
}
|
||||
|
||||
if ($alerts && $original != $filename) {
|
||||
$message = t('Your filename has been renamed to conform to site policy.');
|
||||
drupal_set_message($message);
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo the effect of upload_munge_filename().
|
||||
*/
|
||||
function upload_unmunge_filename($filename) {
|
||||
return str_replace('_.', '.', $filename);
|
||||
return db_result(db_query('SELECT SUM(f.filesize) FROM {files} f INNER JOIN {upload} u ON f.fid = u.fid'));
|
||||
}
|
||||
|
||||
function upload_save(&$node) {
|
||||
|
@ -701,69 +542,52 @@ function upload_save(&$node) {
|
|||
// Remove file. Process removals first since no further processing
|
||||
// will be required.
|
||||
if ($file->remove) {
|
||||
// Remove file previews...
|
||||
if (strpos($file->fid, 'upload') !== FALSE) {
|
||||
file_delete($file->filepath);
|
||||
}
|
||||
|
||||
// Remove managed files.
|
||||
else {
|
||||
db_query('DELETE FROM {file_revisions} WHERE fid = %d AND vid = %d', $fid, $node->vid);
|
||||
// Only delete a file if it isn't used by any revision
|
||||
$count = db_result(db_query('SELECT COUNT(fid) FROM {file_revisions} WHERE fid = %d', $fid));
|
||||
if ($count < 1) {
|
||||
db_query('DELETE FROM {files} WHERE fid = %d', $fid);
|
||||
file_delete($file->filepath);
|
||||
}
|
||||
}
|
||||
db_query('DELETE FROM {upload} WHERE fid = %d AND vid = %d', $fid, $node->vid);
|
||||
// Remove it from the session in the case of new uploads,
|
||||
// that you want to disassociate before node submission.
|
||||
unset($_SESSION['upload_files'][$fid]);
|
||||
// Move on, so the removed file won't be added to new revisions.
|
||||
continue;
|
||||
}
|
||||
|
||||
// New file upload
|
||||
elseif (strpos($file->fid, 'upload') !== FALSE) {
|
||||
if ($file = file_save_upload($file, $file->filename)) {
|
||||
$file->fid = db_next_id('{files}_fid');
|
||||
db_query("INSERT INTO {files} (fid, nid, filename, filepath, filemime, filesize) VALUES (%d, %d, '%s', '%s', '%s', %d)", $file->fid, $node->nid, $file->filename, $file->filepath, $file->filemime, $file->filesize);
|
||||
db_query("INSERT INTO {file_revisions} (fid, vid, list, description) VALUES (%d, %d, %d, '%s')", $file->fid, $node->vid, $file->list, $file->description);
|
||||
// Tell other modules where the file was stored.
|
||||
$node->files[$fid] = $file;
|
||||
}
|
||||
unset($_SESSION['file_previews'][$fid]);
|
||||
// Create a new revision, or associate a new file needed.
|
||||
if (!empty($node->old_vid) || array_key_exists($fid, $_SESSION['upload_files'])) {
|
||||
db_query("INSERT INTO {upload} (fid, nid, vid, list, description) VALUES (%d, %d, %d, %d, '%s')", $file->fid, $node->nid, $node->vid, $file->list, $file->description);
|
||||
file_set_status($file, FILE_STATUS_PERMANENT);
|
||||
}
|
||||
|
||||
// Create a new revision, as needed
|
||||
elseif ($node->old_vid && is_numeric($fid)) {
|
||||
db_query("INSERT INTO {file_revisions} (fid, vid, list, description) VALUES (%d, %d, %d, '%s')", $file->fid, $node->vid, $file->list, $file->description);
|
||||
}
|
||||
|
||||
// Update existing revision
|
||||
// Update existing revision.
|
||||
else {
|
||||
db_query("UPDATE {file_revisions} SET list = %d, description = '%s' WHERE fid = %d AND vid = %d", $file->list, $file->description, $file->fid, $node->vid);
|
||||
db_query("UPDATE {upload} SET list = %d, description = '%s' WHERE fid = %d AND vid = %d", $file->list, $file->description, $file->fid, $node->vid);
|
||||
file_set_status($file, FILE_STATUS_PERMANENT);
|
||||
}
|
||||
}
|
||||
// Empty the session storage after save. We use this variable to track files
|
||||
// that haven't been related to the node yet.
|
||||
unset($_SESSION['upload_files']);
|
||||
}
|
||||
|
||||
function upload_delete($node) {
|
||||
$files = array();
|
||||
$result = db_query('SELECT * FROM {files} WHERE nid = %d', $node->nid);
|
||||
$result = db_query('SELECT DISTINCT f.* FROM upload u INNER JOIN files f ON u.fid = f.fid WHERE u.nid = %d', $node->nid);
|
||||
while ($file = db_fetch_object($result)) {
|
||||
$files[$file->fid] = $file;
|
||||
}
|
||||
|
||||
foreach ($files as $fid => $file) {
|
||||
// Delete all file revision information associated with the node
|
||||
db_query('DELETE FROM {file_revisions} WHERE fid = %d', $fid);
|
||||
// Delete all files associated with the node
|
||||
db_query('DELETE FROM {files} WHERE fid = %d', $fid);
|
||||
file_delete($file->filepath);
|
||||
}
|
||||
|
||||
// Delete all files associated with the node
|
||||
db_query('DELETE FROM {files} WHERE nid = %d', $node->nid);
|
||||
// Delete all file revision information associated with the node
|
||||
db_query('DELETE FROM {upload} WHERE nid = %d', $node->nid);
|
||||
}
|
||||
|
||||
function upload_delete_revision($node) {
|
||||
if (is_array($node->files)) {
|
||||
foreach ($node->files as $file) {
|
||||
// Check if the file will be used after this revision is deleted
|
||||
$count = db_result(db_query('SELECT COUNT(fid) FROM {file_revisions} WHERE fid = %d', $file->fid));
|
||||
$count = db_result(db_query('SELECT COUNT(fid) FROM {upload} WHERE fid = %d', $file->fid));
|
||||
|
||||
// if the file won't be used, delete it
|
||||
if ($count < 2) {
|
||||
|
@ -774,10 +598,11 @@ function upload_delete_revision($node) {
|
|||
}
|
||||
|
||||
// delete the revision
|
||||
db_query('DELETE FROM {file_revisions} WHERE vid = %d', $node->vid);
|
||||
db_query('DELETE FROM {upload} WHERE vid = %d', $node->vid);
|
||||
}
|
||||
|
||||
function _upload_form($node) {
|
||||
global $user;
|
||||
|
||||
$form['#theme'] = 'upload_form_new';
|
||||
|
||||
|
@ -785,7 +610,8 @@ function _upload_form($node) {
|
|||
$form['files']['#theme'] = 'upload_form_current';
|
||||
$form['files']['#tree'] = TRUE;
|
||||
foreach ($node->files as $key => $file) {
|
||||
// Generate valid URL for both existing attachments and preview of new attachments (these have 'upload' in fid)
|
||||
// Generate valid URL for both existing attachments and preview of new
|
||||
// attachments (these have 'upload' in fid).
|
||||
$description = file_create_url((strpos($file->fid, 'upload') === FALSE ? $file->filepath : file_create_filename($file->filename, file_create_path())));
|
||||
$description = "<small>". check_plain($description) ."</small>";
|
||||
$form['files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => !empty($file->description) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => $description );
|
||||
|
@ -793,9 +619,10 @@ function _upload_form($node) {
|
|||
$form['files'][$key]['size'] = array('#value' => format_size($file->filesize));
|
||||
$form['files'][$key]['remove'] = array('#type' => 'checkbox', '#default_value' => !empty($file->remove));
|
||||
$form['files'][$key]['list'] = array('#type' => 'checkbox', '#default_value' => $file->list);
|
||||
// if the file was uploaded this page request, set value. this fixes the problem
|
||||
// formapi has recognizing new checkboxes. see comments in _upload_prepare.
|
||||
if (isset($_SESSION['file_current_upload']) && $_SESSION['file_current_upload'] == $file->fid) {
|
||||
// If the file was uploaded this page request, set value. this fixes the
|
||||
// problem formapi has recognizing new checkboxes. see comments in
|
||||
// _upload_prepare.
|
||||
if (isset($_SESSION['upload_current_file']) && $_SESSION['upload_current_file'] == $file->fid) {
|
||||
$form['files'][$key]['list']['#value'] = variable_get('upload_list_default', 1);
|
||||
}
|
||||
$form['files'][$key]['filename'] = array('#type' => 'value', '#value' => $file->filename);
|
||||
|
@ -807,12 +634,19 @@ function _upload_form($node) {
|
|||
}
|
||||
|
||||
if (user_access('upload files')) {
|
||||
$limits = _upload_file_limits($user);
|
||||
|
||||
// This div is hidden when the user uploads through JS.
|
||||
$form['new'] = array(
|
||||
'#prefix' => '<div id="attach-hide">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
$form['new']['upload'] = array('#type' => 'file', '#title' => t('Attach new file'), '#size' => 40);
|
||||
$form['new']['upload'] = array(
|
||||
'#type' => 'file',
|
||||
'#title' => t('Attach new file'),
|
||||
'#size' => 40,
|
||||
'#description' => ($limits['resolution'] ? t('Images are larger than %resolution will be resized. ', array('%resolution' => $limits['resolution'])) : '') . t('The maximum upload size is %filesize. Only files with the following extensions may be uploaded: %extensions. ', array('%extensions' => $limits['extensions'], '%filesize' => format_size($limits['file_size']))),
|
||||
);
|
||||
$form['new']['attach'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Attach'),
|
||||
|
@ -824,7 +658,7 @@ function _upload_form($node) {
|
|||
$form['attach-url'] = array('#type' => 'hidden', '#value' => url('upload/js', array('absolute' => TRUE)), '#attributes' => array('class' => 'upload'));
|
||||
}
|
||||
|
||||
// Needed for JS
|
||||
// Needed for JS.
|
||||
$form['current']['vid'] = array('#type' => 'hidden', '#value' => isset($node->vid) ? $node->vid : 0);
|
||||
return $form;
|
||||
}
|
||||
|
@ -861,7 +695,7 @@ function upload_load($node) {
|
|||
$files = array();
|
||||
|
||||
if ($node->vid) {
|
||||
$result = db_query('SELECT * FROM {files} f INNER JOIN {file_revisions} r ON f.fid = r.fid WHERE r.vid = %d ORDER BY f.fid', $node->vid);
|
||||
$result = db_query('SELECT * FROM {files} f INNER JOIN {upload} r ON f.fid = r.fid WHERE r.vid = %d ORDER BY f.fid', $node->vid);
|
||||
while ($file = db_fetch_object($result)) {
|
||||
$files[$file->fid] = $file;
|
||||
}
|
||||
|
@ -870,27 +704,6 @@ function upload_load($node) {
|
|||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check an upload, if it is an image, make sure it fits within the
|
||||
* maximum dimensions allowed.
|
||||
*/
|
||||
function _upload_image($file) {
|
||||
$info = image_get_info($file->filepath);
|
||||
|
||||
if ($info) {
|
||||
list($width, $height) = explode('x', variable_get('upload_max_resolution', '0x0'));
|
||||
if ($width && $height) {
|
||||
$result = image_scale($file->filepath, $file->filepath, $width, $height);
|
||||
if ($result) {
|
||||
$file->filesize = filesize($file->filepath);
|
||||
drupal_set_message(t('The image was resized to fit within the maximum allowed resolution of %resolution pixels.', array('%resolution' => variable_get('upload_max_resolution', 0))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu-callback for JavaScript-based uploads.
|
||||
*/
|
||||
|
@ -903,7 +716,6 @@ function upload_js() {
|
|||
|
||||
// Handle new uploads, and merge tmp files into node-files.
|
||||
_upload_prepare($node);
|
||||
_upload_validate($node);
|
||||
|
||||
$form = _upload_form($node);
|
||||
$form += array(
|
||||
|
@ -915,6 +727,8 @@ function upload_js() {
|
|||
drupal_alter('form', $form, array(), 'upload_js');
|
||||
$form_state = array('submitted' => FALSE);
|
||||
$form = form_builder('upload_js', $form, $form_state);
|
||||
// @todo: Put status messages inside wrapper, instead of above so they do not
|
||||
// persist across ajax reloads.
|
||||
$output = theme('status_messages') . drupal_render($form);
|
||||
// We send the updated file attachments form.
|
||||
print drupal_to_js(array('status' => TRUE, 'data' => $output));
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
// $Id$
|
||||
|
||||
function upload_schema() {
|
||||
$schema['upload'] = array(
|
||||
'fields' => array(
|
||||
'fid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
|
||||
'nid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
|
||||
'vid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
|
||||
'description' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
|
||||
'list' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'size' => 'tiny')
|
||||
),
|
||||
'primary key' => array('fid', 'vid'),
|
||||
'indexes' => array('vid' => array('vid'), 'nid' => array('nid')),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
|
@ -320,32 +320,22 @@ function user_validate_mail($mail) {
|
|||
|
||||
function user_validate_picture(&$form, &$form_state, $form_values) {
|
||||
// If required, validate the uploaded picture.
|
||||
if (isset($form['picture']) && ($file = file_check_upload('picture_upload'))) {
|
||||
// Check that uploaded file is an image, with a maximum file size
|
||||
// and maximum height/width.
|
||||
$validators = array(
|
||||
'file_validate_is_image' => array(),
|
||||
'file_validate_image_resolution' => array(variable_get('user_picture_dimensions', '85x85')),
|
||||
'file_validate_size' => array(variable_get('user_picture_file_size', '30') * 1024),
|
||||
);
|
||||
if ($file = file_save_upload('picture_upload', $validators)) {
|
||||
// The image was saved using file_save_upload() and was added to the
|
||||
// files table as a temorary file. We'll make a copy and let the garbage
|
||||
// collector delete the original upload.
|
||||
$info = image_get_info($file->filepath);
|
||||
list($maxwidth, $maxheight) = explode('x', variable_get('user_picture_dimensions', '85x85'));
|
||||
|
||||
if (!$info || !$info['extension']) {
|
||||
form_set_error('picture_upload', t('The uploaded file was not an image.'));
|
||||
$destination = variable_get('user_picture_path', 'pictures') .'/picture-'. $form['#uid'] .'.'. $info['extension'];
|
||||
if (file_copy($file, $destination, FILE_EXISTS_REPLACE)) {
|
||||
$form_values['picture'] = $file->filepath;
|
||||
}
|
||||
else if (image_get_toolkit()) {
|
||||
image_scale($file->filepath, $file->filepath, $maxwidth, $maxheight);
|
||||
}
|
||||
else if (filesize($file->filepath) > (variable_get('user_picture_file_size', '30') * 1000)) {
|
||||
form_set_error('picture_upload', t('The uploaded image is too large; the maximum file size is %size kB.', array('%size' => variable_get('user_picture_file_size', '30'))));
|
||||
}
|
||||
else if ($info['width'] > $maxwidth || $info['height'] > $maxheight) {
|
||||
form_set_error('picture_upload', t('The uploaded image is too large; the maximum dimensions are %dimensions pixels.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'))));
|
||||
}
|
||||
|
||||
if (!form_get_errors()) {
|
||||
if ($file = file_save_upload('picture_upload', variable_get('user_picture_path', 'pictures') .'/picture-'. $form['#uid'] .'.'. $info['extension'], 1)) {
|
||||
$form_values['picture'] = $file->filepath;
|
||||
}
|
||||
else {
|
||||
form_set_error('picture_upload', t("Failed to upload the picture image; the %directory directory doesn't exist or is not writable.", array('%directory' => variable_get('user_picture_path', 'pictures'))));
|
||||
}
|
||||
else {
|
||||
form_set_error('picture_upload', t("Failed to upload the picture image; the %directory directory doesn't exist or is not writable.", array('%directory' => variable_get('user_picture_path', 'pictures'))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,35 +3,35 @@ body {
|
|||
direction: rtl;
|
||||
}
|
||||
#logo img {
|
||||
float: right;
|
||||
float: right;
|
||||
}
|
||||
#menu {
|
||||
padding: 0.5em 0.5em 0 0.5em;
|
||||
text-align: left;
|
||||
padding: 0.5em 0.5em 0 0.5em;
|
||||
text-align: left;
|
||||
}
|
||||
#navlist {
|
||||
padding: 0 0 1.2em 0.8em;
|
||||
padding: 0 0 1.2em 0.8em;
|
||||
}
|
||||
#subnavlist {
|
||||
padding: 0.5em 0 0.4em 1.2em;
|
||||
padding: 0.5em 0 0.4em 1.2em;
|
||||
}
|
||||
ul.links li {
|
||||
border-right: 1px solid #9cf;
|
||||
border-right: 1px solid #9cf;
|
||||
border-left: inherit;
|
||||
}
|
||||
.block, .box {
|
||||
padding: 0 1.5em 0 0;
|
||||
}
|
||||
.node .taxonomy {
|
||||
padding-right: 1.5em;
|
||||
padding-right: 1.5em;
|
||||
}
|
||||
.node .picture {
|
||||
float: left;
|
||||
}
|
||||
.comment .new {
|
||||
text-align: left;
|
||||
float: left;
|
||||
float: left;
|
||||
}
|
||||
.comment .picture {
|
||||
float: left;
|
||||
float: left;
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ ul.links li.first {
|
|||
font-weight: bold;
|
||||
}
|
||||
.site-name {
|
||||
margin: 0.6em 0 0 ;
|
||||
margin: 0.6em 0 0 ;
|
||||
padding: 0;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue