Index: modules/upload/upload.module =================================================================== RCS file: /cvs/drupal/drupal/modules/upload/upload.module,v retrieving revision 1.152 diff -u -r1.152 upload.module --- modules/upload/upload.module 31 Jan 2007 15:49:26 -0000 1.152 +++ modules/upload/upload.module 2 Feb 2007 07:12:02 -0000 @@ -5,6 +5,7 @@ * @file * File-handling and attaching files to nodes. * + * @todo move {files} table db handling into file.inc. */ /** @@ -81,24 +82,6 @@ $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. */ @@ -241,93 +224,73 @@ '#description' => t('The maximum size of all files a user can have on the site (in megabytes).'), ); } - 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)); - } - } -} - function upload_file_download($file) { $file = file_create_path($file); $result = db_query("SELECT f.* FROM {files} f 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; - } + if (!user_access('view uploaded files')) { + return -1; } - else { + $node = node_load($file->nid); + if (!node_access('view', $node)) { return -1; } + $type = mime_header_encode($file->filemime); + return array( + 'Content-Type: '. $type, + 'Content-Length: '. $file->filesize, + ); } } /** * Save new uploads and attach them to the node object. - * append file_previews to the node object as well. + * append new_uploads to the node object as well. + * + * @note: I can't remember why exactly this was done in prepare. + * I assume it is so the node will have the files attached when + * we reach validation. This way other modules can validate the file + * on nodeapi op == validate. -dopry */ function _upload_prepare(&$node) { - - // 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']); - } - } - - // $_SESSION['file_current_upload'] 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['file_current_upload']); - global $user; + + if (!$_POST) { + unset($_SESSION['upload_new_files']); + } // 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_'. 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; - } - - // 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; - } + $file->fid = db_next_id('{files}_fid'); + // Here something.php.pps becomes something.php_.pps + if ($user->uid != 1) { + $file->filename = upload_munge_filename($file->filename, NULL, 0); + $file->description = $file->filename; + } + + $file->list = variable_get('upload_list_default', 1); + + // The current flag lets up know this file is currently being uploaded. + // This flag is set so we can give the list checkbox a proper value when + // the upload form is rebuilt. Since unchecked checkboxes do not return + // a post value form_builder cannot tell the difference between a new checkbox + // and an unchecked checkbox when assigning a form value. + $file->current = TRUE; + + // Tell upload module to insert into instead of update file_revisions. + $file->new = TRUE; + $_SESSION['upload_new_files'][$file->fid] = $file; + } + + // Attach new files to the node object. + if (!empty($_SESSION['upload_new_files']) && is_array($_SESSION['upload_new_files'])) { + foreach ($_SESSION['upload_new_files'] as $fid => $file) { $node->files[$fid] = $file; } } @@ -391,19 +354,19 @@ } function _upload_validate(&$node) { + global $user; // 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 && !$file->remove) { - global $user; + if ($file->new && !$file->remove) { + $valid = TRUE; // Bypass validation for uid = 1. if ($user->uid != 1) { @@ -436,7 +399,6 @@ } $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; @@ -453,10 +415,22 @@ 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; } + } + // End uid !=1 validation. - if (!$valid) { - unset($node->files[$fid], $_SESSION['file_previews'][$fid]); - file_delete($file->filepath); + if (!$valid) { + unset($node->files[$fid], $_SESSION['upload_new_files'][$fid]); + file_delete($file->filepath); + } + elseif (!empty($file->current)) { + // Put the file in the database. With the file already stored in the database and its final + // location in the filesystem overcome difficulties associated with file previews in earlier + // versions of drupal. + if ($file = file_save_upload($file, $file->filename)) { + db_query("INSERT INTO {files} (fid, uid, filename, filepath, filemime, filesize) VALUES (%d, %d, '%s', '%s', '%s', %d)", $file->fid, $user->uid, $file->filename, $file->filepath, $file->filemime, $file->filesize); + } + else { + form_set_error('upload', t('File Save Failed')); } } } @@ -500,13 +474,17 @@ } } break; + case 'alter': if (isset($node->files) && user_access('view uploaded files')) { // Manipulate so that inline references work in preview + // @todo: remove this since files should be in the database if they + // pass validation. if (!variable_get('clean_url', 0)) { $previews = array(); foreach ($node->files as $file) { - if (strpos($file->fid, 'upload') !== FALSE) { + $file = (object)$file; + if (isset($file->new)) { $previews[] = $file; } } @@ -577,6 +555,7 @@ $header = array(t('Attachment'), t('Size')); $rows = array(); foreach ($files as $file) { + $file = (object)$file; if ($file->list) { $href = $file->fid ? file_create_url($file->filepath) : url(file_create_filename($file->filename, file_create_path())); $text = $file->description ? $file->description : $file->filename; @@ -674,45 +653,25 @@ if (!is_array($node->files)) { return; } - foreach ($node->files as $fid => $file) { // Convert file to object for compatibility $file = (object)$file; - // Remove file. Process removals first since no further processing - // will be required. + // Process removals first since no further processing + // Files will no longer be deleted from the filesystem when disassociated from a node. + // They will remain on the server filesystem until all nodes using it are removed and the + // user deletes the file from the user file browser. 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 {file_revisions} WHERE fid = %d AND vid = %d', $file->fid, $node->vid); } // 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]); + elseif ($file->new) { + db_query("INSERT INTO {file_revisions} (fid, nid, vid, list, description) VALUES (%d, %d, %d, %d, '%s')", $file->fid, $node->nid, $node->vid, $file->list, $file->description); } // Create a new revision, as needed - elseif ($node->old_vid && is_numeric($fid)) { + elseif (!empty($node->old_vid)) { db_query("INSERT INTO {file_revisions} (fid, vid, list, description) VALUES (%d, %d, %d, '%s')", $file->fid, $node->vid, $file->list, $file->description); } @@ -721,39 +680,17 @@ db_query("UPDATE {file_revisions} SET list = %d, description = '%s' WHERE fid = %d AND vid = %d", $file->list, $file->description, $file->fid, $node->vid); } } + + // The node is being submitted, we can unset the temporary data store. + unset($_SESSION['upload_new_files']); } function upload_delete($node) { - $files = array(); - $result = db_query('SELECT * FROM {files} WHERE 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); - file_delete($file->filepath); - } - - // Delete all files associated with the node - db_query('DELETE FROM {files} WHERE nid = %d', $node->nid); + // delete a links to a node. + db_query('DELETE FROM {file_revisions} 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)); - - // if the file won't be used, delete it - if ($count < 2) { - db_query('DELETE FROM {files} WHERE fid = %d', $file->fid); - file_delete($file->filepath); - } - } - } - // delete the revision db_query('DELETE FROM {file_revisions} WHERE vid = %d', $node->vid); } @@ -768,20 +705,21 @@ foreach ($node->files as $key => $file) { $description = file_create_url((strpos($file->fid, 'upload') === FALSE ? $file->filepath : file_create_filename($file->filename, file_create_path()))); $description = "". check_plain($description) .""; - $form['files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => (strlen($file->description)) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => $description ); + $form['files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => (!empty($file->description)) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => $description ); $form['files'][$key]['size'] = array('#value' => format_size($file->filesize)); - $form['files'][$key]['remove'] = array('#type' => 'checkbox', '#default_value' => $file->remove); - $form['files'][$key]['list'] = array('#type' => 'checkbox', '#default_value' => $file->list); + $form['files'][$key]['remove'] = array('#type' => 'checkbox', '#default_value' => isset($file->remove) ? $file->remove : FALSE ); + $form['files'][$key]['list'] = array('#type' => 'checkbox', '#default_value' => isset($file->list) ? $file->list : FALSE ); // 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 ($_SESSION['file_current_upload'] == $file->fid) { + if (isset($file->current)) { $form['files'][$key]['list']['#value'] = variable_get('upload_list_default',1); } $form['files'][$key]['filename'] = array('#type' => 'value', '#value' => $file->filename); $form['files'][$key]['filepath'] = array('#type' => 'value', '#value' => $file->filepath); $form['files'][$key]['filemime'] = array('#type' => 'value', '#value' => $file->filemime); $form['files'][$key]['filesize'] = array('#type' => 'value', '#value' => $file->filesize); + $form['files'][$key]['new'] = array('#type' => 'value', '#value' => isset($file->new)); $form['files'][$key]['fid'] = array('#type' => 'value', '#value' => $file->fid); } } @@ -833,14 +771,12 @@ 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); while ($file = db_fetch_object($result)) { $files[$file->fid] = $file; } } - return $files; } Index: modules/system/system.install =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.install,v retrieving revision 1.76 diff -u -r1.76 system.install --- modules/system/system.install 31 Jan 2007 21:26:56 -0000 1.76 +++ modules/system/system.install 2 Feb 2007 07:12:07 -0000 @@ -3543,6 +3543,23 @@ return $ret; } +function system_update_2002() { + $ret = array(); + switch ($GLOBALS['db_type']) { + case 'mysql': + case 'mysqli': + // Add nid to file_revisions table since it is basically becoming the node_files table. + $ret[] = update_sql('ALTER TABLE {file_revisions} ADD COLUMN nid int unsigned NOT NULL default 0 AFTER fid'); + $ret[] = update_sql('UPDATE {file_revisions} fr JOIN {files} f ON fr.fid = f.fid SET fr.nid = f.nid'); + + // Change owernship of files to users. + $ret[] = update_sql('ALTER TABLE {files} CHANGE COLUMN nid uid int unsigned NOT NULL default 0'); + $ret[] = update_sql('UPDATE {files} f JOIN {node} n ON f.uid = n.nid SET f.uid = n.uid'); + break; + } + return $ret; +} + /** * @} End of "defgroup updates-5.0-to-x.x" * The next series of updates should start at 3000.