diff --git a/filefield_paths.info b/filefield_paths.info index 813ebdd..5bfde40 100644 --- a/filefield_paths.info +++ b/filefield_paths.info @@ -19,4 +19,4 @@ files[] = modules/video.inc ; Simpletest files. ; files[] = tests/file.test -; files[] = tests/filefield_paths.test +files[] = tests/filefield_paths.test diff --git a/filefield_paths.module b/filefield_paths.module index ff4e32a..ae65583 100644 --- a/filefield_paths.module +++ b/filefield_paths.module @@ -104,6 +104,18 @@ function filefield_paths_form_alter(&$form, $form_state, $form_id) { '#weight' => 10, ); + // File exists behavior. Keep disabled if the file name option is the same as the original file, as this can result in + // the uploaded file itself being moved onto itself and then deleted. + $form['instance']['settings']['filefield_paths']['replace_existing_files'] = array( + '#type' => 'checkbox', + '#title' => t('Replace existing files'), + '#description' => t('If a file with the same name already exists in the destination directory, replace it') . '.' . + '
' . t('To use this feature, you must change the "File name" option to be something other than the exact name of the uploaded file') . '.', + '#weight' => 11, + '#default_value' => isset($settings['replace_existing_files']) ? $settings['replace_existing_files'] : FALSE, + '#disabled' => isset($settings['file_name']) && $settings['file_name']['value'] == '[file:ffp-name-only-original].[file:ffp-extension-original]' ? TRUE : FALSE + ); + // Retroactive updates. $form['instance']['settings']['filefield_paths']['retroactive_update'] = array( '#type' => 'checkbox', @@ -111,7 +123,7 @@ function filefield_paths_form_alter(&$form, $form_state, $form_id) { '#description' => t('Move and rename previously uploaded files') . '.' . '
' . t('Warning') . ': ' . t('This feature should only be used on developmental servers or with extreme caution') . '.', - '#weight' => 11, + '#weight' => 12, ); // Active updating. @@ -122,7 +134,7 @@ function filefield_paths_form_alter(&$form, $form_state, $form_id) { '#description' => t('Actively move and rename previously uploaded files as required') . '.' . '
' . t('Warning') . ': ' . t('This feature should only be used on developmental servers or with extreme caution') . '.', - '#weight' => 12 + '#weight' => 13 ); } } diff --git a/modules/filefield_paths.inc b/modules/filefield_paths.inc index 6e8eefc..8811ed8 100644 --- a/modules/filefield_paths.inc +++ b/modules/filefield_paths.inc @@ -65,7 +65,8 @@ function filefield_paths_filefield_paths_process_file($type, $entity, $field, $i // Finalize file if necessary. if ($file !== $old_file) { $dirname = drupal_dirname($file['uri']); - if (file_prepare_directory($dirname, FILE_CREATE_DIRECTORY) && file_move((object) $old_file, $file['uri'])) { + $file_exists_behavior = empty($settings['replace_existing_files'])? FILE_EXISTS_RENAME : FILE_EXISTS_REPLACE; + if (file_prepare_directory($dirname, FILE_CREATE_DIRECTORY) && file_move((object) $old_file, $file['uri'], $file_exists_behavior)) { // Process regular expression. _filefield_paths_replace_path($old_file['uri'], $file['uri'], $entity); diff --git a/tests/assets/test_file_a.txt b/tests/assets/test_file_a.txt new file mode 100644 index 0000000..9b7c2fa --- /dev/null +++ b/tests/assets/test_file_a.txt @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed scelerisque purus sit amet eros molestie id laoreet urna adipiscing. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis pretium adipiscing ullamcorper. In ut nisl et orci viverra condimentum a ut eros. Nullam at sollicitudin libero. Praesent eget odio nisi, placerat tristique purus. Donec tincidunt nulla a lectus condimentum fermentum. Quisque elit augue, porta quis varius at, tincidunt nec nunc. Quisque suscipit feugiat mollis. Donec varius malesuada lacus eget convallis. Donec non vestibulum ligula. Praesent ut tortor in diam pharetra euismod quis auctor nibh. \ No newline at end of file diff --git a/tests/filefield_paths.test b/tests/filefield_paths.test new file mode 100644 index 0000000..d9654b0 --- /dev/null +++ b/tests/filefield_paths.test @@ -0,0 +1,152 @@ +privileged_user = $this->drupalCreateUser(array( + 'bypass node access', + 'administer content types', + 'access administration pages', + )); + $this->drupalLogin($this->privileged_user); + + // Create a new content type with a file field + $this->file_field_name = 'filefield_paths_file'; + $this->content_type_name = $this->createContentTypeWithFileField($this->file_field_name); + + } + + /** + * Creates a content type and adds a file field to it + * + * @param string $file_field_name The name for the file field + * @return string The name of the content type + */ + public function createContentTypeWithFileField($file_field_name) { + $content_type = $this->drupalCreateContentType(); + + $field = array( + 'field_name' => $file_field_name, + 'type' => 'file', + ); + field_create_field($field); + + $field_instance = array( + 'field_name' => $file_field_name, + 'label' => $file_field_name, + 'required' => FALSE, + 'entity_type' => 'node', + 'bundle' => $content_type->name, + ); + field_create_instance($field_instance); + + return $content_type->name; + } + + /** + * Updates a field instance's filefield_paths settings + * + * @param array $settings An associative array of field instance settings. + * Valid keys: file_path, file_name, retroactive_update, active_updating + */ + public function updateFileFieldInstanceSettings($settings) { + $edit = array(); + if(isset($settings['file_path'])) + $edit['instance[settings][filefield_paths][file_path][value]'] = $settings['file_path']; + if(isset($settings['file_name'])) + $edit['instance[settings][filefield_paths][file_name][value]'] = $settings['file_name']; + if(isset($settings['retroactive_update'])) + $edit['instance[settings][filefield_paths][retroactive_update]'] = $settings['retroactive_update']; + if(isset($settings['active_update'])) + $edit['instance[settings][filefield_paths][active_updating]'] = $settings['active_update']; + if(isset($settings['replace_existing_files'])) + $edit['instance[settings][filefield_paths][replace_existing_files]'] = $settings['replace_existing_files']; + $this->drupalPost("admin/structure/types/manage/{$this->content_type_name}/fields/{$this->file_field_name}", $edit, t('Save settings')); + $this->assertText(t('Saved @content_type_name configuration.', array('@content_type_name' => $this->file_field_name))); + } + + /** + * Creates a new node and attaches a file + * + * @return int node id + */ + public function createNodeWithFile($path_to_file) { + // Create node to edit. + $edit = array(); + $edit['title'] = $this->randomName(8); + $edit["body[und][0][value]"] = $this->randomName(16); + $edit['files[' . $this->file_field_name . '_' . LANGUAGE_NONE . '_0]'] = $path_to_file; + $this->drupalPost('node/add/'.$this->content_type_name, $edit, t('Save')); + $this->assertText(t('@title has been created.', array('@title' => $edit['title']))); + // determine node id from url + preg_match('/.*\/(\d+)$/', $this->url, $matches); + return $matches[1]; // nid + } + +} + +/** + * Test option to overwrite existing files + */ +class FileFieldPathsOverwriteExistingTestCase extends FilefieldPathsTestCase { + + public static function getInfo() { + return array( + 'name' => 'Overwrite existing file functionality', + 'description' => 'Verify that existing files can be overwritten.', + 'group' => 'File (Field) Paths', + ); + } + + /** + * Test default file renaming behavior. + * Default filefield_paths behavior is to rename uploaded files if there is a collision. + */ + public function testDefaultRename() { + $file_path = drupal_get_path('module', 'filefield_paths') . '/tests/assets/test_file_a.txt'; + + // for these tests, we maintain the original filename, with ".processed" tacked on the end + $filefield_settings['file_name'] = '[file:ffp-name-only-original].[file:ffp-extension-original].processed'; + $this->updateFileFieldInstanceSettings($filefield_settings); + + // create a new node with a test file + $this->createNodeWithFile($file_path); + $this->assertTrue(file_exists(drupal_realpath('public://test_file_a.txt.processed')), 'File attached to node exists with original filename.'); + + // create another node and upload the same file + $this->createNodeWithFile($file_path); + $this->assertTrue(file_exists(drupal_realpath('public://test_file_a.txt.processed')), 'Originally uploaded file exists.'); + $this->assertTrue(file_exists(drupal_realpath('public://test_file_a.txt_0.processed')), 'Originally uploaded file exists.'); + } + + /** + * Test optional file overwrite behavior. + */ + public function testOverwrite() { + $file_path = drupal_get_path('module', 'filefield_paths') . '/tests/assets/test_file_a.txt'; + + // Enable replace files option + $filefield_settings['file_name'] = '[file:ffp-name-only-original].[file:ffp-extension-original].processed'; + $filefield_settings['replace_existing_files'] = 1; + $this->updateFileFieldInstanceSettings($filefield_settings); + + // create a new node with a test file + $this->createNodeWithFile($file_path); + $this->assertTrue(file_exists(drupal_realpath('public://test_file_a.txt.processed')), 'File attached to node exists with original filename.'); + + // create another node and upload the same file + $this->createNodeWithFile($file_path); + $this->assertTrue(file_exists(drupal_realpath('public://test_file_a.txt.processed')), 'Originally uploaded file has been overwritten.'); + $this->assertFalse(file_exists(drupal_realpath('public://test_file_a.txt_0.processed')), 'No renamed copy of the file exists.'); + } + +} \ No newline at end of file