You are here

webform.install in Webform 6.3

Webform module install/schema hooks.

File

webform.install
View source
<?php

/**
 * @file
 *   Webform module install/schema hooks.
 */

/**
 * Implements hook_schema().
 */
function webform_schema() {
  $schema = array();
  $schema['webform'] = array(
    'description' => 'Table for storing additional properties for webform nodes.',
    'fields' => array(
      'nid' => array(
        'description' => 'The node identifier of a webform.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'confirmation' => array(
        'description' => 'The confirmation message or URL displayed to the user after submitting a form.',
        'type' => 'text',
        'not null' => TRUE,
      ),
      'confirmation_format' => array(
        'description' => 'The input format used by the confirmation message.',
        'type' => 'int',
        'size' => 'tiny',
        'not null' => TRUE,
        'default' => 0,
      ),
      'redirect_url' => array(
        'description' => 'The URL a user is redirected to after submitting a form.',
        'type' => 'varchar',
        'length' => 255,
        'default' => '<confirmation>',
      ),
      'status' => array(
        'description' => 'Boolean value of a webform for open (1) or closed (0).',
        'type' => 'int',
        'size' => 'tiny',
        'not null' => TRUE,
        'default' => 1,
      ),
      'block' => array(
        'description' => 'Boolean value for whether this form be available as a block.',
        'type' => 'int',
        'size' => 'tiny',
        'not null' => TRUE,
        'default' => 0,
      ),
      'teaser' => array(
        'description' => 'Boolean value for whether the entire form should be displayed on the teaser.',
        'type' => 'int',
        'size' => 'tiny',
        'not null' => TRUE,
        'default' => 0,
      ),
      'allow_draft' => array(
        'description' => 'Boolean value for whether submissions to this form be saved as a draft.',
        'type' => 'int',
        'size' => 'tiny',
        'not null' => TRUE,
        'default' => 0,
      ),
      'auto_save' => array(
        'description' => 'Boolean value for whether submissions to this form should be auto-saved between pages.',
        'type' => 'int',
        'size' => 'tiny',
        'not null' => TRUE,
        'default' => 0,
      ),
      'submit_notice' => array(
        'description' => 'Boolean value for whether to show or hide the previous submissions notification.',
        'type' => 'int',
        'size' => 'tiny',
        'not null' => TRUE,
        'default' => 1,
      ),
      'submit_text' => array(
        'description' => 'The title of the submit button on the form.',
        'type' => 'varchar',
        'length' => 255,
      ),
      'submit_limit' => array(
        'description' => 'The number of submissions a single user is allowed to submit within an interval. -1 is unlimited.',
        'type' => 'int',
        'size' => 'tiny',
        'not null' => TRUE,
        'default' => -1,
      ),
      'submit_interval' => array(
        'description' => 'The amount of time in seconds that must pass before a user can submit another submission within the set limit.',
        'type' => 'int',
        'not null' => TRUE,
        'default' => -1,
      ),
      'total_submit_limit' => array(
        'description' => 'The total number of submissions allowed within an interval. -1 is unlimited.',
        'type' => 'int',
        'not null' => TRUE,
        'default' => -1,
      ),
      'total_submit_interval' => array(
        'description' => 'The amount of time in seconds that must pass before another submission can be submitted within the set limit.',
        'type' => 'int',
        'not null' => TRUE,
        'default' => -1,
      ),
    ),
    'primary key' => array(
      'nid',
    ),
  );
  $schema['webform_component'] = array(
    'description' => 'Stores information about components for webform nodes.',
    'fields' => array(
      'nid' => array(
        'description' => 'The node identifier of a webform.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'cid' => array(
        'description' => 'The identifier for this component within this node, starts at 0 for each node.',
        'type' => 'int',
        'size' => 'small',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'pid' => array(
        'description' => 'If this component has a parent fieldset, the cid of that component.',
        'type' => 'int',
        'size' => 'small',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'form_key' => array(
        'description' => 'When the form is displayed and processed, this key can be used to reference the results.',
        'type' => 'varchar',
        'length' => 128,
      ),
      'name' => array(
        'description' => 'The label for this component.',
        'type' => 'varchar',
        'length' => 255,
      ),
      'type' => array(
        'description' => 'The field type of this component (textfield, select, hidden, etc.).',
        'type' => 'varchar',
        'length' => 16,
      ),
      'value' => array(
        'description' => 'The default value of the component when displayed to the end-user.',
        'type' => 'text',
        'not null' => TRUE,
      ),
      'extra' => array(
        'description' => 'Additional information unique to the display or processing of this component.',
        'type' => 'text',
        'not null' => TRUE,
      ),
      'mandatory' => array(
        'description' => 'Boolean flag for if this component is required.',
        'type' => 'int',
        'size' => 'tiny',
        'not null' => TRUE,
        'default' => 0,
      ),
      'weight' => array(
        'description' => 'Determines the position of this component in the form.',
        'type' => 'int',
        'size' => 'small',
        'not null' => TRUE,
        'default' => 0,
      ),
    ),
    'primary key' => array(
      'nid',
      'cid',
    ),
  );
  $schema['webform_emails'] = array(
    'description' => 'Holds information regarding e-mails that should be sent upon submitting a webform',
    'fields' => array(
      'nid' => array(
        'description' => 'The node identifier of a webform.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'eid' => array(
        'description' => 'The e-mail identifier for this row\'s settings.',
        'type' => 'int',
        'unsigned' => TRUE,
        'size' => 'small',
        'not null' => TRUE,
        'default' => 0,
      ),
      'email' => array(
        'description' => 'The e-mail address that will be sent to upon submission. This may be an e-mail address, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
        'type' => 'text',
        'not null' => FALSE,
      ),
      'subject' => array(
        'description' => 'The e-mail subject that will be used. This may be a string, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
        'type' => 'varchar',
        'length' => '255',
        'not null' => FALSE,
      ),
      'from_name' => array(
        'description' => 'The e-mail "from" name that will be used. This may be a string, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
        'type' => 'varchar',
        'length' => '255',
        'not null' => FALSE,
      ),
      'from_address' => array(
        'description' => 'The e-mail "from" e-mail address that will be used. This may be a string, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
        'type' => 'varchar',
        'length' => '255',
        'not null' => FALSE,
      ),
      'template' => array(
        'description' => 'A template that will be used for the sent e-mail. This may be a string or the special key "default", which will use the template provided by the theming layer.',
        'type' => 'text',
        'not null' => FALSE,
      ),
      'excluded_components' => array(
        'description' => 'A list of components that will not be included in the %email_values token. A list of CIDs separated by commas.',
        'type' => 'text',
        'not null' => TRUE,
      ),
      'html' => array(
        'description' => 'Determines if the e-mail will be sent in an HTML format. Requires Mime Mail module.',
        'type' => 'int',
        'unsigned' => TRUE,
        'size' => 'tiny',
        'not null' => TRUE,
        'default' => 0,
      ),
      'attachments' => array(
        'description' => 'Determines if the e-mail will include file attachments. Requires Mime Mail module.',
        'type' => 'int',
        'unsigned' => TRUE,
        'size' => 'tiny',
        'not null' => TRUE,
        'default' => 0,
      ),
    ),
    'primary key' => array(
      'nid',
      'eid',
    ),
  );
  $schema['webform_roles'] = array(
    'description' => 'Holds access information regarding which roles are allowed to submit which webform nodes. Does not prevent access to the webform node entirely, use the {node_access} table for that purpose.',
    'fields' => array(
      'nid' => array(
        'description' => 'The node identifier of a webform.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'rid' => array(
        'description' => 'The role identifier.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
    ),
    'primary key' => array(
      'nid',
      'rid',
    ),
  );
  $schema['webform_submissions'] = array(
    'description' => 'Holds general information about submissions outside of field values.',
    'fields' => array(
      'sid' => array(
        'description' => 'The unique identifier for this submission.',
        'type' => 'serial',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'nid' => array(
        'description' => 'The node identifier of a webform.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'uid' => array(
        'description' => 'The id of the user that completed this submission.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'is_draft' => array(
        'description' => 'Is this a draft of the submission?',
        'type' => 'int',
        'size' => 'tiny',
        'not null' => TRUE,
        'default' => 0,
      ),
      'submitted' => array(
        'description' => 'Timestamp of when the form was submitted.',
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
      ),
      'remote_addr' => array(
        'description' => 'The IP address of the user that submitted the form.',
        'type' => 'varchar',
        'length' => 128,
      ),
    ),
    'primary key' => array(
      'sid',
    ),
    'unique keys' => array(
      'sid_nid' => array(
        'sid',
        'nid',
      ),
    ),
    'indexes' => array(
      'nid_uid_sid' => array(
        'nid',
        'uid',
        'sid',
      ),
      'nid_sid' => array(
        'nid',
        'sid',
      ),
    ),
  );
  $schema['webform_submitted_data'] = array(
    'description' => 'Stores all submitted field data for webform submissions.',
    'fields' => array(
      'nid' => array(
        'description' => 'The node identifier of a webform.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'sid' => array(
        'description' => 'The unique identifier for this submission.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'cid' => array(
        'description' => 'The identifier for this component within this node, starts at 0 for each node.',
        'type' => 'int',
        'size' => 'small',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'no' => array(
        'description' => 'Usually this value is 0, but if a field has multiple values (such as a time or date), it may require multiple rows in the database.',
        'type' => 'varchar',
        'length' => 128,
        'not null' => TRUE,
        'default' => '0',
      ),
      'data' => array(
        'description' => 'The submitted value of this field, may be serialized for some components.',
        'type' => 'text',
        'size' => 'medium',
        'not null' => TRUE,
      ),
    ),
    'primary key' => array(
      'nid',
      'sid',
      'cid',
      'no',
    ),
    'indexes' => array(
      'nid' => array(
        'nid',
      ),
      'sid_nid' => array(
        'sid',
        'nid',
      ),
    ),
  );
  $schema['webform_last_download'] = array(
    'description' => 'Stores last submission number per user download.',
    'fields' => array(
      'nid' => array(
        'description' => 'The node identifier of a webform.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'uid' => array(
        'description' => 'The user identifier.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'sid' => array(
        'description' => 'The last downloaded submission number.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'requested' => array(
        'description' => 'Timestamp of last download request.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
    ),
    'primary key' => array(
      'nid',
      'uid',
    ),
  );
  return $schema;
}

/**
 * Implements hook_install().
 */
function webform_install() {
  module_load_include('inc', 'node', 'content_types');
  db_query("UPDATE {system} SET weight = -1 WHERE name = 'webform' AND type = 'module'");
  drupal_install_schema('webform');

  // Optionally create the default webform type.
  if (variable_get('webform_install_create_content_type', TRUE)) {
    $webform_type = array(
      'type' => 'webform',
      'name' => st('Webform'),
      'module' => 'node',
      'description' => st('Create a new form or questionnaire accessible to users. Submission results and statistics are recorded and accessible to privileged users.'),
      'custom' => TRUE,
      'modified' => TRUE,
      'locked' => FALSE,
    );
    $webform_type = (object) _node_type_set_defaults($webform_type);
    node_type_save($webform_type);
  }
}

/**
 * Implements hook_uninstall().
 */
function webform_uninstall() {

  // Unset webform variables.
  variable_del('webform_node_types');
  variable_del('webform_node_types_primary');
  variable_del('webform_disabled_components');
  variable_del('webform_use_cookies');
  variable_del('webform_default_from_address');
  variable_del('webform_default_from_name');
  variable_del('webform_default_subject');
  variable_del('webform_default_format');
  variable_del('webform_format_override');
  variable_del('webform_csv_delimiter');
  variable_del('webform_allowed_tags');
  variable_del('webform_blocks');
  variable_del('webform_search_index');
  variable_del('webform_email_address_format');
  variable_del('webform_export_format');
  variable_del('webform_submission_access_control');
  variable_del('webform_update_batch_size');
  $component_list = array();
  $path = drupal_get_path('module', 'webform') . '/components';
  $files = file_scan_directory($path, '^.*\\.inc$');
  foreach ($files as $filename => $file) {
    variable_del('webform_enable_' . $file->name, 1);
  }

  // Delete uploaded files.
  $filepath = file_create_path('webform');
  _webform_recursive_delete($filepath);

  // Drop tables.
  drupal_uninstall_schema('webform');
}

/**
 * Implements hook_requirements().
 */
function webform_requirements($phase) {
  $requirements = array();
  $t = get_t();
  if (version_compare(VERSION, '6.16') < 0) {
    $requirements['webform_drupal_version'] = array(
      'title' => $t('Webform'),
      'value' => VERSION,
      'severity' => REQUIREMENT_ERROR,
      'description' => $t('Webform requires at least version @version of Drupal core.', array(
        '@version' => '6.16',
      )),
    );
  }
  return $requirements;
}

/**
 * Set the minimum upgrade version.
 *
 * This should allow updates from any 2.x version of Webform (for D5 or D6),
 * but prevent updates from any version prior to Webform 1.10.
 */
function webform_update_last_removed() {
  return 20;
}

/**
 * Upgrade to Drupal 6. Convert submissions sid column to auto-increment.
 */
function webform_update_6001() {
  $ret = array();

  // Keys must be dropped before altering the column.
  db_drop_primary_key($ret, 'webform_submissions');
  db_drop_unique_key($ret, 'webform_submissions', 'sid_nid');

  // Alter to a primary key and add the unique key back.
  db_change_field($ret, 'webform_submissions', 'sid', 'sid', array(
    'type' => 'serial',
    'not null' => TRUE,
  ), array(
    'primary key' => array(
      'sid',
    ),
  ));
  db_add_unique_key($ret, 'webform_submissions', 'sid_nid', array(
    'sid',
    'nid',
  ));
  return $ret;
}

/**
 * Increase the size of the component instance name.
 */
function webform_update_6200() {
  $ret = array();
  db_change_field($ret, 'webform_component', 'name', 'name', array(
    'type' => 'varchar',
    'length' => 255,
    'default' => 'NULL',
  ));
  return $ret;
}

/**
 * Add a column for email to the webform_component table.
 */
function webform_update_6201() {
  $ret = array();

  // This update will already be run as webform_update_5201 on Drupal 5.
  if (db_column_exists('webform_component', 'email')) {
    return $ret;
  }
  db_add_field($ret, 'webform_component', 'email', array(
    'type' => 'int',
    'size' => 'tiny',
    'not null' => TRUE,
    'default' => 0,
  ));
  $ret[] = update_sql('UPDATE {webform_component} SET email = 1');
  return $ret;
}

/**
 * Per-webform submission access control based on roles.
 */
function webform_update_6202() {
  $ret = array();

  // This update will already be run as webform_update_5202 on Drupal 5.
  if (db_table_exists('webform_roles')) {
    return $ret;
  }
  db_create_table($ret, 'webform_roles', array(
    'description' => 'Holds access information regarding which roles are allowed to submit which webform nodes. Does not prevent access to the webform node entirely, use the {node_access} table for that purpose.',
    'fields' => array(
      'nid' => array(
        'description' => 'The node identifier of a webform.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'rid' => array(
        'description' => 'The role identifier.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
    ),
    'primary key' => array(
      'nid',
      'rid',
    ),
  ));
  $result = db_query("SELECT nid FROM {node} WHERE type = 'webform'");
  while ($node = db_fetch_object($result)) {
    db_query("INSERT INTO {webform_roles} (nid, rid) VALUES (%d, 1)", $node->nid);
    db_query("INSERT INTO {webform_roles} (nid, rid) VALUES (%d, 2)", $node->nid);
  }
  return $ret;
}

/**
 * Cleanup filtering values used by the file component.
 *
 * Previously, file extensions were saved by category, exactly as the FormAPI
 * returned to the submit handler. All extensions are now stored in a single
 * array, including only valid extensions.
 */
function webform_update_6203() {
  $ret = array();

  // This update will already be run as webform_update_5203 on Drupal 5.
  $result = db_query("SELECT nid, cid, extra FROM {webform_component} WHERE type = 'file'");
  while ($component = db_fetch_object($result)) {
    $extra = unserialize($component->extra);
    $extensions = array();

    // Sanity check, set some defaults if no filtering is in place.
    if (!isset($extra['filtering']['types'])) {
      $extra['filtering']['types'] = array(
        'webimages' => drupal_map_assoc(array(
          'png',
          'gif',
          'jpg',
        )),
      );
    }
    elseif (!isset($extra['filtering']['types']['webimages'])) {
      continue;
    }

    // Defined types.
    foreach ($extra['filtering']['types'] as $category => $category_extensions) {
      foreach ((array) $category_extensions as $extension) {
        if (!is_numeric($extension)) {
          $extensions[] = $extension;
        }
      }
    }

    // Additional types.
    $additional_extensions = explode(',', $extra['filtering']['addextensions']);
    foreach ($additional_extensions as $extension) {
      $clean_extension = drupal_strtolower(trim($extension));
      if (!empty($clean_extension) && !in_array($clean_extension, $extensions)) {
        $extensions[] = $clean_extension;
      }
    }
    $extra['filtering']['types'] = $extensions;
    db_query("UPDATE {webform_component} SET extra = '%s' WHERE nid = %d AND cid = %d", serialize($extra), $component->nid, $component->cid);
  }
  return $ret;
}

/**
 * Set all files to permanent status uploaded by Webform.
 */
function webform_update_6204() {
  $ret = array();
  $ret[] = update_sql("UPDATE {files} SET status = 1 WHERE filepath LIKE '" . file_directory_path() . "/webform/%'");
  return $ret;
}

/**
 * Schema fixes to make Drupal 5 upgrades identical to clean Drupal 6 installs.
 */
function webform_update_6205() {
  $ret = array();

  // Remove disp-width and default from webform.nid.
  db_drop_primary_key($ret, 'webform');
  db_change_field($ret, 'webform', 'nid', 'nid', array(
    'type' => 'int',
    'unsigned' => TRUE,
    'not null' => TRUE,
  ), array(
    'primary key' => array(
      'nid',
    ),
  ));

  // Set not null property on webform.confirmation.
  db_change_field($ret, 'webform', 'confirmation', 'confirmation', array(
    'type' => 'text',
    'not null' => TRUE,
  ));

  // Set size to tiny, remove disp-width on webform.submit_limit.
  db_change_field($ret, 'webform', 'submit_limit', 'submit_limit', array(
    'type' => 'int',
    'size' => 'tiny',
    'not null' => TRUE,
    'default' => -1,
  ));

  // Set default value to -1, remove disp-width on webform.submit_interval.
  db_change_field($ret, 'webform', 'submit_interval', 'submit_interval', array(
    'type' => 'int',
    'not null' => TRUE,
    'default' => -1,
  ));

  // Set not null property on webform.additional_validate.
  db_change_field($ret, 'webform', 'additional_validate', 'additional_validate', array(
    'type' => 'text',
    'not null' => TRUE,
  ));

  // Set not null property on webform.additional_submit.
  db_change_field($ret, 'webform', 'additional_submit', 'additional_submit', array(
    'type' => 'text',
    'not null' => TRUE,
  ));

  // Set not null property, default on webform_component.name.
  db_change_field($ret, 'webform_component', 'name', 'name', array(
    'type' => 'varchar',
    'length' => 255,
  ));

  // Set not null property on webform_component.value.
  db_change_field($ret, 'webform_component', 'value', 'value', array(
    'type' => 'text',
    'not null' => TRUE,
  ));

  // Set not null property on webform_component.extra.
  db_change_field($ret, 'webform_component', 'extra', 'extra', array(
    'type' => 'text',
    'not null' => TRUE,
  ));

  // Set column size, disp-width on webform_component.mandatory.
  db_change_field($ret, 'webform_component', 'mandatory', 'mandatory', array(
    'type' => 'int',
    'size' => 'tiny',
    'not null' => TRUE,
    'default' => 0,
  ));

  // Set column size, disp-width, not null property on webform_component.weight.
  db_change_field($ret, 'webform_component', 'weight', 'weight', array(
    'type' => 'int',
    'size' => 'small',
    'not null' => TRUE,
    'default' => 0,
  ));

  // Set unsigned, not null property on webform_submissions.sid.
  db_drop_unique_key($ret, 'webform_submissions', 'sid_nid');
  db_change_field($ret, 'webform_submissions', 'sid', 'sid', array(
    'type' => 'int',
    'unsigned' => TRUE,
    'not null' => TRUE,
  ));
  db_drop_primary_key($ret, 'webform_submissions');
  db_change_field($ret, 'webform_submissions', 'sid', 'sid', array(
    'type' => 'serial',
    'unsigned' => TRUE,
    'not null' => TRUE,
  ), array(
    'primary key' => array(
      'sid',
    ),
    'unique keys' => array(
      'sid_nid' => array(
        'sid',
        'nid',
      ),
    ),
  ));

  // Temporarily drop all keys from the webform_submitted_data table for changes.
  db_drop_primary_key($ret, 'webform_submitted_data');
  db_drop_index($ret, 'webform_submitted_data', 'nid');
  db_drop_index($ret, 'webform_submitted_data', 'sid_nid');

  // Set unsigned, size on webform_submitted_data.no.
  db_change_field($ret, 'webform_submitted_data', 'no', 'no', array(
    'type' => 'int',
    'unsigned' => TRUE,
    'size' => 'tiny',
    'not null' => TRUE,
    'default' => 0,
  ));

  // Set size, not null property on webform_submitted_data.data.
  db_change_field($ret, 'webform_submitted_data', 'data', 'data', array(
    'type' => 'text',
    'size' => 'medium',
    'not null' => TRUE,
  ));

  // Set correct keys.
  db_add_primary_key($ret, 'webform_submitted_data', array(
    'nid',
    'sid',
    'cid',
    'no',
  ));
  db_add_index($ret, 'webform_submitted_data', 'nid', array(
    'nid',
  ));
  db_add_index($ret, 'webform_submitted_data', 'sid_nid', array(
    'sid',
    'nid',
  ));
  return $ret;
}

/**
 * Add a separate column for confirmation message input format.
 */
function webform_update_6301() {
  $ret = array();

  // Safety check to prevent re-adding existing column.
  if (db_column_exists('webform', 'confirmation_format')) {
    return $ret;
  }
  db_add_field($ret, 'webform', 'confirmation_format', array(
    'type' => 'int',
    'size' => 'tiny',
    'not null' => TRUE,
    'default' => 0,
  ));
  $result = db_query("SELECT n.nid, nr.format FROM {node} n INNER JOIN {node_revisions} nr ON n.vid = nr.vid WHERE n.type = 'webform'");
  while ($node = db_fetch_object($result)) {
    db_query('UPDATE {webform} SET confirmation_format = %d WHERE nid = %d', $node->format, $node->nid);
  }
  return $ret;
}

/**
 * Convert node-level e-mail settings to new webform_emails table.
 */
function webform_update_6302() {
  $ret = array();

  // Safety check to prevent recreating the webform_emails table.
  if (db_table_exists('webform_emails')) {
    return $ret;
  }
  $table = array(
    'fields' => array(
      'nid' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'eid' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'size' => 'small',
        'not null' => TRUE,
        'default' => 0,
      ),
      'email' => array(
        'type' => 'text',
        'not null' => FALSE,
      ),
      'subject' => array(
        'type' => 'varchar',
        'length' => '255',
        'not null' => FALSE,
      ),
      'from_name' => array(
        'type' => 'varchar',
        'length' => '255',
        'not null' => FALSE,
      ),
      'from_address' => array(
        'type' => 'varchar',
        'length' => '255',
        'not null' => FALSE,
      ),
      'template' => array(
        'type' => 'text',
        'not null' => FALSE,
      ),
    ),
    'primary key' => array(
      'nid',
      'eid',
    ),
  );
  db_create_table($ret, 'webform_emails', $table);

  // Move over data from the webform table.
  $result = db_query("SELECT w.nid, w.email, w.email_from_name, w.email_from_address, w.email_subject, wc.cid, wc.extra FROM {webform} w LEFT JOIN {webform_component} wc ON w.nid = wc.nid AND type IN ('select', 'hidden', 'email') ORDER BY nid");
  $nid = 0;
  while ($row = db_fetch_object($result)) {

    // Insert an e-mail settings row for the default e-mail.
    if ($row->nid != $nid) {
      $nid = $row->nid;
      $eid = 0;
      if (!empty($row->email)) {
        $eid++;
        db_query("INSERT INTO {webform_emails} (nid, eid, email, subject, from_name, from_address, template) VALUES (%d, %d, '%s', '%s', '%s', '%s', 'default')", $nid, $eid, $row->email, $row->email_subject, $row->email_from_name, $row->email_from_address);
      }
    }

    // Check for an e-mail based on a component.
    if ($row->extra) {
      $extra = unserialize($row->extra);
      if ($extra['email']) {
        $eid++;
        unset($extra['email']);
        db_query("INSERT INTO {webform_emails} (nid, eid, email, subject, from_name, from_address, template) VALUES (%d, %d, '%s', '%s', '%s', '%s', 'default')", $nid, $eid, $row->cid, $row->email_subject, $row->email_from_name, $row->email_from_address);
        db_query("UPDATE {webform_component} SET extra = '%s' WHERE nid = %d AND cid = %d", serialize($extra), $row->nid, $row->cid);
      }
    }
  }

  // Remove columns from webform table.
  db_drop_field($ret, 'webform', 'email');
  db_drop_field($ret, 'webform', 'email_from_name');
  db_drop_field($ret, 'webform', 'email_from_address');
  db_drop_field($ret, 'webform', 'email_subject');
  return $ret;
}

/**
 * Add the submit_notice field and update all existing webforms to the 2.x previous submissions notice default.
 */
function webform_update_6303() {
  $ret = array();
  db_add_field($ret, 'webform', 'submit_notice', array(
    'type' => 'int',
    'size' => 'tiny',
    'not null' => TRUE,
    'default' => 0,
  ));
  $ret[] = update_sql("UPDATE {webform} SET submit_notice = 1 WHERE submit_notice = 0");
  return $ret;
}

/**
 * Convert the webform content type to be owned by Node module.
 */
function webform_update_6304() {
  $ret = array();
  $ret[] = update_sql("UPDATE {node_type} SET module = 'node', custom = 1, modified = 1, locked = 0 WHERE type = 'webform'");
  if (!db_affected_rows()) {
    $ret[] = update_sql("INSERT INTO {node_type} (type, name, module, description, help, has_title, title_label, has_body, body_label, min_word_count, custom, modified, locked, orig_type) VALUES ('webform', 'Webform', 'node', 'Create a new form or questionnaire accessible to users. Submission results and statistics are recorded and accessible to privileged users.', '', 1, 'Title', 1, 'Body', 0, 1, 1, 0, 'webform')");
  }

  // This variable for some reason must be set manually.
  variable_set('webform_node_types', array(
    'webform',
  ));
  return $ret;
}

/**
 * Migrate the renamed permissions. Add separate permissions for delete.
 */
function webform_update_6305() {
  $ret = array();
  $updated_permissions = array(
    'create webforms' => array(
      'add webform content',
    ),
    'edit own webforms' => array(
      'edit own webform content',
      'delete own webform content',
    ),
    'edit webforms' => array(
      'edit any webform content',
      'delete any webform content',
    ),
    'access webform results' => array(
      'access all webform results',
    ),
    'edit webform submissions' => array(
      'edit all webform submissions',
    ),
    'edit own webform submissions' => array(
      'edit own webform submissions',
      'delete own webform submissions',
    ),
    'clear webform results' => array(
      'delete all webform submissions',
    ),
  );
  $result = db_query("SELECT * FROM {role} r INNER JOIN {permission} p ON p.rid = r.rid");
  while ($role = db_fetch_object($result)) {
    $role->perm = drupal_map_assoc(explode(', ', db_escape_string($role->perm)));
    foreach ($updated_permissions as $old => $new) {
      if (isset($role->perm[$old])) {
        unset($role->perm[$old]);
        foreach ($new as $perm) {
          $role->perm[$perm] = $perm;
        }
      }
    }
    $ret[] = update_sql("UPDATE {permission} SET perm = '" . implode(', ', $role->perm) . "' WHERE rid = " . $role->rid);
  }
  return $ret;
}

/**
 * Add the ability to save as draft.
 */
function webform_update_6306() {
  $ret = array();
  db_add_field($ret, 'webform', 'allow_draft', array(
    'type' => 'int',
    'size' => 'tiny',
    'not null' => TRUE,
    'default' => 0,
  ));
  db_add_field($ret, 'webform_submissions', 'is_draft', array(
    'type' => 'int',
    'size' => 'tiny',
    'not null' => TRUE,
    'default' => 0,
  ));
  return $ret;
}

/**
 * Convert the file component to use only FIDs instead of serialized arrays.
 */
function webform_update_6307() {
  $ret = array();
  $result = db_query("SELECT d.*, s.uid, s.submitted FROM {webform_submitted_data} d INNER JOIN {webform_component} c ON d.cid = c.cid AND d.nid = c.nid AND c.type = 'file' INNER JOIN {webform_submissions} s ON d.sid = s.sid");
  while ($row = db_fetch_object($result)) {
    $file = @unserialize($row->data);

    // File name should always exist, even when upgrading from Drupal 5.
    if ($file && isset($file['filename'])) {

      // Create an entry in the files table if needed.
      if (!isset($file['fid'])) {
        db_query("INSERT INTO {files} (uid, filename, filepath, filemime, filesize, status, timestamp) VALUES (%d, '%s', '%s', '%s', %d, 1, %d)", $row->uid, $file['filename'], $file['filepath'], $file['filemime'], $file['filesize'], $row->submitted);
        $fid = db_last_insert_id('files', 'fid');
      }
      else {
        $fid = $file['fid'];
      }

      // Update the submitted data with the FID.
      db_query("UPDATE {webform_submitted_data} SET data = '%d' WHERE nid = %d AND sid = %d AND cid = %d", $fid, $row->nid, $row->sid, $row->cid);
    }
    else {
      db_query("UPDATE {webform_submitted_data} SET data = '' WHERE nid = %d AND sid = %d AND cid = %d", $row->nid, $row->sid, $row->cid);
    }
  }
  $ret[] = array(
    'success' => TRUE,
    'query' => t('Updated file components to use numeric file IDs in the submitted values.'),
  );
  return $ret;
}

/**
 * Convert "Include in e-mail" from the component-level to a per e-mail setting.
 */
function webform_update_6308() {
  $ret = array();

  // Add the new column to the e-mail table.
  db_add_field($ret, 'webform_emails', 'excluded_components', array(
    'type' => 'text',
    'not null' => TRUE,
    'initial' => '',
  ));

  // Build up our EXCLUSION lists, finding all components not in e-mails.
  $result = db_query("SELECT nid, cid FROM {webform_component} WHERE email = 0 ORDER BY nid");
  $nid = 0;
  $excluded_components = array();
  while ($row = db_fetch_object($result)) {
    if ($nid != $row->nid) {
      if (!empty($excluded_components)) {
        db_query("UPDATE {webform_emails} SET excluded_components = '%s' WHERE nid = %d", implode(',', $excluded_components), $nid);
      }
      $nid = $row->nid;
      $excluded_components = array();
    }
    $excluded_components[] = $row->cid;
  }

  // One last query for the last form in the list.
  if (!empty($excluded_components)) {
    db_query("UPDATE {webform_emails} SET excluded_components = '%s' WHERE nid = %d", implode(',', $excluded_components), $nid);
  }
  db_drop_field($ret, 'webform_component', 'email');
  return $ret;
}

/**
 * Fix permissions for all roles by adding an additional space after comma.
 */
function webform_update_6309() {
  $ret = array();
  $result = db_query("SELECT r.rid, p.perm FROM {role} r INNER JOIN {permission} p ON p.rid = r.rid");
  while ($role = db_fetch_object($result)) {
    $perms = explode(',', $role->perm);
    foreach ($perms as $id => $perm) {
      $perms[$id] = db_escape_string(trim($perm));
    }
    $ret[] = update_sql("UPDATE {permission} SET perm = '" . implode(', ', $perms) . "' WHERE rid = " . $role->rid);
  }
  return $ret;
}

/**
 * Add the redirect_url field and update existing webforms to use it.
 */
function webform_update_6310() {
  $ret = array();

  // Safety check to prevent re-adding existing column.
  if (db_column_exists('webform', 'redirect_url')) {
    return $ret;
  }

  // Add the new redirect_url column.
  db_add_field($ret, 'webform', 'redirect_url', array(
    'type' => 'varchar',
    'length' => '255',
  ));

  // If the webform is using the confirmation field as a redirect then move it
  // to the new redirect_url field.
  $result = db_query("SELECT nid, confirmation FROM {webform}");
  while ($row = db_fetch_object($result)) {
    $confirmation = trim(strip_tags($row->confirmation, '<front>'));
    if (valid_url($confirmation, TRUE) || preg_match('/^internal:/', $confirmation)) {
      $redirect_url = preg_replace('/^internal:/', '', $confirmation);
      db_query("UPDATE {webform} SET redirect_url = '%s' WHERE nid = %d", $redirect_url, $row->nid);
      db_query("UPDATE {webform} SET confirmation = '' WHERE nid = %d", $row->nid);
    }
    elseif (preg_match('/^message:/', $confirmation)) {
      $message = preg_replace('/^message:/', '', $confirmation);
      db_query("UPDATE {webform} SET redirect_url = '%s' WHERE nid = %d", 'node/' . $row->nid, $row->nid);
      db_query("UPDATE {webform} SET confirmation = '%s' WHERE nid = %d", $message, $row->nid);
    }
  }
  return $ret;
}

/**
 * Convert the "no" column to be a varchar column instead of an integer.
 */
function webform_update_6311() {
  $ret = array();

  // Change the column.
  db_drop_primary_key($ret, 'webform_submitted_data');
  db_change_field($ret, 'webform_submitted_data', 'no', 'no', array(
    'type' => 'varchar',
    'length' => 128,
    'not null' => TRUE,
    'default' => '0',
  ), array());
  db_add_primary_key($ret, 'webform_submitted_data', array(
    'nid',
    'sid',
    'cid',
    'no',
  ));

  // Update date components.
  $ret[] = update_sql("UPDATE {webform_submitted_data} SET no = 'month' WHERE no = '0' AND (nid, cid) IN (SELECT nid, cid FROM {webform_component} WHERE type = 'date')");
  $ret[] = update_sql("UPDATE {webform_submitted_data} SET no = 'day' WHERE no = '1' AND (nid, cid) IN (SELECT nid, cid FROM {webform_component} WHERE type = 'date')");
  $ret[] = update_sql("UPDATE {webform_submitted_data} SET no = 'year' WHERE no = '2' AND (nid, cid) IN (SELECT nid, cid FROM {webform_component} WHERE type = 'date')");

  // Update time components.
  $ret[] = update_sql("UPDATE {webform_submitted_data} SET no = 'hour' WHERE no = '0' AND (nid, cid) IN (SELECT nid, cid FROM {webform_component} WHERE type = 'time')");
  $ret[] = update_sql("UPDATE {webform_submitted_data} SET no = 'minute' WHERE no = '1' AND (nid, cid) IN (SELECT nid, cid FROM {webform_component} WHERE type = 'time')");
  $ret[] = update_sql("UPDATE {webform_submitted_data} SET no = 'ampm' WHERE no = '2' AND (nid, cid) IN (SELECT nid, cid FROM {webform_component} WHERE type = 'time')");

  // Updating of select and grid components is done in following updates.
  return $ret;
}

/**
 * Convert select options to use numeric keys if none are manually specified.
 */
function webform_update_6312() {
  $ret = array();
  $result = db_query("SELECT * FROM {webform_component} WHERE type = 'select'");
  while ($row = db_fetch_object($result)) {
    $extra = unserialize($row->extra);
    $lines = explode("\n", $extra['items']);

    // Get a list of items that have manual keys or no keys at all.
    $keys = array();
    $values = array();
    foreach ($lines as $line) {
      $line = rtrim($line, "\r\n");
      if (strlen($line) == 0) {
        continue;
      }
      $matches = array();
      if (preg_match('/^\\<([^>]*)\\>$/', $line, $matches)) {
        $keys[] = '<group>';
        $values[] = $line;
      }
      elseif (preg_match('/^([^|]+)\\|(.*)$/', $line, $matches)) {
        $keys[] = $matches[1];
        $values[] = $matches[2];
      }
      else {
        $keys[] = '<unknown>';
        $values[] = $line;
      }
    }

    // Assign new keys to items that have none.
    $new_key = 0;
    $new_keys = array();
    $items = '';
    foreach ($keys as $n => $key) {
      if ($key == '<group>') {
        $items .= $values[$n] . "\n";
      }
      elseif ($key == '<unknown>') {
        while (in_array($new_key, $keys, TRUE) || in_array($new_key, $new_keys, TRUE)) {
          $new_key++;
        }
        $new_keys[$n] = $new_key;
        $items .= $new_key . '|' . $values[$n] . "\n";
      }
      else {
        $items .= $key . '|' . $values[$n] . "\n";
      }
    }

    // While we're here, get rid of the 'Y' value for options.
    foreach ($extra as $key => $value) {
      if ($value === 'Y') {
        $extra[$key] = '1';
      }
      elseif ($value === 'N') {
        $extra[$key] = 0;
      }
    }

    // Update the component.
    $extra['items'] = $items;
    db_query("UPDATE {webform_component} SET extra = '%s' WHERE nid = %d AND cid = %d", serialize($extra), $row->nid, $row->cid);

    // Update the saved results.
    foreach ($new_keys as $delta => $new_key) {
      db_query("UPDATE {webform_submitted_data} SET data = '%s' WHERE nid = %d AND cid = %d AND data = '%s'", $new_key, $row->nid, $row->cid, $values[$delta]);
    }

    // Delete empty rows, which are no longer stored for select lists.
    db_query("DELETE FROM {webform_submitted_data} WHERE data = '' AND nid = %d AND cid = %d");
  }
  return $ret;
}

/**
 * Create keys for all questions (which don't currently have keys at all),
 * create keys for all options that don't yet have any, and then convert the
 * existing data to use these keys.
 */
function webform_update_6313() {
  $ret = array();
  $result = db_query("SELECT * FROM {webform_component} WHERE type = 'grid'");
  while ($row = db_fetch_object($result)) {
    $extra = unserialize($row->extra);
    $lines = explode("\n", $extra['options']);

    // Get a list of items that have manual keys or no keys at all.
    $keys = array();
    $values = array();
    foreach ($lines as $line) {
      $line = rtrim($line, "\r\n");
      if (strlen($line) == 0) {
        continue;
      }
      $matches = array();
      if (preg_match('/^([^|]+)\\|(.*)$/', $line, $matches)) {
        $keys[] = $matches[1];
        $values[] = $matches[2];
      }
      else {
        $keys[] = '<unknown>';
        $values[] = $line;
      }
    }

    // Assign new keys to options that have none.
    $new_key = 0;
    $new_keys = array();
    $options = '';
    foreach ($keys as $n => $key) {
      if ($key == '<unknown>') {
        while (in_array($new_key, $keys, TRUE) || in_array($new_key, $new_keys, TRUE)) {
          $new_key++;
        }
        $new_keys[$n] = $new_key;
        $options .= $new_key . '|' . $values[$n] . "\n";
      }
      else {
        $options .= $key . '|' . $values[$n] . "\n";
      }
    }
    $extra['options'] = $options;

    // Assign question keys. This is easier since they don't have keys at all.
    $lines = explode("\n", $extra['questions']);
    $questions = array();
    foreach ($lines as $delta => $line) {
      $line = rtrim($line, "\r\n");
      if (strlen($line) == 0) {
        continue;
      }
      $questions[$delta] = $delta . '|' . $line;
    }
    $extra['questions'] = implode("\n", $questions);

    // While we're here, get rid of the 'Y' value for options.
    foreach ($extra as $key => $value) {
      if ($value === 'Y') {
        $extra[$key] = '1';
      }
      elseif ($value === 'N') {
        $extra[$key] = 0;
      }
    }

    // Update the component.
    db_query("UPDATE {webform_component} SET extra = '%s' WHERE nid = %d AND cid = %d", serialize($extra), $row->nid, $row->cid);

    // Convert the option values into keys if new ones were created.
    if (!empty($new_keys)) {
      $args = array();
      $sql = "UPDATE {webform_submitted_data} SET data = CASE data";
      foreach ($new_keys as $delta => $new_key) {
        $sql .= " WHEN '%s' THEN '%s'";
        $args[] = $values[$delta];
        $args[] = $new_key;
      }
      $sql .= " ELSE data END WHERE nid = %d AND cid = %d";
      $args[] = $row->nid;
      $args[] = $row->cid;
      db_query($sql, $args);
    }

    // Note: Converting the question values into keys is not necessary because
    // data was already stored based on the question position. Since we assigned
    // permanent keys based on position, all our keys are already accurate.
    // Delete empty rows, which are no longer stored for grids.
    db_query("DELETE FROM {webform_submitted_data} WHERE data = '' AND nid = %d AND cid = %d");
  }
  return $ret;
}

/**
 * Convert Dates and Times into using ISO 8601 strings instead of 3 rows.
 */
function webform_update_6314() {
  $ret = array();

  // Note that we can't use webform_component_include(), because calls to
  // hook_webform_component_info() will fail if webform.module is disabled.
  drupal_load('module', 'webform');
  module_load_include('inc', 'webform', 'components/time');
  module_load_include('inc', 'webform', 'components/date');
  $result = db_query("SELECT c.type, c.extra, d.* FROM {webform_component} c INNER JOIN {webform_submitted_data} d ON c.nid = d.nid AND c.cid = d.cid WHERE c.type = 'time' OR c.type = 'date' ORDER BY d.sid, d.cid");
  $current_cid = NULL;
  $current_sid = NULL;
  while ($row = db_fetch_object($result)) {
    if ($current_cid != $row->cid || $current_sid != $row->sid) {
      $current_cid = $row->cid;
      $current_sid = $row->sid;
      $extra = unserialize($row->extra);
      $value = array();
    }
    $value[$row->no] = $row->data;

    // Update a complete date.
    if ($row->type == 'date' && array_key_exists('day', $value) && array_key_exists('month', $value) && array_key_exists('year', $value)) {
      $value = $value['day'] && $value['month'] && $value['year'] ? webform_date_string($value, 'date') : '';
      db_query("UPDATE {webform_submitted_data} SET no = '0', data = '%s' WHERE sid = %d AND cid = %d AND no = 'day'", $value, $current_sid, $current_cid);
    }

    // Update a complete time.
    if ($row->type == 'time' && array_key_exists('hour', $value) && array_key_exists('minute', $value) && ($extra['hourformat'] == '24-hour' || array_key_exists('ampm', $value))) {
      if ($extra['hourformat'] == '12-hour') {
        $value = webform_time_convert($value, '24-hour');
      }
      $value = $value['hour'] ? webform_date_string($value, 'time') : '';
      db_query("UPDATE {webform_submitted_data} SET no = '0', data = '%s' WHERE sid = %d AND cid = %d AND no = 'hour'", $value, $current_sid, $current_cid);
    }
  }

  // Remove all extra rows (which should just be day, minute, and ampm values).
  db_query("DELETE FROM {webform_submitted_data} WHERE no IN ('day', 'month', 'year', 'hour', 'minute', 'second', 'ampm')");
  $ret[] = array(
    'success' => TRUE,
    'query' => t('Updated date and time components to use ISO 8601 formatted strings in the submitted values.'),
  );
  return $ret;
}

/**
 * Remove "daylight_savings" and the GMT option in "timezone".
 */
function webform_update_6315() {
  $ret = array();
  $result = db_query("SELECT * FROM {webform_component} WHERE type IN ('date', 'time')");
  while ($row = db_fetch_object($result)) {
    $extra = unserialize($row->extra);
    if ($extra['timezone'] == 'gmt') {
      $extra['timezone'] = 'site';
    }
    unset($extra['check_daylight_savings']);
    db_query("UPDATE {webform_component} SET extra = '%s' WHERE nid = %d AND cid = %d", serialize($extra), $row->nid, $row->cid);
  }
  $ret[] = array(
    'success' => TRUE,
    'query' => t('Removed Webform-specific daylight savings handling, now provided by Date API module if available.'),
  );
  return $ret;
}

/**
 * Remove the Webform Debug variable.
 */
function webform_update_6316() {
  $ret = array();
  variable_del('webform_debug');
  $ret[] = array(
    'success' => TRUE,
    'query' => t('Removed the webform_debug variable which is no longer used.'),
  );
  return $ret;
}

/**
 * Remove orphaned e-mail settings of nodes that have been deleted.
 */
function webform_update_6317() {
  $ret = array();
  $ret[] = update_sql("DELETE FROM {webform_emails} WHERE nid NOT IN (SELECT nid FROM {node})");
  return $ret;
}

/**
 * Ensure that the confirmation format column is correctly using size = 'tiny'.
 */
function webform_update_6318() {
  $ret = array();
  db_change_field($ret, 'webform', 'confirmation_format', 'confirmation_format', array(
    'type' => 'int',
    'size' => 'tiny',
    'not null' => TRUE,
    'default' => 0,
  ));
  return $ret;
}

/**
 * Add columns for e-mail HTML and attachment settings.
 */
function webform_update_6319() {
  $ret = array();
  if (!db_column_exists('webform_emails', 'html')) {
    db_add_field($ret, 'webform_emails', 'html', array(
      'type' => 'int',
      'size' => 'tiny',
      'unsigned' => TRUE,
      'default' => 0,
      'not null' => TRUE,
    ));
    db_add_field($ret, 'webform_emails', 'attachments', array(
      'type' => 'int',
      'size' => 'tiny',
      'unsigned' => TRUE,
      'default' => 0,
      'not null' => TRUE,
    ));
  }
  return $ret;
}

/**
 * Set the default for the "submit_notice" column to 1.
 */
function webform_update_6320() {
  $ret = array();
  db_change_field($ret, 'webform', 'submit_notice', 'submit_notice', array(
    'type' => 'int',
    'size' => 'tiny',
    'not null' => TRUE,
    'default' => 1,
  ));
  return $ret;
}

/**
 * Add field for block feature and redirection setting.
 */
function webform_update_6321() {
  $ret = array();
  db_add_field($ret, 'webform', 'block', array(
    'type' => 'int',
    'size' => 'tiny',
    'not null' => TRUE,
    'default' => 0,
  ));
  db_change_field($ret, 'webform', 'redirect_url', 'redirect_url', array(
    'type' => 'varchar',
    'length' => 255,
    'default' => '<confirmation>',
  ));
  update_sql("UPDATE {webform} SET redirect_url = '<confirmation>' WHERE redirect_url = '' OR redirect_url IS NULL");
  return $ret;
}

/**
 * Set additional_validate and additional_submit columns to allow NULL.
 */
function webform_update_6322() {
  $ret = array();
  if (db_column_exists('webform', 'additional_validate')) {
    db_change_field($ret, 'webform', 'additional_validate', 'additional_validate', array(
      'type' => 'text',
      'not null' => FALSE,
    ));
    db_change_field($ret, 'webform', 'additional_submit', 'additional_submit', array(
      'type' => 'text',
      'not null' => FALSE,
    ));
  }
  return $ret;
}

/**
 * Add column for webform status (open or closed).
 */
function webform_update_6323() {
  $ret = array();
  if (!db_column_exists('webform', 'status')) {
    db_add_field($ret, 'webform', 'status', array(
      'type' => 'int',
      'size' => 'tiny',
      'not null' => TRUE,
      'default' => 1,
    ));
  }
  return $ret;
}

/**
 * Add the ability to auto-save as draft between pages.
 */
function webform_update_6324() {
  $ret = array();
  db_add_field($ret, 'webform', 'auto_save', array(
    'type' => 'int',
    'size' => 'tiny',
    'not null' => TRUE,
    'default' => 0,
  ));
  return $ret;
}

/**
 * Remove orphaned and unnecessary rows in the webform table.
 */
function webform_update_6325() {
  $ret = array();
  $result = db_query("SELECT nid FROM {webform} WHERE\n    nid NOT IN\n    (SELECT DISTINCT(w1.nid) FROM {webform} w1 INNER JOIN {webform_component} wc ON w1.nid = wc.nid)\n    AND nid NOT IN\n    (SELECT w2.nid FROM {webform} w2 INNER JOIN {node} n ON w2.nid = n.nid WHERE n.type = 'webform')");
  $empty_nids = array();
  while ($row = db_fetch_object($result)) {
    $empty_nids[] = $row->nid;
  }
  if (!empty($empty_nids)) {
    $empty_list = implode(',', $empty_nids);
    $ret[] = update_sql("DELETE FROM {webform} WHERE nid in ({$empty_list})");
  }
  return $ret;
}

/**
 * Add an index for nid_uid_sid to webform_submissions.
 */
function webform_update_6326() {
  $ret = array();
  db_add_index($ret, 'webform_submissions', 'nid_uid_sid', array(
    'nid',
    'uid',
    'sid',
  ));
  return $ret;
}

/**
 * Remove unused Webform variables.
 */
function webform_update_6327() {
  $ret = array();
  variable_del('node_types');
  variable_del('components');
  return $ret;
}

/**
 * Convert the Date component start and end year options to start and end date.
 */
function webform_update_6328() {
  $ret = array();
  $result = db_query("SELECT * FROM {webform_component} WHERE type = 'date'");
  while ($component = db_fetch_array($result)) {
    $component['extra'] = unserialize($component['extra']);
    if (!isset($component['extra']['start_date']) && !isset($component['end_date'])) {
      foreach (array(
        'year_start' => 'start_date',
        'year_end' => 'end_date',
      ) as $key => $replacement) {
        $value = isset($component['extra'][$key]) ? trim($component['extra'][$key]) : '';

        // Relative years.
        if (preg_match('/[-+][ ]*[0-9]+/', $value)) {
          $component['extra'][$replacement] = $value == 1 ? $value . ' year' : $value . ' years';
        }
        elseif (is_numeric($value)) {
          $component['extra'][$replacement] = 'Dec 31 ' . $value;
        }
        unset($component['extra'][$key]);
      }
      $component['extra'] = serialize($component['extra']);
      drupal_write_record('webform_component', $component, array(
        'nid',
        'cid',
      ));
    }
  }
  return $ret;
}

/**
 * Add webform_last_download table to store last downloaded sid per user.
 */
function webform_update_6329() {

  // Safety check to prevent recreating the webform_last_download table.
  $ret = array();
  if (db_table_exists('webform_last_download')) {
    return $ret;
  }
  $schema['webform_last_download'] = array(
    'description' => 'Stores last submission number per user download.',
    'fields' => array(
      'nid' => array(
        'description' => 'The node identifier of a webform.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'uid' => array(
        'description' => 'The user identifier.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'sid' => array(
        'description' => 'The last downloaded submission number.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'requested' => array(
        'description' => 'Timestamp of last download request.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
    ),
    'primary key' => array(
      'nid',
      'uid',
    ),
  );
  db_create_table($ret, 'webform_last_download', $schema['webform_last_download']);
  return $ret;
}

/**
 * Add additional columns for total submission limit.
 */
function webform_update_6330() {
  $ret = array();
  if (!db_column_exists('webform', 'total_submit_limit')) {
    db_add_field($ret, 'webform', 'total_submit_limit', array(
      'type' => 'int',
      'not null' => TRUE,
      'default' => -1,
    ));
  }
  if (!db_column_exists('webform', 'total_submit_interval')) {
    db_add_field($ret, 'webform', 'total_submit_interval', array(
      'type' => 'int',
      'not null' => TRUE,
      'default' => -1,
    ));
  }
  return $ret;
}

/**
 * Add an index for nid_sid to webform_submissions.
 */
function webform_update_6331() {

  // Even though we already have an index 'nid_uid_sid', adding the index for
  // 'nid_sid' saves us a tablesort on the node/x/webform-results page.
  $ret = array();
  db_add_index($ret, 'webform_submissions', 'nid_sid', array(
    'nid',
    'sid',
  ));
  return $ret;
}

/**
 * Recursively delete all files and folders in the specified filepath, then
 * delete the containing folder.
 *
 * Note that this only deletes visible files with write permission
 *
 * @param string $path
 *   A filepath relative to file_directory_path
 */
function _webform_recursive_delete($path) {
  if ($path && is_dir($path)) {
    $listing = $path . '/*';
    foreach (glob($listing) as $file) {
      if (is_file($file) === TRUE) {
        @unlink($file);
      }
      elseif (is_dir($file) === TRUE) {
        _webform_recursive_delete($file);
      }
    }
    @rmdir($path);
  }
}

Functions

Namesort descending Description
webform_install Implements hook_install().
webform_requirements Implements hook_requirements().
webform_schema Implements hook_schema().
webform_uninstall Implements hook_uninstall().
webform_update_6001 Upgrade to Drupal 6. Convert submissions sid column to auto-increment.
webform_update_6200 Increase the size of the component instance name.
webform_update_6201 Add a column for email to the webform_component table.
webform_update_6202 Per-webform submission access control based on roles.
webform_update_6203 Cleanup filtering values used by the file component.
webform_update_6204 Set all files to permanent status uploaded by Webform.
webform_update_6205 Schema fixes to make Drupal 5 upgrades identical to clean Drupal 6 installs.
webform_update_6301 Add a separate column for confirmation message input format.
webform_update_6302 Convert node-level e-mail settings to new webform_emails table.
webform_update_6303 Add the submit_notice field and update all existing webforms to the 2.x previous submissions notice default.
webform_update_6304 Convert the webform content type to be owned by Node module.
webform_update_6305 Migrate the renamed permissions. Add separate permissions for delete.
webform_update_6306 Add the ability to save as draft.
webform_update_6307 Convert the file component to use only FIDs instead of serialized arrays.
webform_update_6308 Convert "Include in e-mail" from the component-level to a per e-mail setting.
webform_update_6309 Fix permissions for all roles by adding an additional space after comma.
webform_update_6310 Add the redirect_url field and update existing webforms to use it.
webform_update_6311 Convert the "no" column to be a varchar column instead of an integer.
webform_update_6312 Convert select options to use numeric keys if none are manually specified.
webform_update_6313 Create keys for all questions (which don't currently have keys at all), create keys for all options that don't yet have any, and then convert the existing data to use these keys.
webform_update_6314 Convert Dates and Times into using ISO 8601 strings instead of 3 rows.
webform_update_6315 Remove "daylight_savings" and the GMT option in "timezone".
webform_update_6316 Remove the Webform Debug variable.
webform_update_6317 Remove orphaned e-mail settings of nodes that have been deleted.
webform_update_6318 Ensure that the confirmation format column is correctly using size = 'tiny'.
webform_update_6319 Add columns for e-mail HTML and attachment settings.
webform_update_6320 Set the default for the "submit_notice" column to 1.
webform_update_6321 Add field for block feature and redirection setting.
webform_update_6322 Set additional_validate and additional_submit columns to allow NULL.
webform_update_6323 Add column for webform status (open or closed).
webform_update_6324 Add the ability to auto-save as draft between pages.
webform_update_6325 Remove orphaned and unnecessary rows in the webform table.
webform_update_6326 Add an index for nid_uid_sid to webform_submissions.
webform_update_6327 Remove unused Webform variables.
webform_update_6328 Convert the Date component start and end year options to start and end date.
webform_update_6329 Add webform_last_download table to store last downloaded sid per user.
webform_update_6330 Add additional columns for total submission limit.
webform_update_6331 Add an index for nid_sid to webform_submissions.
webform_update_last_removed Set the minimum upgrade version.
_webform_recursive_delete Recursively delete all files and folders in the specified filepath, then delete the containing folder.