Index: upload.module =================================================================== RCS file: /cvs/drupal/drupal/modules/upload.module,v retrieving revision 1.51 diff -u -F^f -r1.51 upload.module --- upload.module 2 Sep 2005 02:11:41 -0000 1.51 +++ upload.module 16 Sep 2005 11:12:29 -0000 @@ -15,6 +15,12 @@ function upload_help($section) { return t('Allows users to upload and attach files to content.'); case 'admin/settings/upload': return t('

Users with the upload files permission can upload attachments. You can choose which post types can take attachments on the content types settings page.

', array('%permissions' => url('admin/access'), '%types' => url('admin/settings/content-types'))); + case 'admin/upload': + return t('

Below is a list of all files uploaded to the site and statistics about those files.

Clicking a filename shows the file, clicking a title opens the post where the file was uploaded, and clicking an author will show information about the post\'s author.

'); + case 'admin/upload/usage': + return t('

Below is a list of all the the users that have uploaded files to the site.

Clicking a username will show information about the user.

'); + case 'upload': + return t('

Below is information about the files you can upload, a list of all the files you have uploaded, and statistics about those files.

Clicking a filename shows the file, while clicking a title opens the post where the file was uploaded.

'); } } @@ -22,7 +28,7 @@ function upload_help($section) { * Implementation of hook_perm(). */ function upload_perm() { - return array('upload files', 'view uploaded files'); + return array('upload files', 'view uploaded files', 'administer uploaded files'); } /** @@ -48,6 +54,67 @@ function upload_link($type, $node = 0, $ } /** + * Return statistics about uploads + * + * @return + * Themed statistical information. +**/ +function upload_statistics() { + global $user; + $extensions = array(); + + // get the total number and size of upload files + // upload_space_used() wasn't used as it only returns the latter and would result in two queries instead of one + $result = db_query('SELECT SUM(f.filesize) AS filesize, COUNT(f.fid) AS count FROM {files} f INNER JOIN {node} n ON f.nid = n.nid WHERE n.uid = %d GROUP BY f.fid', $user->uid); + + // there should only be one result from the above query + $stats = db_fetch_object($result); + $total_size = (is_null($stats->filesize)) ? 0 : $stats->filesize; + $total_num = (is_null($stats->count)) ? 0 : $stats->count; + + // determine what upload settings apply to the user + // User 1 can upload any file of any size without limit + if ($user->uid == 1) { + $max_one_size = t('unlimited'); + $max_all_size = t('unlimited'); + $extensions = t('unlimited'); + } + else { + foreach ($user->roles as $rid => $value) { + $temp = explode(' ', variable_get("upload_extensions_$rid", variable_get("upload_extensions_default", "jpg jpeg gif png txt html doc xls pdf ppt pps"))); + $extensions = array_merge($extensions, $temp); + + $uploadsize = variable_get("upload_uploadsize_$rid", variable_get("upload_uploadsize_default", 1)) * 1024 * 1024; + $max_one_size = ($uploadsize > $max_one_size) ? $uploadsize : $max_one_size; + $usersize = variable_get("upload_usersize_$rid", variable_get("upload_usersize_default", 10)) * 1024 * 1024; + $max_all_size = ($usersize > $max_all_size) ? $usersize : $max_all_size; + } + + // prepare information + $percent = ($max_all_size) ? '('. sprintf("%.2f", ($total_size / $max_all_size) * 100) .'%)' : ''; + $max_all_size = format_size($max_all_size); + $max_one_size = format_size($max_one_size); + $extensions = implode(' ', $extensions); + } + + // output information + $items = array(); + $items[] = t('

Allowed file extensions: %extensions

', array('%extensions' => $extensions)); + $items[] .= t('

Maximum size of each file: %size

', array('%size' => $max_one_size)); + $items[] .= t('

Maximum size of all files: %size

', array('%size' => $max_all_size)); + $content = theme('item_list', $items); + $output = theme('box', t('Allowances'), $content); + + $items = array(); + $items[] = t('

Total files: %total

', array('%total' => $total_num)); + $items[] .= t('

Disk usage: %used of %max %percent

', array('%used' => format_size($total_size), '%max' => $max_all_size, '%percent' => $percent)); + $content = theme('item_list', $items); + $output .= theme('box', t('Statistics'), $content); + + return $output; +} + +/** * Implementation of hook_menu(). */ function upload_menu($may_cache) { @@ -56,7 +123,7 @@ function upload_menu($may_cache) { if ($may_cache) { $items[] = array( 'path' => 'admin/settings/upload', 'title' => t('uploads'), - 'callback' => 'upload_admin', + 'callback' => 'upload_settings', 'access' => user_access('administer site configuration'), 'type' => MENU_NORMAL_ITEM ); @@ -66,6 +133,30 @@ function upload_menu($may_cache) { 'access' => user_access('upload files'), 'type' => MENU_CALLBACK ); + $items[] = array( + 'path' => 'upload', 'title' => t('my uploads'), + 'callback' => 'upload_page', + 'access' => user_access('upload files'), + 'type' => MENU_NORMAL_ITEM + ); + $items[] = array( + 'path' => 'admin/upload', 'title' => t('uploads'), + 'callback' => 'upload_admin', + 'access' => user_access('administer uploaded files'), + 'type' => MENU_NORMAL_ITEM + ); + $items[] = array( + 'path' => 'admin/upload/list', 'title' => t('list'), + 'access' => user_access('administer uploaded files'), + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10 + ); + $items[] = array( + 'path' => 'admin/upload/usage', 'title' => t('disk space usage'), + 'callback' => 'upload_admin_usage', + 'access' => user_access('administer uploaded files'), + 'type' => MENU_LOCAL_TASK + ); } else { // Add handlers for previewing new uploads. @@ -86,19 +177,270 @@ function upload_menu($may_cache) { return $items; } +/* + * Menu callback: + * Page where admins manage uploaded files + */ function upload_admin() { + + $operations = array( + 'delete' => array(t('Delete the selected files'), '') + ); + + // Handle operations + $op = $_POST['op']; + $edit = $_POST['edit']; + + if (($op == t('Update') || $op == t('Delete all')) && isset($edit['operation']) && isset($edit['files'])) { + $edit['files'] = array_diff($edit['files'], array(0)); + if (count($edit['files']) == 0) { + form_set_error('', t('Please select some items to perform the update on.')); + } + else { + if ($edit['operation'] == 'delete') { + // Mass delete + if ($edit['confirm']) { + upload_delete('files', $edit['files']); + drupal_set_message(t('The items have been deleted.')); + } + else { + $extra = ''; + $extra .= form_hidden('operation', 'delete'); + + $output = theme('confirm', + t('Are you sure you want to delete these items?'), + 'admin/upload', + t('This action cannot be undone.'), + t('Delete all'), + t('Cancel'), + $extra); + return $output; + } + } + } + } + + // Get statistics about files + $total_size = upload_total_space_used(); + $content = t('

Total disk space used: %size

', array('%size' => format_size($total_size))); + $output .= theme('box', t('Statistics'), $content); + + $header = array( + NULL, + array('data' => t('Filename'), 'field' => 'filename'), + array('data' => t('Title'), 'field' => 'title'), + array('data' => t('Author'), 'field' => 'name'), + array('data' => t('Size'), 'field' => 'filesize', 'sort' => 'desc') + ); + + $sql = 'SELECT f.fid, n.nid, n.status, n.title, f.filename, f.filepath, f.filesize, n.uid, u.name FROM {files} f INNER JOIN {node} n ON f.nid = n.nid INNER JOIN {users} u ON u.uid = n.uid'; + $sql .= tablesort_sql($header); + $result = pager_query($sql, 50); + + // Make sure the update controls are disabled if we don't have any rows + // to select from. + $disabled = !db_num_rows($result); + + $options = array(); + foreach ($operations as $key => $value) { + $options[$key] = $value[0]; + } + + $form = form_select(NULL, 'operation', 0, $options, NULL, ($disabled ? 'disabled="disabled"' : '')); + $form .= form_submit(t('Update'), 'op', ($disabled ? array('disabled' => 'disabled') : array())); + + $output .= form_group(t('Update options'), "
$form
"); + + $destination = drupal_get_destination(); + while ($file = db_fetch_object($result)) { + $format_user->name = $file->name; + $format_user->uid = $file->uid; + + $rows[] = array( + form_checkbox(NULL, 'files]['. $file->fid, 1, 0), + ''. check_plain($file->filename) .'', + ($file->status) ? l($file->title, "node/$file->nid") : $file->title, + theme('username', $format_user), + format_size($file->filesize) + ); + } + + if ($pager = theme('pager', NULL, 50, 0, tablesort_pager())) { + $rows[] = array(array('data' => $pager, 'colspan' => '5')); + } + + if (!$rows) { + $rows[] = array(array('data' => t('No uploaded files available.'), 'colspan' => '5')); + } + $output .= theme('table', $header, $rows); + return form($output, 'post', url('admin/upload')); +} + +/** + * Menu callback: + * Page listing disk space usage for each user +**/ +function upload_admin_usage() { + $header = array( + array('data' => t('Username'), 'field' => 'name'), + array('data' => t('Size'), 'field' => 'filesize', 'sort' => 'desc') + ); + + $sql = 'SELECT SUM(f.filesize) AS filesize, u.uid, u.name FROM {files} f INNER JOIN {node} n ON f.nid = n.nid INNER JOIN {users} u ON u.uid = n.uid GROUP BY u.uid'; + $sql .= tablesort_sql($header); + $result = pager_query($sql, 50); + + // Make sure the update controls are disabled if we don't have any rows + // to select from. + $disabled = !db_num_rows($result); + + while ($users = db_fetch_object($result)) { + $format_user->name = $users->name; + $format_user->uid = $users->uid; + + $rows[] = array( + theme('username', $format_user), + format_size($users->filesize) + ); + } + + if ($pager = theme('pager', NULL, 50, 0, tablesort_pager())) { + $rows[] = array(array('data' => $pager, 'colspan' => '2')); + } + + if (!$rows) { + $rows[] = array(array('data' => t('No users have uploaded files.'), 'colspan' => '2')); + } + + return theme('table', $header, $rows); +} + +/* + * Menu callback: + * Page where users manage their uploaded files + */ +function upload_page() { + global $user; + + $operations = array( + 'delete' => array(t('Delete the selected files'), '') + ); + + // Handle operations + $op = $_POST['op']; + $edit = $_POST['edit']; + + if (($op == t('Update') || $op == t('Delete all')) && isset($edit['operation']) && isset($edit['files'])) { + $edit['files'] = array_diff($edit['files'], array(0)); + if (count($edit['files']) == 0) { + form_set_error('', t('Please select some items to perform the update on.')); + } + else { + if ($edit['operation'] == 'delete') { + // Mass delete + if ($edit['confirm']) { + upload_delete('files', $edit['files']); + drupal_set_message(t('The items have been deleted.')); + } + else { + $extra = ''; + $extra .= form_hidden('operation', 'delete'); + + $output = theme('confirm', + t('Are you sure you want to delete these items?'), + 'upload', + t('This action cannot be undone.'), + t('Delete all'), + t('Cancel'), + $extra); + return $output; + } + } + } + } + + $output .= upload_statistics(); + + $header = array( + NULL, + array('data' => t('Filename'), 'field' => 'filename'), + array('data' => t('Title'), 'field' => 'title'), + array('data' => t('Size'), 'field' => 'filesize', 'sort' => 'desc') + ); + + $sql = 'SELECT f.fid, n.nid, n.status, n.title, f.filename, f.filepath, f.filesize, n.uid FROM {files} f INNER JOIN {node} n ON f.nid = n.nid WHERE n.uid = %d'; + $sql .= tablesort_sql($header); + $result = pager_query($sql, 50, 0, NULL, $user->uid); + + // Make sure the update controls are disabled if we don't have any rows + // to select from. + $disabled = !db_num_rows($result); + + $options = array(); + foreach ($operations as $key => $value) { + $options[$key] = $value[0]; + } + + $form = form_select(NULL, 'operation', 0, $options, NULL, ($disabled ? 'disabled="disabled"' : '')); + $form .= form_submit(t('Update'), 'op', ($disabled ? array('disabled' => 'disabled') : array())); + + $output .= form_group(t('Update options'), "
$form
"); + + $destination = drupal_get_destination(); + while ($file = db_fetch_object($result)) { + $rows[] = array( + form_checkbox(NULL, 'files]['. $file->fid, 1, 0), + ''. check_plain($file->filename) .'', + ($file->status) ? l($file->title, "node/$file->nid") : $file->title, + format_size($file->filesize) + ); + } + + if ($pager = theme('pager', NULL, 50, 0, tablesort_pager())) { + $rows[] = array(array('data' => $pager, 'colspan' => '4')); + } + + if (!$rows) { + $rows[] = array(array('data' => t('No uploaded files available.'), 'colspan' => '4')); + } + $output .= theme('table', $header, $rows); + return form($output, 'post', url('upload')); +} + +function upload_settings() { system_settings_save(); - $group .= form_textfield(t('Maximum resolution for uploaded images'), 'upload_max_resolution', variable_get('upload_max_resolution', 0), 15, 10, t('The maximum allowed image size expressed as WIDTHxHEIGHT (e.g. 640x480). Set to 0 for no restriction.')); + $upload_extensions_default = variable_get("upload_extensions_default", "jpg jpeg gif png txt html doc xls pdf ppt pps"); + $upload_uploadsize_default = variable_get("upload_uploadsize_default", 1); + $upload_usersize_default = variable_get("upload_usersize_default", 10); + + $group = form_textfield(t('Maximum resolution for uploaded images'), 'upload_max_resolution', variable_get('upload_max_resolution', 0), 15, 10, t('The maximum allowed image size expressed as WIDTHxHEIGHT (e.g. 640x480). Set to 0 for no restriction.')); + $group .= form_textfield(t('Default permitted file extensions'), "upload_extensions_default", $upload_extensions_default, 60, 255, t('Default extensions that users can upload. Separate extensions with a space and do not include the leading dot.')); + $group .= form_textfield(t('Default maximum file size per upload'), "upload_uploadsize_default", $upload_uploadsize_default, 5, 5, t('Default maximum size of a file a user can upload (in megabytes).')); + $group .= form_textfield(t('Default total file size per user'), "upload_usersize_default", $upload_usersize_default, 5, 5, t('Default maximum size of all files a user can have on the site (in megabytes).')); $output = form_group(t('General settings'), $group); $roles = user_roles(0, 'upload files'); foreach ($roles as $rid => $role) { - $group = form_textfield(t('Permitted file extensions'), "upload_extensions_$rid", variable_get("upload_extensions_$rid", "jpg jpeg gif png txt html doc xls pdf ppt pps"), 60, 255, t('Extensions that users in this role can upload. Separate extensions with a space and do not include the leading dot.')); - $group .= form_textfield(t('Maximum file size per upload'), "upload_uploadsize_$rid", variable_get("upload_uploadsize_$rid", 1), 5, 5, t('The maximum size of a file a user can upload (in megabytes).')); - $group .= form_textfield(t('Total file size per user'), "upload_usersize_$rid", variable_get("upload_usersize_$rid", 10), 5, 5, t('The maximum size of all files a user can have on the site (in megabytes).')); + $group = form_textfield(t('Permitted file extensions'), "upload_extensions_$rid", variable_get("upload_extensions_$rid", $upload_extensions_default), 60, 255, t('Extensions that users in this role can upload. Separate extensions with a space and do not include the leading dot.')); + $group .= form_textfield(t('Maximum file size per upload'), "upload_uploadsize_$rid", variable_get("upload_uploadsize_$rid", $upload_uploadsize_default), 5, 5, t('Maximum size of a file a user can upload (in megabytes).')); + $group .= form_textfield(t('Total file size per user'), "upload_usersize_$rid", variable_get("upload_usersize_$rid", $upload_usersize_default), 5, 5, t('Maximum size of all files a user can have on the site (in megabytes).')); $output .= form_group(t('Settings for %role', array('%role' => theme('placeholder', $role))), $group); } @@ -143,6 +485,7 @@ function upload_nodeapi(&$node, $op, $ar break; case 'validate': + $node->files = upload_load($node); // Double check existing files: @@ -164,60 +507,62 @@ function upload_nodeapi(&$node, $op, $ar } } - if (($file = file_check_upload('upload')) && user_access('upload files')) { - global $user; - - $file = _upload_image($file); - - // Don't do any checks for uid #1. - if ($user->uid != 1) { - // Validate file against all users roles. Only denies an upload when - // all roles prevent it. - $total_usersize = upload_space_used($user->uid) + $filesize; - foreach ($user->roles as $rid => $name) { - $extensions = variable_get("upload_extensions_$rid", 'jpg jpeg gif png txt html doc xls pdf ppt pps'); - $uploadsize = variable_get("upload_uploadsize_$rid", 1) * 1024 * 1024; - $usersize = variable_get("upload_usersize_$rid", 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']++; + if ($file = file_check_upload('upload')) { + if (user_access('upload files')) { + global $user; + + $file = _upload_image($file); + + // Don't do any checks for uid #1. + if ($user->uid != 1) { + // Validate file against all users roles. Only denies an upload when + // all roles prevent it. + $total_usersize = upload_space_used($user->uid) + $filesize; + 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")); + $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", 10)) * 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']++; + } } } - } - // Rename possibly executable scripts to prevent accidental execution. - // Uploaded files are attachments and should be shown in their original - // form, rather than run. - if (preg_match('/\.(php|pl|py|cgi|asp)$/i', $file->filename)) { - $file->filename .= '.txt'; - $file->filemime = 'text/plain'; - } + // Rename possibly executable scripts to prevent accidental execution. + // Uploaded files are attachments and should be shown in their original + // form, rather than run. + if (preg_match('/\.(php|pl|py|cgi|asp)$/i', $file->filename)) { + $file->filename .= '.txt'; + $file->filemime = 'text/plain'; + } - if ($error['extension'] == count($user->roles) && $user->uid != 1) { - 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' => theme('placeholder', $file->filename), '%files-allowed' => theme('placeholder', $extensions)))); - } - elseif ($error['uploadsize'] == count($user->roles) && $user->uid != 1) { - 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' => theme('placeholder', $file->filename), '%maxsize' => theme('placeholder', format_size($uploadsize))))); - } - elseif ($error['usersize'] == count($user->roles) && $user->uid != 1) { + if ($error['extension'] == count($user->roles) && $user->uid != 1) { + 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' => theme('placeholder', $file->filename), '%files-allowed' => theme('placeholder', $extensions)))); + } + elseif ($error['uploadsize'] == count($user->roles) && $user->uid != 1) { + 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' => theme('placeholder', $file->filename), '%maxsize' => theme('placeholder', format_size($uploadsize))))); + } + elseif ($error['usersize'] == count($user->roles) && $user->uid != 1) { 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' => theme('placeholder', $file->filename), '%quota' => theme('placeholder', format_size($usersize))))); - } - else { - $key = 'upload_'. count($_SESSION['file_uploads']); - $file->source = $key; - $file->list = 1; - $file = file_save_upload($file); - $node->files[$key] = $file; + } + else { + $key = 'upload_'. count($_SESSION['file_uploads']); + $file->source = $key; + $file->list = 1; + $file = file_save_upload($file); + $node->files[$key] = $file; + } } } break; @@ -284,7 +629,7 @@ function upload_nodeapi(&$node, $op, $ar break; case 'delete': - upload_delete($node); + upload_delete('node', $node); break; case 'search result': return $node->files ? format_plural(count($node->files), '1 attachment', '%count attachments') : null; @@ -317,7 +662,7 @@ function upload_nodeapi(&$node, $op, $ar * @param $uid * The integer user id of a user. * @return - * The ammount of disk space used by the user in bytes. + * The amount of disk space used by the user in bytes. */ function upload_space_used($uid) { return db_result(db_query('SELECT SUM(f.filesize) FROM {files} f INNER JOIN {node_revisions} n ON f.vid = n.vid WHERE uid = %d', $uid)); @@ -327,7 +672,7 @@ function upload_space_used($uid) { * Determine how much disk space is occupied by uploaded files. * * @return - * The ammount of disk space used by uploaded files in bytes. + * The amount of disk space used by uploaded files in bytes. */ function upload_total_space_used() { return db_result(db_query('SELECT SUM(f.filesize) FROM {files} f INNER JOIN {node_revisions} n ON f.vid = n.vid')); @@ -375,12 +720,25 @@ function upload_save($node) { return; } -function upload_delete($node) { - $node->files = upload_load($node); - foreach ($node->files as $file) { - file_delete($file->filepath); +function upload_delete($op, $arg) { + switch($op) { + case 'node': + $node = $arg; + $node->files = upload_load($node); + foreach ($node->files as $file) { + file_delete($file->filepath); + } + db_query('DELETE FROM {files} WHERE nid = %d', $node->nid); + break; + case 'files': + foreach ($arg as $fid => $value) { + db_query('SELECT filepath FROM {files} WHERE fid = %d', $fid); + $file = db_fetch_object($result); + file_delete($file->filepath); + db_query('DELETE FROM {files} WHERE fid = %d', $fid); + } + break; } - db_query("DELETE FROM {files} WHERE nid = %d", $node->nid); } function upload_form($node) {