I am using Workbench Moderation to manage the publishing of a content type with a Field Collection field. The node view is setup to display it's Field Collection through a View. The most recent version of the Field Collection data always appears on "published" even though it is "draft" or "needs review."

Steps to reproduce:

  1. Create a content type with a Field Collection field ("Number of values" doesn't matter)
  2. Add a text field to the Field Collection field
  3. Create a View block that takes the NID as an argument (provide default value, content id from URL), has a relationship the Field Collection and then displays the text field from the Field Collection.
  4. Add the View block to a region of the page
  5. Create a node and define the Title, Body and define at least one Field Collection instance
  6. Save as "published"
  7. Edit the node you just created and change the Title, Body and Field Collection value(s)
  8. Save as "draft"
  9. View the published node and you will notice that while the Title and Body still hold the original value of the published version of the node, but the Field Collection field is displaying the most recent version of that field's value-- the "draft" version.

As pointed out by @insparrow in this comment, you get the same versioning display problem when loading the Field Collection value programmatically. Here's her example code, of course yo'll have to change the name of the Field Collection field (field_itinerary_events) and the Node ID (24) to match what you have defined

$node = node_load(24);
$fc_id = field_get_items('node', $node, 'field_itinerary_events');
$fc = entity_load('field_collection_item', array($fc_id[0]['value']));
dpm($fc);

The problem with node_load was also pointed out by @hass in this comment when using Link Checker.

In the end, I highly doubt this is a problem with Views, rather a problem with how Field Collections is attaching itself to the node which Views uses.

Screenshot example:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

John Pitcairn’s picture

When programmatically loading an entity I've found you must explicitly specify the revision to entity_load(). For your code above:

if (($node = node_load(24)) && ($fc_ids = field_get_items('node', $node, 'field_itinerary_events'))) {
  foreach ($fc_ids as $fc_id) {
    // Load the correct entity revisions. Note the conditions parameter is deprecated.
    if ($fc = entity_load('field_collection_item', array($fc_id['value']), array('revision_id' => array($fc_id['revision_id'])))) {
      dpm($fc);
    }
  }
}
capellic’s picture

OK, makes sense. Since I was always looking in $fc_id[0]['value'] (the 0 index), I was always getting the most recent revision. Your loop successfully identifies the published version.

So what's the next step here? Sounds like Field Collection's Views integration needs to be aware of this? Note that I don't always want the published version. For example when looking at the Draft or Needs Review (node/[NID]/draft) version of the node, I want to see the most recent values. So, it could be said, I only care about getting the published version when viewing the Published version of node (node/[NID]).

I'm looking around in the Views integration files and having never done this sort of module work, I am at a loss.

From an interface perspective, it would be nice if when setting relationship to the Field Collection that we're able to define Published or Draft or Needs Review. Note, my view is setup as "content" with a relationship to the Field Collection. I've tried to put a condition to require that the Field Collection be published. I see that I can bind on the Revision ID. I tried by hard-coding the revision ID of what came back from the dpm($fc) and that didn't work, either.

I then tried to create a "field collection" view without any luck. It's only returning the most recent version of the field collection with no filtering to allow for a different version.

Or do I need to write code for hook_views_query_alter()?

John Pitcairn’s picture

Issue tags: +views integration

Views code is not my strong point either ;-)

I think we may need to hear from the folks who wrote the views integration for Field Collection.

John Pitcairn’s picture

Title: Most recent Field Collection revision always appears (in Views and node_load) when using Workbench Moderation » Most recent Field Collection revision always appears in View when using Workbench Moderation
Version: 7.x-1.0-beta5 » 7.x-1.x-dev

Updating title and version.

I think the relationship on FIeld Collection fields is basically just an entity relationship, and relationships for Entity Reference fields do return the correct (published) revision's fields in conjunction with Workbench Moderation.

So when I get the chance (um...) I'll compare the Field Collection views integration with the Entity Reference views integration to see whether a solution can be found in the latter. Unless somebody beats me to it (hint).

capellic’s picture

Thanks John. This issue is on the back burner for us for the next several weeks but will get onto the front burner soon enough. Meaning, I can't carve out the time to do any research at the moment.

capellic’s picture

Any update on this?

John Pitcairn’s picture

Not from me. I dislocated my shoulder a few weeks ago, so any non-essential work is deferred for a few more weeks at least.

kscheirer’s picture

What would we add to field_collection's views code to handle the workbench-based statuses you've mentioned above? I took a quick look at the views code, and it seems fairly sane. Maybe we could add something to the options_form()

John Pitcairn’s picture

I think start by looking at the query generated by something that does work as expected (entityreference?) vs the query generated by field_collection. Is there a difference?

John Pitcairn’s picture

I have a simple reproducible test case setup.

The problem occurs only when displaying a field in a Field Collection, via a Views relationship. The most recent revision of the Field Collection is used.

This is not the case when displaying a field in a node referenced from an Entityreference field, via a Views relationship. The published revision of the node is used.

The problem also does not occur when displaying the Field Collection directly as a field (ie without the relationship), using a view mode. So there may be a workaround available if you do not need full use of Views' field handlers - create a custom view mode (Display Suite is useful) for the Field Collection containing the fields you require, and add the parent field collection to your view as a field, using that view mode.

Investigating...

John Pitcairn’s picture

(incorrect speculation removed)

John Pitcairn’s picture

Assigned: Unassigned » John Pitcairn
Issue tags: +revisions, +workbench moderation workflow

Tagging

John Pitcairn’s picture

It isn't the views integration that is at fault. I've looked at what happens when a node is saved as draft. The node contains a textfield, and a field collection containing the same textfield.

  • The {field_collection_item} table row is immediately updated to the new revision_id as default revision.
  • The {field_data_fieldname} table row is not updated where the node is the parent entity.
  • The {field_data_fieldname} table row is updated immediately where the field collection is the parent entity.

If the {field_data_fieldname} row is manually reverted to the published field collection revision_id and textfield value, Views displays the correct data.

Finally, the {field_collection_item} revision_id must also point to the published revision, otherwise the draft tab will display the published value for the field collection textfield.

John Pitcairn’s picture

I think our problem is that Field Collection uses Entity API to support revisions, Workbench Moderation is not Entity API aware, and there is no way for Workbench Moderation to add the default_revision property to the host node before field_collection_field_presave() saves the entity.

John Pitcairn’s picture

The patch at #1781190: Field Collection saved on presave will help us out. With that applied, modules can add an Entity API default_revision property to the host node before the Field Collection is saved. Then I think it should be possible to implement all the functionality we need in a separate module, rather than patching Field Collection or Workbench Moderation - though I have started an issue and patch against Workbench Moderation: #1989610: Support basic Entity API default_revision property on nodes

@capellic and others - when done, this will ensure node Views always uses the published revision's field collection fields. If you want Views to use fields from the revision being viewed, then you'll need to create a node revision view, with the node revision vid as an argument.

John Pitcairn’s picture

John Pitcairn’s picture

Alternately I'm happy to provide this as a patch. Opinions?

capellic’s picture

Alternately I'm happy to provide this as a patch. Opinions?

I think a patch is best because this doesn't feel like an extension of the module, rather it feels like something that needs to be a part of the nuts and bolts. I could see many people mistakenly assuming it's sorted out and not think to look for a module to fix the revision problem.

John Pitcairn’s picture

DO NOT USE THIS PATCH.

John Pitcairn’s picture

I'd argue that rather than Field Collection explicitly supporting Workbench Moderation (or Revisioning, or whatever), it should be the other way around, and patches should be to Workbench Moderation. Until Drupal 8 and full support for node entity forward revisions, it's a bit of a chicken-or-egg situation, so an extension module to handle the integration for Drupal 7 does make sense to me.

hass’s picture

There are a lot of situation where workbench cannot solve the issue by itself. The problem is core node_save() only and we cannot fix/backport these changes in this late state. I'm not quite sure if you have really understood the root cause of this issue. Your comments sound a bit confusing to me. But I may have not understood you.

John Pitcairn’s picture

I understand that problem, I have written custom modules dealing with the fallout from workbench moderation's implementation (and revisioning's implementation in D6) for a number of sites in production. It's no fun.

But if a separate module can solve both the field collection issues we are dealing with here, then so could workbench moderation, without patching field collection. One solution is a generic 1-line change, but the other would require explicit field collection support code - though I note there is already taxonomy support code in there.

Personally I think problems caused by the way workbench moderation has to handle things should be fixed in workbench moderation wherever possible, and in this case investigation shows that both can be dealt with there. Yes I know core is at fault, but that won't be fixed in D7. Workbench moderation is the next stage in the propagation of the problem.

On the other hand, a separate integration module keeps it self contained, and there is a specific issue queue, etc. If both patches make it into field collection, or one into workbench moderation and the other into field collection, that's fine and I will remove the module. But until then I know I'd rather be using a separate integration module with both recommended versions than tracking dev + patches on production sites ;-)

Apologies for being over-verbose across several issues while working my way through this. I appreciate the feedback happening now.

hass’s picture

The problem I see here is an idea like http://drupal.org/node/1807460#comment-7408560 that unset($node->original);. This cannot work. You need to keep in mind that you alter the $node object and presave hooks from other modules may expect the $node->original. Your presave hook is not the last that may be called. This makes it so impossible to do things in this way.

I had the same type of issues in linkchecker, see linkchecker_node_update(). I have an if/else and if the "if" matches I'm adding something to DB and "else" I'm deleting something. If this is a save of a non-public forward revision I need to skip add and delete and do nothing. This is something I have not expected at all as no code in core works this way. Well that's why we have so many troubles here... Looks like this need to be the same here.

Just to say how WB works... A node save of a forward revision executes all hooks like every other node_save() and saves the node as live version until the shutdown fires again, calls all hooks again and overwrites some references to keep the real published version live and remove the forward revision from public again. This process is totally ill as we fire everything twice - all times. There may be no way to workaround module specific code until we are at D8 and this is really far away from us...

John Pitcairn’s picture

OK, after a closer examination of node_save(), I get it now. $node->original is always present because entity_load_unchanged() is called to put it there. I had been inspecting $live_revision before the call to node_save(). Rats.

So the patch at #1807460: Field collection doesn't play nice with workbench moderation (patch) really is necessary and can't be handled any other way.

Amber Himes Matz’s picture

Here is how I was able to workaround this issue:

  1. Patch applied to Field Collection 7.x-1.0-beta5+1-dev in comment #1 at #1807460: Field collection doesn't play nice with workbench moderation (patch)
  2. My installed versions of related modules: Entity 7.x-1.1; Workbench Moderation 7.x-1.3; Views 7.x-3.5
  3. A View of Content Revisions (node_revisions base table) with the Vid as Contextual filter will solve the problem after the patch is applied.

However, in my case, the Views content pane of Content (node base table) had already been placed via Panelizer on myriad nodes and it would have been nightmarish to replace them with the Content revisions View content pane because of a problem with Panelizer and Workbench Moderation which precludes an editor from seeing changes to content panes until the node is moderated to published (but I won't go into it further because it's out of the scope of this issue).

Since it was prohibitive to replace the content pane in panelizer with a working Content Revisions content pane, I had to to find a way to use the same exact view, but get the new working revisions-based view to actually be the one to display.

My solution:

  1. On the View of Content content pane, replace the Nid contextual filter with Vid. Update the argument input to use From Context, Node => Revision ID.
  2. Create a new View of Content Revisions and a content pane display and replicate the fields and contextual filters of the original View of Content. Update the Argument input and for the Vid, use "Argument wildcard" (this was the trick).
  3. On the View of Content, add No Results behavior => Global: View area and select the Content Revisions View content pane. Check "Inherit contextual filters."
  4. Save both views.

On a published node, create a new draft and make a change to a field collection value. Do not publish this revision. On View Published, you should see NOT see your change, but you SHOULD see your change on View draft.

John Pitcairn’s picture

Are those field collection values being accessed through a field collection relationship? Nice workaround for existing panels, where you have access to nid and vid as context. But that won't work outside panels, you won't have the context, or the argument options you describe - your solution is panels-specific. It's also a different problem to the one we are trying to solve:

A plain Content view should always show the published node's field collection field values when using a relationship. Currently it does not, it always shows the latest revision's values.

The patch at #19 (or the linked module) fixes this.

steve.m’s picture

Note that the patch at #19 does not work as provided because it is namespaced as field_collection_wbm. If you want to add this hook to the field_collection module itself, the new function has to be named field_collection_workbench_moderation_transition().

John Pitcairn’s picture

@steve.m - true. I think a patch to field_collection is probably not such a desirable solution anyway, since it can be handled by an optional module.

capellic’s picture

Wondering if this module will help:
https://drupal.org/sandbox/gielfeldt/2120183

I've informed the module maintainer about our issue:
https://drupal.org/node/2130007

gielfeldt’s picture

I don't believe the module will help out in this case. Views Field Join Node Revision is only for index optimization in Views queries between field_* and node_revision.

Even though I've only skimmed this thread, and not tried out the scenario, the issue at hand here sounds more like an integration problem between field collections and entities as a whole. (Workbench Moderation _should_ update the status field for the proper node revision, AFAIR).

John Pitcairn’s picture

The fundamental issue is really that core node_save() forces the new node revision to be the "current" or "default" revision, so workbench_moderation has to do another save in a shutdown function to set the correct default revision.

There is (necessarily) no new node revision created at this time, and the resulting entity_save() for the field collection checks whether the parent entity (node) is creating a new revision to determine whether its field revisions need updating. Neither workbench moderation nor field_collection can do anything about this, it's a core problem.

So either workbench_moderation needs to make specific allowances for field_collection, or vice versa - neither of which is likely to be very palatable to the maintainers if there is any alternative - and In this case it can be handled by a separate integration module as per my sandbox.

Paul Rowell’s picture

Just to jump in here.

Applying the patch at https://drupal.org/comment/7999497#comment-7999497 alongside the sandbox module: https://drupal.org/sandbox/johnpitcairn/1991516 did the job. However when saving a content as draft twice it errors:
PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1: INSERT INTO...

I'll carry on digging and post back if I find out more...

Paul Rowell’s picture

Ok, so it seems it was the link module (no idea why) but it was trying to insert data in the wrong order - updating to latest dev fixed it.

You can ignore me now <_<

John Pitcairn’s picture

Issue summary: View changes

Hiding the patch file from #19 in the issue summary. Do not use this patch.

briand44’s picture

I am having issues after applying the patch at https://drupal.org/comment/7999497#comment-7999497 and enabling the sandbox module from https://drupal.org/sandbox/johnpitcairn/1991516. The issue of the Field Collection field displaying the most recent version of that field's value when viewing the published node is solved but now when I view the draft I don't see the most recent version of that field either. On the edit page it shows the correct values but not when viewing the draft. Let me know if I am doing something wrong or if others are experiencing this as well. Thanks.

John Pitcairn’s picture

Are you talking about a view attached to the node as a block or entity attachment? You will always get the published revision in that view, even for the view on the draft, because the view is a node view, not a node revision view.

briand44’s picture

Thanks for the response. That makes sense but I still can't get it to work when I create a node revision view. I have a content revision view where I have added a content pane display with vid as an argument and a relationship to the field collection field. When I output one of the field collection fields using the relationship it always shows the published data not that revisions data. Any suggestions? Thanks.

John Pitcairn’s picture

If you disable the sandbox module, then save a new unpublished revision, do you:

A - get what you expect from the content revision view?

B - also get the old behaviour from a content view (ie it always shows latest revision)?

briand44’s picture

Without the sandbox module it always shows the latest revision on both the content view and the content revision view.

With the sandbox module it always shows the published revision on both the content view and the content revision view.

Thanks for your help!

John Pitcairn’s picture

In the views preview, if you give it the correct vid as an argument, do you get the correct result for the content revision view?

briand44’s picture

No, I get the same results posted in #39 in the views preview. The latest revision shows if I'm not using the sandbox module and the published revision shows if I have the sandbox module enabled. Thanks.

John Pitcairn’s picture

Assigned: John Pitcairn » Unassigned
Status: Needs review » Active

Changing status, there's nothing to review here.

John Pitcairn’s picture

There appear to be some Views issues in play here. See #1754354: Node revisions only displays latest revision

Possibly related patch to Workbench Moderation: #1963992: Don't use $reset flag with node_load()

dgtlmoon’s picture

Sharing my solution here, I created a new views_handler_field_field and overwrote the existing handler with a little..

function mymodule_views_data_alter(&$data) {
  foreach($data as $name => &$data_field) {
    foreach($data_field as $field_name => &$field_set) {
      if(isset($field_set['field']['handler']) && $field_set['field']['handler'] == 'views_handler_field_field') {
        $field_set['field']['handler']='mymodule_views_handler_field_field';
      }
    }
  }

Specifically I targeted the ::post_execute(&$values), it was the same except in the loop that loads all the entities I done some logic like

      foreach ($entities_by_type as $entity_type => $entity_ids) {
        $entity_info = entity_get_info($entity_type);
        if (empty($this->definition['is revision'])) {

          // Workaround for field_collection not loading the right revision based on workbench
          // @note #1901892 there's an open issue for this in d.o
          if ($entity_type == 'field_collection_item') {
            foreach ($entity_ids as $id) {
              $conditions = array();

and then the target VID was set by manually joining the revision field information back to the node and setting that info into $conditions which is pass to my modified entity_load handler. I changed the loop to accept the revision_id's (it does not load revision_ids unless your field is setup for it)

$conditions = array();
if ($this->field_alias == 'field_collection_item_field_data_field_fc_group_links_item_i') {

                  // Field collections table would usually ALWAYS bump the revision_id in field_collection_item::revision_id on every save regardless of workbench
                  // So we have to join the tables ourselves and figure out what it should have been, then push this back into the views field loader

                  $vid = YOUR_FUNCTION_TO_FIGURE_OUT_THE_APPROPRIATE_VID(arg(1));

                  $conditions['revision_id'] = db_query("SELECT fc_group.field_fc_group_links_revision_id FROM field_revision_field_nk_fc_related_link nk_fc
                      left join field_revision_field_fc_group_links fc_group on fc_group.revision_id = nk_fc.field_nk_fc_related_link_revision_id
                      where nk_fc.revision_id=:vid
                      and fc_group.field_fc_group_links_value = :id", array(
                    ':id' => $id,
                    ':vid' => $vid
                  ))->fetchField();
}
 $entities += entity_load($entity_type, array($id), $conditions);

So basically my horrible solution was to

  • Write my custom views_handler_field_field implementation
  • Change the views_handler_field_field() load loop to accept revision_id's for non revision fields
  • Do a little lefty joiny to figure out what would have been the right revision_id of that field at the VID of the node being viewed
  • Push that result as a entity_load argument

It seems to me that PanelsPaneController::save() from fieldable_panels_pane calls

        field_attach_update('fieldable_panels_pane', $entity);
        module_invoke_all('fieldable_panels_pane_update', $entity);
        module_invoke_all('entity_update', $entity, 'fieldable_panels_pane');

And it's these hooks that make field_collections rewrite the data table for the field collections field so you end up always seeing the new content.

When it should actually just update a revision, but this feels like it would take a lot of work to repair in field_collections

in short - there just doesnt seem to be an answer here, if you compare your 'fieldable_panels_panes' table with the _data table for your field, you will see that the revision_id in the _data table is higher than the fieldable_panels_panes table - the _data table for the field is always referring to something in the revision of the field

jproctor’s picture

Slightly different manifestation of the same problem: I had a view of field collection items that relies on host entity id, and if the host entity (a node in my case) was still in moderation, the view could find the field collection item but not its host entity. In fact, $fcitem->hostEntity() consistently failed even in non-views code.

This comment is to add that host entity symptom so it's searchable, and to say thanks to @john-pitcairn for the sandbox project. Doesn't clean up my old data (that's okay; I know how to find them), but it does prevent new problems. Workbench Moderation 1.4, Field Collection 1.0-beta10, and Views 3.13, if anyone's curious whether it is still necessary and still works.

alex.verhoeven’s picture

I banged my head against this for a while, and after consulting with JRB, I was able to overcome the issue described in #39, where it was one or the other, showing either the published or the draft revision of the field collection on both displays. I am using an EVA to render the field collections back into the node.

The view needs to be started as a Content Revision view (just adding the 'Content revision: Content' relationship to an existing Content view did not work for me), and needs to use the following:

- Display type EVA
- Entity content settings Argument needs to be a Token, using [node:vid]
- Contextual Filter needs to be 'Content revision: Vid'
- First Relationship needs to be 'Content revision: Content' (should already be there because it's a Content Revisions view)
- Second Relationship needs to be 'Content (historical data): Field Collection Name:revision_id' (require this relationship)
- Your fields from the Field Collection should look be like:
'(field collection item revision from field_collection_name) Field collection item (historical data): Field Name'

Hope that helps somebody else because this issue kicked my butt and there was not a clear fix to be found.

deneus18’s picture

#29 solved my problem as well.
Thanks @capellic

I have a view with field collection, the node is using workbench.
When you display a view using fields (and not display mode) the views will display the most recent revision without taking into account the workbench status.

With we sandbox module, you need to save all your nodes first, then the views will display the last published revision and not the last recent revision anymore.

djouuuuh’s picture

Subscribing to the issue

xenophyle’s picture

In views_plugin_query_default::get_result_entities()

$is_revision = !empty($table_data['table']['revision']);

is false for field collections, even if using revisions. Changing this line to

$is_revision = !empty($table_data['table']['revision']) || ($base_table == 'field_collection_item_revision');

causes my view to show the right field collection revisions. (In case it is not obvious, this is not a fix.)

I am having trouble figuring out what is going wrong. Is Views using the wrong check to determine whether to use revisions? Is Entity API's hook_views_data() implementation wrong? Is Field Collection providing the wrong information to someone?