With the latest 3.x branch, I am reading the following in webform_hooks.php:
/**
* Modify a loaded Webform component.
*
* IMPORTANT: This hook does not actually exist because components are loaded
* in bulk as part of webform_node_load(). Use hook_nodeapi() to modify loaded
* components when the node is loaded. This example is provided merely to point
* to hook_nodeapi().
*
* @see hook_nodeapi()
* @see webform_node_load()
*/
function hook_webform_component_load() {
// This hook does not exist. Instead use hook_nodeapi().
}
However, within hook_nodeapi('load'), the webform data is not yet attached to the node, and thus it is not possible to modify any of its properties.
I need to implement something like #406486: Allow pluggable select list values, and #151603: Can I put options in a select field from a database query?. This can be achieved by copy & paste 90% of the select component that is already implemented in webform, but it looks like a horrible way to achieve a dynamic select list.
The attached patch (not tested) provides a way to alter the webform components after they are built. It can also be done on a component basis, but this is a matter of preference.
I'd like to get your feedback on this approach - if the general direction is fine, I'll test a patch and submit it.
| Comment | File | Size | Author |
|---|---|---|---|
| webform_alter.patch | 687 bytes | yhager |
Comments
Comment #1
yhager commentedThis is quite confusing. I tried this patch, and modified the select options, and they got saved into the webform node.
On the other hand, the default value was not saved.
Maybe all this should be handled through hook_form_alter() instead?
Comment #2
quicksketchThe $node->webform information isn't available yet only if your module has a weight of 0 (since Webform starts with a "w", it's going to be last usually otherwise). You can also use use nodeapi $op == 'alter' (I think), if you don't want to adjust your module weight.
Comment #3
yhager commentedI checked the weight, webform had '-1' and my module '0'.
The problem is that node_invoke_nodeapi() does not merge the data from previous hooks until all hooks have finished running.
'alter' doesn't look like what I am after (if the docs at http://api.drupal.org/api/function/hook_nodeapi/6 are correct).
In addition, I managed to manipulate the webform using hook_form_alter(), by modifying the $form['submitted'] array (even adding a ctools dependent field works!). Am I on the right track?
Comment #4
quicksketchOh, hmm I didn't think about that problem that the $additions don't show up as they're added, that doesn't seem like it's correct. Anyway, of course you can use hook_form_alter() if it works just as well, though managing submitted data could get pretty sticky when dealing with multipage forms, drafts, and such.
Comment #5
pcave commentedI'm having the same issue. Has anyone come up with a better way than hook_form_alter. That can be royal pain like you said.
Comment #6
andyf commentedDid anything ever happen with this? For Ubercart Event Registration we have a webform component that represents an Ubercart attribute. I'd like to set the component's label from the attribute on node load, but there's no neat and simple way to do so AFAICT.
Comment #7
quicksketchIn #245424: Make Webform multilingual (i18n) aware through contributed modules we added hook_webform_component_render_alter() (for forms) and hook_webform_component_display_alter() (for displaying submissions). For users here, that may be a better alternative than a higher-level webform_alter() hook.
Comment #8
andyf commentedThanks for the info. As the component's a custom one, there's no problem with rendering it properly - the only issue I have is with the label shown on the webform component form. Currently I'm using a form_alter to modify
$form['#node']. Dunno if there's a nicer way?Comment #9
quicksketchHm, well the hook described in this issue wouldn't have helped you in any case then. You should be able to use the _webform_edit_[component_name]() hook in your custom component to change the form as you'd like it. Even though an existing $form variable isn't passed in, if you make an element with the same key as one that Webform would make by default, it will take precedence over the original.
Comment #10
andyf commentedSorry, not the individual component form...
webform_components_form(ie what you'll find atnode/%webform_menu/webform). (I've already overridden the label and description onwebform_component_edit_formjust as you describe and that bit works great.) Thanks for your time.Comment #11
quicksketchAh, the webform_components_form() is indeed only accessible via hook_form_alter(). Since there's already a specific hook for that provided by Drupal (hook_form_webform_component_form_alter()), I don't see any reason to add a hook for that one.
Going back to the original request here: a hook for nodeapi($op = 'load'), I'm going to mark this issue won't fix. In Drupal 7 it's entirely unnecessary since hook_node_load() passes the $nodes array around by reference.
Comment #12
andyf commentedSorry to reopen, I didn't explain myself very well. It's not really the form itself I want to alter. What I'd like in essence is to modify a component on load (in this case its name). I need the name to be loaded from an Ubercart Attribute, which can be changed via UC's UI. Ideally I'd just implement some kind of webform_alter (or component_load) and modify it directly. I tried to use hook_node_api('load') but as this issue says, that doesn't work. To emulate the effect of a truly dynamic component label turns out to be something of a chore (unless I'm missing something). Currently I've had to:
And that still leaves me stuck with the headers on the table and analysis results pages. When I started out I thought it would be simpler :)
The only way I can see to make the idea work properly (without more webform hooks or using hook_menu_alter() to change the webform menu loader) is to completely reverse my approach, and hook into UC, and when the attribute's values get changed, update the webform components. This seems uglier to me (imho) than using some kind of webform-based alter, and Ubercart supports global attributes with per class and per product customisations, so it's probably not a fun task.
To be honest I'm a bit surprised there's only been three people asking for the ability to modify a component on load, so I wonder if I've just missed a trick somewhere?!
Any help much appreciated.
Comment #13
quicksketchAlthough this issue is ancient, there are some recent updates. We added a hook for specifying component defaults in #2013523: Add an alter hook for component defaults. Which is sort of like an "onload", except you can't actually merge in data there, you can only provide default values. In Drupal 7 hook_node_load() gives you the opportunity to modify $nodes by reference on load, so to modify loaded components you would modify the loaded $nodes array to merge in any new settings you want into the component. On the other hand, usually the settings for the component are saved and loaded for you, so needing to load in your own custom data is unusual.
Considering there haven't been any further requests for this, I'm going to won't fix this again. At this point we're not adding APIs to the 3.x version, and considering this pretty much only affects Drupal 6, I think it's unlikely to be changed at this point.