diff --git a/modules/simpletest/tests/upgrade/drupal-6.upload.database.php b/modules/simpletest/tests/upgrade/drupal-6.upload.database.php index 46ebe2c..7315f55 100644 --- a/modules/simpletest/tests/upgrade/drupal-6.upload.database.php +++ b/modules/simpletest/tests/upgrade/drupal-6.upload.database.php @@ -127,6 +127,48 @@ db_insert('files')->fields(array( 'status' => '1', 'timestamp' => '1285708958', )) +// On some Drupal 6 sites, more than one file can have the same filepath. See +// https://www.drupal.org/node/1260938. +->values(array( + 'fid' => '12', + 'uid' => '1', + 'filename' => 'duplicate-name.png', + 'filepath' => 'sites/default/files/duplicate-name.png', + 'filemime' => 'image/png', + 'filesize' => '314', + 'status' => '1', + 'timestamp' => '1285708958', +)) +->values(array( + 'fid' => '13', + 'uid' => '1', + 'filename' => 'duplicate-name.png', + 'filepath' => 'sites/default/files/duplicate-name.png', + 'filemime' => 'image/png', + 'filesize' => '314', + 'status' => '1', + 'timestamp' => '1285708958', +)) +->values(array( + 'fid' => '14', + 'uid' => '1', + 'filename' => 'duplicate-name.png', + 'filepath' => 'sites/default/files/duplicate-name.png', + 'filemime' => 'image/png', + 'filesize' => '314', + 'status' => '1', + 'timestamp' => '1285708958', +)) +->values(array( + 'fid' => '15', + 'uid' => '1', + 'filename' => 'duplicate-name.png', + 'filepath' => 'sites/default/files/duplicate-name.png', + 'filemime' => 'image/png', + 'filesize' => '314', + 'status' => '1', + 'timestamp' => '1285708958', +)) ->execute(); db_insert('node')->fields(array( @@ -197,6 +239,23 @@ db_insert('node')->fields(array( 'tnid' => '0', 'translate' => '0', )) +->values(array( + 'nid' => '41', + 'vid' => '55', + 'type' => 'page', + 'language' => '', + 'title' => 'node title 41 revision 55', + 'uid' => '1', + 'status' => '1', + 'created' => '1285709012', + 'changed' => '1285709012', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) ->execute(); db_insert('node_revisions')->fields(array( @@ -254,6 +313,28 @@ db_insert('node_revisions')->fields(array( 'timestamp' => '1285709012', 'format' => '1', )) +->values(array( + 'nid' => '41', + 'vid' => '54', + 'uid' => '1', + 'title' => 'node title 41 revision 54', + 'body' => "Attachments:\r\nduplicate-name.png", + 'teaser' => "Attachments:\r\nduplicate-name.png", + 'log' => '', + 'timestamp' => '1285709012', + 'format' => '1', +)) +->values(array( + 'nid' => '41', + 'vid' => '55', + 'uid' => '1', + 'title' => 'node title 41 revision 55', + 'body' => "Attachments:\r\nduplicate-name.png\r\nduplicate-name.png\r\nduplicate-name.png", + 'teaser' => "Attachments:\r\nduplicate-name.png\r\nduplicate-name.png\r\nduplicate-name.png", + 'log' => '', + 'timestamp' => '1285709012', + 'format' => '1', +)) ->execute(); db_create_table('upload', array( @@ -415,6 +496,38 @@ db_insert('upload')->fields(array( 'list' => '1', 'weight' => '0', )) +->values(array( + 'fid' => '12', + 'nid' => '41', + 'vid' => '54', + 'description' => 'duplicate-name.png', + 'list' => '1', + 'weight' => '0', +)) +->values(array( + 'fid' => '13', + 'nid' => '41', + 'vid' => '55', + 'description' => 'duplicate-name-hidden.png', + 'list' => '0', + 'weight' => '0', +)) +->values(array( + 'fid' => '14', + 'nid' => '41', + 'vid' => '55', + 'description' => 'duplicate-name-shown-1.png', + 'list' => '1', + 'weight' => '0', +)) +->values(array( + 'fid' => '15', + 'nid' => '41', + 'vid' => '55', + 'description' => 'duplicate-name-shown-2.png', + 'list' => '1', + 'weight' => '0', +)) ->execute(); // Add series of entries for invalid node vids to the {upload} table. @@ -431,7 +544,7 @@ for ($i = 30; $i < 250; $i += 2) { ->values(array( 'fid' => $i, 'nid' => '40', - 'vid' => 24 + $i, + 'vid' => 26 + $i, 'description' => 'crazy-basename.png', 'list' => '1', 'weight' => '0', @@ -440,7 +553,7 @@ for ($i = 30; $i < 250; $i += 2) { ->values(array( 'fid' => 2, 'nid' => '40', - 'vid' => 24 + $i + 1, + 'vid' => 26 + $i + 1, 'description' => 'crazy-basename.png', 'list' => '1', 'weight' => '0', diff --git a/modules/simpletest/tests/upgrade/upgrade.upload.test b/modules/simpletest/tests/upgrade/upgrade.upload.test index be352bd..211ed0a 100644 --- a/modules/simpletest/tests/upgrade/upgrade.upload.test +++ b/modules/simpletest/tests/upgrade/upgrade.upload.test @@ -64,12 +64,28 @@ class UploadUpgradePathTestCase extends UpgradePathTestCase { } $this->assertIdentical($filenames, $recorded_filenames, 'The uploaded files are present in the same order after the upgrade.'); } + // Test for the file with repeating basename to only have the streaming // path replaced. $node = node_load(40, 53); $repeated_basename_file = $node->upload[LANGUAGE_NONE][4]; $this->assertEqual($repeated_basename_file['uri'], 'private://drupal-6/file/directory/path/crazy-basename.png', "The file with the repeated basename path only had the stream portion replaced"); + // Ensure that duplicate filepaths are reduced. + $node0 = node_load(41, 54); + $node1 = node_load(41, 55); + // Ensure that both revisions point to the same file ID. + $items0 = field_get_items('node', $node0, 'upload'); + $this->assertEqual(count($items0), 1); + $items1 = field_get_items('node', $node1, 'upload'); + $this->assertEqual(count($items1), 1); + $this->assertEqual($items0[0]['fid'], $items1[0]['fid']); + // The revision with more than one reference to the same file should retain + // the first reference that isn't hidden. + $this->assertEqual($items1[0]['description'], 'duplicate-name-shown-1.png'); + // No duplicate files should remain on the Drupal 7 site. + $this->assertEqual(0, db_query("SELECT COUNT(*) FROM {file_managed} GROUP BY uri HAVING COUNT(fid) > 1")->fetchField()); + // Make sure the file settings were properly migrated. $d6_file_directory_temp = '/drupal-6/file/directory/temp'; $d6_file_directory_path = '/drupal-6/file/directory/path'; diff --git a/modules/system/system.install b/modules/system/system.install index 64c989a..e29df54 100644 --- a/modules/system/system.install +++ b/modules/system/system.install @@ -2806,12 +2806,72 @@ function system_update_7061(&$sandbox) { // Initialize batch update information. $sandbox['progress'] = 0; $sandbox['last_vid_processed'] = -1; - $sandbox['max'] = db_query("SELECT COUNT(*) FROM {system_update_7061}")->fetchField(); + $sandbox['max'] = db_query("SELECT COUNT(*) FROM {system_update_7061}")->fetchField() + + db_query('SELECT COUNT(*) FROM {files}')->fetchField(); + + // There is a chance that we have multiple file entries pointing to the same + // physical file, see https://www.drupal.org/node/1260938. + // Create batch requests to update those files, by deduplicating them + // and point to the first of the possible file IDs. + $sandbox['last_fid_processed'] = -1; + + // Therefore create a mapping table between old and new FIDs. + if (!db_table_exists('system_update_7061_fid_mapping')) { + $table = array( + 'fields' => array( + 'old_fid' => array('type' => 'int'), + 'new_fid' => array('type' => 'int'), + ), + 'primary key' => array('old_fid'), + ); + db_create_table('system_update_7061_fid_mapping', $table); + } + } + + $limit = variable_get('upload_update_batch_size', 100); + + $fid_filepath = db_query_range('SELECT fid, filepath FROM {files} WHERE fid > :lastfid ORDER BY FID ASC', 0, $limit, array(':lastfid' => $sandbox['last_fid_processed'])) + ->fetchAll(); + if (!empty($fid_filepath)) { + foreach ($fid_filepath as $row) { + // Check whether for the filepath of this particular file we have + // duplicate entries. + $duplicate_fids = db_query('SELECT fid FROM {files} WHERE filepath = :filepath ORDER BY fid ASC', array(':filepath' => $row->filepath)) + ->fetchCol(); + + // Detect whether we have duplicates. + if (count($duplicate_fids) > 1) { + // In case we have duplicate FIDs for a given filepath, keep just the first + // one. + $kept_fid = array_shift($duplicate_fids); + db_delete('files') + ->condition('fid', $duplicate_fids, 'IN') + ->execute(); + db_update('upload') + ->fields(array('fid' => $kept_fid)) + ->condition('fid', $duplicate_fids, 'IN') + ->execute(); + + $insert = db_insert('system_update_7061_fid_mapping') + ->fields(array('old_fid', 'new_fid')); + foreach ($duplicate_fids as $fid) { + $insert->values(array('old_fid' => $fid, 'new_fid' => $kept_fid)); + } + $insert->execute(); + } + + $sandbox['last_fid_processed'] = $row->fid; + $sandbox['progress']++; + } + + // In case we update still files we skip the other part of the update + // function. + $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max']; + return; } // Determine vids for this batch. // Process all files attached to a given revision during the same batch. - $limit = variable_get('upload_update_batch_size', 100); $vids = db_query_range('SELECT vid FROM {system_update_7061} WHERE vid > :lastvid ORDER BY vid', 0, $limit, array(':lastvid' => $sandbox['last_vid_processed'])) ->fetchCol();