[ Index ]

PHP Cross Reference of Drupal 6 (yi-drupal)

title

Body

[close]

/sites/all/modules/webform/ -> webform.module (source)

   1  <?php
   2  
   3  /**
   4   * This module provides a simple way to create forms and questionnaires.
   5   *
   6   * The initial development of this module was sponsered by ÅF Industri AB, Open
   7   * Source City and Karlstad University Library. Continued development sponsored
   8   * by Lullabot.
   9   *
  10   * @author Nathan Haug <nate@lullabot.com>
  11   */
  12  
  13  /**
  14   * Implements hook_help().
  15   */
  16  function webform_help($section = 'admin/help#webform', $arg = NULL) {
  17    $output = '';
  18    switch ($section) {
  19      case 'admin/settings/webform':
  20        module_load_include('inc', 'webform', 'includes/webform.admin');
  21        $type_list = webform_admin_type_list();
  22        $output = t('Webform enables nodes to have attached forms and questionnaires.');
  23        if ($type_list) {
  24          $output .= ' ' . t('To add one, create a !types piece of content.', array('!types' => $type_list));
  25        }
  26        else {
  27          $output .= ' <strong>' . t('Webform is currently not enabled on any content types.') . '</strong> ' . t('To use Webform, please enable it on at least one content type on this page.');
  28        }
  29        $output = '<p>' . $output . '</p>';
  30        break;
  31      case 'admin/content/webform':
  32        $output = '<p>' . t('This page lists all of the content on the site that may have a webform attached to it.') . '</p>';
  33        break;
  34      case 'admin/help#webform':
  35        module_load_include('inc', 'webform', 'includes/webform.admin');
  36        $types = webform_admin_type_list();
  37        if (empty($types)) {
  38          $types = t('Webform-enabled piece of content');
  39          $types_message = t('Webform is currently not enabled on any content types.') . ' ' . t('Visit the <a href="!url">Webform settings</a> page and enable Webform on at least one content type.', array('!url' => url('admin/settings/webform')));
  40        }
  41        else {
  42          $types_message = t('Optional: Enable Webform on multiple types by visiting the <a href="!url">Webform settings</a> page.', array('!url' => url('admin/settings/webform')));
  43        }
  44        $output = t("<p>This module lets you create forms or questionnaires and define their content. Submissions from these forms are stored in the database and optionally also sent by e-mail to a predefined address.</p>
  45        <p>Here is how to create one:</p>
  46        <ul>
  47          <li>!webform-types-message</li>
  48          <li>Go to <a href=\"!create-content\">Create content</a> and add a !types piece of content.</li>
  49          <li>After saving the new content, you will be redirected to the main field list of the form that will be created. Add the fields you would like on your form.</li>
  50          <li>Once finished adding fields, you may want to send e-mails to administrators or back to the user who filled out the form. Click on the <em>Emails</em> sub-tab underneath the <em>Webform</em> tab on the piece of content.</li>
  51          <li>Finally, visit the <em>Form settings</em> sub-tab under the <em>Webform</em> tab to configure remaining configurations options for your form.
  52            <ul>
  53            <li>Add a confirmation message and/or redirect URL that is to be displayed after successful submission.</li>
  54            <li>Set a submission limit.</li>
  55            <li>Determine which roles may submit the form.</li>
  56            <li>Advanced configuration options such as allowing drafts or show users a message indicating how they can edit their submissions.</li>
  57            </ul>
  58          </li>
  59          <li>Your form is now ready for viewing. After receiving submissions, you can check the results users have submitted by visiting the <em>Results</em> tab on the piece of content.</li>
  60        </ul>
  61        <p>Help on adding and configuring the components will be shown after you add your first component.</p>
  62        ", array('!webform-types-message' => $types_message, '!create-content' => url('node/add'), '!types' => $types));
  63        break;
  64      case 'node/%/webform/components':
  65        $output .= '<p>' . t('This page displays all the components currently configured for this webform node. You may add any number of components to the form, even multiple of the same type. To add a new component, fill in a name and select a type from the fields at the bottom of the table. Submit the form to create the new component or update any changed form values.') . '</p>';
  66        $output .= '<p>' . t('Click on any existing component\'s name to edit its settings.') . '</p>';
  67        break;
  68      case 'node/%/submission/%/resend':
  69        $output .= '<p>' . t('This form may be used to resend e-mails configured for this webform. Check the e-mails that need to be sent and click <em>Resend e-mails</em> to send these e-mails again.') . '</p>';
  70        break;
  71    }
  72  
  73    return $output;
  74  }
  75  
  76  /**
  77   * Implements hook_menu().
  78   */
  79  function webform_menu() {
  80    $items = array();
  81  
  82    // Submissions listing.
  83    $items['admin/content/webform'] = array(
  84      'title' => 'Webforms',
  85      'page callback' => 'webform_admin_content',
  86      'access callback' => 'user_access',
  87      'access arguments' => array('access all webform results'),
  88      'description' => 'View and edit all the available webforms on your site.',
  89      'file' => 'includes/webform.admin.inc',
  90      'type' => MENU_NORMAL_ITEM,
  91    );
  92  
  93    // Admin Settings.
  94    $items['admin/settings/webform'] = array(
  95      'title' => 'Webform settings',
  96      'page callback' => 'drupal_get_form',
  97      'page arguments' => array('webform_admin_settings'),
  98      'access callback' => 'user_access',
  99      'access arguments' => array('administer site configuration'),
 100      'description' => 'Global configuration of webform functionality.',
 101      'file' => 'includes/webform.admin.inc',
 102      'type' => MENU_NORMAL_ITEM,
 103    );
 104  
 105    // Node page tabs.
 106    $items['node/%webform_menu/done'] = array(
 107      'title' => 'Webform confirmation',
 108      'page callback' => '_webform_confirmation',
 109      'page arguments' => array(1),
 110      'access callback' => 'node_access',
 111      'access arguments' => array('view', 1),
 112      'type' => MENU_CALLBACK,
 113    );
 114    $items['node/%webform_menu/webform'] = array(
 115      'title' => 'Webform',
 116      'page callback' => 'webform_components_page',
 117      'page arguments' => array(1),
 118      'access callback' => 'node_access',
 119      'access arguments' => array('update', 1),
 120      'file' => 'includes/webform.components.inc',
 121      'weight' => 1,
 122      'type' => MENU_LOCAL_TASK,
 123    );
 124    $items['node/%webform_menu/webform/components'] = array(
 125      'title' => 'Form components',
 126      'page callback' => 'webform_components_page',
 127      'page arguments' => array(1),
 128      'access callback' => 'node_access',
 129      'access arguments' => array('update', 1),
 130      'file' => 'includes/webform.components.inc',
 131      'weight' => 0,
 132      'type' => MENU_DEFAULT_LOCAL_TASK,
 133    );
 134    $items['node/%webform_menu/webform/configure'] = array(
 135      'title' => 'Form settings',
 136      'page callback' => 'drupal_get_form',
 137      'page arguments' => array('webform_configure_form', 1),
 138      'access callback' => 'node_access',
 139      'access arguments' => array('update', 1),
 140      'file' => 'includes/webform.pages.inc',
 141      'weight' => 2,
 142      'type' => MENU_LOCAL_TASK,
 143    );
 144  
 145    // Node e-mail forms.
 146    $items['node/%webform_menu/webform/emails'] = array(
 147      'title' => 'E-mails',
 148      'page callback' => 'drupal_get_form',
 149      'page arguments' => array('webform_emails_form', 1),
 150      'access callback' => 'node_access',
 151      'access arguments' => array('update', 1),
 152      'file' => 'includes/webform.emails.inc',
 153      'weight' => 1,
 154      'type' => MENU_LOCAL_TASK,
 155    );
 156    $items['node/%webform_menu/webform/emails/%webform_menu_email'] = array(
 157      'title' => 'Edit e-mail settings',
 158      'load arguments' => array(1),
 159      'page arguments' => array('webform_email_edit_form', 1, 4),
 160      'access callback' => 'node_access',
 161      'access arguments' => array('update', 1),
 162      'file' => 'includes/webform.emails.inc',
 163      'type' => MENU_CALLBACK,
 164    );
 165    $items['node/%webform_menu/webform/emails/%webform_menu_email/delete'] = array(
 166      'title' => 'Delete e-mail settings',
 167      'load arguments' => array(1),
 168      'page arguments' => array('webform_email_delete_form', 1, 4),
 169      'access callback' => 'node_access',
 170      'access arguments' => array('update', 1),
 171      'type' => MENU_CALLBACK,
 172    );
 173  
 174    // Node component forms.
 175    $items['node/%webform_menu/webform/components/%webform_menu_component'] = array(
 176      'load arguments' => array(1, 5),
 177      'page callback' => 'drupal_get_form',
 178      'page arguments' => array('webform_component_edit_form', 1, 4, FALSE),
 179      'access callback' => 'node_access',
 180      'access arguments' => array('update', 1),
 181      'file' => 'includes/webform.components.inc',
 182      'type' => MENU_LOCAL_TASK,
 183    );
 184    $items['node/%webform_menu/webform/components/%webform_menu_component/clone'] = array(
 185      'load arguments' => array(1, 5),
 186      'page callback' => 'drupal_get_form',
 187      'page arguments' => array('webform_component_edit_form', 1, 4, TRUE),
 188      'access callback' => 'node_access',
 189      'access arguments' => array('update', 1),
 190      'file' => 'includes/webform.components.inc',
 191      'type' => MENU_LOCAL_TASK,
 192    );
 193    $items['node/%webform_menu/webform/components/%webform_menu_component/delete'] = array(
 194      'load arguments' => array(1, 5),
 195      'page callback' => 'drupal_get_form',
 196      'page arguments' => array('webform_component_delete_form', 1, 4),
 197      'access callback' => 'node_access',
 198      'access arguments' => array('update', 1),
 199      'file' => 'includes/webform.components.inc',
 200      'type' => MENU_LOCAL_TASK,
 201    );
 202  
 203    // AJAX callback for loading select list options.
 204    $items['webform/ajax/options/%webform_menu'] = array(
 205      'load arguments' => array(3),
 206      'page callback' => 'webform_select_options_ajax',
 207      'access callback' => 'node_access',
 208      'access arguments' => array('update', 3),
 209      'file' => 'components/select.inc',
 210      'type' => MENU_CALLBACK,
 211    );
 212  
 213    // Node webform results.
 214    $items['node/%webform_menu/webform-results'] = array(
 215      'title' => 'Results',
 216      'page callback' => 'webform_results_submissions',
 217      'page arguments' => array(1, FALSE, '50'),
 218      'access callback' => 'webform_results_access',
 219      'access arguments' => array(1),
 220      'file' => 'includes/webform.report.inc',
 221      'weight' => 2,
 222      'type' => MENU_LOCAL_TASK,
 223    );
 224    $items['node/%webform_menu/webform-results/submissions'] = array(
 225      'title' => 'Submissions',
 226      'page callback' => 'webform_results_submissions',
 227      'page arguments' => array(1, FALSE, '50'),
 228      'access callback' => 'webform_results_access',
 229      'access arguments' => array(1),
 230      'file' => 'includes/webform.report.inc',
 231      'weight' => 4,
 232      'type' => MENU_DEFAULT_LOCAL_TASK,
 233    );
 234    $items['node/%webform_menu/webform-results/analysis'] = array(
 235      'title' => 'Analysis',
 236      'page callback' => 'webform_results_analysis',
 237      'page arguments' => array(1),
 238      'access callback' => 'webform_results_access',
 239      'access arguments' => array(1),
 240      'file' => 'includes/webform.report.inc',
 241      'weight' => 5,
 242      'type' => MENU_LOCAL_TASK,
 243    );
 244    $items['node/%webform_menu/webform-results/analysis/%webform_menu_component'] = array(
 245      'title' => 'Analysis',
 246      'load arguments' => array(1, 4),
 247      'page callback' => 'webform_results_analysis',
 248      'page arguments' => array(1, array(), 4),
 249      'access callback' => 'webform_results_access',
 250      'access arguments' => array(1),
 251      'file' => 'includes/webform.report.inc',
 252      'type' => MENU_CALLBACK,
 253    );
 254    $items['node/%webform_menu/webform-results/table'] = array(
 255      'title' => 'Table',
 256      'page callback' => 'webform_results_table',
 257      'page arguments' => array(1, '50'),
 258      'access callback' => 'webform_results_access',
 259      'access arguments' => array(1),
 260      'file' => 'includes/webform.report.inc',
 261      'weight' => 6,
 262      'type' => MENU_LOCAL_TASK,
 263    );
 264    $items['node/%webform_menu/webform-results/download'] = array(
 265      'title' => 'Download',
 266      'page callback' => 'drupal_get_form',
 267      'page arguments' => array('webform_results_download_form', 1),
 268      'access callback' => 'webform_results_access',
 269      'access arguments' => array(1),
 270      'file' => 'includes/webform.report.inc',
 271      'weight' => 7,
 272      'type' => MENU_LOCAL_TASK,
 273    );
 274    $items['node/%webform_menu/webform-results/clear'] = array(
 275      'title' => 'Clear',
 276      'page callback' => 'drupal_get_form',
 277      'page arguments' => array('webform_results_clear_form', 1),
 278      'access callback' => 'webform_results_clear_access',
 279      'access arguments' => array(1),
 280      'file' => 'includes/webform.report.inc',
 281      'weight' => 8,
 282      'type' => MENU_LOCAL_TASK,
 283    );
 284  
 285    // Node submissions.
 286    $items['node/%webform_menu/submissions'] = array(
 287      'title' => 'Submissions',
 288      'page callback' => 'webform_results_submissions',
 289      'page arguments' => array(1, TRUE, '50'),
 290      'access callback' => 'webform_submission_access',
 291      'access arguments' => array(1, NULL, 'list'),
 292      'file' => 'includes/webform.report.inc',
 293      'type' => MENU_CALLBACK,
 294    );
 295    $items['node/%webform_menu/submission/%webform_menu_submission'] = array(
 296      'title' => 'Webform submission',
 297      'load arguments' => array(1),
 298      'page callback' => 'webform_submission_page',
 299      'page arguments' => array(1, 3, 'html'),
 300      'title callback' => 'webform_submission_title',
 301      'title arguments' => array(1, 3),
 302      'access callback' => 'webform_submission_access',
 303      'access arguments' => array(1, 3, 'view'),
 304      'file' => 'includes/webform.submissions.inc',
 305      'type' => MENU_CALLBACK,
 306    );
 307    $items['node/%webform_menu/submission/%webform_menu_submission/view'] = array(
 308      'title' => 'View',
 309      'load arguments' => array(1),
 310      'page callback' => 'webform_submission_page',
 311      'page arguments' => array(1, 3, 'html'),
 312      'access callback' => 'webform_submission_access',
 313      'access arguments' => array(1, 3, 'view'),
 314      'weight' => 0,
 315      'file' => 'includes/webform.submissions.inc',
 316      'type' => MENU_DEFAULT_LOCAL_TASK,
 317    );
 318    $items['node/%webform_menu/submission/%webform_menu_submission/edit'] = array(
 319      'title' => 'Edit',
 320      'load arguments' => array(1),
 321      'page callback' => 'webform_submission_page',
 322      'page arguments' => array(1, 3, 'form'),
 323      'access callback' => 'webform_submission_access',
 324      'access arguments' => array(1, 3, 'edit'),
 325      'weight' => 1,
 326      'file' => 'includes/webform.submissions.inc',
 327      'type' => MENU_LOCAL_TASK,
 328    );
 329    $items['node/%webform_menu/submission/%webform_menu_submission/delete'] = array(
 330      'title' => 'Delete',
 331      'load arguments' => array(1),
 332      'page callback' => 'drupal_get_form',
 333      'page arguments' => array('webform_submission_delete_form', 1, 3),
 334      'access callback' => 'webform_submission_access',
 335      'access arguments' => array(1, 3, 'delete'),
 336      'weight' => 2,
 337      'file' => 'includes/webform.submissions.inc',
 338      'type' => MENU_LOCAL_TASK,
 339    );
 340    $items['node/%webform_menu/submission/%webform_menu_submission/resend'] = array(
 341      'title' => 'Resend e-mails',
 342      'load arguments' => array(1),
 343      'page callback' => 'drupal_get_form',
 344      'page arguments' => array('webform_submission_resend', 1, 3),
 345      'access callback' => 'webform_results_access',
 346      'access arguments' => array(1),
 347      'file' => 'includes/webform.submissions.inc',
 348      'type' => MENU_CALLBACK,
 349    );
 350  
 351    return $items;
 352  }
 353  
 354  /**
 355   * Menu loader callback. Load a webform node if the given nid is a webform.
 356   */
 357  function webform_menu_load($nid) {
 358    if (!is_numeric($nid)) {
 359      return FALSE;
 360    }
 361    $node = node_load($nid);
 362    if (!isset($node->type) || !in_array($node->type, webform_variable_get('webform_node_types'))) {
 363      return FALSE;
 364    }
 365    return $node;
 366  }
 367  
 368  /**
 369   * Menu loader callback. Load a webform submission if the given sid is a valid.
 370   */
 371  function webform_menu_submission_load($sid, $nid) {
 372    module_load_include('inc', 'webform', 'includes/webform.submissions');
 373    $submission = webform_get_submission($nid, $sid);
 374    return empty($submission) ? FALSE : $submission;
 375  }
 376  
 377  /**
 378   * Menu loader callback. Load a webform component if the given cid is a valid.
 379   */
 380  function webform_menu_component_load($cid, $nid, $type) {
 381    module_load_include('inc', 'webform', 'includes/webform.components');
 382    if ($cid == 'new') {
 383      $components = webform_components();
 384      $component = in_array($type, array_keys($components)) ? array('type' => $type, 'nid' => $nid, 'name' => $_GET['name'], 'mandatory' => $_GET['mandatory'], 'pid' => $_GET['pid'], 'weight' => $_GET['weight']) : FALSE;
 385    }
 386    else {
 387      $node = node_load($nid);
 388      $component = isset($node->webform['components'][$cid]) ? $node->webform['components'][$cid] : FALSE;
 389    }
 390    if ($component) {
 391      webform_component_defaults($component);
 392    }
 393    return $component;
 394  }
 395  
 396  
 397  /**
 398   * Menu loader callback. Load a webform e-mail if the given eid is a valid.
 399   */
 400  function webform_menu_email_load($eid, $nid) {
 401    module_load_include('inc', 'webform', 'includes/webform.emails');
 402    $node = node_load($nid);
 403    $email = webform_email_load($eid, $nid);
 404    if ($eid == 'new') {
 405      if (isset($_GET['option']) && isset($_GET['email'])) {
 406        $type = $_GET['option'];
 407        if ($type == 'custom') {
 408          $email['email'] = $_GET['email'];
 409        }
 410        elseif ($type == 'component' && isset($node->webform['components'][$_GET['email']])) {
 411          $email['email'] = $_GET['email'];
 412        }
 413      }
 414    }
 415  
 416    return $email;
 417  }
 418  
 419  function webform_submission_access($node, $submission, $op = 'view', $account = NULL) {
 420    global $user;
 421    $account = isset($account) ? $account : $user;
 422  
 423    $access_all = user_access('access all webform results', $account);
 424    $access_own_submission = isset($submission) && user_access('access own webform submissions', $account) && (($account->uid && $account->uid == $submission->uid) || isset($_SESSION['webform_submission'][$submission->sid]));
 425    $access_node_submissions = user_access('access own webform results', $account) && $account->uid == $node->uid;
 426  
 427    $general_access = $access_all || $access_own_submission || $access_node_submissions;
 428  
 429    // Disable the page cache for anonymous users in this access callback,
 430    // otherwise the "Access denied" page gets cached.
 431    if (!$account->uid && user_access('access own webform submissions', $account)) {
 432      webform_disable_page_cache();
 433    }
 434  
 435    $module_access = count(array_filter(module_invoke_all('webform_submission_access', $node, $submission, $op, $account))) > 0;
 436  
 437    switch ($op) {
 438      case 'view':
 439        return $module_access || $general_access;
 440      case 'edit':
 441        return $module_access || ($general_access && (user_access('edit all webform submissions', $account) || (user_access('edit own webform submissions', $account) && $account->uid == $submission->uid)));
 442      case 'delete':
 443        return $module_access || ($general_access && (user_access('delete all webform submissions', $account) || (user_access('delete own webform submissions', $account) && $account->uid == $submission->uid)));
 444      case 'list':
 445        return $module_access || user_access('access all webform results', $account) || (user_access('access own webform submissions', $account) && ($account->uid || isset($_SESSION['webform_submission']))) || (user_access('access own webform results', $account) && $account->uid == $node->uid);
 446    }
 447  }
 448  
 449  /**
 450   * Menu access callback. Ensure a user both access and node 'view' permission.
 451   */
 452  function webform_results_access($node, $account = NULL) {
 453    global $user;
 454    $account = isset($account) ? $account : $user;
 455  
 456    $module_access = count(array_filter(module_invoke_all('webform_results_access', $node, $account))) > 0;
 457  
 458    return node_access('view', $node, $account) && ($module_access || user_access('access all webform results', $account) || (user_access('access own webform results', $account) && $account->uid == $node->uid));
 459  }
 460  
 461  function webform_results_clear_access($node, $account = NULL) {
 462    global $user;
 463    $account = isset($account) ? $account : $user;
 464  
 465    $module_access = count(array_filter(module_invoke_all('webform_results_clear_access', $node, $account))) > 0;
 466  
 467    return webform_results_access($node, $account) && ($module_access || user_access('delete all webform submissions', $account));
 468  }
 469  
 470  /**
 471   * Implements hook_init().
 472   */
 473  function webform_init() {
 474    // Use the administrative theme if set to use on content editing pages.
 475    // See system_init().
 476    if (variable_get('node_admin_theme', '0') && arg(0) == 'node' && (arg(2) == 'webform' || arg(2) == 'webform-results')) {
 477      global $custom_theme;
 478      $custom_theme = variable_get('admin_theme', '0');
 479      drupal_add_css(drupal_get_path('module', 'system') . '/admin.css', 'module');
 480  
 481      // Support for Admin module (1.x).
 482      if (function_exists('_admin_init_theme') && empty($custom_theme)) {
 483        _admin_init_theme();
 484      }
 485    }
 486  }
 487  
 488  /**
 489   * Implements hook_perm().
 490   */
 491  function webform_perm() {
 492    return array(
 493      'access all webform results',
 494      'access own webform results',
 495      'edit all webform submissions',
 496      'delete all webform submissions',
 497      'access own webform submissions',
 498      'edit own webform submissions',
 499      'delete own webform submissions',
 500    );
 501  }
 502  
 503  /**
 504   * Implements hook_theme().
 505   */
 506  function webform_theme() {
 507    $theme = array(
 508      // webform.module.
 509      'webform_view' => array(
 510        'arguments' => array('node' => NULL, 'teaser' => NULL, 'page' => NULL, 'form' => NULL, 'enabled' => NULL),
 511      ),
 512      'webform_view_messages' => array(
 513        'arguments' => array('node' => NULL, 'teaser' => NULL, 'page' => NULL, 'submission_count' => NULL, 'user_limit_exceeded' => NULL, 'total_limit_exceeded' => NULL, 'allowed_roles' => NULL, 'closed' => NULL, 'cached' => FALSE),
 514      ),
 515      'webform_form' => array(
 516        'arguments' => array('form' => NULL),
 517        'template' => 'templates/webform-form',
 518        'pattern' => 'webform_form_[0-9]+',
 519      ),
 520      'webform_confirmation' => array(
 521        'arguments' => array('node' => NULL, 'sid' => NULL),
 522        'template' => 'templates/webform-confirmation',
 523        'pattern' => 'webform_confirmation_[0-9]+',
 524      ),
 525      'webform_element' => array(
 526        'arguments' => array('element' => NULL, 'value' => NULL),
 527      ),
 528      'webform_element_wrapper' => array(
 529        'arguments' => array('element' => NULL, 'content' => NULL),
 530      ),
 531      'webform_element_text' => array(
 532        'arguments' => array('element' => NULL, 'value' => NULL),
 533      ),
 534      'webform_mail_message' => array(
 535        'arguments' => array('node' => NULL, 'submission' => NULL, 'email' => NULL),
 536        'template' => 'templates/webform-mail',
 537        'pattern' => 'webform_mail(_[0-9]+)?',
 538      ),
 539      'webform_mail_headers' => array(
 540        'arguments' => array('node' => NULL, 'submission' => NULL, 'email' => NULL),
 541        'pattern' => 'webform_mail_headers_[0-9]+',
 542      ),
 543      'webform_token_help' => array(
 544        'arguments' => array('groups' => array()),
 545      ),
 546      // webform.admin.inc.
 547      'webform_admin_settings' => array(
 548        'arguments' => array('form' => NULL),
 549        'file' => 'includes/webform.admin.inc',
 550      ),
 551      'webform_admin_content' => array(
 552        'arguments' => array('nodes' => NULL),
 553        'file' => 'includes/webform.admin.inc',
 554      ),
 555      // webform.emails.inc.
 556      'webform_emails_form' => array(
 557        'arguments' => array('form' => NULL),
 558        'file' => 'includes/webform.emails.inc',
 559      ),
 560      'webform_email_add_form' => array(
 561        'arguments' => array('form' => NULL),
 562        'file' => 'includes/webform.emails.inc',
 563      ),
 564      'webform_email_edit_form' => array(
 565        'arguments' => array('form' => NULL),
 566        'file' => 'includes/webform.emails.inc',
 567      ),
 568      // webform.components.inc.
 569      'webform_components_page' => array(
 570        'arguments' => array('node' => NULL, 'form' => NULL),
 571        'file' => 'includes/webform.components.inc',
 572      ),
 573      'webform_components_form' => array(
 574        'arguments' => array('form' => NULL),
 575        'file' => 'includes/webform.components.inc',
 576      ),
 577      'webform_component_select' => array(
 578        'arguments' => array('element' => NULL),
 579        'file' => 'includes/webform.components.inc',
 580      ),
 581      // webform.pages.inc.
 582      'webform_advanced_redirection_form' => array(
 583        'arguments' => array('form' => NULL),
 584        'file' => 'includes/webform.pages.inc',
 585      ),
 586      'webform_advanced_submit_limit_form' => array(
 587        'arguments' => array('form' => NULL),
 588        'file' => 'includes/webform.pages.inc',
 589      ),
 590      'webform_advanced_total_submit_limit_form' => array(
 591        'arguments' => array('form' => NULL),
 592        'file' => 'includes/webform.pages.inc',
 593      ),
 594      // webform.report.inc.
 595      'webform_results_per_page' => array(
 596        'arguments' => array('total_count' => NULL, 'pager_count' => NULL),
 597        'file' => 'includes/webform.report.inc',
 598      ),
 599      'webform_results_submissions_header' => array(
 600        'arguments' => array('node' => NULL),
 601        'file' => 'includes/webform.report.inc',
 602      ),
 603      'webform_results_submissions' => array(
 604        'arguments' => array('element' => NULL),
 605        'template' => 'templates/webform-results-submissions',
 606        'file' => 'includes/webform.report.inc',
 607      ),
 608      'webform_results_table_header' => array(
 609        'arguments' => array('node' => NULL),
 610        'file' => 'includes/webform.report.inc',
 611      ),
 612      'webform_results_table' => array(
 613        'arguments' => array('node' => NULL, 'components' => NULL, 'submissions' => NULL, 'node' => NULL, 'total_count' => NULL, 'pager_count' => NULL),
 614        'file' => 'includes/webform.report.inc',
 615      ),
 616      'webform_results_download_range' => array(
 617        'arguments' => array('element' => NULL),
 618        'file' => 'includes/webform.report.inc',
 619      ),
 620      'webform_results_download_select_format' => array(
 621        'arguments' => array('element' => NULL),
 622        'file' => 'includes/webform.report.inc',
 623      ),
 624      'webform_results_analysis' => array(
 625        'arguments' => array('node' => NULL, 'data' => NULL, 'sids' => array(), 'component' => NULL),
 626        'file' => 'includes/webform.report.inc',
 627      ),
 628      // webform.submissions.inc
 629      'webform_submission' => array(
 630        'arguments' => array('renderable' => NULL),
 631        'template' => 'templates/webform-submission',
 632        'pattern' => 'webform_submission_[0-9]+',
 633        'file' => 'includes/webform.submissions.inc',
 634      ),
 635      'webform_submission_page' => array(
 636        'arguments' => array('node' => NULL, 'submission' => NULL, 'submission_content' => NULL, 'submission_navigation' => NULL, 'submission_information' => NULL, 'submission_actions' => NULL, 'mode' => NULL),
 637        'template' => 'templates/webform-submission-page',
 638        'file' => 'includes/webform.submissions.inc',
 639      ),
 640      'webform_submission_information' => array(
 641        'arguments' => array('node' => NULL, 'submission' => NULL, 'mode' => 'display'),
 642        'template' => 'templates/webform-submission-information',
 643        'file' => 'includes/webform.submissions.inc',
 644      ),
 645      'webform_submission_navigation' => array(
 646        'arguments' => array('node' => NULL, 'submission' => NULL, 'mode' => NULL),
 647        'template' => 'templates/webform-submission-navigation',
 648        'file' => 'includes/webform.submissions.inc',
 649      ),
 650      'webform_submission_resend' => array(
 651        'arguments' => array('form' => NULL),
 652        'file' => 'includes/webform.submissions.inc',
 653      ),
 654    );
 655  
 656    // Theme functions in all components.
 657    $components = webform_components(TRUE);
 658    foreach ($components as $type => $component) {
 659      if ($theme_additions = webform_component_invoke($type, 'theme')) {
 660        $theme = array_merge($theme, $theme_additions);
 661      }
 662    }
 663    return $theme;
 664  }
 665  
 666  /**
 667   * Implements hook_elements().
 668   */
 669  function webform_elements() {
 670    // A few of our components need to be defined here because Drupal does not
 671    // provide these components natively. Because this hook fires on every page
 672    // load (even on non-webform pages), we don't put this in the component .inc
 673    // files because of the unnecessary loading that it would require.
 674    $elements['webform_time'] = array('#input' => 'TRUE');
 675    $elements['webform_grid'] = array('#input' => 'TRUE');
 676  
 677    $elements['webform_email'] = array(
 678      '#input' => TRUE,
 679      '#theme' => 'webform_email',
 680      '#size' => 60,
 681    );
 682    $elements['webform_number'] = array(
 683      '#input' => TRUE,
 684      '#theme' => 'webform_number',
 685      '#min' => NULL,
 686      '#max' => NULL,
 687      '#step' => NULL,
 688    );
 689  
 690    return $elements;
 691  }
 692  
 693  /**
 694   * Implements hook_webform_component_info().
 695   */
 696  function webform_webform_component_info() {
 697    return array(
 698      'date' => array(
 699        'label' => t('Date'),
 700        'description' => t('Presents month, day, and year fields.'),
 701        'features' => array(
 702          'conditional' => FALSE,
 703        ),
 704        'file' => 'components/date.inc',
 705      ),
 706      'email' => array(
 707        'label' => t('E-mail'),
 708        'description' => t('A special textfield that accepts e-mail addresses.'),
 709        'file' => 'components/email.inc',
 710        'features' => array(
 711          'email_address' => TRUE,
 712          'spam_analysis' => TRUE,
 713        ),
 714      ),
 715      'fieldset' => array(
 716        'label' => t('Fieldset'),
 717        'description' => t('Fieldsets allow you to organize multiple fields into groups.'),
 718        'features' => array(
 719          'csv' => FALSE,
 720          'default_value' => FALSE,
 721          'required' => FALSE,
 722          'conditional' => FALSE,
 723          'group' => TRUE,
 724          'title_inline' => FALSE,
 725        ),
 726        'file' => 'components/fieldset.inc',
 727      ),
 728      'file' => array(
 729        'label' => t('File'),
 730        'description' => t('Allow users to upload files of configurable types.'),
 731        'features' => array(
 732          'conditional' => FALSE,
 733          'default_value' => FALSE,
 734          'attachment' => TRUE,
 735        ),
 736        'file' => 'components/file.inc',
 737      ),
 738      'grid' => array(
 739        'label' => t('Grid'),
 740        'description' => t('Allows creation of grid questions, denoted by radio buttons.'),
 741        'features' => array(
 742          'conditional' => FALSE,
 743          'default_value' => FALSE,
 744          'title_inline' => FALSE,
 745        ),
 746        'file' => 'components/grid.inc',
 747      ),
 748      'hidden' => array(
 749        'label' => t('Hidden'),
 750        'description' => t('A field which is not visible to the user, but is recorded with the submission.'),
 751        'file' => 'components/hidden.inc',
 752        'features' => array(
 753          'required' => FALSE,
 754          'description' => FALSE,
 755          'email_address' => TRUE,
 756          'email_name' => TRUE,
 757          'title_display' => FALSE,
 758          'private' => FALSE,
 759        ),
 760      ),
 761      'markup' => array(
 762        'label' => t('Markup'),
 763        'description' => t('Displays text as HTML in the form; does not render a field.'),
 764        'features' => array(
 765          'csv' => FALSE,
 766          'default_value' => FALSE,
 767          'description' => FALSE,
 768          'email' => FALSE,
 769          'required' => FALSE,
 770          'conditional' => FALSE,
 771          'title_display' => FALSE,
 772          'private' => FALSE,
 773        ),
 774        'file' => 'components/markup.inc',
 775      ),
 776      'number' => array(
 777        'label' => t('Number'),
 778        'description' => t('A numeric input field (either as textfield or select list).'),
 779        'features' => array(
 780        ),
 781        'file' => 'components/number.inc',
 782      ),
 783      'pagebreak' => array(
 784        'label' => t('Page break'),
 785        'description' => t('Organize forms into multiple pages.'),
 786        'features' => array(
 787          'csv' => FALSE,
 788          'default_value' => FALSE,
 789          'description' => FALSE,
 790          'private' => FALSE,
 791          'required' => FALSE,
 792          'title_display' => FALSE,
 793        ),
 794        'file' => 'components/pagebreak.inc',
 795      ),
 796      'select' => array(
 797        'label' => t('Select options'),
 798        'description' => t('Allows creation of checkboxes, radio buttons, or select menus.'),
 799        'file' => 'components/select.inc',
 800        'features' => array(
 801          'default_value' => FALSE,
 802          'email_address' => TRUE,
 803          'email_name' => TRUE,
 804        ),
 805      ),
 806      'textarea' => array(
 807        'label' => t('Textarea'),
 808        'description' => t('A large text area that allows for multiple lines of input.'),
 809        'file' => 'components/textarea.inc',
 810        'features' => array(
 811          'spam_analysis' => TRUE,
 812          'title_inline' => FALSE,
 813        ),
 814      ),
 815      'textfield' => array(
 816        'label' => t('Textfield'),
 817        'description' => t('Basic textfield type.'),
 818        'file' => 'components/textfield.inc',
 819        'features' => array(
 820          'email_name' => TRUE,
 821          'spam_analysis' => TRUE,
 822        ),
 823      ),
 824      'time' => array(
 825        'label' => t('Time'),
 826        'description' => t('Presents the user with hour and minute fields. Optional am/pm fields.'),
 827        'features' => array(
 828          'conditional' => FALSE,
 829        ),
 830        'file' => 'components/time.inc',
 831      ),
 832    );
 833  }
 834  
 835  /**
 836   * Implements hook_forms().
 837   *
 838   * All webform_client_form forms share the same form handler
 839   */
 840  function webform_forms($form_id) {
 841    $forms = array();
 842    if (strpos($form_id, 'webform_client_form_') === 0) {
 843      $forms[$form_id]['callback'] = 'webform_client_form';
 844    }
 845    return $forms;
 846  }
 847  
 848  /**
 849   * Implements hook_webform_select_options_info().
 850   */
 851  function webform_webform_select_options_info() {
 852    module_load_include('inc', 'webform', 'includes/webform.options');
 853    return _webform_options_info();
 854  }
 855  
 856  /**
 857   * Implements hook_webform_webform_submission_actions().
 858   */
 859  function webform_webform_submission_actions($node, $submission) {
 860    $actions = array();
 861    $destination = drupal_get_destination();
 862  
 863    if (module_exists('print_pdf') && user_access('access PDF version')) {
 864      $actions['printpdf'] = array(
 865        'title' => t('Download PDF'),
 866        'href' => 'printpdf/' . $node->nid . '/submission/' . $submission->sid,
 867        'query' => $destination,
 868      );
 869    }
 870  
 871    if (module_exists('print') && user_access('access print')) {
 872      $actions['print'] = array(
 873        'title' => t('Print'),
 874        'href' => 'print/' . $node->nid . '/submission/' . $submission->sid,
 875      );
 876    }
 877  
 878    if (webform_results_access($node) && count($node->webform['emails'])) {
 879      $actions['resend'] = array(
 880        'title' => t('Resend e-mails'),
 881        'href' => 'node/' . $node->nid . '/submission/' . $submission->sid . '/resend',
 882        'query' => drupal_get_destination(),
 883      );
 884    }
 885  
 886    return $actions;
 887  }
 888  
 889  /**
 890   * Implements hook_webform_submission_render_alter().
 891   */
 892  function webform_webform_submission_render_alter(&$renderable) {
 893    // If displaying a submission to end-users who are viewing their own
 894    // submissions (and not through an e-mail), do not show hidden values.
 895    // This needs to be implemented at the level of the entire submission, since
 896    // individual components do not get contextual information about where they
 897    // are being displayed.
 898    $node = $renderable['#node'];
 899    $is_admin = webform_results_access($node);
 900    if (empty($renderable['#email']) && !$is_admin) {
 901      // Find and hide the display of all hidden components.
 902      foreach ($node->webform['components'] as $cid => $component) {
 903        if ($component['type'] == 'hidden') {
 904          $parents = webform_component_parent_keys($node, $component);
 905          $element = &$renderable;
 906          foreach ($parents as $pid) {
 907            $element = &$element[$pid];
 908          }
 909          $element['#access'] = FALSE;
 910        }
 911      }
 912    }
 913  }
 914  
 915  /**
 916   * Implements hook_file_download().
 917   *
 918   * Only allow users with view webform submissions to download files.
 919   */
 920  function webform_file_download($file) {
 921    global $user;
 922  
 923    // If the Webform directory doesn't exist, don't attempt to deliver a file.
 924    $webform_directory = file_directory_path() . '/webform/';
 925    if (!is_dir($webform_directory)) {
 926      return;
 927    }
 928  
 929    $file = file_check_location(file_directory_path() . '/' . $file, $webform_directory);
 930    if ($file && (user_access('access all webform results') || user_access('access own webform results'))) {
 931      $info = image_get_info(file_create_path($file));
 932      if (isset($info['mime_type'])) {
 933        $headers = array('Content-type: ' . $info['mime_type']);
 934      }
 935      else {
 936        $headers = array(
 937          'Content-type: force-download',
 938          'Content-disposition: attachment',
 939        );
 940      }
 941      return $headers;
 942    }
 943  }
 944  
 945  /**
 946   * Implements hook_node_type().
 947   */
 948  function webform_node_type($op, $info) {
 949    $webform_types = webform_variable_get('webform_node_types');
 950    $affected_type = isset($info->old_type) ? $info->old_type : $info->type;
 951    $key = array_search($affected_type, $webform_types);
 952    if ($key !== FALSE) {
 953      if ($op == 'update') {
 954        $webform_types[$key] = $info->type;
 955      }
 956      if ($op == 'delete') {
 957        unset($webform_types[$key]);
 958      }
 959      variable_set('webform_node_types', $webform_types);
 960    }
 961  }
 962  
 963  /**
 964   * Implements hook_nodeapi().
 965   */
 966  function webform_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
 967    if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
 968      return;
 969    }
 970  
 971    switch ($op) {
 972      case 'insert':
 973        webform_node_insert($node);
 974        break;
 975      case 'update':
 976        webform_node_update($node);
 977        break;
 978      case 'delete':
 979        webform_node_delete($node);
 980        break;
 981      case 'prepare':
 982        webform_node_prepare($node);
 983        break;
 984      case 'prepare translation':
 985        webform_node_prepare_translation($node);
 986        break;
 987      case 'load':
 988        return webform_node_load($node);
 989      case 'view':
 990        return webform_node_view($node, $teaser, $page);
 991    }
 992  }
 993  
 994  /**
 995   * Implements hook_node_insert().
 996   */
 997  function webform_node_insert($node) {
 998    if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
 999      return;
1000    }
1001  
1002    // If added directly through node_save(), set defaults for the node.
1003    if (!isset($node->webform)) {
1004      $node->webform = webform_node_defaults();
1005    }
1006  
1007    // Do not make an entry if this node does not have any Webform settings.
1008    if ($node->webform == webform_node_defaults() && !in_array($node->type, webform_variable_get('webform_node_types_primary'))) {
1009      return;
1010    }
1011  
1012    module_load_include('inc', 'webform', 'includes/webform.components');
1013    module_load_include('inc', 'webform', 'includes/webform.emails');
1014  
1015    // Insert the webform.
1016    $node->webform['nid'] = $node->nid;
1017    $node->webform['record_exists'] = (bool) drupal_write_record('webform', $node->webform);
1018  
1019    // Insert the components into the database. Used with clone.module.
1020    if (isset($node->webform['components']) && !empty($node->webform['components'])) {
1021      foreach ($node->webform['components'] as $cid => $component) {
1022        $component['nid'] = $node->nid; // Required for clone.module.
1023        webform_component_insert($component);
1024      }
1025    }
1026  
1027    // Insert emails. Also used with clone.module.
1028    if (isset($node->webform['emails']) && !empty($node->webform['emails'])) {
1029      foreach ($node->webform['emails'] as $eid => $email) {
1030        $email['nid'] = $node->nid;
1031        webform_email_insert($email);
1032      }
1033    }
1034  
1035    // Set the per-role submission access control.
1036    foreach (array_filter($node->webform['roles']) as $rid) {
1037      db_query('INSERT INTO {webform_roles} (nid, rid) VALUES (%d, %d)', $node->nid, $rid);
1038    }
1039  }
1040  
1041  /**
1042   * Implements hook_node_update().
1043   */
1044  function webform_node_update($node) {
1045    if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
1046      return;
1047    }
1048  
1049    // Check if this node needs a webform record at all. If it matches the
1050    // defaults, any existing record will be deleted.
1051    webform_check_record($node);
1052  
1053    // If a webform row doesn't even exist, we can assume it needs to be inserted.
1054    // If the the webform matches the defaults, no row will be inserted.
1055    if (!$node->webform['record_exists']) {
1056      webform_node_insert($node);
1057      return;
1058    }
1059  
1060    // Update the webform entry.
1061    $node->webform['nid'] = $node->nid;
1062    drupal_write_record('webform', $node->webform, array('nid'));
1063  
1064    // Compare the webform components and don't do anything if it's not needed.
1065    $original = node_load($node->nid);
1066  
1067    if ($original->webform['components'] != $node->webform['components']) {
1068      module_load_include('inc', 'webform', 'includes/webform.components');
1069  
1070      $original_cids = array_keys($original->webform['components']);
1071      $current_cids = array_keys($node->webform['components']);
1072  
1073      $all_cids = array_unique(array_merge($original_cids, $current_cids));
1074      $deleted_cids = array_diff($original_cids, $current_cids);
1075      $inserted_cids = array_diff($current_cids, $original_cids);
1076  
1077      foreach ($all_cids as $cid) {
1078        if (in_array($cid, $inserted_cids)) {
1079          webform_component_insert($node->webform['components'][$cid]);
1080        }
1081        elseif (in_array($cid, $deleted_cids)) {
1082          webform_component_delete($node, $original->webform['components'][$cid]);
1083        }
1084        elseif ($node->webform['components'][$cid] != $original->webform['components'][$cid]) {
1085          $node->webform['components'][$cid]['nid'] = $node->nid;
1086          webform_component_update($node->webform['components'][$cid]);
1087        }
1088      }
1089    }
1090  
1091    // Compare the webform e-mails and don't do anything if it's not needed.
1092    if ($original->webform['emails'] != $node->webform['emails']) {
1093      module_load_include('inc', 'webform', 'includes/webform.emails');
1094  
1095      $original_eids = array_keys($original->webform['emails']);
1096      $current_eids = array_keys($node->webform['emails']);
1097  
1098      $all_eids = array_unique(array_merge($original_eids, $current_eids));
1099      $deleted_eids = array_diff($original_eids, $current_eids);
1100      $inserted_eids = array_diff($current_eids, $original_eids);
1101  
1102      foreach ($all_eids as $eid) {
1103        if (in_array($eid, $inserted_eids)) {
1104          webform_email_insert($node->webform['emails'][$eid]);
1105        }
1106        elseif (in_array($eid, $deleted_eids)) {
1107          webform_email_delete($node, $original->webform['emails'][$eid]);
1108        }
1109        elseif ($node->webform['emails'][$eid] != $original->webform['emails'][$eid]) {
1110          $node->webform['emails'][$eid]['nid'] = $node->nid;
1111          webform_email_update($node->webform['emails'][$eid]);
1112        }
1113      }
1114    }
1115  
1116    // Just delete and re-insert roles if they've changed.
1117    if ($original->webform['roles'] != $node->webform['roles']) {
1118      db_query('DELETE FROM {webform_roles} WHERE nid = %d', $node->nid);
1119      foreach (array_filter($node->webform['roles']) as $rid) {
1120        db_query('INSERT INTO {webform_roles} (nid, rid) VALUES (%d, %d)', $node->nid, $rid);
1121      }
1122    }
1123  }
1124  
1125  /**
1126   * Implements hook_delete().
1127   */
1128  function webform_node_delete($node) {
1129    if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
1130      return;
1131    }
1132  
1133    // Allow components clean up extra data, such as uploaded files.
1134    module_load_include('inc', 'webform', 'includes/webform.components');
1135    foreach ($node->webform['components'] as $cid => $component) {
1136      webform_component_delete($node, $component);
1137    }
1138  
1139    // Remove any trace of webform data from the database.
1140    db_query('DELETE FROM {webform} WHERE nid = %d', $node->nid);
1141    db_query('DELETE FROM {webform_component} WHERE nid = %d', $node->nid);
1142    db_query('DELETE FROM {webform_emails} WHERE nid = %d', $node->nid);
1143    db_query('DELETE FROM {webform_roles} WHERE nid = %d', $node->nid);
1144    db_query('DELETE FROM {webform_submissions} WHERE nid = %d', $node->nid);
1145    db_query('DELETE FROM {webform_submitted_data} WHERE nid = %d', $node->nid);
1146    db_query('DELETE FROM {webform_last_download} WHERE nid = %d', $node->nid);
1147  }
1148  
1149  /**
1150   * Default settings for a newly created webform node.
1151   */
1152  function webform_node_defaults() {
1153    $defaults = array(
1154      'confirmation' => '',
1155      'confirmation_format' => (string) filter_resolve_format(FILTER_FORMAT_DEFAULT),
1156      'redirect_url' => '<confirmation>',
1157      'teaser' => '0',
1158      'block' => '0',
1159      'allow_draft' => '0',
1160      'auto_save' => '0',
1161      'submit_notice' => '1',
1162      'submit_text' => '',
1163      'submit_limit' => '-1',
1164      'submit_interval' => '-1',
1165      'total_submit_limit' => '-1',
1166      'total_submit_interval' => '-1',
1167      'status' => '1',
1168      'record_exists' => FALSE,
1169      'roles' => array('1', '2'),
1170      'emails' => array(),
1171      'components' => array(),
1172    );
1173    drupal_alter('webform_node_defaults', $defaults);
1174    return $defaults;
1175  }
1176  
1177  /**
1178   * Implements hook_node_prepare().
1179   */
1180  function webform_node_prepare(&$node) {
1181    if (!isset($node->webform)) {
1182      $node->webform = webform_node_defaults();
1183    }
1184  }
1185  
1186  /**
1187   * Implements hook_node_prepare_translation().
1188   */
1189  function webform_node_prepare_translation(&$node) {
1190    // Copy all Webform settings over to translated versions of this node.
1191    if (isset($node->translation_source)) {
1192      $source_node = node_load($node->translation_source->nid);
1193      $node->webform = $source_node->webform;
1194    }
1195  }
1196  
1197  /**
1198   * Implements hook_node_load().
1199   */
1200  function webform_node_load($node) {
1201    module_load_include('inc', 'webform', 'includes/webform.components');
1202    $additions = array();
1203  
1204    if (isset($node->nid)) {
1205      $webform = db_fetch_array(db_query('SELECT * FROM {webform} WHERE nid = %d', $node->nid));
1206  
1207      // If a webform record doesn't exist, just return the defaults.
1208      if (!$webform) {
1209        $additions['webform'] = webform_node_defaults();
1210        return $additions;
1211      }
1212  
1213      $additions['webform'] = $webform;
1214      $additions['webform']['record_exists'] = TRUE;
1215  
1216      $additions['webform']['roles'] = array();
1217      $result = db_query('SELECT rid FROM {webform_roles} WHERE nid = %d', $node->nid);
1218      while ($role = db_fetch_object($result)) {
1219        $additions['webform']['roles'][] = $role->rid;
1220      }
1221  
1222      $additions['webform']['emails'] = array();
1223      $result = db_query('SELECT * FROM {webform_emails} WHERE nid = %d', $node->nid);
1224      while ($email = db_fetch_array($result)) {
1225        $additions['webform']['emails'][$email['eid']] = $email;
1226        $additions['webform']['emails'][$email['eid']]['excluded_components'] = array_filter(explode(',', $email['excluded_components']));
1227        if (variable_get('webform_format_override', 0)) {
1228          $additions['webform']['emails'][$email['eid']]['html'] = variable_get('webform_default_format', 0);
1229        }
1230      }
1231    }
1232  
1233    $additions['webform']['components'] = array();
1234    $result = db_query('SELECT * FROM {webform_component} WHERE nid = %d ORDER BY weight, name', $node->nid);
1235    while ($c = db_fetch_array($result)) {
1236      $component =& $additions['webform']['components'][$c['cid']];
1237      $component['nid'] = $node->nid;
1238      $component['cid'] = $c['cid'];
1239      $component['form_key'] = $c['form_key'] ? $c['form_key'] : $c['cid'];
1240      $component['name'] = $c['name'];
1241      $component['type'] = $c['type'];
1242      $component['value'] = $c['value'];
1243      $component['extra'] = unserialize($c['extra']);
1244      $component['mandatory'] = $c['mandatory'];
1245      $component['pid'] = $c['pid'];
1246      $component['weight'] = $c['weight'];
1247  
1248      webform_component_defaults($component);
1249    }
1250  
1251    // Organize the components into a fieldset-based order.
1252    if (!empty($additions['webform']['components'])) {
1253      $component_tree = array();
1254      $page_count = 1;
1255      _webform_components_tree_build($additions['webform']['components'], $component_tree, 0, $page_count);
1256      $additions['webform']['components'] = _webform_components_tree_flatten($component_tree['children']);
1257    }
1258    return $additions;
1259  }
1260  
1261  /**
1262   * Implements hook_link().
1263   * Always add a "view form" link.
1264   */
1265  function webform_link($type, $node = NULL, $teaser = FALSE) {
1266    $links = array();
1267    if (isset($node->type) && $node->type === 'webform') {
1268      if ($teaser && !$node->webform['teaser']) {
1269        $links['webform_goto'] = array(
1270          'title' => t('Go to form'),
1271          'href' => 'node/' . $node->nid,
1272          'attributes' => array('title' => t('View this form.'), 'class' => 'read-more')
1273        );
1274      }
1275    }
1276    return $links;
1277  }
1278  
1279  /**
1280   * Implements hook_form_alter().
1281   */
1282  function webform_form_alter(&$form, $form_state, $form_id) {
1283    $matches = array();
1284    if (isset($form['#node']->type) && $form_id == $form['#node']->type . '_node_form' && in_array($form['#node']->type, webform_variable_get('webform_node_types'))) {
1285      $node = $form['#node'];
1286      // Preserve all Webform options currently set on the node.
1287      $form['webform'] = array(
1288        '#type' => 'value',
1289        '#value' => $node->webform,
1290      );
1291  
1292      // If a new node, redirect the user to the components form after save.
1293      if (empty($node->nid) && in_array($node->type, webform_variable_get('webform_node_types_primary'))) {
1294        $form['buttons']['submit']['#submit'][] = 'webform_form_submit';
1295      }
1296    }
1297  }
1298  
1299  /**
1300   * Submit handler for the webform node form.
1301   *
1302   * Redirect the user to the components form on new node inserts. Note that this
1303   * fires after the hook_submit() function above.
1304   */
1305  function webform_form_submit($form, &$form_state) {
1306    drupal_set_message(t('The new webform %title has been created. Add new fields to your webform with the form below.', array('%title' => $form_state['values']['title'])));
1307    $form_state['redirect'] = 'node/' . $form_state['nid'] . '/webform/components';
1308  }
1309  
1310  /**
1311   * Implements hook_node_view().
1312   */
1313  function webform_node_view(&$node, $teaser, $page) {
1314    global $user;
1315    // If empty, a teaser, or a new node (during preview) do not display.
1316    if (empty($node->webform['components']) || ($teaser && !$node->webform['teaser']) || empty($node->nid)) {
1317      return;
1318    }
1319  
1320    // Do not include the form in the search index if indexing is disabled.
1321    if (module_exists('search') && $node->build_mode == NODE_BUILD_SEARCH_INDEX && !variable_get('webform_search_index', 1)) {
1322      return;
1323    }
1324  
1325    $info = array();
1326    $submission = array();
1327    $submission_count = 0;
1328    $enabled = TRUE;
1329    $logging_in = FALSE;
1330    $total_limit_exceeded = FALSE;
1331    $user_limit_exceeded = FALSE;
1332    $closed = FALSE;
1333    $allowed_roles = array();
1334  
1335    // Let webform form know if the node is displayed as teaser or not.
1336    $node->webform['is_teaser'] = $teaser;
1337  
1338    // If a teaser, tell the form to load subsequent pages on the node page.
1339    if ($teaser && !isset($node->webform['action'])) {
1340      $query = array_diff_key($_GET, array('q' => ''));
1341      $node->webform['action'] = url('node/' . $node->nid, array('query' => $query));
1342    }
1343  
1344    // When logging in using a form on the same page as a webform node, suppress
1345    // output messages so that they don't show up after the user has logged in.
1346    // See http://drupal.org/node/239343.
1347    if (isset($_POST['op']) && isset($_POST['name']) && isset($_POST['pass'])) {
1348      $logging_in = TRUE;
1349    }
1350  
1351    if ($node->webform['status'] == 0) {
1352      $closed = TRUE;
1353      $enabled = FALSE;
1354    }
1355    else {
1356      // Check if the user's role can submit this webform.
1357      if (variable_get('webform_submission_access_control', 1)) {
1358        foreach ($node->webform['roles'] as $rid) {
1359          $allowed_roles[$rid] = isset($user->roles[$rid]) ? TRUE : FALSE;
1360        }
1361        if (array_search(TRUE, $allowed_roles) === FALSE && $user->uid != 1) {
1362          $enabled = FALSE;
1363        }
1364      }
1365      else {
1366        // If not using Webform submission access control, allow for all roles.
1367        $allowed_roles = array_keys(user_roles());
1368      }
1369    }
1370  
1371    // Get a count of previous submissions by this user. Note that the
1372    // webform_submission_access() function may disable the page cache for
1373    // anonymous users if they are allowed to edit their own submissions!
1374    if ($page && webform_submission_access($node, NULL, 'list')) {
1375      module_load_include('inc', 'webform', 'includes/webform.submissions');
1376      $submission_count = webform_get_submission_count($node->nid, $user->uid);
1377    }
1378  
1379    // Check if this page is cached or not.
1380    $cached = $user->uid == 0 && (variable_get('cache', 0) || (function_exists('drupal_page_is_cacheable') && drupal_page_is_cacheable() === FALSE));
1381  
1382    // Check if the user can add another submission.
1383    if ($node->webform['submit_limit'] != -1) { // -1: Submissions are never throttled.
1384      module_load_include('inc', 'webform', 'includes/webform.submissions');
1385  
1386      // Disable the form if the limit is exceeded and page cache is not active.
1387      if (($user_limit_exceeded = _webform_submission_user_limit_check($node)) && !$cached) {
1388        $enabled = FALSE;
1389      }
1390    }
1391  
1392    // Check if the user can add another submission if there is a limit on total
1393    // submissions.
1394    if ($node->webform['total_submit_limit'] != -1) { // -1: Submissions are never throttled.
1395      module_load_include('inc', 'webform', 'includes/webform.submissions');
1396  
1397      // Disable the form if the limit is exceeded and page cache is not active.
1398      if (($total_limit_exceeded = _webform_submission_total_limit_check($node)) && !$cached) {
1399        $enabled = FALSE;
1400      }
1401    }
1402  
1403    // Check if this user has a draft for this webform.
1404    $is_draft = FALSE;
1405    if (($node->webform['allow_draft'] || $node->webform['auto_save']) && $user->uid != 0) {
1406      // Draft found - display form with draft data for further editing.
1407      if ($draft_sid = _webform_fetch_draft_sid($node->nid, $user->uid)) {
1408        module_load_include('inc', 'webform', 'includes/webform.submissions');
1409        $submission = webform_get_submission($node->nid, $draft_sid);
1410        $enabled = TRUE;
1411        $is_draft = TRUE;
1412      }
1413    }
1414  
1415    // Render the form and generate the output.
1416    $form = !empty($node->webform['components']) ? drupal_get_form('webform_client_form_' . $node->nid, $node, $submission, $is_draft) : '';
1417    $output = theme('webform_view', $node, $teaser, $page, $form, $enabled);
1418  
1419    // Remove the surrounding <form> tag if this is a preview.
1420    if ($node->build_mode == NODE_BUILD_PREVIEW) {
1421      $output = preg_replace('/<\/?form[^>]*>/', '', $output);
1422    }
1423  
1424    // Print out messages for the webform.
1425    if ($node->build_mode != NODE_BUILD_PREVIEW && !isset($node->webform_block) && !$logging_in) {
1426      theme('webform_view_messages', $node, $teaser, $page, $submission_count, $user_limit_exceeded, $total_limit_exceeded, $allowed_roles, $closed, $cached);
1427    }
1428  
1429    if (isset($output)) {
1430      if (module_exists('content')) {
1431        $weight = content_extra_field_weight($node->type, 'webform');
1432      }
1433      $node->content['webform'] = array('#value' => $output, '#weight' => isset($weight) ? $weight : 10);
1434    }
1435  }
1436  
1437  /**
1438   * Output the Webform into the node content.
1439   *
1440   * @param $node
1441   *   The webform node object.
1442   * @param $teaser
1443   *   If this webform is being displayed as the teaser view of the node.
1444   * @param $page
1445   *   If this webform node is being viewed as the main content of the page.
1446   * @param $form
1447   *   The rendered form.
1448   * @param $enabled
1449   *   If the form allowed to be completed by the current user.
1450   */
1451  function theme_webform_view($node, $teaser, $page, $form, $enabled) {
1452    // Only show the form if this user is allowed access.
1453    if ($enabled) {
1454      return $form;
1455    }
1456  }
1457  
1458  /**
1459   * Display a message to a user if they are not allowed to fill out a form.
1460   *
1461   * @param $node
1462   *   The webform node object.
1463   * @param $teaser
1464   *   If this webform is being displayed as the teaser view of the node.
1465   * @param $page
1466   *   If this webform node is being viewed as the main content of the page.
1467   * @param $submission_count
1468   *   The number of submissions this user has already submitted. Not calculated
1469   *   for anonymous users.
1470   * @param $user_limit_exceeded
1471   *   Boolean value if the submission limit for this user has been exceeded.
1472   * @param $total_limit_exceeded
1473   *   Boolean value if the total submission limit has been exceeded.
1474   * @param $allowed_roles
1475   *   A list of user roles that are allowed to submit this webform.
1476   * @param $closed
1477   *   Boolean value if submissions are closed.
1478   * @param $cached
1479   *   Whether the page contents are being cached. Messages that are user-specific
1480   *   should not be shown when the page is cached, otherwise the message may be
1481   *   shown to other users. Some messages should always be shown even if the page
1482   *   is cached, such as "Submissions for this form are closed", because they
1483   *   apply to all users equally.
1484   */
1485  function theme_webform_view_messages($node, $teaser, $page, $submission_count, $user_limit_exceeded, $total_limit_exceeded, $allowed_roles, $closed, $cached) {
1486    global $user;
1487  
1488    $type = 'status';
1489  
1490    if ($closed) {
1491      $message = t('Submissions for this form are closed.');
1492    }
1493    // If open and not allowed to submit the form, give an explanation.
1494    elseif (array_search(TRUE, $allowed_roles) === FALSE && $user->uid != 1) {
1495      if (empty($allowed_roles)) {
1496        // No roles are allowed to submit the form.
1497        $message = t('Submissions for this form are closed.');
1498      }
1499      elseif (isset($allowed_roles[2])) {
1500        // The "authenticated user" role is allowed to submit and the user is currently logged-out.
1501        $login = url('user/login', array('query' => drupal_get_destination()));
1502        $register = url('user/register', array('query' => drupal_get_destination()));
1503        if (variable_get('user_register', 1) == 0) {
1504          $message = t('You must <a href="!login">login</a> to view this form.', array('!login' => $login));
1505        }
1506        else {
1507          $message = t('You must <a href="!login">login</a> or <a href="!register">register</a> to view this form.', array('!login' => $login, '!register' => $register));
1508        }
1509      }
1510      else {
1511        // The user must be some other role to submit.
1512        $message = t('You do not have permission to view this form.');
1513      }
1514    }
1515  
1516    // If the user has exceeded the limit of submissions, explain the limit.
1517    elseif ($user_limit_exceeded && !$cached) {
1518      if ($node->webform['submit_interval'] == -1 && $node->webform['submit_limit'] > 1) {
1519        $message = t('You have submitted this form the maximum number of times (@count).', array('@count' => $node->webform['submit_limit']));
1520      }
1521      elseif ($node->webform['submit_interval'] == -1 && $node->webform['submit_limit'] == 1) {
1522        $message = t('You have already submitted this form.');
1523      }
1524      else {
1525        $message = t('You may not submit another entry at this time.');
1526      }
1527      $type = 'error';
1528    }
1529    elseif ($total_limit_exceeded && !$cached) {
1530      if ($node->webform['total_submit_interval'] == -1 && $node->webform['total_submit_limit'] > 1) {
1531        $message = t('This form has received the maximum number of entries.');
1532      }
1533      else {
1534        $message = t('You may not submit another entry at this time.');
1535      }
1536    }
1537  
1538    // If the user has submitted before, give them a link to their submissions.
1539    if ($submission_count > 0 && $node->webform['submit_notice'] == 1 && !$cached) {
1540      if (empty($message)) {
1541        $message = t('You have already submitted this form.') . ' ' . t('<a href="!url">View your previous submissions</a>.', array('!url' => url('node/' . $node->nid . '/submissions')));
1542      }
1543      else {
1544        $message .= ' ' . t('<a href="!url">View your previous submissions</a>.', array('!url' => url('node/' . $node->nid . '/submissions')));
1545      }
1546    }
1547  
1548    if ($page && isset($message)) {
1549      drupal_set_message($message, $type, FALSE);
1550    }
1551  }
1552  
1553  /**
1554   * Implements hook_mail().
1555   */
1556  function webform_mail($key, &$message, $params) {
1557    $message['headers'] = array_merge($message['headers'], $params['headers']);
1558    $message['subject'] = $params['subject'];
1559    $message['body'][] = $params['message'];
1560  }
1561  
1562  /**
1563   * Implements hook_block().
1564   */
1565  function webform_block($op = 'list', $delta = 0, $edit = array()) {
1566    // Get the node ID from delta.
1567    $nid = drupal_substr($delta, strrpos($delta, '-') + 1);
1568    // The result will be FALSE if this is not a webform node block.
1569    if ($op != 'list' && !db_result(db_query("SELECT block FROM {webform} WHERE nid = %d", $nid))) {
1570      return;
1571    }
1572  
1573    switch ($op) {
1574      case 'list':
1575        return webform_block_info();
1576      case 'view':
1577        return webform_block_view($delta);
1578      case 'configure':
1579        return webform_block_configure($delta);
1580      case 'save':
1581        webform_block_save($delta, $edit);
1582        break;
1583    }
1584  }
1585  
1586  /**
1587   * Implements hook_block_info().
1588   */
1589  function webform_block_info() {
1590    $blocks = array();
1591    $webform_node_types = webform_variable_get('webform_node_types');
1592    if (!empty($webform_node_types)) {
1593      $placeholders = db_placeholders($webform_node_types, 'varchar');
1594      $result = db_query("SELECT n.title, n.nid FROM {webform} w LEFT JOIN {node} n ON w.nid = n.nid WHERE w.block = 1 AND n.type IN ($placeholders)", $webform_node_types);
1595      while ($data = db_fetch_object($result)) {
1596        $blocks['client-block-' . $data->nid] = array(
1597          'info' => t('Webform: !title', array('!title' => $data->title)),
1598          'cache' => BLOCK_NO_CACHE,
1599        );
1600      }
1601    }
1602    return $blocks;
1603  }
1604  
1605  /**
1606   * Implements hook_block_view().
1607   */
1608  function webform_block_view($delta = '') {
1609    global $user;
1610  
1611    // Load the block-specific configuration settings.
1612    $webform_blocks = variable_get('webform_blocks', array());
1613    $settings = isset($webform_blocks[$delta]) ? $webform_blocks[$delta] : array();
1614    $settings += array(
1615      'display' => 'form',
1616      'pages_block' => 0,
1617    );
1618  
1619    // Get the node ID from delta.
1620    $nid = drupal_substr($delta, strrpos($delta, '-') + 1);
1621  
1622    // Load node in current language.
1623    if (module_exists('translation')) {
1624      global $language;
1625      if (($translations = translation_node_get_translations($nid)) && (isset($translations[$language->language]))) {
1626        $nid = $translations[$language->language]->nid;
1627      }
1628    }
1629  
1630    // The webform node to display in the block.
1631    $node = node_load($nid);
1632  
1633    // Return if user has no access to the webform node.
1634    if (!node_access('view', $node)) {
1635      return;
1636    }
1637  
1638    // This is a webform node block.
1639    $node->webform_block = TRUE;
1640  
1641    // Use the node title for the block title.
1642    $subject = $node->title;
1643  
1644    // If not displaying pages in the block, set the #action property on the form.
1645    if ($settings['pages_block']) {
1646      $node->webform['action'] = FALSE;
1647    }
1648    else {
1649      $query = array_diff_key($_GET, array('q' => ''));
1650      $node->webform['action'] = url('node/' . $node->nid, array('query' => $query));
1651    }
1652  
1653    // Generate the content of the block based on display settings.
1654    if ($settings['display'] == 'form') {
1655      webform_node_view($node, FALSE, TRUE, FALSE);
1656      $content = $node->content['webform']['#value'];
1657    }
1658    else {
1659      $teaser = ($settings['display'] == 'teaser') ? TRUE : FALSE;
1660      $content = node_view($node, $teaser, TRUE, FALSE);
1661    }
1662  
1663    // Create the block.
1664    $block = array(
1665      'subject' => $subject,
1666      'content' => $content,
1667    );
1668    return $block;
1669  }
1670  
1671  /**
1672   * Implements hook_block_configure().
1673   */
1674  function webform_block_configure($delta = '') {
1675    // Load the block-specific configuration settings.
1676    $webform_blocks = variable_get('webform_blocks', array());
1677    $settings = isset($webform_blocks[$delta]) ? $webform_blocks[$delta] : array();
1678    $settings += array(
1679      'display' => 'form',
1680      'pages_block' => 0,
1681    );
1682  
1683    $form = array();
1684    $form['display'] = array(
1685      '#type' => 'radios',
1686      '#title' => t('Display mode'),
1687      '#default_value' => $settings['display'],
1688      '#options' => array(
1689        'form' => t('Form only'),
1690        'full' => t('Full node'),
1691        'teaser' => t('Teaser'),
1692      ),
1693      '#description' => t('The display mode determines how much of the webform to show within the block.'),
1694    );
1695  
1696    $form['pages_block'] = array(
1697      '#type' => 'checkbox',
1698      '#title' => t('Show all webform pages in block'),
1699      '#default_value' => $settings['pages_block'],
1700      '#description' => t('By default multi-page webforms redirect to the node page for all pages after the first one. If checked, all pages will be shown in the block instead.'),
1701    );
1702  
1703    return $form;
1704  }
1705  
1706  /**
1707   * Implements hook_block_save().
1708   */
1709  function webform_block_save($delta = '', $edit = array()) {
1710    // Load the previously defined block-specific configuration settings.
1711    $settings = variable_get('webform_blocks', array());
1712    // Build the settings array.
1713    $new_settings[$delta] = array(
1714      'display' => $edit['display'],
1715      'pages_block' => $edit['pages_block'],
1716    );
1717    // We store settings for multiple blocks in just one variable
1718    // so we merge the existing settings with the new ones before save.
1719    variable_set('webform_blocks', array_merge($settings, $new_settings));
1720  }
1721  
1722  /**
1723   * Client form generation function. If this is displaying an existing
1724   * submission, pass in the $submission variable with the contents of the
1725   * submission to be displayed.
1726   *
1727   * @param $form_state
1728   *   The current form values of a submission, used in multipage webforms.
1729   * @param $node
1730   *   The current webform node.
1731   * @param $submission
1732   *   An object containing information about the form submission if we're
1733   *   displaying a result.
1734   * @param $is_draft
1735   *   Optional. Set to TRUE if displaying a draft.
1736   * @param $filter
1737   *   Whether or not to filter the contents of descriptions and values when
1738   *   building the form. Values need to be unfiltered to be editable by
1739   *   Form Builder.
1740   */
1741  function webform_client_form(&$form_state, $node, $submission, $is_draft = FALSE, $filter = TRUE) {
1742    global $user;
1743  
1744    module_load_include('inc', 'webform', 'includes/webform.components');
1745    module_load_include('inc', 'webform', 'includes/webform.submissions');
1746  
1747    $form['#process'] = array(
1748      'webform_client_form_includes',
1749    );
1750  
1751    // If in a multi-step form, a submission ID may be specified in form state.
1752    // Load this submission. This allows anonymous users to use auto-save.
1753    if (empty($submission) && !empty($form_state['values']['details']['sid'])) {
1754      $submission = webform_get_submission($node->nid, $form_state['values']['details']['sid']);
1755      $is_draft = $submission->is_draft;
1756    }
1757  
1758    // Bind arguments to $form to make them available in theming and form_alter.
1759    $form['#node'] = $node;
1760    $form['#submission'] = $submission;
1761    $form['#is_draft'] = $is_draft;
1762    $form['#filter'] = $filter;
1763  
1764    // Add a theme function for this form.
1765    $form['#theme'] = array('webform_form_' . $node->nid, 'webform_form');
1766  
1767    // Add a css class for all client forms.
1768    $form['#attributes'] = array('class' => 'webform-client-form');
1769  
1770    // Set the encoding type (necessary for file uploads).
1771    $form['#attributes']['enctype'] = 'multipart/form-data';
1772  
1773    // Sometimes when displaying a webform as a teaser or block, a custom action
1774    // property is set to direct the user to the node page.
1775    if (!empty($node->webform['action'])) {
1776      $form['#action'] = $node->webform['action'];
1777    }
1778  
1779    $form['#submit'] = array('webform_client_form_pages', 'webform_client_form_submit');
1780    $form['#validate'] = array('webform_client_form_validate');
1781  
1782    if (is_array($node->webform['components']) && !empty($node->webform['components'])) {
1783      // Prepare a new form array.
1784      $form['submitted'] = array(
1785        '#tree' => TRUE
1786      );
1787      $form['details'] = array(
1788        '#tree' => TRUE,
1789      );
1790  
1791      // Put the components into a tree structure.
1792      if (!isset($form_state['storage']['component_tree'])) {
1793        $form_state['webform']['component_tree'] = array();
1794        $form_state['webform']['page_count'] = 1;
1795        $form_state['webform']['page_num'] = 1;
1796        _webform_components_tree_build($node->webform['components'], $form_state['webform']['component_tree'], 0, $form_state['webform']['page_count']);
1797      }
1798      else {
1799        $form_state['webform']['component_tree'] = $form_state['storage']['component_tree'];
1800        $form_state['webform']['page_count'] = $form_state['storage']['page_count'];
1801        $form_state['webform']['page_num'] = $form_state['storage']['page_num'];
1802      }
1803  
1804      // Shorten up our variable names.
1805      $component_tree = $form_state['webform']['component_tree'];
1806      $page_count = $form_state['webform']['page_count'];
1807      $page_num = $form_state['webform']['page_num'];
1808  
1809      if ($page_count > 1) {
1810        $next_page_labels = array();
1811        $prev_page_labels = array();
1812      }
1813  
1814      // Recursively add components to the form. The unfiltered version of the
1815      // form (typically used in Form Builder), includes all components.
1816      foreach ($component_tree['children'] as $cid => $component) {
1817        $component_value = isset($form_state['values']['submitted'][$cid]) ? $form_state['values']['submitted'][$cid] : NULL;
1818        if ($filter == FALSE || _webform_client_form_rule_check($node, $component, $page_num, $form_state)) {
1819          if ($component['type'] == 'pagebreak') {
1820            $next_page_labels[$component['page_num'] - 1] = !empty($component['extra']['next_page_label']) ? $component['extra']['next_page_label'] : t('Next Page >');
1821            $prev_page_labels[$component['page_num']] = !empty($component['extra']['prev_page_label']) ? $component['extra']['prev_page_label'] : t('< Previous Page');
1822          }
1823          _webform_client_form_add_component($node, $component, $component_value, $form['submitted'], $form, $form_state, $submission, 'form', $page_num, $filter);
1824        }
1825      }
1826  
1827      // These form details help managing data upon submission.
1828      $form['details']['nid'] = array(
1829        '#type' => 'value',
1830        '#value' => $node->nid,
1831      );
1832      $form['details']['sid'] = array(
1833        '#type' => 'hidden',
1834        '#value' => isset($submission->sid) ? $submission->sid : '',
1835      );
1836      $form['details']['uid'] = array(
1837        '#type' => 'value',
1838        '#value' => isset($submission->uid) ? $submission->uid : $user->uid,
1839      );
1840      $form['details']['page_num'] = array(
1841        '#type'  => 'hidden',
1842        '#value' => $page_num,
1843      );
1844      $form['details']['page_count'] = array(
1845        '#type'  => 'hidden',
1846        '#value' => $page_count,
1847      );
1848      $form['details']['finished'] = array(
1849        '#type' => 'hidden',
1850        '#value' => isset($submission->is_draft) ? (!$submission->is_draft) : 0,
1851      );
1852  
1853      // Add buttons for pages, drafts, and submissions.
1854      $form['actions'] = array(
1855        '#tree' => FALSE,
1856        '#weight' => 1000,
1857        '#prefix' => '<div id="edit-actions" class="form-actions form-wrapper">',
1858        '#suffix' => '</div>',
1859      );
1860  
1861      // Add the draft button.
1862      if ($node->webform['allow_draft'] && (empty($submission) || $submission->is_draft) && $user->uid != 0) {
1863        $form['actions']['draft'] = array(
1864          '#type' => 'submit',
1865          '#value' => t('Save Draft'),
1866          '#weight' => -2,
1867          '#validate' => array(),
1868          '#attributes' => array('formnovalidate' => 'formnovalidate'),
1869        );
1870      }
1871  
1872      if ($page_count > 1) {
1873        // Add the submit button(s).
1874        if ($page_num > 1) {
1875          $form['actions']['previous'] = array(
1876            '#type' => 'submit',
1877            '#value' => $prev_page_labels[$page_num],
1878            '#weight' => 5,
1879            '#validate' => array(),
1880            '#attributes' => array('formnovalidate' => 'formnovalidate'),
1881          );
1882        }
1883        if ($page_num == $page_count) {
1884          $form['actions']['submit'] = array(
1885            '#type' => 'submit',
1886            '#value' => empty($node->webform['submit_text']) ? t('Submit') : t($node->webform['submit_text']),
1887            '#weight' => 10,
1888          );
1889        }
1890        elseif ($page_num < $page_count) {
1891          $form['actions']['next'] = array(
1892            '#type' => 'submit',
1893            '#value' => $next_page_labels[$page_num],
1894            '#weight' => 10,
1895          );
1896        }
1897      }
1898      else {
1899        // Add the submit button.
1900        $form['actions']['submit'] = array(
1901          '#type' => 'submit',
1902          '#value' => empty($node->webform['submit_text']) ? t('Submit') : t($node->webform['submit_text']),
1903          '#weight' => 10,
1904        );
1905      }
1906    }
1907  
1908    return $form;
1909  }
1910  
1911  /**
1912   * Process function for webform_client_form().
1913   *
1914   * Include all the enabled components for this form to ensure availability.
1915   */
1916  function webform_client_form_includes($form, $form_state) {
1917    $components = webform_components();
1918    foreach ($components as $component_type => $component) {
1919      webform_component_include($component_type);
1920    }
1921    return $form;
1922  }
1923  
1924  /**
1925   * Check if a component should be displayed on the current page.
1926   */
1927  function _webform_client_form_rule_check($node, $component, $page_num, $form_state = NULL, $submission = NULL) {
1928    $conditional_values = isset($component['extra']['conditional_values']) ? $component['extra']['conditional_values'] : NULL;
1929    $conditional_component = isset($component['extra']['conditional_component']) && isset($node->webform['components'][$component['extra']['conditional_component']]) ? $node->webform['components'][$component['extra']['conditional_component']] : NULL;
1930    $conditional_cid = $conditional_component['cid'];
1931  
1932    // Check the rules for this entire page. Note individual page breaks are
1933    // checked down below in the individual component rule checks.
1934    $show_page = TRUE;
1935    if ($component['page_num'] > 1 && $component['type'] != 'pagebreak') {
1936      foreach ($node->webform['components'] as $cid => $page_component) {
1937        if ($page_component['type'] == 'pagebreak' && $page_component['page_num'] == $page_num) {
1938          $show_page = _webform_client_form_rule_check($node, $page_component, $page_num, $form_state, $submission);
1939          break;
1940        }
1941      }
1942    }
1943  
1944    // Check any parents' visibility rules.
1945    $show_parent = $show_page;
1946    if ($show_parent && $component['pid'] && isset($node->webform['components'][$component['pid']])) {
1947      $parent_component = $node->webform['components'][$component['pid']];
1948      $show_parent = _webform_client_form_rule_check($node, $parent_component, $page_num, $form_state, $submission);
1949    }
1950  
1951    // Check the individual component rules.
1952    $show_component = $show_parent;
1953    if ($show_component && ($page_num == 0 || $component['page_num'] == $page_num) && $conditional_component && strlen(trim($conditional_values))) {
1954      $input_values = array();
1955      if (isset($form_state)) {
1956        $input_value = isset($form_state['values']['submitted'][$conditional_cid]) ? $form_state['values']['submitted'][$conditional_cid] : NULL;
1957        $input_values = is_array($input_value) ? $input_value : array($input_value);
1958      }
1959      elseif (isset($submission)) {
1960        $input_values = isset($submission->data[$conditional_cid]['value']) ? $submission->data[$conditional_cid]['value'] : array();
1961      }
1962  
1963      $test_values = array_map('trim', explode("\n", $conditional_values));
1964      if (empty($input_values) && !empty($test_values)) {
1965        $show_component = FALSE;
1966      }
1967      else {
1968        foreach ($input_values as $input_value) {
1969          if ($show_component = in_array($input_value, $test_values)) {
1970            break;
1971          }
1972        }
1973      }
1974  
1975      if ($component['extra']['conditional_operator'] == '!=') {
1976        $show_component = !$show_component;
1977      }
1978    }
1979  
1980    // Private component?
1981    if ($component['extra']['private']) {
1982      $show_component = webform_results_access($node);
1983    }
1984  
1985    return $show_component;
1986  }
1987  
1988  /**
1989   * Add a component to a renderable array. Called recursively for fieldsets.
1990   *
1991   * This function assists in the building of the client form, as well as the
1992   * display of results, and the text of e-mails.
1993   *
1994   * @param $component
1995   *   The component to be added to the form.
1996   * @param $component_value
1997   *   The components current value if known.
1998   * @param $parent_fieldset
1999   *   The fieldset to which this element will be added.
2000   * @param $form
2001   *   The entire form array.
2002   * @param $form_state
2003   *   The form state.
2004   * @param $submission
2005   *   The Webform submission as retrieved from the database.
2006   * @param $format
2007   *   The format the form should be displayed as. May be one of the following:
2008   *   - form: Show as an editable form.
2009   *   - html: Show as HTML results.
2010   *   - text: Show as plain text.
2011   * @param $filter
2012   *   Whether the form element properties should be filtered. Only set to FALSE
2013   *   if needing the raw properties for editing.
2014   *
2015   * @see webform_client_form()
2016   * @see webform_submission_render()
2017   */
2018  function _webform_client_form_add_component($node, $component, $component_value, &$parent_fieldset, &$form, $form_state, $submission, $format = 'form', $page_num = 0, $filter = TRUE) {
2019    $cid = $component['cid'];
2020  
2021    // Load with submission information if necessary.
2022    if ($format != 'form') {
2023      // This component is display only.
2024      $data = empty($submission->data[$cid]['value']) ? NULL : $submission->data[$cid]['value'];
2025      if ($display_element = webform_component_invoke($component['type'], 'display', $component, $data, $format)) {
2026        // Ensure the component is added as a property.
2027        $display_element['#webform_component'] = $component;
2028  
2029        // Allow modules to modify a "display only" webform component.
2030        drupal_alter('webform_component_display', $display_element, $component);
2031  
2032        // The form_builder() function usually adds #parents and #id for us, but
2033        // because these are not marked for #input, we need to add them manually.
2034        if (!isset($display_element['#parents'])) {
2035          $parents = isset($parent_fieldset['#parents']) ? $parent_fieldset['#parents'] : array('submitted');
2036          $parents[] = $component['form_key'];
2037          $display_element['#parents'] = $parents;
2038        }
2039        if (!isset($display_element['#id'])) {
2040          $display_element['#id'] = form_clean_id('edit-' . implode('-', $display_element['#parents']));
2041        }
2042  
2043        // Add the element into the proper parent in the display.
2044        $parent_fieldset[$component['form_key']] = $display_element;
2045      }
2046    }
2047    // Show the component only on its form page, or if building an unfiltered
2048    // version of the form (such as for Form Builder).
2049    elseif ($component['page_num'] == $page_num || $filter == FALSE) {
2050      // Add this user-defined field to the form (with all the values that are always available).
2051      $data = isset($submission->data[$cid]['value']) ? $submission->data[$cid]['value'] : NULL;
2052      if ($element = webform_component_invoke($component['type'], 'render', $component, $data, $filter)) {
2053        // Ensure the component is added as a property.
2054        $element['#webform_component'] = $component;
2055  
2056        // Allow modules to modify a webform component that is going to be render in a form.
2057        drupal_alter('webform_component_render', $element, $component);
2058  
2059        // Add the element into the proper parent in the form.
2060        $parent_fieldset[$component['form_key']] = $element;
2061  
2062        // Override the value if one already exists in the form state.
2063        if (isset($component_value)) {
2064          $parent_fieldset[$component['form_key']]['#default_value'] = $component_value;
2065          if (is_array($component_value)) {
2066            foreach ($component_value as $key => $value) {
2067              if (isset($parent_fieldset[$component['form_key']][$key])) {
2068                $parent_fieldset[$component['form_key']][$key]['#default_value'] = $value;
2069              }
2070            }
2071          }
2072        }
2073      }
2074      else {
2075        drupal_set_message(t('The webform component @type is not able to be displayed', array('@type' => $component['type'])));
2076      }
2077    }
2078  
2079    // Disable validation initially on all elements. We manually validate
2080    // all webform elements in webform_client_form_validate().
2081    if (isset($parent_fieldset[$component['form_key']])) {
2082      $parent_fieldset[$component['form_key']]['#validated'] = TRUE;
2083      $parent_fieldset[$component['form_key']]['#webform_validated'] = FALSE;
2084    }
2085  
2086    if (isset($component['children']) && is_array($component['children'])) {
2087      foreach ($component['children'] as $scid => $subcomponent) {
2088        $subcomponent_value = isset($form_state['values']['submitted'][$scid]) ? $form_state['values']['submitted'][$scid] : NULL;
2089        if (_webform_client_form_rule_check($node, $subcomponent, $page_num, $form_state, $submission)) {
2090          _webform_client_form_add_component($node, $subcomponent, $subcomponent_value, $parent_fieldset[$component['form_key']], $form, $form_state, $submission, $format, $page_num, $filter);
2091        }
2092      }
2093    }
2094  }
2095  
2096  function webform_client_form_validate($form, &$form_state) {
2097    $node = node_load($form_state['values']['details']['nid']);
2098    $finished = $form_state['values']['details']['finished'];
2099  
2100    // Check that the submissions have not exceeded the total submission limit.
2101    if ($node->webform['total_submit_limit'] != -1) {
2102      module_load_include('inc', 'webform', 'includes/webform.submissions');
2103      // Check if the total number of entries was reached before the user submitted
2104      // the form.
2105      if (!$finished && $total_limit_exceeded = _webform_submission_total_limit_check($node)) {
2106        // Show the user the limit has exceeded.
2107        theme('webform_view_messages', $node, 0, 1, 0, NULL, $total_limit_exceeded, array_keys(user_roles()), FALSE, FALSE);
2108        form_set_error('', NULL);
2109        return;
2110      }
2111    }
2112  
2113    // Check that the user has not exceeded the submission limit.
2114    // This usually will only apply to anonymous users when the page cache is
2115    // enabled, because they may submit the form even if they do not have access.
2116    if ($node->webform['submit_limit'] != -1) { // -1: Submissions are never throttled.
2117      module_load_include('inc', 'webform', 'includes/webform.submissions');
2118  
2119      if (!$finished && $user_limit_exceeded = _webform_submission_user_limit_check($node)) {
2120        // Assume that webform_view_messages will print out the necessary message,
2121        // then stop the processing of the form with an empty form error.
2122        theme('webform_view_messages', $node, 0, 1, 0, $user_limit_exceeded, NULL, array_keys(user_roles()), FALSE, FALSE);
2123        form_set_error('', NULL);
2124        return;
2125      }
2126    }
2127  
2128    // Run all #element_validate and #required checks. These are skipped initially
2129    // by setting #validated = TRUE on all components when they are added.
2130    _webform_client_form_validate($form, $form_state);
2131  }
2132  
2133  /**
2134   * Recursive validation function to trigger normal Drupal validation.
2135   *
2136   * This function imitates _form_validate in Drupal's form.inc, only it sets
2137   * a different property to ensure that validation has occurred.
2138   */
2139  function _webform_client_form_validate($elements, &$form_state, $first_run = TRUE) {
2140    static $form;
2141    if ($first_run) {
2142      $form = $elements;
2143    }
2144  
2145    // Recurse through all children.
2146    foreach (element_children($elements) as $key) {
2147      if (isset($elements[$key]) && $elements[$key]) {
2148        _webform_client_form_validate($elements[$key], $form_state, FALSE);
2149      }
2150    }
2151    // Validate the current input.
2152    if (isset($elements['#webform_validated']) && $elements['#webform_validated'] == FALSE) {
2153      if (isset($elements['#needs_validation'])) {
2154        // Make sure a value is passed when the field is required.
2155        // A simple call to empty() will not cut it here as some fields, like
2156        // checkboxes, can return a valid value of '0'. Instead, check the
2157        // length if it's a string, and the item count if it's an array. For
2158        // radios, FALSE means that no value was submitted, so check that too.
2159        if ($elements['#required'] && (!count($elements['#value']) || (is_string($elements['#value']) && strlen(trim($elements['#value'])) == 0) || $elements['#value'] === FALSE)) {
2160          form_error($elements, t('!name field is required.', array('!name' => $elements['#title'])));
2161        }
2162  
2163        // Verify that the value is not longer than #maxlength.
2164        if (isset($elements['#maxlength']) && drupal_strlen($elements['#value']) > $elements['#maxlength']) {
2165          form_error($elements, t('!name cannot be longer than %max characters but is currently %length characters long.', array('!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'], '%max' => $elements['#maxlength'], '%length' => drupal_strlen($elements['#value']))));
2166        }
2167  
2168        if (isset($elements['#options']) && isset($elements['#value'])) {
2169          if ($elements['#type'] == 'select') {
2170            $options = form_options_flatten($elements['#options']);
2171          }
2172          else {
2173            $options = $elements['#options'];
2174          }
2175          if (is_array($elements['#value'])) {
2176            $value = $elements['#type'] == 'checkboxes' ? array_keys(array_filter($elements['#value'])) : $elements['#value'];
2177            foreach ($value as $v) {
2178              if (!isset($options[$v])) {
2179                form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
2180                watchdog('form', 'Illegal choice %choice in !name element.', array('%choice' => $v, '!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
2181              }
2182            }
2183          }
2184          elseif ($elements['#value'] !== '' && !isset($options[$elements['#value']])) {
2185            form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
2186            watchdog('form', 'Illegal choice %choice in %name element.', array('%choice' => $elements['#value'], '%name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
2187          }
2188        }
2189      }
2190  
2191      // Call any element-specific validators. These must act on the element
2192      // #value data.
2193      if (isset($elements['#element_validate'])) {
2194        foreach ($elements['#element_validate'] as $function) {
2195          if (function_exists($function))  {
2196            $function($elements, $form_state, $form);
2197          }
2198        }
2199      }
2200      $elements['#webform_validated'] = TRUE;
2201    }
2202  }
2203  
2204  /**
2205   * Handle the processing of pages and conditional logic.
2206   */
2207  function webform_client_form_pages($form, &$form_state) {
2208    $node = node_load($form_state['values']['details']['nid']);
2209  
2210    // Move special settings to storage.
2211    if (isset($form_state['webform']['component_tree'])) {
2212      $form_state['storage']['component_tree'] = $form_state['webform']['component_tree'];
2213      $form_state['storage']['page_count'] = $form_state['webform']['page_count'];
2214      $form_state['storage']['page_num'] = $form_state['webform']['page_num'];
2215    }
2216  
2217    // Perform post processing by components.
2218    _webform_client_form_submit_process($node, $form_state['values']['submitted']);
2219  
2220    // Flatten trees within the submission.
2221    $form_state['values']['submitted_tree'] = $form_state['values']['submitted'];
2222    $form_state['values']['submitted'] = _webform_client_form_submit_flatten($node, $form_state['values']['submitted']);
2223  
2224    // Assume the form is completed unless the page logic says otherwise.
2225    $form_state['webform_completed'] = TRUE;
2226  
2227    // Check for a multi-page form that is not yet complete.
2228    $submit_op = !empty($form['actions']['submit']['#value']) ? $form['actions']['submit']['#value'] : t('Submit');
2229    $draft_op = !empty($form['actions']['draft']['#value']) ? $form['actions']['draft']['#value'] : t('Save Draft');
2230    if (!in_array($form_state['values']['op'], array($submit_op, $draft_op))) {
2231      // Store values from the current page in the form state storage.
2232      if (is_array($form_state['values']['submitted'])) {
2233        foreach ($form_state['values']['submitted'] as $key => $val) {
2234          $form_state['storage']['submitted'][$key] = $val;
2235        }
2236      }
2237  
2238      // Update form state values with those from storage.
2239      if (isset($form_state['storage']['submitted'])) {
2240        foreach ($form_state['storage']['submitted'] as $key => $val) {
2241          $form_state['values']['submitted'][$key] = $val;
2242        }
2243      }
2244  
2245      // Set the page number.
2246      if (!isset($form_state['storage']['page_num'])) {
2247        $form_state['storage']['page_num'] = 1;
2248      }
2249      if (end($form_state['clicked_button']['#parents']) == 'next') {
2250        $direction = 1;
2251      }
2252      else {
2253        $direction = 0;
2254      }
2255  
2256      // If the next page has no components that need to be displayed, skip it.
2257      if (isset($direction)) {
2258        $components = $direction ? $node->webform['components'] : array_reverse($node->webform['components'], TRUE);
2259        $last_component = end($node->webform['components']);
2260        foreach ($components as $component) {
2261          if ($component['type'] == 'pagebreak' && (
2262              $direction == 1 && $component['page_num'] > $form_state['storage']['page_num'] ||
2263              $direction == 0 && $component['page_num'] <= $form_state['storage']['page_num'])) {
2264            $previous_pagebreak = $component;
2265            continue;
2266          }
2267          if (isset($previous_pagebreak)) {
2268            $page_num = $previous_pagebreak['page_num'] + $direction - 1;
2269            // If we've found an component on this page, advance to that page.
2270            if ($component['page_num'] == $page_num && _webform_client_form_rule_check($node, $component, $page_num, $form_state)) {
2271              $form_state['storage']['page_num'] = $page_num;
2272              break;
2273            }
2274            // If we've gotten to the end of the form without finding any more
2275            // components, set the page number more than the max, ending the form.
2276            elseif ($direction && $component['cid'] == $last_component['cid']) {
2277              $form_state['storage']['page_num'] = $page_num + 1;
2278            }
2279          }
2280        }
2281      }
2282  
2283      // The form is done if the page number is greater than the page count.
2284      $form_state['webform_completed'] = $form_state['storage']['page_num'] > $form_state['storage']['page_count'];
2285    }
2286  
2287    // Merge any stored submission data for multistep forms.
2288    if (isset($form_state['storage']['submitted'])) {
2289      $original_values = is_array($form_state['values']['submitted']) ? $form_state['values']['submitted'] : array();
2290      unset($form_state['values']['submitted']);
2291  
2292      foreach ($form_state['storage']['submitted'] as $key => $val) {
2293        $form_state['values']['submitted'][$key] = $val;
2294      }
2295      foreach ($original_values as $key => $val) {
2296        $form_state['values']['submitted'][$key] = $val;
2297      }
2298  
2299      // Remove the variable so it doesn't show up in the additional processing.
2300      unset($original_values);
2301    }
2302  
2303    // Inform the submit handlers that a draft will be saved.
2304    $form_state['save_draft'] = $form_state['values']['op'] == $draft_op || ($node->webform['auto_save'] && !$form_state['webform_completed']);
2305  
2306    // Determine what we need to do on the next page.
2307    if (!empty($form_state['save_draft']) || !$form_state['webform_completed']) {
2308      // Rebuild the form and display the current (on drafts) or next page.
2309      $form_state['rebuild'] = TRUE;
2310    }
2311    else {
2312      // Remove the form state storage now that we're done with the pages.
2313      $form_state['rebuild'] = FALSE;
2314      unset($form_state['storage']);
2315    }
2316  }
2317  
2318  /**
2319   * Submit handler for saving the form values and sending e-mails.
2320   */
2321  function webform_client_form_submit($form, &$form_state) {
2322    module_load_include('inc', 'webform', 'includes/webform.submissions');
2323    module_load_include('inc', 'webform', 'includes/webform.components');
2324    global $user;
2325  
2326    if (empty($form_state['save_draft']) && empty($form_state['webform_completed'])) {
2327      return;
2328    }
2329  
2330    $node = $form['#node'];
2331    $sid = $form_state['values']['details']['sid'] ? (int) $form_state['values']['details']['sid'] : NULL;
2332  
2333    // Check if user is submitting as a draft.
2334    $is_draft = (int) !empty($form_state['save_draft']);
2335  
2336    if (!$sid) {
2337      // Create a submission object.
2338      $submission = (object) array(
2339        'nid' => $node->nid,
2340        'uid' => $user->uid,
2341        'submitted' => time(),
2342        'remote_addr' => ip_address(),
2343        'is_draft' => $is_draft,
2344        'data' => webform_submission_data($node, $form_state['values']['submitted']),
2345      );
2346    }
2347    else {
2348      // To maintain time and user information, load the existing submission.
2349      $submission = webform_get_submission($node->webform['nid'], $sid);
2350      $submission->is_draft = $is_draft;
2351  
2352      // Merge with new submission data. The + operator maintains numeric keys.
2353      // This maintains existing data with just-submitted data when a user resumes
2354      // a submission previously saved as a draft.
2355      $new_data = webform_submission_data($node, $form_state['values']['submitted']);
2356      $submission->data = $new_data + $submission->data;
2357    }
2358  
2359    // If there is no data to be saved (such as on a multipage form with no fields
2360    // on the first page), process no further. Submissions with no data cannot
2361    // be loaded from the database as efficiently, so we don't save them at all.
2362    if (empty($submission->data)) {
2363      return;
2364    }
2365  
2366    // Save the submission to the database.
2367    if (!$sid) {
2368      // No sid was found thus insert it in the dataabase.
2369      $form_state['values']['details']['sid'] = $sid = webform_submission_insert($node, $submission);
2370      $form_state['values']['details']['is_new'] = TRUE;
2371  
2372      // Set a cookie including the server's submission time.
2373      // The cookie expires in the length of the interval plus a day to compensate for different timezones.
2374      if (variable_get('webform_use_cookies', 0)) {
2375        $cookie_name = 'webform-' . $node->nid;
2376        $time = time();
2377        $params = session_get_cookie_params();
2378        setcookie($cookie_name . '[' . $time . ']', $time, $time + $node->webform['submit_interval'] + 86400, $params['path'], $params['domain'], $params['secure']);
2379      }
2380  
2381      // Save session information about this submission for anonymous users,
2382      // allowing them to access or edit their submissions.
2383      if (!$user->uid && user_access('access own webform submissions')) {
2384        $_SESSION['webform_submission'][$form_state['values']['details']['sid']] = $node->nid;
2385      }
2386    }
2387    else {
2388      // Sid was found thus update the existing sid in the database.
2389      webform_submission_update($node, $submission);
2390      $form_state['values']['details']['is_new'] = FALSE;
2391    }
2392  
2393    // Check if this form is sending an email.
2394    if (!$is_draft && !$form_state['values']['details']['finished']) {
2395      $submission = webform_get_submission($node->webform['nid'], $sid, TRUE);
2396      webform_submission_send_mail($node, $submission);
2397    }
2398  
2399    // Strip out empty tags added by WYSIWYG editors if needed.
2400    $confirmation = strlen(trim(strip_tags($node->webform['confirmation']))) ? $node->webform['confirmation'] : '';
2401  
2402    // Clean up the redirect URL and filter it for webform tokens.
2403    $redirect_url = trim($node->webform['redirect_url']);
2404    $redirect_url = _webform_filter_values($redirect_url, $node, $submission, NULL, FALSE, TRUE);
2405  
2406    // Remove the domain name from the redirect.
2407    $redirect_url = preg_replace('/^' . preg_quote($GLOBALS['base_url'], '/') . '\//', '', $redirect_url);
2408  
2409    // Check confirmation and redirect_url fields.
2410    $message = NULL;
2411    $redirect = NULL;
2412    $external_url = FALSE;
2413    if (isset($form['actions']['draft']['#value']) && $form_state['values']['op'] == $form['actions']['draft']['#value']) {
2414      $message = t('Submission saved. You may return to this form later and it will restore the current values.');
2415    }
2416    elseif ($is_draft) {
2417      $redirect = NULL;
2418    }
2419    elseif (!empty($form_state['values']['details']['finished'])) {
2420      $message = t('Submission updated.');
2421    }
2422    elseif ($redirect_url == '<none>') {
2423      $redirect = NULL;
2424    }
2425    elseif ($redirect_url == '<confirmation>') {
2426      $redirect = array('node/' . $node->nid . '/done', 'sid=' . $sid);
2427    }
2428    elseif (valid_url($redirect_url, TRUE)) {
2429      $redirect = $redirect_url;
2430      $external_url = TRUE;
2431    }
2432    elseif ($redirect_url && strpos($redirect_url, 'http') !== 0) {
2433      $parts = parse_url($redirect_url);
2434      $query = $parts['query'] ? ($parts['query'] . '&sid=' . $sid) : ('sid=' . $sid);
2435      $redirect = array($parts['path'], $query, $parts['fragment']);
2436    }
2437  
2438    // Show a message if manually set.
2439    if (isset($message)) {
2440      drupal_set_message($message);
2441    }
2442    // If redirecting and we have a confirmation message, show it as a message.
2443    elseif (!$is_draft && !$external_url && (!empty($redirect_url) && $redirect_url != '<confirmation>') && !empty($confirmation)) {
2444      drupal_set_message(check_markup($confirmation, $node->webform['confirmation_format'], FALSE));
2445    }
2446  
2447    $form_state['redirect'] = $redirect;
2448  }
2449  
2450  /**
2451   * Post processes the submission tree with any updates from components.
2452   *
2453   * @param $node
2454   *   The full webform node.
2455   * @param $form_values
2456   *   The form values for the form.
2457   * @param $types
2458   *   Optional. Specific types to perform processing.
2459   * @param $parent
2460   *   Internal use. The current parent CID whose children are being processed.
2461   */
2462  function _webform_client_form_submit_process($node, &$form_values, $types = NULL, $parent = 0) {
2463    if (is_array($form_values)) {
2464      foreach ($form_values as $form_key => $value) {
2465        $cid = webform_get_cid($node, $form_key, $parent);
2466        if (is_array($value) && isset($node->webform['components'][$cid]['type']) && webform_component_feature($node->webform['components'][$cid]['type'], 'group')) {
2467          _webform_client_form_submit_process($node, $form_values[$form_key], $types, $cid);
2468        }
2469  
2470        if (isset($node->webform['components'][$cid])) {
2471          // Call the component process submission function.
2472          $component = $node->webform['components'][$cid];
2473          if ((!isset($types) || in_array($component['type'], $types)) && webform_component_implements($component['type'], 'submit')) {
2474            $form_values[$component['form_key']] = webform_component_invoke($component['type'], 'submit', $component, $form_values[$component['form_key']]);
2475          }
2476        }
2477      }
2478    }
2479  }
2480  
2481  /**
2482   * Flattens a submitted form back into a single array representation (rather than nested fields)
2483   */
2484  function _webform_client_form_submit_flatten($node, $fieldset, $parent = 0) {
2485    $values = array();
2486  
2487    if (is_array($fieldset)) {
2488      foreach ($fieldset as $form_key => $value) {
2489        $cid = webform_get_cid($node, $form_key, $parent);
2490  
2491        if (is_array($value) && webform_component_feature($node->webform['components'][$cid]['type'], 'group')) {
2492          $values += _webform_client_form_submit_flatten($node, $value, $cid);
2493        }
2494        else {
2495          $values[$cid] = $value;
2496        }
2497      }
2498    }
2499  
2500    return $values;
2501  }
2502  
2503  /**
2504   * Prints the confirmation message after a successful submission.
2505   */
2506  function _webform_confirmation($node) {
2507    drupal_set_title(check_plain($node->title));
2508    webform_set_breadcrumb($node);
2509    $sid = isset($_GET['sid']) ? $_GET['sid'] : NULL;
2510    return theme(array('webform_confirmation_' . $node->nid, 'webform_confirmation'), $node, $sid);
2511  }
2512  
2513  /**
2514   * Prepare for theming of the webform form.
2515   */
2516  function template_preprocess_webform_form(&$vars) {
2517    drupal_add_css(drupal_get_path('module', 'webform') . '/css/webform.css');
2518    drupal_add_js(drupal_get_path('module', 'webform') . '/js/webform.js');
2519  
2520    if (isset($vars['form']['details']['nid']['#value'])) {
2521      $vars['nid'] = $vars['form']['details']['nid']['#value'];
2522    }
2523    elseif (isset($vars['form']['submission']['#value'])) {
2524      $vars['nid'] = $vars['form']['submission']['#value']->nid;
2525    }
2526  }
2527  
2528  /**
2529   * Prepare for theming of the webform submission confirmation.
2530   */
2531  function template_preprocess_webform_confirmation(&$vars) {
2532    $confirmation = check_markup($vars['node']->webform['confirmation'], $vars['node']->webform['confirmation_format'], FALSE);
2533    // Strip out empty tags added by WYSIWYG editors if needed.
2534    $vars['confirmation_message'] = strlen(trim(strip_tags($confirmation))) ? $confirmation : '';
2535  }
2536  
2537  /**
2538   * Prepare to theme the contents of e-mails sent by webform.
2539   */
2540  function template_preprocess_webform_mail_message(&$vars) {
2541    global $user;
2542  
2543    $vars['user'] = $user;
2544    $vars['ip_address'] = ip_address();
2545  }
2546  
2547  /**
2548   * A Form API #pre_render function. Sets display based on #title_display.
2549   *
2550   * This function is used regularly in D6 for all elements, but specifically for
2551   * fieldsets in D7, which don't support #title_display natively.
2552   */
2553  function webform_element_title_display($element) {
2554    if (isset($element['#title_display']) && strcmp($element['#title_display'], 'none') === 0) {
2555      $element['#title'] = NULL;
2556    }
2557    return $element;
2558  }
2559  
2560  /**
2561   * A Form API #post_render function. Wraps displayed elements in their label.
2562   *
2563   * Note: this entire function may be removed in Drupal 7, which supports
2564   * #theme_wrappers natively.
2565   */
2566  function webform_element_wrapper($content, $elements) {
2567    if (isset($elements['#theme_wrappers'])) {
2568      foreach ($elements['#theme_wrappers'] as $theme_wrapper) {
2569        $content = theme($theme_wrapper, $elements, $content);
2570      }
2571    }
2572    return $content;
2573  }
2574  
2575  /**
2576   * Replacement for theme_form_element().
2577   */
2578  function theme_webform_element($element, $value) {
2579    $wrapper_classes = array(
2580     'form-item',
2581    );
2582    $output = '<div class="' . implode(' ', $wrapper_classes) . '" id="' . $element['#id'] . '-wrapper">' . "\n";
2583    $required = !empty($element['#required']) ? '<span class="form-required" title="' . t('This field is required.') . '">*</span>' : '';
2584  
2585    if (!empty($element['#title'])) {
2586      $title = $element['#title'];
2587      $output .= ' <label for="' . $element['#id'] . '">' . t('!title: !required', array('!title' => filter_xss_admin($title), '!required' => $required)) . "</label>\n";
2588    }
2589  
2590    $output .= '<div id="' . $element['#id'] . '">' . $value . '</div>' . "\n";
2591  
2592    if (!empty($element['#description'])) {
2593      $output .= ' <div class="description">' . $element['#description'] . "</div>\n";
2594    }
2595  
2596    $output .= "</div>\n";
2597  
2598    return $output;
2599  }
2600  
2601  /**
2602   * Wrap form elements in a DIV with appropriate classes and an ID.
2603   *
2604   * This is a temporary work-around until we can use theme_webform_element() for
2605   * all webform form elements in Drupal 7.
2606   */
2607  function theme_webform_element_wrapper($element, $content) {
2608    // All elements using this for display only are given the "display" type.
2609    if (isset($element['#format']) && $element['#format'] == 'html') {
2610      $type = 'display';
2611    }
2612    else {
2613      $type = (isset($element['#type']) && !in_array($element['#type'], array('markup', 'textfield', 'webform_email', 'webform_number'))) ? $element['#type'] : $element['#webform_component']['type'];
2614    }
2615  
2616    // Convert the parents array into a string, excluding the "submitted" wrapper.
2617    $nested_level = $element['#parents'][0] == 'submitted' ? 1 : 0;
2618    $parents = str_replace('_', '-', implode('--', array_slice($element['#parents'], $nested_level)));
2619  
2620    $wrapper_classes = array(
2621      'webform-component',
2622      'webform-component-' . $type,
2623    );
2624  
2625    if (isset($element['#title_display']) && strcmp($element['#title_display'], 'inline') === 0) {
2626      $wrapper_classes[] = 'webform-container-inline';
2627    }
2628  
2629    $output = '';
2630    $output .= '<div class="' . implode(' ', $wrapper_classes) . '" id="webform-component-' . $parents . '">';
2631    $output .= $content;
2632    $output .= '</div>';
2633    return $output;
2634  }
2635  
2636  /**
2637   * Output a form element in plain text format.
2638   */
2639  function theme_webform_element_text($element, $value) {
2640    $output = '';
2641    $is_group = webform_component_feature($element['#webform_component']['type'], 'group');
2642  
2643    // Output the element title.
2644    if (isset($element['#title'])) {
2645      if ($is_group) {
2646        $output .= '--' . $element['#title'] . '--';
2647      }
2648      elseif (!in_array(drupal_substr($element['#title'], -1), array('?', ':', '!', '%', ';', '@'))) {
2649        $output .= $element['#title'] . ':';
2650      }
2651      else {
2652        $output .= $element['#title'];
2653      }
2654    }
2655  
2656    // Wrap long values at 65 characters, allowing for a few fieldset indents.
2657    // It's common courtesy to wrap at 75 characters in e-mails.
2658    if ($is_group && drupal_strlen($value) > 65) {
2659      $value = wordwrap($value, 65, "\n");
2660      $lines = explode("\n", $value);
2661      foreach ($lines as $key => $line) {
2662        $lines[$key] = '  ' . $line;
2663      }
2664      $value = implode("\n", $lines);
2665    }
2666  
2667    // Add the value to the output.
2668    if ($value) {
2669      $output .= (strpos($value, "\n") === FALSE ? ' ' : "\n") . $value;
2670    }
2671  
2672    // Indent fieldsets.
2673    if ($is_group) {
2674      $lines = explode("\n", $output);
2675      foreach ($lines as $number => $line) {
2676        if (strlen($line)) {
2677          $lines[$number] = '  ' . $line;
2678        }
2679      }
2680      $output = implode("\n", $lines);
2681      $output .= "\n";
2682    }
2683  
2684    if ($output) {
2685      $output .= "\n";
2686    }
2687  
2688    return $output;
2689  }
2690  
2691  /**
2692   * Theme the headers when sending an email from webform.
2693   *
2694   * @param $node
2695   *   The complete node object for the webform.
2696   * @param $submission
2697   *   The webform submission of the user.
2698   * @param $email
2699   *   If you desire to make different e-mail headers depending on the recipient,
2700   *   you can check the $email['email'] property to output different content.
2701   *   This will be the ID of the component that is a conditional e-mail
2702   *   recipient. For the normal e-mails, it will have the value of 'default'.
2703   * @return
2704   *   An array of headers to be used when sending a webform email. If headers
2705   *   for "From", "To", or "Subject" are set, they will take precedence over
2706   *   the values set in the webform configuration.
2707   */
2708  function theme_webform_mail_headers($node, $submission, $email) {
2709    $headers = array(
2710      'X-Mailer' => 'Drupal Webform (PHP/' . phpversion() . ')',
2711    );
2712    return $headers;
2713  }
2714  
2715  /**
2716   * Check if current user has a draft of this webform, and return the sid.
2717   */
2718  function _webform_fetch_draft_sid($nid, $uid) {
2719    $result = db_query("SELECT * FROM {webform_submissions} WHERE nid = %d AND uid = %d AND is_draft = 1 ORDER BY submitted DESC", $nid, $uid);
2720    $row = db_fetch_array($result);
2721    if (isset($row['sid'])) {
2722      return (int) $row['sid'];
2723    }
2724    return FALSE;
2725  }
2726  
2727  /**
2728   * Filters all special tokens provided by webform, such as %post and %profile.
2729   *
2730   * @param $string
2731   *   The string to have its tokens replaced.
2732   * @param $node
2733   *   If replacing node-level tokens, the node for which tokens will be created.
2734   * @param $submission
2735   *   If replacing submission-level tokens, the submission for which tokens will
2736   *   be created.
2737   * @param $email
2738   *   If replacing tokens within the context of an e-mail, the Webform e-mail
2739   *   settings array.
2740   * @param $strict
2741   *   Boolean value indicating if the results should be run through check_plain.
2742   *   This is used any time the values will be output as HTML, but not in
2743   *   default values or e-mails.
2744   * @param $allow_anonymous
2745   *   Boolean value indicating if all tokens should be replaced for anonymous
2746   *   users, even if they contain sensitive user information such as %session or
2747   *   %ip_address. This is disabled by default to prevent user data from being
2748   *   preserved in the anonymous page cache and should only be used in
2749   *   non-cached situations, such as e-mails.
2750   */
2751  function _webform_filter_values($string, $node = NULL, $submission = NULL, $email = NULL, $strict = TRUE, $allow_anonymous = FALSE) {
2752    global $user;
2753    static $replacements;
2754  
2755    // Don't do any filtering if the string is empty.
2756    if (strlen(trim($string)) == 0) {
2757      return $string;
2758    }
2759  
2760    // Setup default token replacements.
2761    if (!isset($replacements)) {
2762      $replacements['unsafe'] = array();
2763      $replacements['safe']['%site'] = variable_get('site_name', 'drupal');
2764      $replacements['safe']['%date'] = format_date(time(), 'large');
2765    }
2766  
2767    // Node replacements.
2768    if (isset($node) && !array_key_exists('%nid', $replacements['safe'])) {
2769      $replacements['safe']['%nid'] = $node->nid;
2770      $replacements['safe']['%title'] = $node->title;
2771    }
2772  
2773    // Determine the display format.
2774    $format = isset($email['html']) && $email['html'] ? 'html' : 'text';
2775  
2776    // Submission replacements.
2777    if (isset($submission) && !isset($replacements['email'][$format])) {
2778      module_load_include('inc', 'webform', 'includes/webform.components');
2779  
2780      // Set the submission ID.
2781      $replacements['unsafe']['%sid'] = $submission->sid;
2782  
2783      // E-mails may be sent in two formats, keep tokens separate for each one.
2784      $replacements['email'][$format] = array();
2785  
2786      // Populate token values for each component.
2787      foreach ($submission->data as $cid => $value) {
2788        $component = $node->webform['components'][$cid];
2789  
2790        // Find by form key.
2791        $parents = webform_component_parent_keys($node, $component);
2792        $form_key = implode('][', $parents);
2793        $display_element = webform_component_invoke($component['type'], 'display', $component, $value['value'], $format);
2794  
2795        // Ensure the component is added as a property.
2796        $display_element['#webform_component'] = $component;
2797  
2798        if (empty($display_element['#parents'])) {
2799          $display_element['#parents'] = array_merge(array('submitted'), $parents);
2800        }
2801        if (empty($display_element['#id'])) {
2802          $display_element['#id'] = form_clean_id('edit-' . implode('-', $display_element['#parents']));
2803        }
2804        $replacements['email'][$format]['%email[' . $form_key . ']'] = drupal_render($display_element);
2805        $replacements['email'][$format]['%value[' . $form_key . ']'] = isset($display_element['#children']) ? $display_element['#children'] : '';
2806      }
2807  
2808      // Provide blanks for components in the webform but not in the submission.
2809      $missing_components = array_diff_key($node->webform['components'], $submission->data);
2810      foreach ($missing_components as $component) {
2811        $parents = webform_component_parent_keys($node, $component);
2812        $form_key = implode('][', $parents);
2813        $replacements['email'][$format]['%email[' . $form_key . ']'] = '';
2814        $replacements['email'][$format]['%value[' . $form_key . ']'] = '';
2815      }
2816  
2817      // Submission edit URL.
2818      $replacements['unsafe']['%submission_url'] = url('node/' . $node->nid . '/submission/' . $submission->sid, array('absolute' => TRUE));
2819    }
2820  
2821    // Token for the entire form tree for e-mails.
2822    if (isset($submission) && isset($email)) {
2823      $replacements['email'][$format]['%email_values'] = webform_submission_render($node, $submission, $email, $format);
2824    }
2825  
2826    // Provide a list of candidates for token replacement.
2827    $special_tokens = array(
2828      'safe' => array(
2829        '%get' => $_GET,
2830        '%post' => $_POST,
2831      ),
2832      'unsafe' => array(
2833        '%cookie' => $_COOKIE,
2834        '%session' => isset($_SESSION) ? $_SESSION : array(),
2835        '%request' => $_REQUEST,
2836        '%server' => $_SERVER,
2837        '%profile' => (array) $user,
2838      ),
2839    );
2840  
2841    // Replacements of global variable tokens.
2842    if (!isset($replacements['specials_set'])) {
2843      $replacements['specials_set'] = TRUE;
2844  
2845      // Load profile information if available.
2846      if ($user->uid) {
2847        $account = user_load($user->uid);
2848        $special_tokens['unsafe']['%profile'] = (array) $account;
2849      }
2850  
2851      // User replacements.
2852      if (!array_key_exists('%uid', $replacements['unsafe'])) {
2853        $replacements['unsafe']['%uid'] = !empty($user->uid) ? $user->uid : '';
2854        $replacements['unsafe']['%username'] = isset($user->name) ? $user->name : '';
2855        $replacements['unsafe']['%useremail'] = isset($user->mail) ? $user->mail : '';
2856        $replacements['unsafe']['%ip_address'] = ip_address();
2857      }
2858  
2859      // Populate the replacements array with special variables.
2860      foreach ($special_tokens as $safe_state => $tokens) {
2861        foreach ($tokens as $token => $variable) {
2862          // Safety check in case $_POST or some other global has been removed
2863          // by a naughty module, in which case $variable may be NULL.
2864          if (!is_array($variable)) {
2865            continue;
2866          }
2867  
2868          foreach ($variable as $key => $value) {
2869            // This special case for profile module dates.
2870            if ($token == '%profile' && is_array($value) && isset($value['year'])) {
2871              $replacement = webform_strtodate(webform_date_format(), $value['month'] . '/' . $value['day'] . '/' . $value['year'], 'UTC');
2872            }
2873            else {
2874              // Checking for complex types (arrays and objects) fails here with
2875              // incomplete objects (see http://php.net/is_object), so we check
2876              // for simple types instead.
2877              $replacement = (is_string($value) || is_bool($value) || is_numeric($value)) ? $value : '';
2878            }
2879            $replacements[$safe_state][$token . '[' . $key . ']'] = $replacement;
2880          }
2881        }
2882      }
2883    }
2884  
2885    // Make a copy of the replacements so we don't affect the static version.
2886    $safe_replacements = $replacements['safe'];
2887  
2888    // Restrict replacements for anonymous users. Not all tokens can be used
2889    // because they may expose session or other private data to other users when
2890    // anonymous page caching is enabled.
2891    if ($user->uid || $allow_anonymous) {
2892      $safe_replacements += $replacements['unsafe'];
2893      if (isset($replacements['email'][$format])) {
2894        $safe_replacements += $replacements['email'][$format];
2895      }
2896    }
2897    else {
2898      foreach ($replacements['unsafe'] as $key => $value) {
2899        $safe_replacements[$key] = '';
2900      }
2901    }
2902  
2903    $find = array_keys($safe_replacements);
2904    $replace = array_values($safe_replacements);
2905    $string = str_replace($find, $replace, $string);
2906  
2907    // Clean up any unused tokens.
2908    foreach ($special_tokens as $safe_state => $tokens) {
2909      foreach (array_keys($tokens) as $token) {
2910        $string = preg_replace('/\\' . $token . '\[\w+\]/', '', $string);
2911      }
2912    }
2913  
2914    return $strict ? _webform_filter_xss($string) : $string;
2915  }
2916  
2917  /**
2918   * Filters all special tokens provided by webform, and allows basic layout in descriptions.
2919   */
2920  function _webform_filter_descriptions($string, $node = NULL, $submission = NULL) {
2921    return strlen($string) == 0 ? '' : check_markup(_webform_filter_values($string, $node, $submission, NULL, FALSE));
2922  }
2923  
2924  /**
2925   * Filter labels for display by running through XSS checks.
2926   */
2927  function _webform_filter_xss($string) {
2928    static $allowed_tags;
2929    $allowed_tags = isset($allowed_tags) ? $allowed_tags : webform_variable_get('webform_allowed_tags');
2930    return filter_xss($string, $allowed_tags);
2931  }
2932  
2933  
2934  /**
2935   * Utility function to ensure that a webform record exists in the database.
2936   *
2937   * @param $node
2938   *   The node object to check if a database entry exists.
2939   * @return
2940   *   This function should always return TRUE if no errors were encountered,
2941   *   ensuring that a webform table row has been created. Will return FALSE if
2942   *   a record does not exist and a new one could not be created.
2943   */
2944  function webform_ensure_record(&$node) {
2945    if (!$node->webform['record_exists']) {
2946      // Even though webform_node_insert() would set this property to TRUE,
2947      // we set record_exists to trigger a difference from the defaults.
2948      $node->webform['record_exists'] = TRUE;
2949      webform_node_insert($node);
2950    }
2951    return $node->webform['record_exists'];
2952  }
2953  
2954  /**
2955   * Utility function to check if a webform record is necessary in the database.
2956   *
2957   * If the node is no longer using any webform settings, this function will
2958   * delete the settings from the webform table. Note that this function will NOT
2959   * delete rows from the webform table if the node-type is exclusively used for
2960   * webforms (per the "webform_node_types_primary" variable).
2961   *
2962   * @param $node
2963   *   The node object to check if a database entry is still required.
2964   * @return
2965   *   Returns TRUE if the webform still has a record in the database. Returns
2966   *   FALSE if the webform does not have a record or if the previously existing
2967   *   record was just deleted.
2968   */
2969  function webform_check_record(&$node) {
2970    $webform = $node->webform;
2971    $webform['record_exists'] = FALSE;
2972    unset($webform['nid']);
2973  
2974    // Don't include empty values in the comparison, this makes it so modules that
2975    // extend Webform with empty defaults won't affect cleanup of rows.
2976    $webform = array_filter($webform);
2977    $defaults = array_filter(webform_node_defaults());
2978    if ($webform == $defaults && !in_array($node->type, webform_variable_get('webform_node_types_primary'))) {
2979      webform_node_delete($node);
2980      $node->webform = webform_node_defaults();
2981    }
2982    return $node->webform['record_exists'];
2983  }
2984  
2985  /**
2986   * Given a form_key and a list of form_key parents, determine the cid.
2987   *
2988   * @param $node
2989   *   A fully loaded node object.
2990   * @param $form_key
2991   *   The form key for which we're finding a cid.
2992   * @param $parent
2993   *   The cid of the parent component.
2994   */
2995  function webform_get_cid(&$node, $form_key, $pid) {
2996    foreach ($node->webform['components'] as $cid => $component) {
2997      if ($component['form_key'] == $form_key && $component['pid'] == $pid) {
2998        return $cid;
2999      }
3000    }
3001  }
3002  
3003  /**
3004   * Retreive a Drupal variable with the appropriate default value.
3005   */
3006  function webform_variable_get($variable) {
3007    switch ($variable) {
3008      case 'webform_allowed_tags':
3009        $result = variable_get('webform_allowed_tags', array('a', 'em', 'strong', 'code', 'img'));
3010        break;
3011      case 'webform_default_from_name':
3012        $result = variable_get('webform_default_from_name', variable_get('site_name', ''));
3013        break;
3014      case 'webform_default_from_address':
3015        $result = variable_get('webform_default_from_address', variable_get('site_mail', ini_get('sendmail_from')));
3016        break;
3017      case 'webform_default_subject':
3018        $result = variable_get('webform_default_subject', t('Form submission from: %title'));
3019        break;
3020      case 'webform_node_types':
3021        $result = variable_get('webform_node_types', array('webform'));
3022        break;
3023      case 'webform_node_types_primary':
3024        $result = variable_get('webform_node_types_primary', array('webform'));
3025        break;
3026    }
3027    return $result;
3028  }
3029  
3030  function theme_webform_token_help($groups = array()) {
3031    $groups = empty($groups) ? array('basic', 'node', 'special') : $groups;
3032  
3033    static $tokens = array();
3034  
3035    if (empty($tokens)) {
3036      $tokens['basic'] = array(
3037        'title' => t('Basic tokens'),
3038        'tokens' => array(
3039          '%username' => t('The name of the user if logged in. Blank for anonymous users.'),
3040          '%useremail' => t('The e-mail address of the user if logged in. Blank for anonymous users.'),
3041          '%ip_address' => t('The IP address of the user.'),
3042          '%site' => t('The name of the site (i.e. %site_name)', array('%site_name' => variable_get('site_name', ''))),
3043          '%date' => t('The current date, formatted according to the site settings.'),
3044        ),
3045      );
3046  
3047      $tokens['node'] = array(
3048        'title' => t('Node tokens'),
3049        'tokens' => array(
3050          '%nid' => t('The node ID.'),
3051          '%title' => t('The node title.'),
3052        ),
3053      );
3054  
3055      $tokens['special'] = array(
3056        'title' => t('Special tokens'),
3057        'tokens' => array(
3058          '%profile[' . t('key') . ']' => t('Any user profile field or value, such as %profile[name] or %profile[profile_first_name]'),
3059          '%get[' . t('key') . ']' => t('Tokens may be populated from the URL by creating URLs of the form http://example.com/my-form?foo=bar. Using the token %get[foo] would print "bar".'),
3060          '%post[' . t('key') . ']' => t('Tokens may also be populated from POST values that are submitted by forms.'),
3061        ),
3062        'description' => t('In addition to %get and %post, the following super tokens may be used, though only with logged-in users: %server, %cookie, and %request. For example %server[HTTP_USER_AGENT] or %session[id].'),
3063      );
3064  
3065      $tokens['email'] = array(
3066        'title' => t('E-mail tokens'),
3067        'tokens' => array(
3068          '%email_values' => t('All included components in a hierarchical structure.'),
3069          '%email[' . t('key') . '] ' => t('A formatted value and field label. Elements may be accessed such as <em>%email[fieldset_a][key_b]</em>. Do not include quotes.'),
3070          '%submission_url' => t('The URL for viewing the completed submission.'),
3071        ),
3072      );
3073  
3074      $tokens['submission'] = array(
3075        'title' => t('Submission tokens'),
3076        'tokens' => array(
3077          '%sid' => t('The unique submission ID.'),
3078          '%value[key]' => t('A value without additional formatting. Elements may be accessed such as <em>%value[fieldset_a][key_b]</em>. Do not include quotes.'),
3079        ),
3080      );
3081    }
3082  
3083    $output = '';
3084    $output .= '<p>' . t('You may use special tokens in this field that will be replaced with dynamic values.') . '</p>';
3085  
3086    foreach ($tokens as $group_name => $group) {
3087      if (!is_array($groups) || in_array($group_name, $groups)) {
3088        $items = array();
3089        foreach ($group['tokens'] as $token => $token_description) {
3090          $items[] = $token . ' - ' . $token_description;
3091        }
3092        $output .= theme('item_list', $items, $group['title']);
3093        $output .= isset($group['description']) ? '<p>' . $group['description']  . '</p>' : '';
3094      }
3095    }
3096  
3097    $fieldset = array(
3098      '#title' => t('Token values'),
3099      '#type' => 'fieldset',
3100      '#collapsible' => TRUE,
3101      '#collapsed' => TRUE,
3102      '#children' => '<div>' . $output . '</div>',
3103    );
3104    return theme('fieldset', $fieldset);
3105  }
3106  
3107  function _webform_safe_name($name) {
3108    $new = trim($name);
3109  
3110    // If transliteration is available, use it to convert names to ASCII.
3111    if (function_exists('transliteration_get')) {
3112      $new = transliteration_get($new, '');
3113      $new = str_replace(array(' ', '-', '/'), array('_', '_', '_'), $new);
3114    }
3115    else {
3116      $new = str_replace(
3117        array(' ', '-', '/', '€', 'ƒ', 'Š', 'Ž', 'š', 'ž', 'Ÿ', '¢', '¥', 'µ', 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'à', 'á', 'â', 'ã', 'ä', 'å', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'ÿ', 'Œ',  'œ',  'Æ',  'Ð',  'Þ',  'ß',  'æ',  'ð',  'þ'),
3118        array('_', '_', '_', 'E', 'f', 'S', 'Z', 's', 'z', 'Y', 'c', 'Y', 'u', 'A', 'A', 'A', 'A', 'A', 'A', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', 'N', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'Y', 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'n', 'o', 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u', 'y', 'y', 'OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th'),
3119        $new);
3120    }
3121  
3122    $new = drupal_strtolower($new);
3123    $new = preg_replace('/[^a-z0-9_]/', '', $new);
3124    return $new;
3125  }
3126  
3127  /**
3128   * Given an email address and a name, format an e-mail address.
3129   *
3130   * @param $address
3131   *   The e-mail address.
3132   * @param $name
3133   *   The name to be used in the formatted address.
3134   * @param $node
3135   *   The webform node if replacements will be done.
3136   * @param $submission
3137   *   The webform submission values if replacements will be done.
3138   * @param $encode
3139   *   Encode the text for use in an e-mail.
3140   * @param $single
3141   *   Force a single value to be returned, even if a component expands to
3142   *   multiple addresses. This is useful to ensure a single e-mail will be
3143   *   returned for the "From" address.
3144   * @param $format
3145   *   The e-mail format, defaults to the site-wide setting. May be either "short"
3146   *   or "long".
3147   */
3148  function webform_format_email_address($address, $name, $node = NULL, $submission = NULL, $encode = TRUE, $single = TRUE, $format = NULL) {
3149    if (!isset($format)) {
3150      $format = variable_get('webform_email_address_format', 'long');
3151    }
3152  
3153    if ($name == 'default') {
3154      $name = webform_variable_get('webform_default_from_name');
3155    }
3156    elseif (is_numeric($name) && isset($node->webform['components'][$name])) {
3157      if (isset($submission->data[$name]['value'])) {
3158        $name = $submission->data[$name]['value'];
3159      }
3160      else {
3161        $name = t('Value of !component', array('!component' => $node->webform['components'][$name]['name']));
3162      }
3163    }
3164  
3165    if ($address == 'default') {
3166      $address = webform_variable_get('webform_default_from_address');
3167    }
3168    elseif (is_numeric($address) && isset($node->webform['components'][$address])) {
3169      if (isset($submission->data[$address]['value'])) {
3170        $values = $submission->data[$address]['value'];;
3171        $address = array();
3172        foreach ($values as $value) {
3173          $address = array_merge($address, explode(',', $value));
3174        }
3175      }
3176      else {
3177        $address = t('Value of "!component"', array('!component' => $node->webform['components'][$address]['name']));
3178      }
3179    }
3180  
3181    // Convert arrays into a single value for From values.
3182    if ($single) {
3183      $address = is_array($address) ? reset($address) : $address;
3184      $name = is_array($name) ? reset($name) : $name;
3185    }
3186  
3187    // Address may be an array if a component value was used on checkboxes.
3188    if (is_array($address)) {
3189      foreach ($address as $key => $individual_address) {
3190        $address[$key] = _webform_filter_values($individual_address, $node, $submission, NULL, FALSE, TRUE);
3191      }
3192    }
3193    else {
3194      $address = _webform_filter_values($address, $node, $submission, NULL, FALSE, TRUE);
3195    }
3196  
3197    if ($format == 'long' && !empty($name)) {
3198      $name = _webform_filter_values($name, $node, $submission, NULL, FALSE, TRUE);
3199      if ($encode) {
3200        $name = mime_header_encode($name);
3201      }
3202      return '"' . $name . '" <' . $address . '>';
3203    }
3204    else {
3205      return $address;
3206    }
3207  }
3208  
3209  /**
3210   * Given an email subject, format it with any needed replacements.
3211   */
3212  function webform_format_email_subject($subject, $node = NULL, $submission = NULL) {
3213    if ($subject == 'default') {
3214      $subject = webform_variable_get('webform_default_subject');
3215    }
3216    elseif (is_numeric($subject) && isset($node->webform['components'][$subject])) {
3217      $component = $node->webform['components'][$subject];
3218      if (isset($submission->data[$subject]['value'])) {
3219        $display_function = '_webform_display_' . $component['type'];
3220        $value = $submission->data[$subject]['value'];
3221  
3222        // Convert the value to a clean text representation if possible.
3223        if (function_exists($display_function)) {
3224          $display = $display_function($component, $value, 'text');
3225          $display['#theme_wrappers'] = array();
3226          $subject = str_replace("\n", ' ', drupal_render($display));
3227        }
3228        else {
3229          $subject = $value;
3230        }
3231      }
3232      else {
3233        $subject = t('Value of "!component"', array('!component' => $component['name']));
3234      }
3235    }
3236  
3237    // Convert arrays to strings (may happen if checkboxes are used as the value).
3238    if (is_array($subject)) {
3239      $subject = reset($subject);
3240    }
3241  
3242    return _webform_filter_values($subject, $node, $submission, NULL, FALSE, TRUE);
3243  }
3244  
3245  /**
3246   * Convert an array of components into a tree
3247   */
3248  function _webform_components_tree_build($src, &$tree, $parent, &$page_count) {
3249    foreach ($src as $cid => $component) {
3250      if ($component['pid'] == $parent) {
3251        _webform_components_tree_build($src, $component, $cid, $page_count);
3252        if ($component['type'] == 'pagebreak') {
3253          $page_count++;
3254        }
3255        $tree['children'][$cid] = $component;
3256        $tree['children'][$cid]['page_num'] = $page_count;
3257      }
3258    }
3259    return $tree;
3260  }
3261  
3262  /**
3263   * Flatten a component tree into a flat list.
3264   */
3265  function _webform_components_tree_flatten($tree) {
3266    $components = array();
3267    foreach ($tree as $cid => $component) {
3268      if (isset($component['children'])) {
3269        unset($component['children']);
3270        $components[$cid] = $component;
3271        // array_merge() can't be used here because the keys are numeric.
3272        $children = _webform_components_tree_flatten($tree[$cid]['children']);
3273        foreach ($children as $ccid => $ccomponent) {
3274          $components[$ccid] = $ccomponent;
3275        }
3276      }
3277      else {
3278        $components[$cid] = $component;
3279      }
3280    }
3281    return $components;
3282  }
3283  
3284  /**
3285   * Helper for the uasort in webform_tree_sort()
3286   */
3287  function _webform_components_sort($a, $b) {
3288    if ($a['weight'] == $b['weight']) {
3289      return strcasecmp($a['name'], $b['name']);
3290    }
3291    return ($a['weight'] < $b['weight']) ? -1 : 1;
3292  }
3293  
3294  /**
3295   * Sort each level of a component tree by weight and name
3296   */
3297  function _webform_components_tree_sort($tree) {
3298    if (isset($tree['children']) && is_array($tree['children'])) {
3299      $children = array();
3300      uasort($tree['children'], '_webform_components_sort');
3301      foreach ($tree['children'] as $cid => $component) {
3302        $children[$cid] = _webform_components_tree_sort($component);
3303      }
3304      $tree['children'] = $children;
3305    }
3306    return $tree;
3307  }
3308  
3309  /**
3310   * Get a list of all available component definitions.
3311   */
3312  function webform_components($include_disabled = FALSE, $reset = FALSE) {
3313    static $components, $disabled;
3314  
3315    if (!isset($components) || $reset) {
3316      $components = array();
3317      $disabled = array_flip(variable_get('webform_disabled_components', array()));
3318      foreach (module_implements('webform_component_info') as $module) {
3319        $module_components = module_invoke($module, 'webform_component_info');
3320        foreach ($module_components as $type => $info) {
3321          $module_components[$type]['module'] = $module;
3322          $module_components[$type]['enabled'] = !array_key_exists($type, $disabled);
3323        }
3324        $components += $module_components;
3325      }
3326      drupal_alter('webform_component_info', $components);
3327      ksort($components);
3328    }
3329  
3330    return $include_disabled ? $components : array_diff_key($components, $disabled);
3331  }
3332  
3333  /**
3334   * Build a list of components suitable for use as select list options.
3335   */
3336  function webform_component_options($include_disabled = FALSE) {
3337    $component_info = webform_components($include_disabled);
3338    $options = array();
3339    foreach ($component_info as $type => $info) {
3340      $options[$type] = $info['label'];
3341    }
3342    return $options;
3343  }
3344  
3345  /**
3346   * Load a component file into memory.
3347   *
3348   * @param $component_type
3349   *   The string machine name of a component.
3350   */
3351  function webform_component_include($component_type) {
3352    static $included = array();
3353  
3354    // No need to load components that have already been added once.
3355    if (!isset($included[$component_type])) {
3356      $components = webform_components(TRUE);
3357      $included[$component_type] = TRUE;
3358  
3359      if (($info = $components[$component_type]) && isset($info['file'])) {
3360        $pathinfo = pathinfo($info['file']);
3361        $basename = basename($pathinfo['basename'], '.' . $pathinfo['extension']);
3362        $path = (!empty($pathinfo['dirname']) ? $pathinfo['dirname'] . '/' : '') . $basename;
3363        module_load_include($pathinfo['extension'], $info['module'], $path);
3364      }
3365    }
3366  }
3367  
3368  /**
3369   * Invoke a component callback.
3370   *
3371   * @param $type
3372   *   The component type as a string.
3373   * @param $callback
3374   *   The callback to execute.
3375   * @param ...
3376   *   Any additional parameters required by the $callback.
3377   */
3378  function webform_component_invoke($type, $callback) {
3379    $args = func_get_args();
3380    $type = array_shift($args);
3381    $callback = array_shift($args);
3382    $function = '_webform_' . $callback . '_' . $type;
3383    webform_component_include($type);
3384    if (function_exists($function)) {
3385      return call_user_func_array($function, $args);
3386    }
3387  }
3388  
3389  /**
3390   * Check if a component implements a particular hook.
3391   *
3392   * @param $type
3393   *   The component type as a string.
3394   * @param $callback
3395   *   The callback to check.
3396   */
3397  function webform_component_implements($type, $callback) {
3398    $function = '_webform_' . $callback . '_' . $type;
3399    webform_component_include($type);
3400    return function_exists($function);
3401  }
3402  
3403  /**
3404   * Disable the Drupal page cache.
3405   */
3406  function webform_disable_page_cache() {
3407    // PressFlow and Drupal 7 method.
3408    if (function_exists('drupal_page_is_cacheable')) {
3409      drupal_page_is_cacheable(FALSE);
3410    }
3411    // Drupal 6 hack to disable page cache.
3412    else {
3413      $GLOBALS['conf']['cache'] = CACHE_DISABLED;
3414    }
3415  }
3416  
3417  /**
3418   * Set the necessary breadcrumb for the page we are on.
3419   */
3420  function webform_set_breadcrumb($node, $submission = NULL) {
3421    $breadcrumb = drupal_get_breadcrumb();
3422  
3423    if (isset($node)) {
3424      $webform_breadcrumb = array();
3425      $webform_breadcrumb[] = array_shift($breadcrumb);
3426      $webform_breadcrumb[] = l($node->title, 'node/' . $node->nid);
3427      if (isset($submission)) {
3428        $last_link = array_shift($breadcrumb);
3429        $webform_breadcrumb[] = l(t('Submissions'), 'node/' . $node->nid . '/submissions');
3430        if (isset($last_link)) {
3431          $webform_breadcrumb[] = $last_link;
3432        }
3433      }
3434      $breadcrumb = $webform_breadcrumb;
3435    }
3436  
3437    drupal_set_breadcrumb($breadcrumb);
3438  }
3439  
3440  /**
3441   * Convert an ISO 8601 date or time into an array.
3442   *
3443   * This converts full format dates or times. Either a date or time may be
3444   * provided, in which case only those portions will be returned. Dashes and
3445   * colons must be used, never implied.
3446   *
3447   * Formats:
3448   * Dates: YYYY-MM-DD
3449   * Times: HH:MM:SS
3450   * Datetimes: YYYY-MM-DDTHH:MM:SS
3451   *
3452   * @param $string
3453   *   An ISO 8601 date, time, or datetime.
3454   * @param $type
3455   *   If wanting only specific fields back, specify either "date" or "time".
3456   *   Leaving empty will return an array with both date and time keys, even if
3457   *   some are empty. Returns an array with the following keys:
3458   *   - year
3459   *   - month
3460   *   - day
3461   *   - hour (in 24hr notation)
3462   *   - minute
3463   *   - second
3464   */
3465  function webform_date_array($string, $type = NULL) {
3466    $pattern = '/((\d{4}?)-(\d{2}?)-(\d{2}?))?(T?(\d{2}?):(\d{2}?):(\d{2}?))?/';
3467    $matches = array();
3468    preg_match($pattern, $string, $matches);
3469    $matches += array_fill(0, 9, '');
3470  
3471    $return = array();
3472  
3473    // Check for a date string.
3474    if ($type == 'date' || !isset($type)) {
3475      $return['year'] = $matches[2] !== '' ? (int) $matches[2] : '';
3476      $return['month'] = $matches[3] !== '' ? (int) $matches[3] : '';
3477      $return['day'] = $matches[4] !== '' ? (int) $matches[4] : '';
3478    }
3479  
3480    // Check for a time string.
3481    if ($type == 'time' || !isset($type)) {
3482      $return['hour'] = $matches[6] !== '' ? (int) $matches[6] : '';
3483      $return['minute'] = $matches[7] !== '' ? (int) $matches[7] : '';
3484      $return['second'] = $matches[8] !== '' ? (int) $matches[8] : '';
3485    }
3486  
3487    return $return;
3488  }
3489  
3490  /**
3491   * Convert an array of a date or time into an ISO 8601 compatible string.
3492   *
3493   * @param $array
3494   *   The array to convert to a date or time string.
3495   * @param $type
3496   *   If wanting a specific string format back specify either "date" or "time".
3497   *   Otherwise a full ISO 8601 date and time string will be returned.
3498   */
3499  function webform_date_string($array, $type = NULL) {
3500    $string = '';
3501  
3502    if ($type == 'date' || !isset($type)) {
3503      $string .= empty($array['year']) ? '0000' : sprintf('%04d', $array['year']);
3504      $string .= '-';
3505      $string .= empty($array['month']) ? '00' : sprintf('%02d', $array['month']);
3506      $string .= '-';
3507      $string .= empty($array['day']) ? '00' : sprintf('%02d', $array['day']);
3508    }
3509  
3510    if (!isset($type)) {
3511      $string .= 'T';
3512    }
3513  
3514    if ($type == 'time' || !isset($type)) {
3515      $string .= empty($array['hour']) ? '00' :  sprintf('%02d', $array['hour']);
3516      $string .= ':';
3517      $string .= empty($array['minute']) ? '00' :  sprintf('%02d', $array['minute']);
3518      $string .= ':';
3519      $string .= empty($array['second']) ? '00' :  sprintf('%02d', $array['second']);
3520    }
3521  
3522    return $string;
3523  }
3524  
3525  /**
3526   * Get a date format according to the site settings.
3527   *
3528   * @param $size
3529   *   A choice of 'short', 'medium', or 'long' date formats.
3530   */
3531  function webform_date_format($size = 'medium') {
3532      // Format date according to site's given format.
3533      $format = variable_get('date_format_' . $size, 'D, m/d/Y - H:i');
3534      $time = 'aABgGhHisueIOPTZ';
3535      $day_of_week = 'Dlw';
3536      $special = ',-: ';
3537      $date_format = trim($format, $time . $day_of_week . $special);
3538  
3539      // Ensure that a day, month, and year value are present. Use a default
3540      // format if all the values are not found.
3541      if (!preg_match('/[dj]/', $date_format) || !preg_match('/[FmMn]/', $date_format) || !preg_match('/[oYy]/', $date_format)) {
3542        $date_format = 'm/d/Y';
3543      }
3544  
3545      return $date_format;
3546  }
3547  
3548  /**
3549   * Return a date in the desired format taking into consideration user timezones.
3550   */
3551  function webform_strtodate($format, $string, $timezone_name = NULL) {
3552    // Adjust the time based on the user or site timezone.
3553    // The "timezone_name" variable is provided by DateAPI in Drupal 6.
3554    if (variable_get('configurable_timezones', 1) && $timezone_name == 'user') {
3555      $timezone_name = isset($GLOBALS['user']->timezone_name) ? $GLOBALS['user']->timezone_name : NULL;
3556    }
3557    // If the timezone is still empty or not set, use the site timezone.
3558    if (empty($timezone_name) || $timezone_name == 'user') {
3559      $timezone_name = variable_get('date_default_timezone_name', NULL);
3560    }
3561  
3562    if (!empty($timezone_name) && class_exists('DateTimeZone')) {
3563      // Suppress errors if encountered during string conversion. Exceptions are
3564      // only supported for DateTime in PHP 5.3 and higher.
3565      try {
3566        @$timezone = new DateTimeZone($timezone_name);
3567        @$datetime = new DateTime($string, $timezone);
3568        return @$datetime->format($format);
3569      }
3570      catch (Exception $e) {
3571        return '';
3572      }
3573    }
3574    else {
3575      return date($format, strtotime($string));
3576    }
3577  }
3578  
3579  /**
3580   * Get a timestamp in GMT time, ensuring timezone accuracy.
3581   */
3582  function webform_strtotime($date) {
3583    $current_tz = date_default_timezone_get();
3584    date_default_timezone_set('UTC');
3585    $timestamp = strtotime($date);
3586    date_default_timezone_set($current_tz);
3587    return $timestamp;
3588  }
3589  
3590  /**
3591   * Wrapper function for tt() if i18nstrings enabled.
3592   */
3593  function webform_tt($name, $string, $langcode = NULL, $update = FALSE) {
3594    if (function_exists('tt')) {
3595      return tt($name, $string, $langcode, $update);
3596    }
3597    else {
3598      return $string;
3599    }
3600  }
3601  
3602  /**
3603   * Check if any available HTML mail handlers are available for Webform to use.
3604   */
3605  function webform_email_html_capable() {
3606    // The D7 version of this function is much more capable. This function exists
3607    // as a wrapper for consistency in the rest of Webform.
3608    return module_exists('mimemail');
3609  }
3610  
3611  /**
3612   * Implements hook_views_api().
3613   */
3614  function webform_views_api() {
3615    return array(
3616      'api' => 2.0,
3617      'path' => drupal_get_path('module', 'webform') . '/views',
3618    );
3619  }
3620  
3621  /**
3622   * Implements hook_content_extra_fields().
3623   */
3624  function webform_content_extra_fields($type_name) {
3625    $extra = array();
3626    if (in_array($type_name, webform_variable_get('webform_node_types'))) {
3627      $extra['webform'] = array(
3628        'label' => t('Webform'),
3629        'description' => t('Webform client form.'),
3630        'weight' => 10,
3631      );
3632    }
3633    return $extra;
3634  }
3635  
3636  /**
3637   * Implements hook_mollom_form_list().
3638   */
3639  function webform_mollom_form_list() {
3640    $forms = array();
3641    $webform_types = webform_variable_get('webform_node_types');
3642    if (empty($webform_types)) {
3643      return $forms;
3644    }
3645  
3646    $placeholders = db_placeholders($webform_types, 'varchar');
3647    $result = db_query(db_rewrite_sql("SELECT n.nid, n.title FROM {node} n WHERE n.type IN ($placeholders)", 'n', 'nid', $webform_types), $webform_types);
3648  
3649    while ($node = db_fetch_object($result)) {
3650      $form_id = 'webform_client_form_' . $node->nid;
3651      $forms[$form_id] = array(
3652        'title' => t('@name form', array('@name' => $node->title)),
3653        'entity' => 'webform',
3654        'delete form' => 'webform_submission_delete_form',
3655      );
3656    }
3657    return $forms;
3658  }
3659  
3660  /**
3661   * Implements hook_mollom_form_info().
3662   */
3663  function webform_mollom_form_info($form_id) {
3664    module_load_include('inc', 'webform', 'includes/webform.components');
3665  
3666    $nid = drupal_substr($form_id, 20);
3667    $node = node_load($nid);
3668    $form_info = array(
3669      'title' => t('@name form', array('@name' => $node->title)),
3670      'mode' => MOLLOM_MODE_ANALYSIS,
3671      'bypass access' => array('edit all webform submissions', 'edit any webform content'),
3672      'entity' => 'webform',
3673      'elements' => array(),
3674      'mapping' => array(
3675        'post_id' => 'details][sid',
3676        'author_id' => 'details][uid',
3677      ),
3678    );
3679    // Add components as elements.
3680    // These components can be enabled for textual analysis (when not using a
3681    // CAPTCHA-only protection) in Mollom's form configuration.
3682    foreach ($node->webform['components'] as $cid => $component) {
3683      if (webform_component_feature($component['type'], 'spam_analysis')) {
3684        $parents = implode('][', webform_component_parent_keys($node, $component));
3685        $form_info['elements']['submitted][' . $parents] = check_plain(t($component['name']));
3686      }
3687    }
3688    // Assign field mappings based on webform configuration.
3689    // Since multiple emails can be configured, we iterate over all and take
3690    // over the assigned component for the field mapping in any email, unless
3691    // we already assigned one. We are not interested in administratively
3692    // configured static strings, only user-submitted values.
3693    foreach ($node->webform['emails'] as $email) {
3694      // Subject (post_title).
3695      if (!isset($form_info['mapping']['post_title'])) {
3696        $cid = $email['subject'];
3697        if (is_numeric($cid)) {
3698          $parents = implode('][', webform_component_parent_keys($node, $node->webform['components'][$cid]));
3699          $form_info['mapping']['post_title'] = 'submitted][' . $parents;
3700        }
3701      }
3702      // From name (author_name).
3703      if (!isset($form_info['mapping']['author_name'])) {
3704        $cid = $email['from_name'];
3705        if (is_numeric($cid)) {
3706          $parents = implode('][', webform_component_parent_keys($node, $node->webform['components'][$cid]));
3707          $form_info['mapping']['author_name'] = 'submitted][' . $parents;
3708        }
3709      }
3710      // From address (author_mail).
3711      if (!isset($form_info['mapping']['author_mail'])) {
3712        $cid = $email['from_address'];
3713        if (is_numeric($cid)) {
3714          $parents = implode('][', webform_component_parent_keys($node, $node->webform['components'][$cid]));
3715          $form_info['mapping']['author_mail'] = 'submitted][' . $parents;
3716        }
3717      }
3718    }
3719  
3720    return $form_info;
3721  }


Generated: Mon Jul 9 18:01:44 2012 Cross-referenced by PHPXref 0.7