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.

CommentFileSizeAuthor
webform_alter.patch687 bytesyhager

Comments

yhager’s picture

This 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?

quicksketch’s picture

The $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.

yhager’s picture

I 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?

quicksketch’s picture

Oh, 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.

pcave’s picture

I'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.

andyf’s picture

Did 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.

quicksketch’s picture

In #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.

andyf’s picture

Thanks 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?

quicksketch’s picture

the only issue I have is with the label shown on the webform component form.

Hm, 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.

andyf’s picture

Sorry, not the individual component form... webform_components_form (ie what you'll find at node/%webform_menu/webform). (I've already overridden the label and description on webform_component_edit_form just as you describe and that bit works great.) Thanks for your time.

quicksketch’s picture

Status: Needs review » Closed (won't fix)

Ah, 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.

andyf’s picture

Status: Closed (won't fix) » Active

Sorry 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:

  • use the custom component functions (for render, display and headers) to load the attribute name and modify the component;
  • use a form_alter on webform_components_form() to grab $node and modify the components before they get rendered by theme_webform_components();
  • use a form_alter to drupal_set_title() on the edit attribute page.

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.

quicksketch’s picture

Status: Active » Closed (won't fix)

Although 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.