Webforms cannot be easily be exported like other component such as views, content types, contexts and variables, since they are basically considered as content (if you really want to follow this path, you can use UUID, or consider switching to Entity Forms, which adopts a different approach). However, you can still write code in a module or an install profile to create one. The following is an adaptation of the code found on this page.

EDIT: DuaelFr recently wrote Webform Features!

$node = new stdClass();
$node->type = 'webform';
node_object_prepare($node);
$node->title = 'Contact Us';
$node->language = 'en';
$node->body[LANGUAGE_NONE][0]['value']   = '';
$node->body[LANGUAGE_NONE][0]['format']  = 'full_html';
$node->uid = 1;
$node->promote = 0;
$node->comment = 0;

// Create the webform components.
$components = array(
  array(
  'name' => 'Gender',
  'form_key' => 'gender',
  'type' => 'select',
  'mandatory' => 1,
  'weight' => 0,
  'pid' => 0,
  'extra' => array(
    'title_display' => 'inline',
    'private' => 0,
    'items' => "Mrs|Mrs\nMiss|Miss\nMr|Mr",
    'aslist' => 1,
  ),
),
array(
  'name' => 'Last name',
  'form_key' => 'name',
  'type' => 'textfield',
  'mandatory' => 1,
  'weight' => 5,
  'pid' => 0,
  'extra' => array(
    'title_display' => 'inline',
    'private' => 0,
  ),
),
array(
  'name' => 'First name',
  'form_key' => 'first_name',
  'type' => 'textfield',
  'mandatory' => 1,
  'weight' => 10,
  'pid' => 0,
  'extra' => array(
    'title_display' => 'inline',
    'private' => 0,
  ),
),
array(
  'name' => 'City',
  'form_key' => 'city',
  'type' => 'textfield',
  'mandatory' => 0,
  'weight' => 15,
  'pid' => 0,
  'extra' => array(
    'title_display' => 'inline',
    'private' => 0,
  ),
),
array(
  'name' => 'Country',
  'form_key' => 'country',
  'type' => 'select',
  'mandatory' => 0,
  'weight' => 20,
  'pid' => 0,
  'extra' => array(
    'title_display' => 'inline',
    'private' => 0,
    'aslist' => 1,
    'options_source' => 'countries',
  ),
),
array(
  'name' => 'Email address',
  'form_key' => 'email_address',
  'type' => 'email',
  'mandatory' => 1,
  'weight' => 25,
  'pid' => 0,
  'extra' => array(
    'title_display' => 'inline',
    'private' => 0,
  ),
),
array(
  'name' => 'Subject',
  'form_key' => 'subject',
  'type' => 'select',
  'mandatory' => 1,
  'weight' => 30,
  'pid' => 0,
  'extra' => array(
    'title_display' => 'inline',
    'private' => 0,
    'items' => "s1|Subject 1\nother|Other",
    'aslist' => 1,
  ),
),
array(
  'name' => 'Message',
  'form_key' => 'message',
  'type' => 'textarea',
  'mandatory' => 1,
  'weight' => 35,
  'pid' => 0,
  'extra' => array(
    'title_display' => 'inline',
    'private' => 0,
    ),
  ),
  array(
    'name' => 'Mandatory Fields',
    'form_key' => 'mandatory_fields',
    'type' => 'markup',
    'mandatory' => 0,
    'weight' => 40,
    'pid' => 0,
    'value' => '<p>Fields with * are mandatory</p>',
    'extra' => array(
      'title_display' => 'inline',
      'private' => 0,
      'format'=> 'full_html',
    ),
  ),
);

// Setup notification email.
$emails = array(
  array(
    'email' => 'somebody@example.tld',
    'subject' => 'default',
    'from_name' => 'default',
    'from_address' => 'default',
    'template' => 'default',
    'excluded_components' => array(),
  ),
);

// Attach the webform to the node.
$node->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' => '',
  'submit_limit' => '-1', // User can submit more than once.
  'submit_interval' => '-1',
  'total_submit_limit' => '-1',
  'total_submit_interval' => '-1',
  'record_exists' => TRUE,
  'roles' => array(
    0 => '1', // Anonymous user can submit this webform.
  ),
  'emails' => $emails,
  'components' => $components,
);

// Save the node.
node_save($node); 

Handy tip: You can create the webform manually, then use the node_export module to export it. This provides data structures which can be cut-and-pasted into your code. It includes both the components and any conditionals you have set up.

Comments

dblue’s picture

Very timely, thanks for creating this page just before I needed to find it... One thing that might be useful when coding a webform with a lot of components, to keep your code a little shorter you can leave default properties off the components if you pass them through the function webform_component_defaults before adding them to the node.

foreach($components as &$component) {
  webform_component_defaults($component);
}

And the default values for components can be seen in the functions _webform_defaults_<webform-component-type> found in each include file in the components subdirectory of the webform module.

yukare’s picture

This is good, but how someone can change the template for email in code ?

Fernando Correa da Conceição
http://jaguaribe.net.br

sisko’s picture

Great write-up and the code works flawlessly,

However, how would you adjust the code to add components to an existing webform rather than creating a new one?

FMB’s picture

A webform is just a node after all: you could load it with node_load(), add components (something like $node->components[] = array(...)) and save it back with node_save().

DieterAtWork’s picture

Thx for this, it helped me include the forms inside my install profile, though I got a problem by which I'm totally baffled. I have a translated node, and I create them both and link them with ->tnid. I add exactly the same form, with translated texts of course, and one is added fine, the other is missing the email and last component. I got no errors, I can't figure out what's going on. If someone has had this before or knows what to do?

thx in advance.

SOLUTION:

I found out that the second node_save($node) did cause the problem. I have to save the node twice, as I use the nid as tid, and I only can get that after the first save. Now to solve the problem, I added the webform before the first save, and did unset($node->webform) between the first and second save. It now works. It appears that on save, the info is used like it was first created, always, and not checked if it already existed.

Anonymous’s picture

If you have the webform_conditional module installed you might want to make a field react to the value of another field

'extra' => array(
    'title_display' => 'inline',
    'private' => 0,
    'items' => "s1|Subject 1\nother|Other",
    'aslist' => 1,
    'webform_conditional_field_value' => '1', // The value to react against, e.g '1 = Yes, 2=No'
    'webform_conditional_cid' => '10', // the field cid to react against
    'webform_conditional_operator' => '='
  ),
ibexy’s picture

Thanks for the code. It works perfect.
How can it be modified to work with a "grid" type component. For example to produce the below gride formation:

                          Yes      No
The sea is blue         []       []
Ludo.R’s picture

Does this work for branch 7.x-4.x?