Requires the core File module to be enabled.

1. Create a new node, attach a file, publish revision.
2. Create new revision of node in draft, delete file. Do not publish revision.
3. Edit the draft revision again, change anything, save and do not publish.
4. View current published revision, the file you have attached to the published revision is no longer present.

This issue appears to be present in Revisioning 7.x-1.4 and also confirm this issue as present in 7.x-1.x-dev.

I haven't gone looking in the code yet to find out where the issue may lie. Can anyone else recreate and confirm this problem?

Thanks.

Comments

sambonner’s picture

OK, its disappointing none of the module maintainers seem interested in this because it is definitely an issue.

I've identified the precise problem. Core drupal does not handle the concept of editing the same revision multiple times, this is something introduced by Revisioning. The consequences of this, as related to my particular problem can be seen in part of the file_field_update function in file.field.inc reproduced below:

/**
 * Implements hook_field_update().
 *
 * Checks for files that have been removed from the object.
 */
function file_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
  // On new revisions, all files are considered to be a new usage and no
  // deletion of previous file usages are necessary.

  if (!empty($entity->revision)) {
    foreach ($items as $item) {
      $file = (object) $item;
      file_usage_add($file, 'file', $entity_type, $id);
    }
    return;
  }

The issue I encountered occurs because $entity->revision is set to indicate if there is a new revision or not (1 if new, empty if not new), clearly, on multiple unpublished revision edits there is *not* a new revision so this is empty. In core drupal this is fine, when using Revisioning it obviously is not.

I'm not really to sure how to proceed from here, obviously I could hack the file_field_update function in core and change the if statement to check, for example $entity->is_pending as well, but that isn't really solving the problem.

Does anyone have any ideas? I've wondered if it is possible to override file_field_update and rewrite it within the revisioning module but I haven't been able to find any suggestion of this being achievable thus far.

rdeboer’s picture

Status: Active » Closed (won't fix)

Sorry for late reply, your submission fell between the cracks.
Yes, you've analysed it perfectly above.
Too hard and time consuming, unless someone wants to provide a patch...
Rik

jeffam’s picture

There's a similar issue with Workbench Moderation. They're working to fix it properly, but buried in the comments is mention of a workaround using the File Lock module. (See http://drupal.org/node/1084436#comment-6404974).

Just an FYI for those struggling with this issue and in need of a quick fix.

rdeboer’s picture

Thanks jeffam for sharing!
Rik

seanbfuller’s picture

This also seems to be an issue for 6.x-3.15 with filefield and imagefield cck modules. For the site in question, sometimes when editing a draft revision files would disappear from the published version. The problem has been around since October 2012 or so, but it may have been around before that. Same steps to replicate as in d7 with core fields: create and publish, edit and remove the file, save again and the file is gone.

To deal with this, I've created a quick module ("revisioning_file_keep") that implements hook_file_references(). Here's what my code looks like (note the dsm() calls that are just for debugging):

/**
 * Implementation of hook_file_references(). 
 */
function revisioning_file_keep_file_references($file) {
  dsm('called revisioning_file_keep_file_references');
  $file = (object) $file;
  dsm($file);
  $count = 0;

  // If we have a valid field name, then look for existing records.
  if (isset($file->field_name)){
    if ($file->field_name) {
      dsm('we have a good field name ('. $file->field_name .').');
      // Look up any references in the cck table
      $table = 'content_'. $file->field_name;
      $fid_column = $file->field_name . '_fid';
      dsm('args are: '. $table .', '. $fid_column .', '. $file->fid);
      $result = db_query("SELECT * FROM {%s} WHERE %s = %d", $table, $fid_column, $file->fid);
      while($record = db_fetch_object($result)) {
        dsm($record);
        $count++;
      }
    }
  }
  dsm('count is '. $count);
  return $count ? array('revisioning_file_keep' => $count) : NULL;
}

I'm having a hard time finding documentation on this hook so I'm not sure all the cases for when this might get called. We're continuing to test, but so far this seems to be working. Will report back if we run into anything else. Otherwise this will hopefully help other people still running revisioning with d6 who might be seeing this behavior.

rdeboer’s picture

Thanks Sean!
Great patch!
Rik

sambonner’s picture

Hmmm, nice work Sean, I had been thinking about a way to handle this bug in revisioning-7.x and had been planning to do some work using hook_file_delete to effectively re-attach a file to a revision if it is in draft, but your method seems better. I'll have a go implementing something similar for 7.x and post it up to this issue when I've got something working.

Thanks,
Sam

sambonner’s picture

Status: Closed (won't fix) » Needs review

OK, I've worked out a solution that I think is actually pretty nice and very simple for this bug. I've used a hook_node_update function to call file_add_usage and add an additional record to the file_usage table that uses the VID as the ID for the record when a file is attached to a node.

This prevents files being deleted by drupal during multiple updates on draft revisions because when file_delete is called there is still a file_usage record for that file in the table. At the moment I have this sitting in a custom module, however I wonder if Rik would consider a patch for Revisioning? I'm not sure how one would implement a check to see if there were files attached to the node, but if it could be done then it would be a simple matter to adapt the code below to work within Revisioning.

example_node_update($node) {
  if ((isset($node->field_example_file_field['und'][0]) && !empty($node->field_example_file_field['und'][0]))
      && $node->revision_moderation == TRUE){
    $file = (object) $node->field_example_file_field['und'][0];
    file_usage_add($file, 'example_module', 'revision', $node->vid);
  }
}

That's it! Works really nicely from the testing I have done. I'd be happy to have a go creating a patch against Revisioning-7.x-dev if it is something the maintainer would be interested in.

Thanks,
Sam

rdeboer’s picture

Hi Sam,
Thanks for all your detective work on this.
I'd be happy to incorporate this into Revisioning's hook_node_update(), with a few tweaks, like removal of the hard-coded name of the file field and language, 'und'.
So maybe, we should start with this:

  // ....
  if (!empty($node->revision_moderation)) { 
    $lang = $node->language; // is this right?
    if (!empty($node->field_example_file_field[$lang][0])) {
      $file = (object) $node->field_example_file_field[$lang][0];
      file_usage_add($file, 'example_module', 'revision', $node->vid);
    }
  }

Or better still, use the field API...
What if the node has multiple file attachment fields? We need to investigate this a bit more...

sambonner’s picture

Hi Rik, good points! I'll have a think tonight and try and get a patch rolled that addresses these problems.

Thanks,
Sam

sambonner’s picture

Finally attaching patch for this, sorry for the delay.

I've rewritten it to use field API as suggested, and handle multiple file upload fields.

I've tested this on a vanilla install for drupal 7.22 running php 5.3 and postgres 9.2 as well as a live site which has been using an older version of revisioning and has many revisions created using an older version of the module and it seems to be working fine.

I've also added backwards compatibility so that sites with nodes and revisions created under previous releases of revisioning will no longer experience this issue when creating new revisions (ie, will add file usage records to old files).

Thanks,
Sam

sambonner’s picture

Oops, this one is better, changed so that the module name added to the file_usage record is revisioning.

rdeboer’s picture

Whoa!
That patch has grown a bit since you're last one, Sam.
Good work!
However this bit, which you have in there twice, can't be right, or...?

      $file_fields = array();
      $file_fields[$field_name] = $value;

It's inside a for-loop (not shown), so it means that the file_fields array gets reset to empty on every iteration, so that at the end of it only the last field name is in the array? That's not intended is it?

Pity we just missed the 7.x-1.5 release, but happy to check it into dev today or tomorrow, if you could confirm the above.
Thanks again,
Rik

sambonner’s picture

Thanks Rik, you're entirely right, that was a silly thing for me to miss, my testing must not have caught that particular use case, sorry about that!

Tested latest patch with a content type with multiple file fields, now working correctly. Thanks for spotting that.

Sam

rdeboer’s picture

Version: 7.x-1.4 » 7.x-1.5
Assigned: Unassigned » rdeboer
Status: Needs review » Fixed

Checked into 7.x-1.x branch (7.x-1.x-dev snapshot) with attribution.
Thanks Sam.
Rik

chrbak’s picture

When I save the last published revision of a node (without making any changes, just open and save) I am getting the following warning:

Warning: Invalid argument supplied for foreach() in revisioning_node_update() (line 646 of ...sites\all\modules\revisioning\revisioning.module).

Thanks for your great job.

chrbak’s picture

I am using the 1.5 version with the above patch #14. My original node has a file field with two images attached but at line 646 the $old_files variable seems to be not an array. When I use the

if (is_array($old_files)) { }

the warning disappears.

chrbak’s picture

The field I mentioned above is an image field not a file, my mistake. I have and a file field but is empty.

rdeboer’s picture

Thanks chrbak,
Being the return value of field_get_items(), $old_files can potentially equal FALSE, which is probably what's happening.
I've checked in a fix.
Rik

PS: @sambonner, would you like to double-check this?

sambonner’s picture

Thanks Rik, a check there makes a lot of sense, all seems to working well from my tests. Glad someone else is finding this useful :)

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.