I'm using hook_field_attach_form() in http://drupal.org/project/metatags to reliably attach form elements into entity forms. I have to add a submit handler to 'cleanup' some $form_state['values'] compared to meta tag defaults to see which values actually need to be saved. Unfortunately this means that unless the form has specified a specific submit handler in its form definition, the default FORM_ID_validate() and FORM_ID_submit() handlers are not added to $form['#validate'] and $form['#submit'] respectively since those arrays exist.
The problem lies in drupal_prepare_form() which just fails if $form['#validate'] or $form['#submit'] are defined at all. It should *STILL* attempt to add its default validate and submit handlers by checking in_array().
This issue is a major core blocker for the Meta tags issue, which we had to workaround for now using the following:
/**
* Implements hook_field_attach_form().
*/
function metatag_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity);
$instance = "{$entity_type}:{$bundle}";
$metatags = isset($entity->metatags) ? $entity->metatags : array();
$options['token types'] = array(token_get_entity_mapping('entity', $entity_type));
$options['context'] = $entity_type;
// This function adds $form['#submit'][] = 'metatag_metatags_form_submit' but
// because of this core bug cannot be added here anymore.
//metatag_metatags_form($form, $instance, $metatags, $options);
$form['#metatags'] = array(
'instance' => $instance,
'metatags' => $metatags,
'options' => $options,
);
}
/**
* Implements hook_form_alter().
*/
function metatag_form_alter(&$form, $form_state, $form_id) {
if (!empty($form['#metatags']) && !isset($form['metatags'])) {
extract($form['#metatags']);
metatag_metatags_form($form, $instance, $metatags, $options);
}
}
| Comment | File | Size | Author |
|---|---|---|---|
| #28 | drupal-1284642-28.patch | 840 bytes | harry slaughter |
| #18 | drupal-1284642-18.patch | 2.55 KB | grndlvl |
| #16 | drupal-1284642-16.patch | 2.63 KB | tim.plunkett |
| #5 | drupal-n1284642-5-d8.patch | 2.61 KB | damienmckenna |
Comments
Comment #1
chx commentedAye and then you would override the defaults how?
Comment #2
chx commentedComment #3
jherencia commentedComment #4
damienmckenna@chx: If the default function exists it should always be added if it isn't already, e.g. changing this:
to this:
Comment #5
damienmckennaHere's a patch following my idea from #7 above applied to both #validate and #submit.
Comment #6
dave reidCorrect #5 is exactly what I had in mind. Thanks for beating me to the patch DamienMcKenna. :)
@chx Could you clarify what you're asking?
Comment #7
xen commentedI think what chx is asking is how to replace a default submit handler, rather than add another one. The answer would be hook_form_alter , but then you're asking those that want to replace a submit handler to do the same workaround as you're currently doing to add one.
Really, it would be much handier if the #validate/#submit attributes is already set when hook_field_attach_form is invoked, but that moves the responsibility to the original form builder (which might come from who knows where) or field_attach_form (which is an odd place).
Comment #8
damienmckenna@Xen/@chx: the patch above doesn't do anything strange and completely allows you to override the default validation/submit handlers, it only adds the default ones to the process list if the functions actually exist.
Lets turn this around: please explain why the patch does not provide what most people would expect to happen.
Comment #9
xen commented@DamienMcKenna: It changes how you can override the default handler, in the slightly odd case of hook_field_attach_form() (and possibly others) where you have third party code that get the form handed over *before* drupal_prepare_form has processed it. Before the patch you simply set the attribute in the relevant function, after the patch you will have to add an form_alter to do it (which seems odd, as your module was already handed the complete form once).
Not saying it is the right behavior now, but it is a change of behavior, and I wasn't as surprised over this as Dave obviously was, as I thought it was a known thing. We have the same kind of issue with #element_validate, which is not available in form_alters, as it's added later in form_builder(). That's another case where you currently have to add in the default explicitly, if you want it, because it will only be added if not set explicitly.
What we have now is "defaults is applied if nothing else has set it", with the patch it's "the defaults are always set". Not saying it shouldn't be changed, but then #element_validate should be fixed to (which is not as trivial), or it will be confusing as hell that two handlers that's so alike, behave very differently.
Comment #10
dave reidTagging.
Comment #11
dave reid.
Comment #12
catchComment #13
dave reid@Xen: If the root-level form was treated exactly the same as its form elements, I would agree. But it's not.
Comment #14
xen commented@Dave Reid: I don't think that's a good reason to diverge them further.
Comment #15
catchNeeds a re-roll, and a non-suffixed patch.
Comment #16
tim.plunkettHere's a straight reroll.
Comment #18
grndlvl commented[ignore]
Comment #19
grndlvl commentedIgnore me don't know what I was thinking.
Comment #20
grndlvl commentedBack to needs work as #16 still failed.
Comment #21
berdirHad a quick look at the test failures. Looked at the block availability test and the problem is quite obvious.
Previously, you were able to override the default behavior by adding an explicit submit callback. node_form() for example does some crazy things by adding the generic submit callback on the submit button and then maybe add a node type specific one and always initialize #submit to an empty array to prevent the default behavior.
Because what happens now is that the same submit callback is re-added globally on the form, and because that one does also execute global submit callbacks, for some weird backwards compatible thingy, this means we're running into an endless loop.
This would be quite a change, not sure if that qualifies as bug fix and certainly not something that's backportable. The only backportable bugfix for this problem that I can think of is adding the same check at the beginning of field_attach_form() to make sure that submit/validate callbacks are initialized at that point. I've been running into the same problem with privatemsg and organic groups a while ago.
Comment #22
tim.plunkettIs this still relevant? Our entity forms have changed a lot.
Comment #23
damienmckenna@tim.plunkett: Maybe it should be changed to be just a D7 issue then?
Comment #24
tim.plunkettYes, if its not relevant that's what would happen.
Comment #25
plachNow form action handlers are consistently attached at button level, which is the recommended way also for attaching custom handlers. I don't think this is still an issue in D8 and would be ok to moving it to D7. OTOH if we change the logic in
drupal_prepare_form(), we should do that consistently in both branches.Comment #25.0
plachFixing PHP tags
Comment #26
jhedstromBack to D7 as per #22, #23, and #25.
Comment #27
harry slaughterWell, I just wasted half a day wondering why my form_alter submit/validate handlers weren't being called.
I can't imagine how much pain this has caused developers. Shouldn't this be a priority rather than a 4 year old bug?
Comment #28
harry slaughterThis patch makes a small change to form_execute_handlers() to include both sets of submit/validate callbacks.
Comment #29
mrded commented