How to create webforms programatically
Well, not really. I mean, you can create webforms programatically pretty easily. This tutorial will show you how easy. Or you could just use the rules module if you just want the node created. But also, I want to share the things that got me scratching my head, like creating the description of each webform component programatically.
I am sure you have had this scenario as well. You enabled the webform module and tought the site admins how to create them and add components. But each time they want to create a new webform, they send an email to you asking for help, and you practically end up creating the nodes for them, since it has already been 3 weeks since last time you told them how it was done. Ok, so let's make it dead simple for them. Create a node with a title, and put all components in the body - one per line. Ah, no more emails, and code nerds can go back to their terminal and away from clicking with a mouse.
Use case:
So I have this site where you can sign up for parts of an order. Like a co-op. Let's say we are ordering kittens. So an admin puts out the news that he is shipping out a new order, and these are the kittens that are up for grabs.
- Grey kitten
- Black kitten
- Killer kitten
- Clown kitten
We want to create a webform to find out how many kittens of each type the users of the site would want in this order.
So instead of me telling the admins to create a webform and add numeric components for each kitten, I just tell them to go ahead and click the big button that says “create order” (visible for admins only). In the title field, they give the order a name (like the name of the supplier) and in the body field they list all kittens available, one kitten per line. Optionally they can also add an URL to an animated gif of the kitten, if included on the same line in a parentheses. So much easier for them, so they can concentrate on kitten distribution instead. I also added a description field so they can use that for a closer description of the order. Ok, so this is the code (Drupal 7):
<?php/** * Implements hook_node_insert(). */function mymodule_node_insert($node) { // The node type I am using is called 'order'. if ($node->type == 'order') { // I am using the body field for values. $body = field_get_items('node', $node, 'body'); $comp = explode("\n", $body[0]['value']); // $comp now holds an array with all lines in the body field. $i = 1; $components = array(); foreach ($comp as $c) { if ($c == '') { // Skip empty lines. Maybe the non-tech admin pressed an extra enter? continue; } // Create the webform components array. Not sure if we need all these // values, but let's be sure. $components[$i] = array( 'cid' => $i, 'pid' => '0', // I don't trust the admin to make a key based on input :) 'form_key' => 'kitten_' . $i, 'name' => $c, // I want all lines to be numeric type component. 'type' => 'number', 'value' => '', 'extra' => array( 'title_display' => 'before', 'private' => 0, 'type' => 'textfield', 'decimals' => '', 'separator' => ',', 'point' => '.', 'unique' => 0, 'integer' => 0, 'conditional_operator' => '=', 'excludezero' => 0, 'field_prefix' => '', 'field_suffix' => '', 'description' => '', 'attributes' => array(), // The number has to be positive. 'min' => '0', 'max' => '', 'step' => '', 'conditional_component' => '', 'conditional_values' => '', ), 'mandatory' => '0', 'weight' => $i, 'page_num' => 1, ); $i++; } // Alright, let's create the node object. $n = new stdClass(); $n->type = 'webform'; // Let the user be the author. $n->uid = $node->uid; // Let the 'order' title be the webform title. $n->title = $node->title; // I put them all on the frontpage. $n->promote = 1; // And we are allowed to comment. $n->comment = 2; // Enter webform array. $n->webform = array( 'confirmation' => '', 'confirmation_format' => NULL, 'redirect_url' => '<confirmation>', 'status' => '1', 'block' => '0', 'teaser' => '0', 'allow_draft' => '0', 'auto_save' => '0', 'submit_notice' => '1', 'submit_text' => '', // Users can only submit once how many kittens they want. 'submit_limit' => '1', 'submit_interval' => '-1', 'total_submit_limit' => '-1', 'total_submit_interval' => '-1', 'record_exists' => TRUE, 'roles' => array( // Only authenticated users can submit this webform. 0 => '2', ), 'emails' => array(), // Here comes our $components array. 'components' => $components, ); // Here i also add the description field to the webform. $description = field_get_items('node', $node, 'field_description'); if (!empty($description[0]['value'])) { // Blah blah. } // Save the node. node_save($n); // You could also put something like drupal_goto('node/' . $n->nid) here if // you want. My use case is different. }}
Ok. This is all pretty straight forward, eh? So the thing that had me going nuts a while, was adding the links as the description of each component. Looking at a webform node object, one would think it would go in the “extra” array of each component. This is how a webform node with a component with a description looks like:
$node->webform['components'][1]['extra']['description'] = 'A description';
But after repeating different takes on adding it to the array (even trying to modify $n after it is saved, and saving it again), nothing seemed to do the trick. Luckily, webform has its own hooks. Enter hook_webform_component_presave(). Or “Modify a Webform component before it is saved to the database.” as it says in the documentation. Perfect! Let's go ahead and add a link to the animated gif as a description:
<?php/** * Implements hook_webform_component_presave(). */function mymodule_webform_component_presave(&$component) { // Maybe I should do some more robust checking here, but this is a simple site // and the body description clearly states how to format lines with links. // Also, remember if you have other use cases for the webform, you should // check that this is actually a webform created by above code! if (strpos($component['name'], '(http') > -1) { // Take the URL out of the name of the component. $name = substr($component['name'], 0, strpos($component['name'], '(http')); // Add animated gif link as description. $component['extra']['description'] = l(t('View animated gif with @name', array( '@name' => $name)), substr($component['name'], strpos($component['name'], 'http'), -2 )); // Set name to the new name (without the URL). $component['name'] = $name; }}
Bottom line? Webform is awesome for these kind of user submissions, and now with programatically creating them just like we want, things just got real easy for the non-techies on this site. Now let's see how many killer kittens I can afford. Sorry for the large GIF this time, but I could just not help myself. Killer kitten to follow (5.7MB):