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'))); } } /** * Implementation of hook_perm(). */ function upload_perm() { return array('upload files', 'view uploaded files'); } /** * Implementation of hook_link(). */ function upload_link($type, $node = 0, $main = 0) { $links = array(); // Display a link with the number of attachments if ($main && $type == 'node' && $node->files && user_access('view uploaded files')) { $num_files = 0; foreach ($node->files as $file) { if ($file->list) { $num_files++; } } if ($num_files) { $links[] = l(format_plural($num_files, '1 attachment', '%count attachments'), "node/$node->nid", array('title' => t('Read full article to view attachments.')), NULL, 'attachments'); } } return $links; } /** * Implementation of hook_menu(). */ function upload_menu($may_cache) { $items = array(); if ($may_cache) { $items[] = array( 'path' => 'upload/js', 'callback' => 'upload_js', 'access' => user_access('upload files'), 'type' => MENU_CALLBACK ); } else { // Add handlers for previewing new uploads. if ($_SESSION['file_uploads']) { foreach ($_SESSION['file_uploads'] as $key => $file) { $filename = file_create_filename($file->filename, file_create_path()); $items[] = array( 'path' => $filename, 'title' => t('file download'), 'callback' => 'upload_download', 'access' => user_access('view uploaded files'), 'type' => MENU_CALLBACK ); $_SESSION['file_uploads'][$key]->_filename = $filename; } } } return $items; } function upload_settings() { $form['settings_general'] = array('#type' => 'fieldset', '#title' => t('General settings')); $form['settings_general']['upload_max_resolution'] = array( '#type' => 'textfield', '#title' => t('Maximum resolution for uploaded images'), '#default_value' => variable_get('upload_max_resolution', 0), '#size' => 15, '#maxlength' => 10, '#description' => t('The maximum allowed image size expressed as WIDTHxHEIGHT (e.g. 640x480). Set to 0 for no restriction.') ); $roles = user_roles(0, 'upload files'); foreach ($roles as $rid => $role) { $form["settings_role_$rid"] = array('#type' => 'fieldset', '#title' => t('Settings for %role', array('%role' => theme('placeholder', $role))), '#collapsible' => TRUE, '#collapsed' => TRUE); $form["settings_role_$rid"]["upload_extensions_$rid"] = array( '#type' => 'textfield', '#title' => t('Permitted file extensions'), '#default_value' => variable_get("upload_extensions_$rid", "jpg jpeg gif png txt html doc xls pdf ppt pps"), '#size' => 60, '#maxlength' => 255, '#description' => t('Extensions that users in this role can upload. Separate extensions with a space and do not include the leading dot.') ); $form["settings_role_$rid"]["upload_uploadsize_$rid"] = array( '#type' => 'textfield', '#title' => t('Maximum file size per upload'), '#default_value' => variable_get("upload_uploadsize_$rid", 1), '#size' => 5, '#maxlength' => 5, '#description' => t('The maximum size of a file a user can upload (in megabytes).') ); $form["settings_role_$rid"]["upload_usersize_$rid"] = array( '#type' => 'textfield', '#title' => t('Total file size per user'), '#default_value' => variable_get("upload_usersize_$rid", 10), '#size' => 5, '#maxlength' => 5, '#description' => t('The maximum size of all files a user can have on the site (in megabytes).') ); } return $form; } function upload_download() { foreach ($_SESSION['file_uploads'] as $file) { if ($file->_filename == $_GET['q']) { file_transfer($file->filepath, array('Content-Type: '. $file->filemime, 'Content-Length: '. $file->filesize)); } } } function upload_file_download($file) { if (user_access('view uploaded files')) { $file = file_create_path($file); $result = db_query(db_rewrite_sql("SELECT f.nid, f.* FROM {files} f WHERE filepath = '%s'", 'f'), $file); if ($file = db_fetch_object($result)) { $name = mime_header_encode($file->filename); // Serve images and text inline for the browser to display rather than download. $disposition = ereg('^(text/|image/)', $file->filemime) ? 'inline' : 'attachment'; return array('Content-Type: '. $file->filemime .'; name='. $name, 'Content-Length: '. $file->filesize, 'Content-Disposition: '. $disposition .'; filename='. $name); } } } /** * Implementation of hook_nodeapi(). */ function upload_nodeapi(&$node, $op, $arg) { switch ($op) { case 'settings': $form['upload_'. $node->type] = array( '#type' => 'radios', '#title' => t('Attachments'), '#default_value' => variable_get('upload_'. $node->type, 1), '#options' => array(t('Disabled'), t('Enabled')) ); return $form; case 'validate': $node->files = upload_load($node); // Double check existing files: if (is_array($node->list)) { foreach ($node->list as $key => $value) { if ($file = file_check_upload($key)) { $node->files[$file->source] = $file; $node->files[$key]->list = $node->list[$key]; $node->files[$key]->remove = $node->remove[$key]; $node->files[$key]->description = $node->description[$key]; if ($file->source) { $filesize += $file->filesize; } } } } else { foreach ($node->files as $key => $file) { $node->list[$key] = $file->list; } } 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']++; } } } // 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) { 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; } } for ($x = 0; $x < count($_SESSION['file_uploads']); $x++) { $key = 'upload_' . $x; if ($file = file_check_upload($key)) { $node->files[$key] = $file; } } break; case 'form': if (variable_get("upload_$node->type", 1) == 1 && user_access('upload files')) { $output = upload_form($node); $output['#attributes'] = array('enctype' => 'multipart/form-data'); } break; case 'load': if (variable_get("upload_$node->type", 1) == 1) { $output['files'] = upload_load($node); } break; case 'view': if ($node->files && user_access('view uploaded files')) { $header = array(t('Attachment'), t('Size')); $rows = array(); $previews = array(); // Build list of attached files foreach ($node->files as $file) { if ($file->list) { $rows[] = array( ''. check_plain($file->description ? $file->description : $file->filename) .'', format_size($file->filesize) ); // We save the list of files still in preview for later if (!$file->fid) { $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). if (!variable_get('clean_url', 0)) { 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); } } $teaser = $arg; // Add the attachments list if (count($rows) && !$teaser) { $node->body .= theme('table', $header, $rows, array('id' => 'attachments')); } } break; case 'insert': case 'update': if (user_access('upload files')) { upload_save($node); } break; case 'delete': upload_delete($node); break; case 'search result': return $node->files ? format_plural(count($node->files), '1 attachment', '%count attachments') : null; case 'rss item': if ($node->files) { $files = array(); foreach ($node->files as $file) { if ($file->list) { $files[] = $file; } } if (count($files) > 0) { // RSS only allows one enclosure per item $file = array_shift($files); return array(array('key' => 'enclosure', 'attributes' => array('url' => file_create_url($file->filepath), 'length' => $file->filesize, 'type' => $file->filemime))); } } break; } return $output; } /** * Determine how much disk space is occupied by a user's uploaded files. * * @param $uid * The integer user id of a user. * @return * The ammount 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)); } /** * Determine how much disk space is occupied by uploaded files. * * @return * The ammount 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')); } function upload_save($node) { foreach ((array)$node->files as $key => $file) { if ($file->source && !$file->remove) { // Clean up the session: unset($_SESSION['file_uploads'][$file->source]); // Insert new files: if ($file = file_save_upload($file, $file->filename)) { $fid = db_next_id('{files}_fid'); db_query("INSERT INTO {files} (fid, nid, vid, filename, filepath, filemime, filesize, list, description) VALUES (%d, %d, %d, '%s', '%s', '%s', %d, %d, '%s')", $fid, $node->nid, $node->vid, $file->filename, $file->filepath, $file->filemime, $file->filesize, $node->list[$key], $file->description); } } } // Remove or update existing files: foreach ((array)$node->remove as $key => $value) { if ($node->remove[$key]) { db_query('DELETE FROM {files} WHERE fid = %d AND vid = %d', $key, $node->vid); // We only delete a file if it isn't used anymore by any revision. $count = db_result(db_query('SELECT COUNT(fid) FROM {files} WHERE fid = %d', $key)); if (!($count > 0)) { file_delete($file->filepath); } } } foreach ((array)$node->list as $key => $value) { if (!$node->remove[$key]) { db_query('UPDATE {files} SET list = %d, description = \'%s\' WHERE fid = %d AND vid = %d', $node->list[$key], $node->description[$key], $key, $node->vid); } } if ($node->old_vid) { foreach ((array)$node->remove as $key => $remove) { if (!$remove) { $file = db_fetch_object(db_query('SELECT * FROM {files} WHERE vid = %d AND fid = %d', $node->old_vid, $key)); db_query("INSERT INTO {files} (fid, nid, vid, filename, filepath, filemime, filesize, list, description) VALUES (%d, %d, %d, '%s', '%s', '%s', %d, %d, '%s')", $key, $node->nid, $node->vid, $file->filename, $file->filepath, $file->filemime, $file->filesize, $file->list, $file->description); } } } return; } function upload_delete($node) { $node->files = upload_load($node); foreach ($node->files as $file) { file_delete($file->filepath); } db_query("DELETE FROM {files} WHERE nid = %d", $node->nid); } function upload_form($node) { drupal_add_js('misc/progress.js'); drupal_add_js('misc/upload.js'); $form['attachments'] = array( '#type' => 'fieldset', '#title' => t('File attachments'), '#collapsible' => TRUE, '#collapsed' => empty($node->files), '#description' => t('Changes made to the attachments are not permanent until you save this post. The first "listed" file will be included in RSS feeds.'), '#prefix' => '
', '#suffix' => '
' ); $form['attachments'] += _upload_form($node); return $form; } function _upload_form($node) { $header = array(t('Delete'), t('List'), t('Description'), t('Size')); $rows = array(); $output = ''; $form['#theme'] = 'upload_form_new'; if (is_array($node->files) && count($node->files)) { $form['current']['#theme'] = 'upload_form_current'; $form['current']['description']['#tree'] = TRUE; foreach ($node->files as $key => $file) { $options[$key] = ''; if ($file->remove) { $remove[] = $key; } if ($file->list) { $list[] = $key; } $description = "". file_create_url(($file->fid ? $file->filepath : file_create_filename($file->filename, file_create_path()))) .""; $form['current']['description'][$key] = array('#type' => 'textfield', '#default_value' => $file->description ? $file->description : $file->filename, '#size' => 60, '#maxlength' => 256, '#description' => $description ); $form['current']['size'][$key] = array('#type' => 'markup', '#value' => format_size($file->filesize)); } $form['current']['remove'] = array('#type' => 'checkboxes', '#options' => $options, '#default_value' => $remove); $form['current']['list'] = array('#type' => 'checkboxes', '#options' => $options, '#default_value' => $list); $form['files'][$key] = array('#type' => 'hidden', '#value' => 1); } if (user_access('upload files')) { $form['new']['upload'] = array('#type' => 'file', '#title' => t('Attach new file'), '#size' => 40); $form['new']['fileop'] = array('#type' => 'button', '#value' => t('Attach'), '#name'=> 'fileop', '#attributes' => array('id' => 'fileop')); // The class triggers the js upload behaviour. $form['fileop'] = array('#type' => 'hidden', '#value' => url('upload/js', NULL, NULL, TRUE), '#attributes' => array('class' => 'upload')); } return $form; } function theme_upload_form_new($form) { $output .= '
' . "\n"; $output .= '
' . "\n"; $output .= form_render($form) . "\n"; $output .= "
\n"; $output .= "
\n"; return $output; } function theme_upload_form_current(&$form) { $header = array(t('Delete'), t('List'), t('Description'), t('Size')); foreach (element_children($form['description']) as $key) { $row = array(); $row[] = form_render($form['remove'][$key]); $row[] = form_render($form['list'][$key]); $row[] = form_render($form['description'][$key]); $row[] = form_render($form['size'][$key]); $rows[] = $row; } $output = theme('table', $header, $rows); $output .= form_render($form); return $output; } function upload_load($node) { $files = array(); if ($node->vid) { $result = db_query("SELECT * FROM {files} WHERE vid = %d", $node->vid); while ($file = db_fetch_object($result)) { $files[$file->fid] = $file; } } 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', 0)); 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' => theme('placeholder', variable_get('upload_max_resolution', 0))))); } } } return $file; } /** * Menu-callback for JavaScript-based uploads. */ function upload_js() { // We only do the upload.module part of the node validation process. $node = array2object($_POST['edit']); upload_nodeapi($node, 'validate', NULL); $form = _upload_form($node); $form = _form_builder($form); $output = theme('status_messages') . form_render($form); // We send the updated file attachments form. print drupal_call_js('window.parent.iframeHandler', $output); exit; }