4

I am attempting to write a patch for the Redirect module that automatically creates a redirect from the old file path to the new one, whenever a file is updated.

I added a hook to redirect.module:

function redirect_file_update($file){
      /**
     * Creates a new redirect from old file to new one when the new one has a different URI.
     */

  if (empty($file->original)) {
    $file->original = entity_load_unchanged('file', $file->fid);
  }
  if ($file->uri !== $file->original->uri) {
    $redirect = new stdClass();
    redirect_object_prepare(
        $redirect,
        array(
        'source' => file_create_url($file->original->uri),
        'source_options' => array(),
        'redirect' => file_create_url($file->uri),
        'redirect_options' => array(),
        )
    );
    module_invoke(
      'redirect',
      'object_prepare',
      $redirect,
      array(
        'source' => file_create_url($file->original->uri),
        'source_options' => array(),
        'redirect' => file_create_url($file->uri),
        'redirect_options' => array(),
      )
    );
    module_invoke('redirect', 'save', $redirect);
  }
}

and I verified with a dpm() that $file->original exists as expected when I'm working with files in the GUI.

But when I run tests it doesn't work. According to hook_file_update() docs, it should be called whenever file_save() is called, but it appears that the code inside my redirect_file_update() is never being called, entity_load_unchanged() is returning the exact same object as plain old $file, and my $file objects don't have an 'original' property as far as the test knows, even after saving. Here's the testing code:

function setUp(array $modules = array()) {
  parent::setUp($modules);

  $this->admin_user = $this->drupalCreateUser(array('administer redirects', 'access site reports', 'access content', 'create article content', 'edit any article content', 'create url aliases'));
  $this->drupalLogin($this->admin_user);
}
  // Test whether replacement of a file redirects the old file URI to the new one.
  function testFileReplacementRedirects () {
  // Create a few test files and save them.
  $files = $this->drupalGetTestFiles('image');
  foreach ($files as $file) {
    file_save($file);
  }
  // Get a list of all the files we just created.
  $fileQuery = new EntityFieldQuery();
  $files_list = $fileQuery->entityCondition('entity_type', 'file')
    ->propertyOrderBy('fid')
    ->execute();
  // Pick out two files to use for our test.
  $test_file_one = file_load($files_list['file'][1]->fid);
  $test_file_two = file_load($files_list['file'][2]->fid);

  // Move file one to file two's URI
  $old_url = file_create_url($test_file_one->uri);
  $new_url = file_create_url($test_file_two->uri);
  $test_file_one = file_move($test_file_one, $dest = $test_file_two->uri, $replace = FILE_EXISTS_REPLACE);
  // Load the old module's URI and see if it redirects to the new one.
  $this->drupalGet($old_url);
  $this_page = $this->drupalGetHeaders(TRUE);
  $this->assertEqual($this_page[0]['location'], $new_url, 'Redirected from ' . $old_url . ' to ' . $new_url . '.');
  $this->assertEqual($this_page[0][':status'], 'HTTP/1.1 301 Moved Permanently', 'Status: ' . $this_page[0][':status']);
}

What is going wrong?

3

$file->original is set from file_save() to make it available to hook_file_presave(), hook_entity_presave(), hook_file_insert(), hook_entity_insert(), hook_file_update(), and hook_entity_update(). It is then removed from file_save() before the function returns the file object to the caller.

// Load the stored entity, if any.
if (!empty($file->fid) && !isset($file->original)) {
  $file->original = entity_load_unchanged('file', $file->fid);
}

Clearly, if the file was not previously saved, $file->original doesn't get set.

  • 1
    Indeed, and that happens because DrupalWebTestCase::drupalGetTestFiles() doesn't create a managed file, it only copies a file into place ready to be saved. The file will be a new one every time, so no $original when file_save() is called :) – Clive Jan 22 '13 at 21:48
  • I added a file_save() after the file_update() but hook_file_update still gets a file with no original property. Shouldn't $file->original be set after a file_save(), and different from $file after a change and a second file_save()? – beth Jan 22 '13 at 22:19
  • Never mind, I had other bugs. Your answer was very helpful. – beth Jan 23 '13 at 5:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.