This behaviour was tricky to lock down but the following are steps to reproduce the issue:

1. Create a field instance of tablefield with Unlimited cardinality, with default rows/columns to 5/5
2. Create a node of this type
3. In the first table entry, change the rows/cols to 1/1, put some text in the only cell
4. In the second entry, change the rows/cols to 1/1, put some text in the only cell
5. Save the node

You end up with the first table defaulting back to 5/5 and the second table being written correctly.

I'm going to poke the source now to try to find the reason.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

er_d0s’s picture

OK so it seems like if you modify a number of row/column settings at once, only the last modified table will be correctly updated.

I THINK the problem lies somewhere around the start of the tablefield_field_widget_form function. Something to do with how the function deals with default values. Note that if a new node has 3 tablefield values in the form, when you click resize tablefield_field_widget_form will be called 3 times.

er_d0s’s picture

Priority: Major » Critical

I'm raising this to critical because in some cases it renders the module unusable, I'll try to work on the problem as soon as I can but if anyone can get to it before me that would be great.

er_d0s’s picture

Status: Active » Needs work
rjacobs’s picture

Title: Unlimited cardinality and changing the number of rows/columns » Unlimited cardinality and changing the number of rows/columns (e.g. shrink table size not being honored as old data persists)
Version: 7.x-2.2 » 7.x-2.x-dev

OK so it seems like if you modify a number of row/column settings at once, only the last modified table will be correctly updated.

Exactly! This is the same issue that we are encountering as well. I posted some notes about it at #1361702: Rebuild or csv upload action that shrinks table size not being honored (old roes/columns persist after save) which I am going to mark as a dup of this issue (as this one seems more recent/active).

I was poking around in the code a bit as well (tablefield_rebuild_form_ajax(), etc.) but did not see an obvious solution. I would like to explore this more but won't personally be able to dedicate time to it until some deadlines pass. We have some workarounds in place that allow us to avoid this issue for now, but I know its going to come back to bite us later. I may jump-back in on this at some point, but will certainly help review any patches that may be posted.

rjacobs’s picture

Status: Needs work » Active

Also I don't think the "needs work" status should be used until after there is a patch available for review (as it signifies that an existing patch was reviewed but needs more work before it can be reviewed further).

er_d0s’s picture

Hey thanks for popping in! This bug has been killing me and I've only been able to devote a few hours here and there but I'm going to have to address it within the next few weeks as I've got a project that's relying on it, hopefully I'll have a patch soon.

Kevin Hankens’s picture

Thanks for keeping this alive :) A patch for this would be most welcome!

er_d0s’s picture

OK! I think I've fixed it, the problem seemed to be that tablefield_rationalize_table() was being called with the default value for the form in the case of the first tablefield in the list. The issue was at line 409, I can see that the start of that function (tablefield_field_widget_form) there's some initial processing to decide what to do with the form in different contexts, line 409 seems to be doing some initial setting up, but I couldn't find a use case where the block was executed without breaking the form. Rather than delete it I just added an extra check to make sure we're in the right context.

Kevin, can you confirm what that block was for?

rjacobs’s picture

Status: Active » Needs review

Thanks erdos, I hope to give this a test shortly. Marking "needs review" as there is now a patch for review.

er_d0s’s picture

rjacobs’s picture

Issue summary: View changes
Status: Needs review » Needs work

Oh man, sorry that it's taken so long to review this as promised. I got hung up on a number of deadlines.

Unfortunately, I didn't have luck with the patch in #8. Even with it applied I was still unable to get more than one re-sized/imported table to save correctly. As before only the last edited table was saved correctly.

No matter what, your analysis helps contextualize the issue, and I agree that something funky is going on in tablefield_field_widget_form() (most likely related to the initial calculation of the $default_value variable). After staring at the code a bit it seems like the default data for each tablefield is being reset (fetched freshly out of the DB) on each ajax submit except for the element that is triggering the ajax submit. Based on my understanding of the FAPI this makes sense, as I believe the entire form must be re-built (calling each fields' hook_field_widget_form()) even if only one form element will be changed via ajax. When this happens, it seems that tablefield_field_widget_form() isn't smart about maintaining the default values from the form_state of any tablefields that are not being updated via the ajax request, and instead it (re)loads their data from the DB. This is of course a problem if they happened to have been updated on a previous ajax request. This "resetting" is not reflected in the browser as the ajax only updates the triggering field visually, but it does affect the default values in the form_state data, which is ultimately what's saved.

So it seems to me that we need to bypass the part of tablefield_field_widget_form() that re-loads fresh data from the DB when a tablefield is being re-built via ajax but is not the field that triggered the ajax. I think that is here (around line 411):

  elseif (isset($items[$delta]['value'])) {
    // Default from database (saved node)
    $default_value = tablefield_rationalize_table(unserialize($items[$delta]['value']));
  }

Changing this to something like the following might ensure that these tablefields are always rebuilt from form_state data (in the final else { } ) as opposed to being refreshed from the DB:

  elseif (isset($items[$delta]['value']) && !isset($form_state['clicked_button']['#value'])) {
    // Default from database (saved node)
    $default_value = tablefield_rationalize_table(unserialize($items[$delta]['value']));
  }

I'll look at this some more and if it's working I'll make a patch.

rjacobs’s picture

Status: Needs work » Needs review
FileSize
1006 bytes

Ok, I've played around a bit more with the change noted from my previous comment and have been seeing good results. With this change we have been able to successfully do several complex sets of edits that were not possible before (import via CSV to overwrite existing data, re-size multiple tables, mix/match imports and re-sizes on same save, etc.). So I'm rolling as a path (made with git format-patch). It's a very simple patch.

erdos, Kevin, would you be able to check this out?

rjacobs’s picture

Just wanted to bump this. Anyway else able to test this (very simple) patch so we can try to get a fix committed?

  • vitalie committed ebb44c7 on 8.x-2.x
    put tablefield table directly into value key on node load; fixed problem...
vitalie’s picture

Status: Needs review » Needs work

Thx @rjacobs. I have also played around and it looks good. There is still a problem if one sets some default values in field settings and locks them. I think this is because those values do not end up in $form_state['input'].

vitalie’s picture

Assigned: Unassigned » vitalie
vitalie’s picture

Status: Needs work » Needs review
FileSize
2.5 KB

Here's a patch based on #12 that deals with locked values.

  • vitalie committed 5b7c36e on 7.x-2.x authored by rjacobs
    Issue #2052215 by er_d0s, rjacobs, vitalie: Unlimited cardinality and...
vitalie’s picture

Status: Needs review » Closed (fixed)
vitalie’s picture

Status: Closed (fixed) » Needs work

This still does not work in the new node creation form, although it does work in the node edit form. Debugging shows that in the case of node creation the following if clause gets triggered:

elseif ($form_state['submitted'] && isset($items[$delta]) && isset($items[$delta]['tablefield'])) {
    // A form was submitted
    $default_value = tablefield_rationalize_table($items[$delta]['tablefield']);
}

in tablefield_widget_form function.

One should probably add to it the && !isset($form_state['clicked_button']['#value']) check as in the patch above.

uno’s picture

Same problem when having more than one tablefield field.

vitalie’s picture

@uno, it might be that the problem you report got fixed by the fix of #2387001: Import CSV option does not work with Field Collection module. Could you check?

uno’s picture

Hi vitalie,

Thanks for reply, could you point me to the fix?

vitalie’s picture

Sorry, @uno, I suspect it is this one: https://www.drupal.org/node/2387001#comment-9574877, I am not sure the patch there will apply cleanly to the 7.x-2.3 version, could you test it on current dev version?

vitalie’s picture

Status: Needs work » Needs review

Re #20, the patch has been committed as part of #1673416: Image upload interferes with Rebuild table

vitalie’s picture

Status: Needs review » Fixed

Status: Fixed » Closed (fixed)

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