The function registry remove issue #497118: Remove the registry (for functions) effectively broke all the AJAX behaviors contained in any .inc file in all of Drupal. When the AJAX call is made, a new bootstrap starts up. Since the original form is usually pulled in by the menu system and the AJAX callback is under a different path, the form function is not available and the whole request bombs out.
We started brainstorming solutions to this in #391330-60: File Field for Core.
So far the only viable solution we've thought of is moving all these form functions back into .module files so that they're always available. However, I think saying "all AJAX forms need to be in .module files" is a really strange requirement. So instead this patch introduces an alternative where we put a new #include_files property on the root $form. Even though every situation I can think of will only need to pull in a single include file, it won't hurt to have the flexibility of multiple include files.
Then every time a form is defined that includes an AJAX behavior, we can declare the include file in the $form array. See the patch for several examples where this is added including:
- Node form
- Taxonomy term pages (fieldable)
- Field configuration in Field UI module
- User profile pages (fieldable)
Any other forms that are in .inc files and contain AJAX behaviors will also need this new #include_files property. It's a pain but a better alternative than moving all these functions into the .module files.
| Comment | File | Size | Author |
|---|---|---|---|
| #12 | drupal.form-include-file.patch | 1.29 KB | sun |
| ajax_include_files.patch | 4.71 KB | quicksketch |
Comments
Comment #1
rfaysubscribing - I'll try to take a look shortly.
Comment #2
yched commentedNot really nice, but much better than 'forms outside .module can't be ajaxified'... No better idea here.
Side note: comment forms need to be fixed as well (fieldable too).
Comment #3
quicksketchOh I meant to mention comment forms. Comment forms do not need changing because comment_form is already in comment.module, so it works because it's always loaded.
Comment #4
yched commentedAh, true :-). Comment.module never does anything like the other kids.
Comment #5
moshe weitzman commentedHah. Comment module is much improved in D7, BTW.
I'm not too famliar with this issue, but it sounds like this property ought to be named #attached_php to fit in with #attached_js, #attached_css, and now #attached_library.
Comment #6
rfayI think we're all pretty reluctant about this, wishing there was a better way.
One thing to be considered: Sometimes a form is altered, adding ajax, by a hook_form_alter. Do we then have to add the altering file to #include_files[] as well?
Where is the C/C++ preprocessor when we need it?
Another question: Is AJAX the only thing broken by this, or are we looking at other systemic problems that we just haven't caught yet? It kind of looks like we're working hard to work around a large issue with large repercussions.
Are there other large swaths of Drupal that are untested by the automated testing framework, besides AJAX? They might be laying in wait for us on this.
Comment #7
webchickI'm still not sure why we need this. How are D5 and D6 modules doing AJAX? They also have no function registry.
Comment #8
quicksketchDrupal 6 manages this because fields can only be assigned to nodes. For example poll module just straight-up cheats and includes this file when doing AJAX requests:
But in D7, field can be attached to just about anything (such as terms, nodes, comments), not to mention my "managed_file" widget would pretty much add an AJAX behavior to any form, the list of includes that could potentially include AJAX behaviors is overwhelming and nearly impossible to compensate for. Especially if we start using the generic AJAX handler (ajax_form_callback), then just "cheating" and including the file isn't possible at all unless that callback knows which file needs to be included, which is what this patch does.
Comment #9
webchickCool, thanks. That makes sense.
That syntax is hella ugly, but I'm not exactly bursting with better ideas here... :( Heading to bed for now and will see if an epiphany strikes in the next 24-48 hours.
Comment #10
sunHow about leveraging menu system to store the 'file' property of the currently active menu router item in $form_state['storage'] for (all) forms?
1) drupal_get_form() on node/add/story - active menu callback (menu_get_item()?): 'file path' => 'modules/node', 'file' => 'node.pages.inc'
2) AJAX request to 'system/ajax' - retrieves cached form, auto-includes the stored file
Thoughts?
Comment #11
quicksketchHey sun, that's actually what I tried first, but the problem is the second time the form is rebuilt, it's being rebuild at "system/ajax" instead of the original menu path, so menu_get_item() works the first time but the second time (say you click "Add another item" twice), then you get "system/ajax" instead of "node/add/story".
I wasn't using $form_state['storage'] in my implementation though, since even touching that completely changes the way the form is built, something I'd rather not get in to.
I did some more thinking about why this is necessary in D7 but not D6, it mostly has to do with our changes to how AJAX forms are handled. In D6 we'd attempt to load the cached form, modify the cached copy, then save it back. This had the inherent problem that it was really fricking hard to accurately modify just a part of the form (say when you need to add another complete CCK field, it's a lot of processing). So in D7 with the new approach, we rebuild the entire form during the AJAX request, then pull out the changed part. This essentially makes it so that AJAX and non-AJAX forms behave identically, since the same thing would happen if JavaScript was disabled (the entire form is rebuilt).
A lot of people figured out this problem during the D6 release cycle and started doing the same thing that we're now doing in D7. katbailey and dmitri even wrote up this nice handbook page on it: http://drupal.org/node/331941. I agree that this is the best approach, even though it's not what we used in several other places (such as CCK's "Add another item" is actually *not* dependent on this in D6). However this entire approach is dependent the form function actually being available.
Comment #12
sunSince #367567: Use AJAX framework for "Add more" / load include file containing form / introduce $form_state['build_info'] is required to debug this in HEAD, I've submitted a full patch over there.
Attaching the relevant extract here, but I'd rather suggest to mark this issue duplicate in favor of the other.
Comment #14
sunMarking as duplicate of #367567: Use AJAX framework for "Add more" / load include file containing form / introduce $form_state['build_info']