[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

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

   1  <?php
   2  // $Id: webform.module,v 1.196.2.58 2010/10/01 00:59:32 quicksketch Exp $
   3  
   4  /**
   5   * This module provides a simple way to create forms and questionnaires.
   6   *
   7   * The initial development of this module was sponsered by ÅF Industri AB, Open
   8   * Source City and Karlstad University Library. Continued development sponsored
   9   * by Lullabot.
  10   *
  11   * @author Nathan Haug <nate@lullabot.com>
  12   */
  13  
  14  /**
  15   * Implementation of hook_help().
  16   */
  17  function webform_help($section = 'admin/help#webform', $arg = NULL) {
  18    $output = '';
  19    switch ($section) {
  20      case 'admin/settings/webform':
  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    }
  69  
  70    return $output;
  71  }
  72  
  73  /**
  74   * Implementation of hook_menu().
  75   */
  76  function webform_menu() {
  77    $items = array();
  78  
  79    // Submissions listing.
  80    $items['admin/content/webform'] = array(
  81      'title' => 'Webforms',
  82      'page callback' => 'webform_admin_content',
  83      'access callback' => 'user_access',
  84      'access arguments' => array('access all webform results'),
  85      'description' => 'View and edit all the available webforms on your site.',
  86      'file' => 'includes/webform.admin.inc',
  87      'type' => MENU_NORMAL_ITEM,
  88    );
  89  
  90    // Admin Settings.
  91    $items['admin/settings/webform'] = array(
  92      'title' => 'Webform settings',
  93      'page callback' => 'drupal_get_form',
  94      'page arguments' => array('webform_admin_settings'),
  95      'access callback' => 'user_access',
  96      'access arguments' => array('administer site configuration'),
  97      'description' => 'Global configuration of webform functionality.',
  98      'file' => 'includes/webform.admin.inc',
  99      'type' => MENU_NORMAL_ITEM,
 100    );
 101  
 102    // Node page tabs.
 103    $items['node/%webform_menu/done'] = array(
 104      'title' => 'Webform confirmation',
 105      'page callback' => '_webform_confirmation',
 106      'page arguments' => array(1),
 107      'access callback' => 'node_access',
 108      'access arguments' => array('view', 1),
 109      'type' => MENU_CALLBACK,
 110    );
 111    $items['node/%webform_menu/webform'] = array(
 112      'title' => 'Webform',
 113      'page callback' => 'webform_components_page',
 114      'page arguments' => array(1),
 115      'access callback' => 'node_access',
 116      'access arguments' => array('update', 1),
 117      'file' => 'includes/webform.components.inc',
 118      'weight' => 1,
 119      'type' => MENU_LOCAL_TASK,
 120    );
 121    $items['node/%webform_menu/webform/components'] = array(
 122      'title' => 'Form components',
 123      'page callback' => 'webform_components_page',
 124      'page arguments' => array(1),
 125      'access callback' => 'node_access',
 126      'access arguments' => array('update', 1),
 127      'file' => 'includes/webform.components.inc',
 128      'weight' => 0,
 129      'type' => MENU_DEFAULT_LOCAL_TASK,
 130    );
 131    $items['node/%webform_menu/webform/configure'] = array(
 132      'title' => 'Form settings',
 133      'page callback' => 'drupal_get_form',
 134      'page arguments' => array('webform_configure_form', 1),
 135      'access callback' => 'node_access',
 136      'access arguments' => array('update', 1),
 137      'file' => 'includes/webform.pages.inc',
 138      'weight' => 2,
 139      'type' => MENU_LOCAL_TASK,
 140    );
 141  
 142    // Node e-mail forms.
 143    $items['node/%webform_menu/webform/emails'] = array(
 144      'title' => 'E-mails',
 145      'page callback' => 'drupal_get_form',
 146      'page arguments' => array('webform_emails_form', 1),
 147      'access callback' => 'node_access',
 148      'access arguments' => array('update', 1),
 149      'file' => 'includes/webform.emails.inc',
 150      'weight' => 1,
 151      'type' => MENU_LOCAL_TASK,
 152    );
 153    $items['node/%webform_menu/webform/emails/%webform_menu_email'] = array(
 154      'title' => 'Edit e-mail settings',
 155      'load arguments' => array(1),
 156      'page arguments' => array('webform_email_edit_form', 1, 4),
 157      'access callback' => 'node_access',
 158      'access arguments' => array('update', 1),
 159      'file' => 'includes/webform.emails.inc',
 160      'type' => MENU_LOCAL_TASK,
 161    );
 162    $items['node/%webform_menu/webform/emails/%webform_menu_email/delete'] = array(
 163      'title' => 'Delete e-mail settings',
 164      'load arguments' => array(1),
 165      'page arguments' => array('webform_email_delete_form', 1, 4),
 166      'access callback' => 'node_access',
 167      'access arguments' => array('update', 1),
 168      'type' => MENU_LOCAL_TASK,
 169    );
 170  
 171    // Node component forms.
 172    $items['node/%webform_menu/webform/components/%webform_menu_component'] = array(
 173      'load arguments' => array(1, 5),
 174      'page callback' => 'drupal_get_form',
 175      'page arguments' => array('webform_component_edit_form', 1, 4, FALSE),
 176      'access callback' => 'node_access',
 177      'access arguments' => array('update', 1),
 178      'type' => MENU_LOCAL_TASK,
 179    );
 180    $items['node/%webform_menu/webform/components/%webform_menu_component/clone'] = array(
 181      'load arguments' => array(1, 5),
 182      'page callback' => 'drupal_get_form',
 183      'page arguments' => array('webform_component_edit_form', 1, 4, TRUE),
 184      'access callback' => 'node_access',
 185      'access arguments' => array('update', 1),
 186      'type' => MENU_LOCAL_TASK,
 187    );
 188    $items['node/%webform_menu/webform/components/%webform_menu_component/delete'] = array(
 189      'load arguments' => array(1, 5),
 190      'page callback' => 'drupal_get_form',
 191      'page arguments' => array('webform_component_delete_form', 1, 4),
 192      'access callback' => 'node_access',
 193      'access arguments' => array('update', 1),
 194      'type' => MENU_LOCAL_TASK,
 195    );
 196  
 197    // AJAX callback for loading select list options.
 198    $items['webform/ajax/options/%webform_menu'] = array(
 199      'load arguments' => array(3),
 200      'page callback' => 'webform_select_options_ajax',
 201      'access callback' => 'node_access',
 202      'access arguments' => array('update', 3),
 203      'file' => 'components/select.inc',
 204      'type' => MENU_CALLBACK,
 205    );
 206  
 207    // Node webform results.
 208    $items['node/%webform_menu/webform-results'] = array(
 209      'title' => 'Results',
 210      'page callback' => 'webform_results_submissions',
 211      'page arguments' => array(1, FALSE, '50'),
 212      'access callback' => 'webform_results_access',
 213      'access arguments' => array(1),
 214      'file' => 'includes/webform.report.inc',
 215      'weight' => 2,
 216      'type' => MENU_LOCAL_TASK,
 217    );
 218    $items['node/%webform_menu/webform-results/submissions'] = array(
 219      'title' => 'Submissions',
 220      'page callback' => 'webform_results_submissions',
 221      'page arguments' => array(1, FALSE, '50'),
 222      'access callback' => 'webform_results_access',
 223      'access arguments' => array(1),
 224      'file' => 'includes/webform.report.inc',
 225      'weight' => 4,
 226      'type' => MENU_DEFAULT_LOCAL_TASK,
 227    );
 228    $items['node/%webform_menu/webform-results/analysis'] = array(
 229      'title' => 'Analysis',
 230      'page callback' => 'webform_results_analysis',
 231      'page arguments' => array(1),
 232      'access callback' => 'webform_results_access',
 233      'access arguments' => array(1),
 234      'file' => 'includes/webform.report.inc',
 235      'weight' => 5,
 236      'type' => MENU_LOCAL_TASK,
 237    );
 238    $items['node/%webform_menu/webform-results/analysis/%webform_menu_component'] = array(
 239      'title' => 'Analysis',
 240      'load arguments' => array(1, 4),
 241      'page callback' => 'webform_results_analysis',
 242      'page arguments' => array(1, array(), 4),
 243      'access callback' => 'webform_results_access',
 244      'access arguments' => array(1),
 245      'file' => 'includes/webform.report.inc',
 246      'type' => MENU_CALLBACK,
 247    );
 248    $items['node/%webform_menu/webform-results/table'] = array(
 249      'title' => 'Table',
 250      'page callback' => 'webform_results_table',
 251      'page arguments' => array(1, '50'),
 252      'access callback' => 'webform_results_access',
 253      'access arguments' => array(1),
 254      'file' => 'includes/webform.report.inc',
 255      'weight' => 6,
 256      'type' => MENU_LOCAL_TASK,
 257    );
 258    $items['node/%webform_menu/webform-results/download'] = array(
 259      'title' => 'Download',
 260      'page callback' => 'drupal_get_form',
 261      'page arguments' => array('webform_results_download_form', 1),
 262      'access callback' => 'webform_results_access',
 263      'access arguments' => array(1),
 264      'file' => 'includes/webform.report.inc',
 265      'weight' => 7,
 266      'type' => MENU_LOCAL_TASK,
 267    );
 268    $items['node/%webform_menu/webform-results/clear'] = array(
 269      'title' => 'Clear',
 270      'page callback' => 'drupal_get_form',
 271      'page arguments' => array('webform_results_clear_form', 1),
 272      'access callback' => 'webform_results_clear_access',
 273      'access arguments' => array(1),
 274      'file' => 'includes/webform.report.inc',
 275      'weight' => 8,
 276      'type' => MENU_LOCAL_TASK,
 277    );
 278  
 279    // Node submissions.
 280    $items['node/%webform_menu/submissions'] = array(
 281      'title' => 'Submissions',
 282      'page callback' => 'webform_results_submissions',
 283      'page arguments' => array(1, TRUE, '50'),
 284      'access callback' => 'webform_submission_access',
 285      'access arguments' => array(1, NULL, 'list'),
 286      'file' => 'includes/webform.report.inc',
 287      'type' => MENU_CALLBACK,
 288    );
 289    $items['node/%webform_menu/submission/%webform_menu_submission'] = array(
 290      'title' => 'Webform submission',
 291      'load arguments' => array(1),
 292      'page callback' => 'webform_submission_page',
 293      'page arguments' => array(1, 3, 'html'),
 294      'title callback' => 'webform_submission_title',
 295      'title arguments' => array(1, 3),
 296      'access callback' => 'webform_submission_access',
 297      'access arguments' => array(1, 3, 'view'),
 298      'file' => 'includes/webform.submissions.inc',
 299      'type' => MENU_CALLBACK,
 300    );
 301    $items['node/%webform_menu/submission/%webform_menu_submission/view'] = array(
 302      'title' => 'View',
 303      'load arguments' => array(1),
 304      'page callback' => 'webform_submission_page',
 305      'page arguments' => array(1, 3, 'html'),
 306      'access callback' => 'webform_submission_access',
 307      'access arguments' => array(1, 3, 'view'),
 308      'weight' => 0,
 309      'file' => 'includes/webform.submissions.inc',
 310      'type' => MENU_DEFAULT_LOCAL_TASK,
 311    );
 312    $items['node/%webform_menu/submission/%webform_menu_submission/edit'] = array(
 313      'title' => 'Edit',
 314      'load arguments' => array(1),
 315      'page callback' => 'webform_submission_page',
 316      'page arguments' => array(1, 3, 'form'),
 317      'access callback' => 'webform_submission_access',
 318      'access arguments' => array(1, 3, 'edit'),
 319      'weight' => 1,
 320      'file' => 'includes/webform.submissions.inc',
 321      'type' => MENU_LOCAL_TASK,
 322    );
 323    $items['node/%webform_menu/submission/%webform_menu_submission/delete'] = array(
 324      'title' => 'Delete',
 325      'load arguments' => array(1),
 326      'page callback' => 'drupal_get_form',
 327      'page arguments' => array('webform_submission_delete_form', 1, 3),
 328      'access callback' => 'webform_submission_access',
 329      'access arguments' => array(1, 3, 'delete'),
 330      'weight' => 2,
 331      'file' => 'includes/webform.submissions.inc',
 332      'type' => MENU_LOCAL_TASK,
 333    );
 334  
 335    return $items;
 336  }
 337  
 338  /**
 339   * Menu loader callback. Load a webform node if the given nid is a webform.
 340   */
 341  function webform_menu_load($nid) {
 342    if (!is_numeric($nid)) {
 343      return FALSE;
 344    }
 345    $node = node_load($nid);
 346    if (!isset($node->type) || !in_array($node->type, webform_variable_get('webform_node_types'))) {
 347      return FALSE;
 348    }
 349    return $node;
 350  }
 351  
 352  /**
 353   * Menu loader callback. Load a webform submission if the given sid is a valid.
 354   */
 355  function webform_menu_submission_load($sid, $nid) {
 356    module_load_include('inc', 'webform', 'includes/webform.submissions');
 357    $submission = webform_get_submission($nid, $sid);
 358    return empty($submission) ? FALSE : $submission;
 359  }
 360  
 361  /**
 362   * Menu loader callback. Load a webform component if the given cid is a valid.
 363   */
 364  function webform_menu_component_load($cid, $nid, $type) {
 365    module_load_include('inc', 'webform', 'includes/webform.components');
 366    if ($cid == 'new') {
 367      $components = webform_components();
 368      $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;
 369    }
 370    else {
 371      $node = node_load($nid);
 372      $component = isset($node->webform['components'][$cid]) ? $node->webform['components'][$cid] : FALSE;
 373    }
 374    if ($component) {
 375      webform_component_defaults($component);
 376    }
 377    return $component;
 378  }
 379  
 380  
 381  /**
 382   * Menu loader callback. Load a webform e-mail if the given eid is a valid.
 383   */
 384  function webform_menu_email_load($eid, $nid) {
 385    module_load_include('inc', 'webform', 'includes/webform.emails');
 386    $node = node_load($nid);
 387    $email = webform_email_load($eid, $nid);
 388    if ($eid == 'new') {
 389      if (isset($_GET['option']) && isset($_GET['email'])) {
 390        $type = $_GET['option'];
 391        if ($type == 'custom') {
 392          $email['email'] = $_GET['email'];
 393        }
 394        elseif ($type == 'component' && isset($node->webform['components'][$_GET['email']])) {
 395          $email['email'] = $_GET['email'];
 396        }
 397      }
 398    }
 399  
 400    return $email;
 401  }
 402  
 403  function webform_submission_access($node, $submission, $op = 'view', $account = NULL) {
 404    global $user;
 405    $account = isset($account) ? $account : $user;
 406  
 407    $access_all = user_access('access all webform results', $account);
 408    $access_own_submission = isset($submission) && user_access('access own webform submissions', $account) && (($account->uid && $account->uid == $submission->uid) || isset($_SESSION['webform_submission'][$submission->sid]));
 409    $access_node_submissions = user_access('access own webform results', $account) && $account->uid == $node->uid;
 410  
 411    $general_access = $access_all || $access_own_submission || $access_node_submissions;
 412  
 413    // Disable the page cache for anonymous users in this access callback,
 414    // otherwise the "Access denied" page gets cached.
 415    if (!$account->uid && user_access('access own webform submissions', $account)) {
 416      webform_disable_page_cache();
 417    }
 418  
 419    $module_access = count(array_filter(module_invoke_all('webform_submission_access', $node, $submission, $op, $account))) > 0;
 420  
 421    switch ($op) {
 422      case 'view':
 423        return $module_access || $general_access;
 424      case 'edit':
 425        return $module_access || ($general_access && (user_access('edit all webform submissions', $account) || (user_access('edit own webform submissions', $account) && $account->uid == $submission->uid)));
 426      case 'delete':
 427        return $module_access || ($general_access && (user_access('delete all webform submissions', $account) || (user_access('delete own webform submissions', $account) && $account->uid == $submission->uid)));
 428      case 'list':
 429        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);
 430    }
 431  }
 432  
 433  /**
 434   * Menu access callback. Ensure a user both access and node 'view' permission.
 435   */
 436  function webform_results_access($node, $account = NULL) {
 437    global $user;
 438    $account = isset($account) ? $account : $user;
 439  
 440    $module_access = count(array_filter(module_invoke_all('webform_results_access', $node, $account))) > 0;
 441  
 442    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));
 443  }
 444  
 445  function webform_results_clear_access($node, $account = NULL) {
 446    global $user;
 447    $account = isset($account) ? $account : $user;
 448  
 449    $module_access = count(array_filter(module_invoke_all('webform_results_clear_access', $node, $account))) > 0;
 450  
 451    return webform_results_access($node, $account) && ($module_access || user_access('delete all webform submissions', $account));
 452  }
 453  
 454  /**
 455   * Implementation of hook_init().
 456   */
 457  function webform_init() {
 458    // Use the administrative theme if set to use on content editing pages.
 459    // See system_init().
 460    if (variable_get('node_admin_theme', '0') && arg(0) == 'node' && (arg(2) == 'webform' || arg(2) == 'webform-results')) {
 461      global $custom_theme;
 462      $custom_theme = variable_get('admin_theme', '0');
 463      drupal_add_css(drupal_get_path('module', 'system') . '/admin.css', 'module');
 464  
 465      // Support for Admin module (1.x).
 466      if (function_exists('_admin_init_theme') && empty($custom_theme)) {
 467        _admin_init_theme();
 468      }
 469    }
 470  }
 471  
 472  /**
 473   * Implementation of hook_perm().
 474   */
 475  function webform_perm() {
 476    return array(
 477      'access all webform results',
 478      'access own webform results',
 479      'edit all webform submissions',
 480      'delete all webform submissions',
 481      'access own webform submissions',
 482      'edit own webform submissions',
 483      'delete own webform submissions',
 484    );
 485  }
 486  
 487  /**
 488   * Implementation of hook_theme().
 489   */
 490  function webform_theme() {
 491    $theme = array(
 492      // webform.module.
 493      'webform_view' => array(
 494        'arguments' => array('node' => NULL, 'teaser' => NULL, 'page' => NULL, 'form' => NULL, 'enabled' => NULL),
 495      ),
 496      'webform_view_messages' => array(
 497        'arguments' => array('node' => NULL, 'teaser' => NULL, 'page' => NULL, 'submission_count' => NULL, 'limit_exceeded' => NULL, 'allowed_roles' => NULL),
 498      ),
 499      'webform_form' => array(
 500        'arguments' => array('form' => NULL),
 501        'template' => 'templates/webform-form',
 502        'pattern' => 'webform_form_[0-9]+',
 503      ),
 504      'webform_advanced_submit_limit_form' => array(
 505        'arguments' => array('form' => NULL),
 506        'file' => 'includes/webform.pages.inc',
 507      ),
 508      'webform_confirmation' => array(
 509        'arguments' => array('node' => NULL, 'sid' => NULL),
 510        'template' => 'templates/webform-confirmation',
 511        'pattern' => 'webform_confirmation_[0-9]+',
 512      ),
 513      'webform_element' => array(
 514        'arguments' => array('element' => NULL, 'value' => NULL),
 515      ),
 516      'webform_element_text' => array(
 517        'arguments' => array('element' => NULL, 'value' => NULL),
 518      ),
 519      'webform_mail_message' => array(
 520        'arguments' => array('node' => NULL, 'submission' => NULL, 'email' => NULL),
 521        'template' => 'templates/webform-mail',
 522        'pattern' => 'webform_mail(_[0-9]+)?',
 523      ),
 524      'webform_mail_headers' => array(
 525        'arguments' => array('node' => NULL, 'submission' => NULL, 'email' => NULL),
 526        'pattern' => 'webform_mail_headers_[0-9]+',
 527      ),
 528      'webform_token_help' => array(
 529        'arguments' => array(),
 530      ),
 531      // webform.admin.inc.
 532      'webform_admin_settings' => array(
 533        'arguments' => array('form' => NULL),
 534        'file' => 'includes/webform.admin.inc',
 535      ),
 536      'webform_admin_content' => array(
 537        'arguments' => array('nodes' => NULL),
 538        'file' => 'includes/webform.admin.inc',
 539      ),
 540      // webform.emails.inc.
 541      'webform_emails_form' => array(
 542        'arguments' => array('form' => NULL),
 543        'file' => 'includes/webform.emails.inc',
 544      ),
 545      'webform_email_add_form' => array(
 546        'arguments' => array('form' => NULL),
 547        'file' => 'includes/webform.emails.inc',
 548      ),
 549      'webform_email_edit_form' => array(
 550        'arguments' => array('form' => NULL),
 551        'file' => 'includes/webform.emails.inc',
 552      ),
 553      // webform.components.inc.
 554      'webform_components_page' => array(
 555        'arguments' => array('node' => NULL, 'form' => NULL),
 556        'file' => 'includes/webform.components.inc',
 557      ),
 558      'webform_components_form' => array(
 559        'arguments' => array('form' => NULL),
 560        'file' => 'includes/webform.components.inc',
 561      ),
 562      'webform_component_select' => array(
 563        'arguments' => array('element' => NULL),
 564        'file' => 'includes/webform.components.inc',
 565      ),
 566      // webform.report.inc.
 567      'webform_results_per_page' => array(
 568        'arguments' => array('total_count' => NULL, 'pager_count' => NULL),
 569        'file' => 'includes/webform.report.inc',
 570      ),
 571      'webform_results_submissions_header' => array(
 572        'arguments' => array('node' => NULL),
 573        'file' => 'includes/webform.report.inc',
 574      ),
 575      'webform_results_submissions' => array(
 576        'arguments' => array('element' => NULL),
 577        'template' => 'templates/webform-results-submissions',
 578        'file' => 'includes/webform.report.inc',
 579      ),
 580      'webform_results_table_header' => array(
 581        'arguments' => array('node' => NULL),
 582        'file' => 'includes/webform.report.inc',
 583      ),
 584      'webform_results_table' => array(
 585        'arguments' => array('node' => NULL, 'components' => NULL, 'submissions' => NULL, 'node' => NULL, 'total_count' => NULL, 'pager_count' => NULL),
 586        'file' => 'includes/webform.report.inc',
 587      ),
 588      'webform_results_download_form' => array(
 589        'arguments' => array('form' => NULL),
 590        'file' => 'includes/webform.report.inc',
 591      ),
 592      'webform_results_download_select_format' => array(
 593        'arguments' => array('element' => NULL),
 594        'file' => 'includes/webform.report.inc',
 595      ),
 596      'webform_results_analysis' => array(
 597        'arguments' => array('node' => NULL, 'data' => NULL, 'sids' => array(), 'component' => NULL),
 598        'file' => 'includes/webform.report.inc',
 599      ),
 600      // webform.submissions.inc
 601      'webform_submission' => array(
 602        'arguments' => array('renderable' => NULL),
 603        'template' => 'templates/webform-submission',
 604        'pattern' => 'webform_submission_[0-9]+',
 605        'file' => 'includes/webform.submissions.inc',
 606      ),
 607      'webform_submission_page' => array(
 608        'arguments' => array('node' => NULL, 'submission' => NULL, 'submission_content' => NULL, 'submission_navigation' => NULL, 'submission_information' => NULL),
 609        'template' => 'templates/webform-submission-page',
 610        'file' => 'includes/webform.submissions.inc',
 611      ),
 612      'webform_submission_information' => array(
 613        'arguments' => array('node' => NULL, 'submission' => NULL),
 614        'template' => 'templates/webform-submission-information',
 615        'file' => 'includes/webform.submissions.inc',
 616      ),
 617      'webform_submission_navigation' => array(
 618        'arguments' => array('node' => NULL, 'submission' => NULL, 'mode' => NULL),
 619        'template' => 'templates/webform-submission-navigation',
 620        'file' => 'includes/webform.submissions.inc',
 621      ),
 622    );
 623  
 624    // Theme functions in all components.
 625    $components = webform_components(TRUE);
 626    foreach ($components as $type => $component) {
 627      if ($theme_additions = webform_component_invoke($type, 'theme')) {
 628        $theme = array_merge($theme, $theme_additions);
 629      }
 630    }
 631    return $theme;
 632  }
 633  
 634  /**
 635   * Implementation of hook_webform_component_info().
 636   */
 637  function webform_webform_component_info() {
 638    return array(
 639      'date' => array(
 640        'label' => t('Date'),
 641        'description' => t('Presents month, day, and year fields.'),
 642        'features' => array(
 643          'conditional' => FALSE,
 644        ),
 645        'file' => 'components/date.inc',
 646      ),
 647      'email' => array(
 648        'label' => t('E-mail'),
 649        'description' => t('A special textfield that accepts e-mail addresses.'),
 650        'file' => 'components/email.inc',
 651        'features' => array(
 652          'email_address' => TRUE,
 653          'spam_analysis' => TRUE,
 654        ),
 655      ),
 656      'fieldset' => array(
 657        'label' => t('Fieldset'),
 658        'description' => t('Fieldsets allow you to organize multiple fields into groups.'),
 659        'features' => array(
 660          'csv' => FALSE,
 661          'required' => FALSE,
 662          'conditional' => FALSE,
 663          'group' => TRUE,
 664        ),
 665        'file' => 'components/fieldset.inc',
 666      ),
 667      'file' => array(
 668        'label' => t('File'),
 669        'description' => t('Allow users to upload files of configurable types.'),
 670        'features' => array(
 671          'conditional' => FALSE,
 672          'attachment' => TRUE,
 673        ),
 674        'file' => 'components/file.inc',
 675      ),
 676      'grid' => array(
 677        'label' => t('Grid'),
 678        'description' => t('Allows creation of grid questions, denoted by radio buttons.'),
 679        'features' => array(
 680          'conditional' => FALSE,
 681        ),
 682        'file' => 'components/grid.inc',
 683      ),
 684      'hidden' => array(
 685        'label' => t('Hidden'),
 686        'description' => t('A field which is not visible to the user, but is recorded with the submission.'),
 687        'file' => 'components/hidden.inc',
 688        'features' => array(
 689          'required' => FALSE,
 690          'email_address' => TRUE,
 691          'email_name' => TRUE,
 692        ),
 693      ),
 694      'markup' => array(
 695        'label' => t('Markup'),
 696        'description' => t('Displays text as HTML in the form; does not render a field.'),
 697        'features' => array(
 698          'csv' => FALSE,
 699          'email' => FALSE,
 700          'required' => FALSE,
 701          'conditional' => FALSE,
 702        ),
 703        'file' => 'components/markup.inc',
 704      ),
 705      'pagebreak' => array(
 706        'label' => t('Page break'),
 707        'description' => t('Organize forms into multiple pages.'),
 708        'features' => array(
 709          'csv' => FALSE,
 710          'required' => FALSE,
 711        ),
 712        'file' => 'components/pagebreak.inc',
 713      ),
 714      'select' => array(
 715        'label' => t('Select options'),
 716        'description' => t('Allows creation of checkboxes, radio buttons, or select menus.'),
 717        'file' => 'components/select.inc',
 718        'features' => array(
 719          'email_address' => TRUE,
 720          'email_name' => TRUE,
 721        ),
 722      ),
 723      'textarea' => array(
 724        'label' => t('Textarea'),
 725        'description' => t('A large text area that allows for multiple lines of input.'),
 726        'file' => 'components/textarea.inc',
 727        'features' => array(
 728          'spam_analysis' => TRUE,
 729        ),
 730      ),
 731      'textfield' => array(
 732        'label' => t('Textfield'),
 733        'description' => t('Basic textfield type.'),
 734        'file' => 'components/textfield.inc',
 735        'features' => array(
 736          'email_name' => TRUE,
 737          'spam_analysis' => TRUE,
 738        ),
 739      ),
 740      'time' => array(
 741        'label' => t('Time'),
 742        'description' => t('Presents the user with hour and minute fields. Optional am/pm fields.'),
 743        'features' => array(
 744          'conditional' => FALSE,
 745        ),
 746        'file' => 'components/time.inc',
 747      ),
 748    );
 749  }
 750  
 751  /**
 752   * Implementation of hook_forms().
 753   *
 754   * All webform_client_form forms share the same form handler
 755   */
 756  function webform_forms($form_id) {
 757    $forms = array();
 758    if (strpos($form_id, 'webform_client_form_') === 0) {
 759      $forms[$form_id]['callback'] = 'webform_client_form';
 760    }
 761    return $forms;
 762  }
 763  
 764  /**
 765   * Implementation of hook_webform_select_options_info().
 766   */
 767  function webform_webform_select_options_info() {
 768    module_load_include('inc', 'webform', 'includes/webform.options');
 769    return _webform_options_info();
 770  }
 771  
 772  /**
 773   * Implementation of hook_file_download().
 774   *
 775   * Only allow users with view webform submissions to download files.
 776   */
 777  function webform_file_download($file) {
 778    global $user;
 779  
 780    // If the Webform directory doesn't exist, don't attempt to deliver a file.
 781    $webform_directory = file_directory_path() . '/webform/';
 782    if (!is_dir($webform_directory)) {
 783      return;
 784    }
 785  
 786    $file = file_check_location(file_directory_path() . '/' . $file, $webform_directory);
 787    if ($file && (user_access('access all webform results') || user_access('access own webform results'))) {
 788      $info = image_get_info(file_create_path($file));
 789      if (isset($info['mime_type'])) {
 790        $headers = array('Content-type: ' . $info['mime_type']);
 791      }
 792      else {
 793        $headers = array(
 794          'Content-type: force-download',
 795          'Content-disposition: attachment',
 796        );
 797      }
 798      return $headers;
 799    }
 800  }
 801  
 802  /**
 803   * Implementation of hook_node_type().
 804   */
 805  function webform_node_type($op, $info) {
 806    $webform_types = webform_variable_get('webform_node_types');
 807    $affected_type = isset($info->old_type) ? $info->old_type : $info->type;
 808    $key = array_search($affected_type, $webform_types);
 809    if ($key !== FALSE) {
 810      if ($op == 'update') {
 811        $webform_types[$key] = $info->type;
 812      }
 813      if ($op == 'delete') {
 814        unset($webform_types[$key]);
 815      }
 816      variable_set('webform_node_types', $webform_types);
 817    }
 818  }
 819  
 820  /**
 821   * Implementation of hook_nodeapi().
 822   */
 823  function webform_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
 824    if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
 825      return;
 826    }
 827  
 828    switch ($op) {
 829      case 'insert':
 830        webform_node_insert($node);
 831        break;
 832      case 'update':
 833        webform_node_update($node);
 834        break;
 835      case 'delete':
 836        webform_node_delete($node);
 837        break;
 838      case 'prepare':
 839        webform_node_prepare($node);
 840        break;
 841      case 'prepare translation':
 842        webform_node_prepare_translation($node);
 843        break;
 844      case 'load':
 845        return webform_node_load($node);
 846      case 'view':
 847        return webform_node_view($node, $teaser, $page);
 848    }
 849  }
 850  
 851  /**
 852   * Implementation of hook_node_insert().
 853   */
 854  function webform_node_insert($node) {
 855    if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
 856      return;
 857    }
 858  
 859    module_load_include('inc', 'webform', 'includes/webform.components');
 860    module_load_include('inc', 'webform', 'includes/webform.emails');
 861  
 862    // Insert the webform.
 863    db_query("INSERT INTO {webform} (nid, confirmation, confirmation_format, redirect_url, teaser, allow_draft, submit_notice, submit_text, submit_limit, submit_interval) VALUES (%d, '%s', %d, '%s', %d, %d, %d, '%s', %d, %d)", $node->nid, $node->webform['confirmation'], $node->webform['confirmation_format'], $node->webform['redirect_url'], $node->webform['teaser'], $node->webform['allow_draft'], $node->webform['submit_notice'], $node->webform['submit_text'], $node->webform['submit_limit'], $node->webform['submit_interval']);
 864  
 865    // Insert the components into the database. Used with clone.module.
 866    if (isset($node->webform['components']) && !empty($node->webform['components'])) {
 867      foreach ($node->webform['components'] as $cid => $component) {
 868        $component['nid'] = $node->nid; // Required for clone.module.
 869        webform_component_insert($component);
 870      }
 871    }
 872  
 873    // Insert emails. Also used with clone.module.
 874    if (isset($node->webform['emails']) && !empty($node->webform['emails'])) {
 875      foreach ($node->webform['emails'] as $eid => $email) {
 876        $email['nid'] = $node->nid;
 877        webform_email_insert($email);
 878      }
 879    }
 880  
 881    // Set the per-role submission access control.
 882    foreach (array_filter($node->webform['roles']) as $rid) {
 883      db_query('INSERT INTO {webform_roles} (nid, rid) VALUES (%d, %d)', $node->nid, $rid);
 884    }
 885  }
 886  
 887  /**
 888   * Implementation of hook_node_update().
 889   */
 890  function webform_node_update($node) {
 891    if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
 892      return;
 893    }
 894  
 895    // Check if there is an existing entry at all for this node.
 896    $exists = db_result(db_query('SELECT nid FROM {webform} WHERE nid = %d', $node->nid));
 897  
 898    // If a webform row doesn't even exist, we can assume it needs to be inserted.
 899    if (!$exists) {
 900      webform_node_insert($node);
 901      return;
 902    }
 903  
 904    // Update the webform entry.
 905    db_query("UPDATE {webform} SET confirmation = '%s', confirmation_format = %d, redirect_url = '%s', teaser = %d, allow_draft = %d, submit_notice = %d, submit_text = '%s', submit_limit = %d, submit_interval = %d where nid = %d", $node->webform['confirmation'], $node->webform['confirmation_format'], $node->webform['redirect_url'], $node->webform['teaser'], $node->webform['allow_draft'], $node->webform['submit_notice'], $node->webform['submit_text'], $node->webform['submit_limit'], $node->webform['submit_interval'], $node->nid);
 906  
 907    // Compare the webform components and don't do anything if it's not needed.
 908    $original = node_load($node->nid);
 909  
 910    if ($original->webform['components'] != $node->webform['components']) {
 911      module_load_include('inc', 'webform', 'includes/webform.components');
 912  
 913      $original_cids = array_keys($original->webform['components']);
 914      $current_cids = array_keys($node->webform['components']);
 915  
 916      $all_cids = $original_cids + $current_cids;
 917      $deleted_cids = array_diff($original_cids, $current_cids);
 918      $inserted_cids = array_diff($current_cids, $original_cids);
 919  
 920      foreach ($all_cids as $cid) {
 921        if (in_array($cid, $inserted_cids)) {
 922          webform_component_insert($node->webform['components'][$cid]);
 923        }
 924        elseif (in_array($cid, $deleted_cids)) {
 925          webform_component_delete($node, $original->webform['components'][$cid]);
 926        }
 927        elseif ($node->webform['components'][$cid] != $original->webform['components'][$cid]) {
 928          $node->webform['components'][$cid]['nid'] = $node->nid;
 929          webform_component_update($node->webform['components'][$cid]);
 930        }
 931      }
 932    }
 933  
 934    // Compare the webform e-mails and don't do anything if it's not needed.
 935    if ($original->webform['emails'] != $node->webform['emails']) {
 936      module_load_include('inc', 'webform', 'includes/webform.emails');
 937  
 938      $original_eids = array_keys($original->webform['emails']);
 939      $current_eids = array_keys($node->webform['emails']);
 940  
 941      $all_eids = $original_eids + $current_eids;
 942      $deleted_eids = array_diff($original_eids, $current_eids);
 943      $inserted_eids = array_diff($current_eids, $original_eids);
 944  
 945      foreach ($all_eids as $eid) {
 946        if (in_array($eid, $inserted_eids)) {
 947          webform_email_insert($node->webform['emails'][$eid]);
 948        }
 949        elseif (in_array($eid, $deleted_eids)) {
 950          webform_email_delete($node, $original->webform['emails'][$eid]);
 951        }
 952        elseif ($node->webform['emails'][$eid] != $original->webform['emails'][$eid]) {
 953          $node->webform['emails'][$eid]['nid'] = $node->nid;
 954          webform_email_update($node->webform['emails'][$eid]);
 955        }
 956      }
 957    }
 958  
 959    // Just delete and re-insert roles if they've changed.
 960    if ($original->webform['roles'] != $node->webform['roles']) {
 961      db_query('DELETE FROM {webform_roles} WHERE nid = %d', $node->nid);
 962      foreach (array_filter($node->webform['roles']) as $rid) {
 963        db_query('INSERT INTO {webform_roles} (nid, rid) VALUES (%d, %d)', $node->nid, $rid);
 964      }
 965    }
 966  }
 967  
 968  /**
 969   * Implementation of hook_delete().
 970   */
 971  function webform_node_delete($node) {
 972    if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
 973      return;
 974    }
 975  
 976    // Allow components clean up extra data, such as uploaded files.
 977    module_load_include('inc', 'webform', 'includes/webform.components');
 978    foreach ($node->webform['components'] as $cid => $component) {
 979      webform_component_delete($node, $component);
 980    }
 981  
 982    // Remove any trace of webform data from the database.
 983    db_query('DELETE FROM {webform} WHERE nid = %d', $node->nid);
 984    db_query('DELETE FROM {webform_component} WHERE nid = %d', $node->nid);
 985    db_query('DELETE FROM {webform_emails} WHERE nid = %d', $node->nid);
 986    db_query('DELETE FROM {webform_roles} WHERE nid = %d', $node->nid);
 987    db_query('DELETE FROM {webform_submissions} WHERE nid = %d', $node->nid);
 988    db_query('DELETE FROM {webform_submitted_data} WHERE nid = %d', $node->nid);
 989  }
 990  
 991  /**
 992   * Default settings for a newly created webform node.
 993   */
 994  function webform_node_defaults() {
 995    return array(
 996      'confirmation' => '',
 997      'confirmation_format' => FILTER_FORMAT_DEFAULT,
 998      'redirect_url' => '',
 999      'teaser' => 0,
1000      'allow_draft' => 0,
1001      'submit_notice' => 0,
1002      'submit_text' => '',
1003      'submit_limit' => -1,
1004      'submit_interval' => -1,
1005      'roles' => array(1, 2),
1006      'emails' => array(),
1007      'components' => array(),
1008    );
1009  }
1010  
1011  /**
1012   * Implementation of hook_node_prepare().
1013   */
1014  function webform_node_prepare(&$node) {
1015    if (!isset($node->webform)) {
1016      $node->webform = webform_node_defaults();
1017    }
1018  }
1019  
1020  /**
1021   * Implementation of hook_node_prepare_translation().
1022   */
1023  function webform_node_prepare_translation(&$node) {
1024    // Copy all Webform settings over to translated versions of this node.
1025    if (isset($node->translation_source)) {
1026      $source_node = node_load($node->translation_source->nid);
1027      $node->webform = $source_node->webform;
1028    }
1029  }
1030  
1031  /**
1032   * Implementation of hook_node_load().
1033   */
1034  function webform_node_load($node) {
1035    module_load_include('inc', 'webform', 'includes/webform.components');
1036    $additions = array();
1037  
1038    if (isset($node->nid)) {
1039      $webform = db_fetch_array(db_query('SELECT * FROM {webform} WHERE nid = %d', $node->nid));
1040  
1041      // If a webform record doesn't exist, just return the defaults.
1042      if (!$webform) {
1043        $additions['webform'] = webform_node_defaults();
1044        return $additions;
1045      }
1046  
1047      $additions['webform'] = $webform;
1048      $additions['webform']['roles'] = array();
1049      $result = db_query('SELECT rid FROM {webform_roles} WHERE nid = %d', $node->nid);
1050      while ($role = db_fetch_object($result)) {
1051        $additions['webform']['roles'][] = $role->rid;
1052      }
1053  
1054      $additions['webform']['emails'] = array();
1055      $result = db_query('SELECT * FROM {webform_emails} WHERE nid = %d', $node->nid);
1056      while ($email = db_fetch_array($result)) {
1057        $additions['webform']['emails'][$email['eid']] = $email;
1058        $additions['webform']['emails'][$email['eid']]['excluded_components'] = array_filter(explode(',', $email['excluded_components']));
1059        if (variable_get('webform_format_override', 0)) {
1060          $additions['webform']['emails'][$email['eid']]['html'] = variable_get('webform_default_format', 0);
1061        }
1062      }
1063    }
1064  
1065    $additions['webform']['components'] = array();
1066  
1067    // If we don't have a NID yet, no point in doing additional queries.
1068    if (!isset($node->nid)) {
1069      return $additions;
1070    }
1071  
1072    $result = db_query('SELECT * FROM {webform_component} WHERE nid = %d ORDER BY weight, name', $node->nid);
1073    while ($c = db_fetch_array($result)) {
1074      $component =& $additions['webform']['components'][$c['cid']];
1075      $component['nid'] = $node->nid;
1076      $component['cid'] = $c['cid'];
1077      $component['form_key'] = $c['form_key'] ? $c['form_key'] : $c['cid'];
1078      $component['name'] = t($c['name']);
1079      $component['type'] = $c['type'];
1080      $component['value'] = $c['value'];
1081      $component['extra'] = unserialize($c['extra']);
1082      $component['mandatory'] = $c['mandatory'];
1083      $component['pid'] = $c['pid'];
1084      $component['weight'] = $c['weight'];
1085  
1086      webform_component_defaults($component);
1087    }
1088  
1089    // Organize the components into a fieldset-based order.
1090    if (!empty($additions['webform']['components'])) {
1091      $component_tree = array();
1092      $page_count = 1;
1093      _webform_components_tree_build($additions['webform']['components'], $component_tree, 0, $page_count);
1094      $additions['webform']['components'] = _webform_components_tree_flatten($component_tree['children']);
1095    }
1096    return $additions;
1097  }
1098  
1099  /**
1100   * Implementation of hook_link().
1101   * Always add a "view form" link.
1102   */
1103  function webform_link($type, $node = NULL, $teaser = FALSE) {
1104    $links = array();
1105    if (isset($node->type) && $node->type === 'webform') {
1106      if ($teaser && !$node->webform['teaser']) {
1107        $links['webform_goto'] = array(
1108          'title' => t('Go to form'),
1109          'href' => 'node/' . $node->nid,
1110          'attributes' => array('title' => t('View this form.'), 'class' => 'read-more')
1111        );
1112      }
1113    }
1114    return $links;
1115  }
1116  
1117  /**
1118   * Implementation of hook_form_alter().
1119   */
1120  function webform_form_alter(&$form, $form_state, $form_id) {
1121    $matches = array();
1122    if (isset($form['#node']->type) && $form_id == $form['#node']->type . '_node_form' && in_array($form['#node']->type, webform_variable_get('webform_node_types'))) {
1123      $node = $form['#node'];
1124      // Preserve all Webform options currently set on the node.
1125      $form['webform'] = array(
1126        '#type' => 'value',
1127        '#value' => $node->webform,
1128      );
1129  
1130      // If a new node, redirect the user to the components form after save.
1131      if (empty($node->nid)) {
1132        $form['actions']['submit']['#submit'][] = 'webform_form_submit';
1133      }
1134    }
1135  }
1136  
1137  /**
1138   * Submit handler for the webform node form.
1139   *
1140   * Redirect the user to the components form on new node inserts. Note that this
1141   * fires after the hook_submit() function above.
1142   */
1143  function webform_form_submit($form, &$form_state) {
1144    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'])));
1145    $form_state['redirect'] = 'node/' . $form_state['nid'] . '/webform/components';
1146  }
1147  
1148  /**
1149   * Implementation of hook_node_view().
1150   */
1151  function webform_node_view(&$node, $teaser, $page) {
1152    global $user;
1153    // If empty, a teaser, or a new node (during preview) do not display.
1154    if (empty($node->webform['components']) || ($teaser && !$node->webform['teaser']) || empty($node->nid)) {
1155      return;
1156    }
1157  
1158    $submission = array();
1159    $submission_count = 0;
1160    $enabled = TRUE;
1161    $logging_in = FALSE;
1162    $limit_exceeded = FALSE;
1163  
1164    // When logging in using a form on the same page as a webform node, surpress
1165    // output messages so that they don't show up after the user has logged in.
1166    // See http://drupal.org/node/239343.
1167    if (isset($_POST['op']) && isset($_POST['name']) && isset($_POST['pass'])) {
1168      $logging_in = TRUE;
1169    }
1170  
1171    // Check if the user's role can submit this webform.
1172    if (variable_get('webform_submission_access_control', 1)) {
1173      $allowed_roles = array();
1174      foreach ($node->webform['roles'] as $rid) {
1175        $allowed_roles[$rid] = isset($user->roles[$rid]) ? TRUE : FALSE;
1176      }
1177      if (array_search(TRUE, $allowed_roles) === FALSE && $user->uid != 1) {
1178        $enabled = FALSE;
1179      }
1180    }
1181    else {
1182      // If not using Webform submission access control, allow for all roles.
1183      $allowed_roles = array_keys(user_roles());
1184    }
1185  
1186    // Check if the user can add another submission.
1187    if ($node->webform['submit_limit'] != -1) { // -1: Submissions are never throttled.
1188      module_load_include('inc', 'webform', 'includes/webform.submissions');
1189  
1190      // Disable the form if the limit is exceeded and page cache is not active.
1191      if (($limit_exceeded = _webform_submission_limit_check($node)) && ($user->uid != 0 || variable_get('cache', 0) == 0)) {
1192        $enabled = FALSE;
1193      }
1194    }
1195  
1196    // Get a count of previous submissions by this user.
1197    if ($page && webform_submission_access($node, NULL, 'list')) {
1198      module_load_include('inc', 'webform', 'includes/webform.submissions');
1199      $submission_count = webform_get_submission_count($node->nid, $user->uid);
1200    }
1201  
1202    // Check if this user has a draft for this webform.
1203    $is_draft = FALSE;
1204    if ($node->webform['allow_draft'] && $user->uid != 0) {
1205      // Draft found - display form with draft data for further editing.
1206      if ($draft_sid = _webform_fetch_draft_sid($node->nid, $user->uid)) {
1207        module_load_include('inc', 'webform', 'includes/webform.submissions');
1208        $submission = webform_get_submission($node->nid, $draft_sid);
1209        $enabled = TRUE;
1210        $is_draft = TRUE;
1211      }
1212    }
1213  
1214    // Render the form and generate the output.
1215    $form = !empty($node->webform['components']) ? drupal_get_form('webform_client_form_' . $node->nid, $node, $submission, $is_draft) : '';
1216    $output = theme('webform_view', $node, $teaser, $page, $form, $enabled);
1217  
1218    // Remove the surrounding <form> tag if this is a preview.
1219    if ($node->build_mode == NODE_BUILD_PREVIEW) {
1220      $output = preg_replace('/<\/?form[^>]*>/', '', $output);
1221    }
1222  
1223    // Print out messages for the webform.
1224    if ($node->build_mode != NODE_BUILD_PREVIEW && !$logging_in) {
1225      theme('webform_view_messages', $node, $teaser, $page, $submission_count, $limit_exceeded, $allowed_roles);
1226    }
1227  
1228    if (isset($output)) {
1229      if (module_exists('content')) {
1230        $weight = content_extra_field_weight($node->type, 'webform');
1231      }
1232      $node->content['webform'] = array('#value' => $output, '#weight' => isset($weight) ? $weight : 10);
1233    }
1234  }
1235  
1236  /**
1237   * Output the Webform into the node content.
1238   *
1239   * @param $node
1240   *   The webform node object.
1241   * @param $teaser
1242   *   If this webform is being displayed as the teaser view of the node.
1243   * @param $page
1244   *   If this webform node is being viewed as the main content of the page.
1245   * @param $form
1246   *   The rendered form.
1247   * @param $enabled
1248   *   If the form allowed to be completed by the current user.
1249   */
1250  function theme_webform_view($node, $teaser, $page, $form, $enabled) {
1251    // Only show the form if this user is allowed access.
1252    if ($enabled) {
1253      return $form;
1254    }
1255  }
1256  
1257  /**
1258   * Display a message to a user if they are not allowed to fill out a form.
1259   *
1260   * @param $node
1261   *   The webform node object.
1262   * @param $teaser
1263   *   If this webform is being displayed as the teaser view of the node.
1264   * @param $page
1265   *   If this webform node is being viewed as the main content of the page.
1266   * @param $submission_count
1267   *   The number of submissions this user has already submitted. Not calculated
1268   *   for anonymous users.
1269   * @param $limit_exceeded
1270   *   Boolean value if the submission limit for this user has been exceeded.
1271   * @param $allowed_roles
1272   *   A list of user roles that are allowed to submit this webform.
1273   */
1274  function theme_webform_view_messages($node, $teaser, $page, $submission_count, $limit_exceeded, $allowed_roles) {
1275    global $user;
1276  
1277    $type = 'notice';
1278    $cached = $user->uid == 0 && variable_get('cache', 0);
1279  
1280    // If not allowed to submit the form, give an explanation.
1281    if (array_search(TRUE, $allowed_roles) === FALSE && $user->uid != 1) {
1282      if (empty($allowed_roles)) {
1283        // No roles are allowed to submit the form.
1284        $message = t('Submissions for this form are closed.');
1285      }
1286      elseif (isset($allowed_roles[2])) {
1287        // The "authenticated user" role is allowed to submit and the user is currently logged-out.
1288        $login = url('user/login', array('query' => drupal_get_destination()));
1289        $register = url('user/register', array('query' => drupal_get_destination()));
1290        if (variable_get('user_register', 1) == 0) {
1291          $message = t('You must <a href="!login">login</a> to view this form.', array('!login' => $login));
1292        }
1293        else {
1294          $message = t('You must <a href="!login">login</a> or <a href="!register">register</a> to view this form.', array('!login' => $login, '!register' => $register));
1295        }
1296      }
1297      else {
1298        // The user must be some other role to submit.
1299        $message = t('You do not have permission to view this form.');
1300      }
1301    }
1302  
1303    // If the user has exceeded the limit of submissions, explain the limit.
1304    elseif ($limit_exceeded && !$cached) {
1305      if ($node->webform['submit_interval'] == -1 && $node->webform['submit_limit'] > 1) {
1306        $message = t('You have submitted this form the maximum number of times (@count).', array('@count' => $node->webform['submit_limit']));
1307      }
1308      elseif ($node->webform['submit_interval'] == -1 && $node->webform['submit_limit'] == 1) {
1309        $message = t('You have already submitted this form.');
1310      }
1311      else {
1312        $message = t('You may not submit another entry at this time.');
1313      }
1314      $type = 'error';
1315    }
1316  
1317    // If the user has submitted before, give them a link to their submissions.
1318    if ($submission_count > 0 && $node->webform['submit_notice'] == 1 && !$cached) {
1319      if (empty($message)) {
1320        $message = t('You have already submitted this form.') . ' ' . t('<a href="!url">View your previous submissions</a>.', array('!url' => url('node/' . $node->nid . '/submissions')));
1321      }
1322      else {
1323        $message .= ' ' . t('<a href="!url">View your previous submissions</a>.', array('!url' => url('node/' . $node->nid . '/submissions')));
1324      }
1325    }
1326  
1327    if ($page && isset($message)) {
1328      drupal_set_message($message, $type);
1329    }
1330  }
1331  
1332  /**
1333   * Implementation of hook_mail().
1334   */
1335  function webform_mail($key, &$message, $params) {
1336    $message['headers'] = array_merge($message['headers'], $params['headers']);
1337    $message['subject'] = $params['subject'];
1338    $message['body'][] = $params['message'];
1339  }
1340  
1341  /**
1342   * Client form generation function. If this is displaying an existing
1343   * submission, pass in the $submission variable with the contents of the
1344   * submission to be displayed.
1345   *
1346   * @param $form_state
1347   *   The current form values of a submission, used in multipage webforms.
1348   * @param $node
1349   *   The current webform node.
1350   * @param $submission
1351   *   An object containing information about the form submission if we're
1352   *   displaying a result.
1353   * @param $is_draft
1354   *   Optional. Set to TRUE if displaying a draft.
1355   * @param $filter
1356   *   Whether or not to filter the contents of descriptions and values when
1357   *   building the form. Values need to be unfiltered to be editable by
1358   *   Form Builder.
1359   */
1360  function webform_client_form(&$form_state, $node, $submission, $is_draft = FALSE, $filter = TRUE) {
1361    global $user;
1362  
1363    drupal_add_css(drupal_get_path('module', 'webform') . '/css/webform.css');
1364    drupal_add_js(drupal_get_path('module', 'webform') . '/js/webform.js');
1365    module_load_include('inc', 'webform', 'includes/webform.components');
1366  
1367    // Bind arguments to $form to make them available in theming and form_alter.
1368    $form['#node'] = $node;
1369    $form['#submission'] = $submission;
1370    $form['#is_draft'] = $is_draft;
1371    $form['#filter'] = $filter;
1372  
1373    // Add a theme function for this form.
1374    $form['#theme'] = array('webform_form_' . $node->nid, 'webform_form');
1375  
1376    // Add a css class for all client forms.
1377    $form['#attributes'] = array('class' => 'webform-client-form');
1378  
1379    // Set the encoding type (necessary for file uploads).
1380    $form['#attributes']['enctype'] = 'multipart/form-data';
1381  
1382    // Set the form action to the node ID in case this is being displayed on the
1383    // teaser, subsequent pages should be on the node page directly.
1384    if (empty($submission)) {
1385      $form['#action'] = url('node/' . $node->nid);
1386    }
1387  
1388    $form['#submit'] = array('webform_client_form_pages', 'webform_client_form_submit');
1389    $form['#validate'] = array('webform_client_form_validate');
1390  
1391    if (is_array($node->webform['components']) && !empty($node->webform['components'])) {
1392      // Prepare a new form array.
1393      $form['submitted'] = array(
1394        '#tree' => TRUE
1395      );
1396      $form['details'] = array(
1397        '#tree' => TRUE,
1398      );
1399  
1400      // Put the components into a tree structure.
1401      if (!isset($form_state['storage']['component_tree'])) {
1402        $form_state['webform']['component_tree'] = array();
1403        $form_state['webform']['page_count'] = 1;
1404        $form_state['webform']['page_num'] = 1;
1405        _webform_components_tree_build($node->webform['components'], $form_state['webform']['component_tree'], 0, $form_state['webform']['page_count']);
1406      }
1407      else {
1408        $form_state['webform']['component_tree'] = $form_state['storage']['component_tree'];
1409        $form_state['webform']['page_count'] = $form_state['storage']['page_count'];
1410        $form_state['webform']['page_num'] = $form_state['storage']['page_num'];
1411      }
1412  
1413      // Shorten up our variable names.
1414      $component_tree = $form_state['webform']['component_tree'];
1415      $page_count = $form_state['webform']['page_count'];
1416      $page_num = $form_state['webform']['page_num'];
1417  
1418      // Recursively add components to the form.
1419      foreach ($component_tree['children'] as $cid => $component) {
1420        $component_value = isset($form_state['values']['submitted'][$component['form_key']]) ? $form_state['values']['submitted'][$component['form_key']] : NULL;
1421        if (_webform_client_form_rule_check($node, $component, $page_num, $form_state)) {
1422          _webform_client_form_add_component($node, $component, $component_value, $form['submitted'], $form, $form_state, $submission, 'form', $page_num, $filter);
1423        }
1424      }
1425  
1426      // These form details help managing data upon submission.
1427      $form['details']['nid'] = array(
1428        '#type' => 'value',
1429        '#value' => $node->nid,
1430      );
1431      $form['details']['sid'] = array(
1432        '#type' => 'hidden',
1433        '#value' => isset($submission->sid) ? $submission->sid : '',
1434      );
1435      $form['details']['uid'] = array(
1436        '#type' => 'value',
1437        '#value' => isset($submission->uid) ? $submission->uid : $user->uid,
1438      );
1439      $form['details']['page_num'] = array(
1440        '#type'  => 'hidden',
1441        '#value' => $page_num,
1442      );
1443      $form['details']['page_count'] = array(
1444        '#type'  => 'hidden',
1445        '#value' => $page_count,
1446      );
1447      $form['details']['finished'] = array(
1448        '#type' => 'hidden',
1449        '#value' => isset($submission->is_draft) ? (!$submission->is_draft) : 0,
1450      );
1451  
1452      // Add buttons for pages, drafts, and submissions.
1453      $form['actions'] = array(
1454        '#tree' => FALSE,
1455        '#weight' => 1000,
1456        '#prefix' => '<div id="edit-actions" class="form-actions form-wrapper">',
1457        '#suffix' => '</div>',
1458      );
1459  
1460      if ($page_count > 1) {
1461        $next_page = t('Next Page >');
1462        $prev_page = t('< Previous Page');
1463  
1464        // Add the submit button(s).
1465       if ($node->webform['allow_draft'] && (empty($submission) || $submission->is_draft) && $user->uid != 0) {
1466          $form['actions']['draft'] = array(
1467            '#type' => 'submit',
1468            '#value' => t('Save Draft'),
1469            '#weight' => -2,
1470            '#validate' => array(),
1471          );
1472        }
1473        if ($page_num > 1) {
1474          $form['actions']['previous'] = array(
1475            '#type' => 'submit',
1476            '#value' => $prev_page,
1477            '#weight' => 5,
1478            '#validate' => array(),
1479          );
1480        }
1481        if ($page_num == $page_count) {
1482          $form['actions']['submit'] = array(
1483            '#type' => 'submit',
1484            '#value' => empty($node->webform['submit_text']) ? t('Submit') : $node->webform['submit_text'],
1485            '#weight' => 10,
1486          );
1487        }
1488        elseif ($page_num < $page_count) {
1489          $form['actions']['next'] = array(
1490            '#type' => 'submit',
1491            '#value' => $next_page,
1492            '#weight' => 10,
1493          );
1494        }
1495      }
1496      else {
1497        // Add the submit button.
1498        $form['actions']['submit'] = array(
1499          '#type' => 'submit',
1500          '#value' => empty($node->webform['submit_text']) ? t('Submit') : $node->webform['submit_text'],
1501          '#weight' => 10,
1502        );
1503      }
1504    }
1505  
1506    return $form;
1507  }
1508  
1509  /**
1510   * Check if a component should be displayed on the current page.
1511   */
1512  function _webform_client_form_rule_check($node, $component, $page_num, $form_state = NULL, $submission = NULL) {
1513    $conditional_values = isset($component['extra']['conditional_values']) ? $component['extra']['conditional_values'] : NULL;
1514    $conditional_component = isset($component['extra']['conditional_component']) && isset($node->webform['components'][$component['extra']['conditional_component']]) ? $node->webform['components'][$component['extra']['conditional_component']] : NULL;
1515  
1516    // Check the rules for this entire page. Note individual page breaks are
1517    // checked down below in the individual component rule checks.
1518    $show_page = TRUE;
1519    if ($component['page_num'] > 1 && $component['type'] != 'pagebreak') {
1520      foreach ($node->webform['components'] as $cid => $page_component) {
1521        if ($page_component['type'] == 'pagebreak' && $page_component['page_num'] == $page_num) {
1522          $show_page = _webform_client_form_rule_check($node, $page_component, $page_num, $form_state, $submission);
1523          break;
1524        }
1525      }
1526    }
1527  
1528    // Check any parents' visibility rules.
1529    $show_parent = $show_page;
1530    if ($show_parent && $component['pid'] && isset($node->webform['components'][$component['pid']])) {
1531      $parent_component = $node->webform['components'][$component['pid']];
1532      $show_parent = _webform_client_form_rule_check($node, $parent_component, $page_num, $form_state, $submission);
1533    }
1534  
1535    // Check the individual component rules.
1536    $show_component = $show_parent;
1537    if ($show_component && ($page_num == 0 || $component['page_num'] == $page_num) && $conditional_component && strlen(trim($conditional_values))) {
1538      $input_values = array();
1539      if (isset($form_state)) {
1540        $parents = webform_component_parent_keys($node, $conditional_component);
1541        $input_value = isset($form_state['values']['submitted']) ? $form_state['values']['submitted'] : array();
1542        foreach ($parents as $parent) {
1543          if (isset($input_value[$parent])) {
1544            $input_value = $input_value[$parent];
1545          }
1546          else {
1547            $input_value = NULL;
1548            break;
1549          }
1550        }
1551        $input_values = is_array($input_value) ? $input_value : array($input_value);
1552      }
1553      elseif (isset($submission)) {
1554        $input_values = $submission->data[$conditional_component['cid']]['value'];
1555      }
1556  
1557      $test_values = array_map('trim', explode("\n", $conditional_values));
1558      if (empty($input_values) && !empty($test_values)) {
1559        $show_component = FALSE;
1560      }
1561      else {
1562        foreach ($input_values as $input_value) {
1563          if ($show_component = in_array($input_value, $test_values)) {
1564            break;
1565          }
1566        }
1567      }
1568  
1569      if ($component['extra']['conditional_operator'] == '!=') {
1570        $show_component = !$show_component;
1571      }
1572    }
1573  
1574    return $show_component;
1575  }
1576  
1577  /**
1578   * Add a component to a renderable array. Called recursively for fieldsets.
1579   *
1580   * This function assists in the building of the client form, as well as the
1581   * display of results, and the text of e-mails.
1582   *
1583   * @param $component
1584   *   The component to be added to the form.
1585   * @param $component_value
1586   *   The components current value if known.
1587   * @param $parent_fieldset
1588   *   The fieldset to which this element will be added.
1589   * @param $form
1590   *   The entire form array.
1591   * @param $form_state
1592   *   The form state.
1593   * @param $submission
1594   *   The Webform submission as retrieved from the database.
1595   * @param $format
1596   *   The format the form should be displayed as. May be one of the following:
1597   *   - form: Show as an editable form.
1598   *   - html: Show as HTML results.
1599   *   - text: Show as plain text.
1600   * @param $filter
1601   *   Whether the form element properties should be filtered. Only set to FALSE
1602   *   if needing the raw properties for editing.
1603   *
1604   * @see webform_client_form
1605   * @see webform_submission_render
1606   */
1607  function _webform_client_form_add_component($node, $component, $component_value, &$parent_fieldset, &$form, $form_state, $submission, $format = 'form', $page_num = 0, $filter = TRUE) {
1608    $cid = $component['cid'];
1609  
1610    // Load with submission information if necessary.
1611    if ($format != 'form') {
1612      // This component is display only.
1613      $data = empty($submission->data[$cid]['value']) ? NULL : $submission->data[$cid]['value'];
1614      if ($display_element = webform_component_invoke($component['type'], 'display', $component, $data, $format)) {
1615        // The form_builder() function usually adds #parents and #id for us, but
1616        // because these are not marked for #input, we need to add them manually.
1617        if (!isset($display_element['#parents'])) {
1618          $parents = isset($parent_fieldset['#parents']) ? $parent_fieldset['#parents'] : array('submitted');
1619          $parents[] = $component['form_key'];
1620          $display_element['#parents'] = $parents;
1621        }
1622        if (!isset($display_element['#id'])) {
1623          $display_element['#id'] = form_clean_id('edit-' . implode('-', $display_element['#parents']));
1624        }
1625        $parent_fieldset[$component['form_key']] = $display_element;
1626      }
1627    }
1628    elseif ($component['page_num'] == $page_num) {
1629      // Add this user-defined field to the form (with all the values that are always available).
1630      $data = isset($submission->data[$cid]['value']) ? $submission->data[$cid]['value'] : NULL;
1631      if ($element = webform_component_invoke($component['type'], 'render', $component, $data, $filter)) {
1632        $parent_fieldset[$component['form_key']] = $element;
1633  
1634        // Override the value if one already exists in the form state.
1635        if (isset($component_value)) {
1636          $parent_fieldset[$component['form_key']]['#default_value'] = $component_value;
1637          if (is_array($component_value)) {
1638            foreach ($component_value as $key => $value) {
1639              if (isset($parent_fieldset[$component['form_key']][$key])) {
1640                $parent_fieldset[$component['form_key']][$key]['#default_value'] = $value;
1641              }
1642            }
1643          }
1644        }
1645      }
1646      else {
1647        drupal_set_message(t('The webform component @type is not able to be displayed', array('@type' => $component['type'])));
1648      }
1649    }
1650  
1651    // Disable validation initially on all elements. We manually validate
1652    // all webform elements in webform_client_form_validate().
1653    if (isset($parent_fieldset[$component['form_key']])) {
1654      $parent_fieldset[$component['form_key']]['#validated'] = TRUE;
1655      $parent_fieldset[$component['form_key']]['#webform_validated'] = FALSE;
1656    }
1657  
1658    if (isset($component['children']) && is_array($component['children'])) {
1659      foreach ($component['children'] as $scid => $subcomponent) {
1660        $subcomponent_value = isset($component_value[$subcomponent['form_key']]) ? $component_value[$subcomponent['form_key']] : NULL;
1661        if (_webform_client_form_rule_check($node, $subcomponent, $page_num, $form_state, $submission)) {
1662          _webform_client_form_add_component($node, $subcomponent, $subcomponent_value, $parent_fieldset[$component['form_key']], $form, $form_state, $submission, $format, $page_num, $filter);
1663        }
1664      }
1665    }
1666  }
1667  
1668  function webform_client_form_validate($form, &$form_state) {
1669    drupal_add_css(drupal_get_path('module', 'webform') . '/css/webform.css');
1670    drupal_add_js(drupal_get_path('module', 'webform') . '/js/webform.js');
1671    $node = node_load($form_state['values']['details']['nid']);
1672    $finished = $form_state['values']['details']['finished'];
1673  
1674    // Check that the user has not exceeded the submission limit.
1675    // This usually will only apply to anonymous users when the page cache is
1676    // enabled, because they may submit the form even if they do not have access.
1677    if ($node->webform['submit_limit'] != -1) { // -1: Submissions are never throttled.
1678      module_load_include('inc', 'webform', 'includes/webform.submissions');
1679  
1680      if (!$finished && $limit_exceeded = _webform_submission_limit_check($node)) {
1681        $error = theme('webform_view_messages', $node, 0, 1, 0, $limit_exceeded, array_keys(user_roles()));
1682        form_set_error('', $error);
1683        return;
1684      }
1685    }
1686  
1687    // Run all #element_validate and #required checks. These are skipped initially
1688    // by setting #validated = TRUE on all components when they are added.
1689    _webform_client_form_validate($form, $form_state);
1690  }
1691  
1692  /**
1693   * Recursive validation function to trigger normal Drupal validation.
1694   *
1695   * This function imitates _form_validate in Drupal's form.inc, only it sets
1696   * a different property to ensure that validation has occurred.
1697   */
1698  function _webform_client_form_validate($elements, &$form_state, $first_run = TRUE) {
1699    static $form;
1700    if ($first_run) {
1701      $form = $elements;
1702    }
1703  
1704    // Recurse through all children.
1705    foreach (element_children($elements) as $key) {
1706      if (isset($elements[$key]) && $elements[$key]) {
1707        _webform_client_form_validate($elements[$key], $form_state, FALSE);
1708      }
1709    }
1710    // Validate the current input.
1711    if (isset($elements['#webform_validated']) && $elements['#webform_validated'] == FALSE) {
1712      if (isset($elements['#needs_validation'])) {
1713        // Make sure a value is passed when the field is required.
1714        // A simple call to empty() will not cut it here as some fields, like
1715        // checkboxes, can return a valid value of '0'. Instead, check the
1716        // length if it's a string, and the item count if it's an array.
1717        if ($elements['#required'] && (!count($elements['#value']) || (is_string($elements['#value']) && strlen(trim($elements['#value'])) == 0))) {
1718          form_error($elements, t('!name field is required.', array('!name' => $elements['#title'])));
1719        }
1720  
1721        // Verify that the value is not longer than #maxlength.
1722        if (isset($elements['#maxlength']) && drupal_strlen($elements['#value']) > $elements['#maxlength']) {
1723          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']))));
1724        }
1725  
1726        if (isset($elements['#options']) && isset($elements['#value'])) {
1727          if ($elements['#type'] == 'select') {
1728            $options = form_options_flatten($elements['#options']);
1729          }
1730          else {
1731            $options = $elements['#options'];
1732          }
1733          if (is_array($elements['#value'])) {
1734            $value = $elements['#type'] == 'checkboxes' ? array_keys(array_filter($elements['#value'])) : $elements['#value'];
1735            foreach ($value as $v) {
1736              if (!isset($options[$v])) {
1737                form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
1738                watchdog('form', 'Illegal choice %choice in !name element.', array('%choice' => $v, '!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
1739              }
1740            }
1741          }
1742          elseif (!isset($options[$elements['#value']])) {
1743            form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
1744            watchdog('form', 'Illegal choice %choice in %name element.', array('%choice' => $elements['#value'], '%name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
1745          }
1746        }
1747      }
1748  
1749      // Call any element-specific validators. These must act on the element
1750      // #value data.
1751      if (isset($elements['#element_validate'])) {
1752        foreach ($elements['#element_validate'] as $function) {
1753          if (function_exists($function))  {
1754            $function($elements, $form_state, $form);
1755          }
1756        }
1757      }
1758      $elements['#webform_validated'] = TRUE;
1759    }
1760  }
1761  
1762  /**
1763   * Handle the processing of pages and conditional logic.
1764   */
1765  function webform_client_form_pages($form, &$form_state) {
1766    $node = node_load($form_state['values']['details']['nid']);
1767  
1768    // Move special settings to storage.
1769    if (isset($form_state['webform']['component_tree'])) {
1770      $form_state['storage']['component_tree'] = $form_state['webform']['component_tree'];
1771      $form_state['storage']['page_count'] = $form_state['webform']['page_count'];
1772      $form_state['storage']['page_num'] = $form_state['webform']['page_num'];
1773    }
1774  
1775    // Check for a multi-page form that is not yet complete.
1776    $submit_op = empty($node->webform['submit_text']) ? t('Submit') : $node->webform['submit_text'];
1777    $draft_op = t('Save Draft');
1778    if (!in_array($form_state['values']['op'], array($submit_op, $draft_op))) {
1779      // Checkboxes need post-processing to maintain their values.
1780      _webform_client_form_submit_process($node, $form_state['values']['submitted'], array('select', 'grid'));
1781  
1782      // Store values from the current page in the form state storage.
1783      if (is_array($form_state['values']['submitted'])) {
1784        foreach ($form_state['values']['submitted'] as $key => $val) {
1785          $form_state['storage']['submitted'][$key] = $val;
1786        }
1787      }
1788  
1789      // Update form state values with those from storage.
1790      if (isset($form_state['storage']['submitted'])) {
1791        foreach ($form_state['storage']['submitted'] as $key => $val) {
1792          $form_state['values']['submitted'][$key] = $val;
1793        }
1794      }
1795  
1796      // Set the page number.
1797      if (!isset($form_state['storage']['page_num'])) {
1798        $form_state['storage']['page_num'] = 1;
1799      }
1800      if (end($form_state['clicked_button']['#parents']) == 'next') {
1801        $direction = 1;
1802      }
1803      else {
1804        $direction = 0;
1805      }
1806  
1807      // If the next page has no components that need to be displayed, skip it.
1808      if (isset($direction)) {
1809        $components = $direction ? $node->webform['components'] : array_reverse($node->webform['components'], TRUE);
1810        $last_component = end($node->webform['components']);
1811        foreach ($components as $component) {
1812          if ($component['type'] == 'pagebreak' && (
1813              $direction == 1 && $component['page_num'] > $form_state['storage']['page_num'] ||
1814              $direction == 0 && $component['page_num'] <= $form_state['storage']['page_num'])) {
1815            $previous_pagebreak = $component;
1816            continue;
1817          }
1818          if (isset($previous_pagebreak)) {
1819            $page_num = $previous_pagebreak['page_num'] + $direction - 1;
1820            // If we've found an component on this page, advance to that page.
1821            if ($component['page_num'] == $page_num && _webform_client_form_rule_check($node, $component, $page_num, $form_state)) {
1822              $form_state['storage']['page_num'] = $page_num;
1823              break;
1824            }
1825            // If we've gotten to the end of the form without finding any more
1826            // components, set the page number more than the max, ending the form.
1827            elseif ($direction && $component['cid'] == $last_component['cid']) {
1828              $form_state['storage']['page_num'] = $page_num + 1;
1829            }
1830          }
1831        }
1832      }
1833  
1834      // Rebuild the form and display the next page.
1835      if ($form_state['storage']['page_num'] <= $form_state['storage']['page_count']) {
1836        $form_state['rebuild'] = TRUE;
1837        return;
1838      }
1839    }
1840  
1841    if (isset($form_state['storage']['submitted'])) {
1842      // Merge any stored submission data for multistep forms.
1843      $original_values = is_array($form_state['values']['submitted']) ? $form_state['values']['submitted'] : array();
1844      unset($form_state['values']['submitted']);
1845  
1846      foreach ($form_state['storage']['submitted'] as $key => $val) {
1847        $form_state['values']['submitted'][$key] = $val;
1848      }
1849      foreach ($original_values as $key => $val) {
1850        $form_state['values']['submitted'][$key] = $val;
1851      }
1852  
1853      // Remove the variable so it doesn't show up in the additional processing.
1854      unset($original_values);
1855    }
1856  
1857    // Remove the form state storage now that we're done with the pages.
1858    unset($form_state['rebuild']);
1859    unset($form_state['storage']);
1860  
1861    // Perform post processing by components.
1862    _webform_client_form_submit_process($node, $form_state['values']['submitted']);
1863  
1864    // Flatten trees within the submission.
1865    $form_state['values']['submitted_tree'] = $form_state['values']['submitted'];
1866    $form_state['values']['submitted'] = _webform_client_form_submit_flatten($node, $form_state['values']['submitted']);
1867  
1868    // Set a flag indicating processing should continue and be saved.
1869    $form_state['webform_completed'] = TRUE;
1870  }
1871  
1872  /**
1873   * Submit handler for saving the form values and sending e-mails.
1874   */
1875  function webform_client_form_submit($form, &$form_state) {
1876    module_load_include('inc', 'webform', 'includes/webform.submissions');
1877    module_load_include('inc', 'webform', 'includes/webform.components');
1878    global $user;
1879  
1880    if (empty($form_state['webform_completed'])) {
1881      return;
1882    }
1883  
1884    $node = node_load($form_state['values']['details']['nid']);
1885  
1886    // Check if user is submitting as a draft.
1887    $is_draft = $form_state['values']['op'] == t('Save Draft');
1888  
1889    // Create a submission object.
1890    $submission = (object) array(
1891      'nid' => $node->nid,
1892      'uid' => $user->uid,
1893      'submitted' => time(),
1894      'remote_addr' => ip_address(),
1895      'is_draft' => $is_draft,
1896      'data' => webform_submission_data($node, $form_state['values']['submitted']),
1897    );
1898  
1899    // Save the submission to the database.
1900    if (empty($form_state['values']['details']['sid'])) {
1901      // No sid was found thus insert it in the dataabase.
1902      $form_state['values']['details']['sid'] = webform_submission_insert($node, $submission);
1903      $form_state['values']['details']['is_new'] = TRUE;
1904  
1905      // Set a cookie including the server's submission time.
1906      // The cookie expires in the length of the interval plus a day to compensate for different timezones.
1907      if (variable_get('webform_use_cookies', 0)) {
1908        $cookie_name = 'webform-' . $node->nid;
1909        $time = time();
1910        setcookie($cookie_name . '[' . $time . ']', $time, $time + $node->webform['submit_interval'] + 86400);
1911      }
1912  
1913      // Save session information about this submission for anonymous users,
1914      // allowing them to access or edit their submissions.
1915      if (!$user->uid && user_access('access own webform submissions')) {
1916        $_SESSION['webform_submission'][$form_state['values']['details']['sid']] = $node->nid;
1917      }
1918    }
1919    else {
1920      // Sid was found thus update the existing sid in the database.
1921      $submission->sid = $form_state['values']['details']['sid'];
1922      webform_submission_update($node, $submission);
1923      $form_state['values']['details']['is_new'] = FALSE;
1924    }
1925  
1926    $sid = $form_state['values']['details']['sid'];
1927  
1928    // Check if this form is sending an email.
1929    if (!$is_draft && !$form_state['values']['details']['finished']) {
1930      $submission = webform_get_submission($node->nid, $sid, TRUE);
1931  
1932      // Create a themed message for mailing.
1933      foreach ($node->webform['emails'] as $eid => $email) {
1934        // Pass through the theme layer if using the default template.
1935        if ($email['template'] == 'default') {
1936          $email['message'] = theme(array('webform_mail_' . $node->nid, 'webform_mail', 'webform_mail_message'), $node, $submission, $email);
1937        }
1938        else {
1939          $email['message'] = $email['template'];
1940        }
1941  
1942        // Replace tokens in the message.
1943        $email['html'] = ($email['html'] && module_exists('mimemail'));
1944        $email['message'] = _webform_filter_values($email['message'], $node, $submission, $email, FALSE, TRUE);
1945  
1946        // Build the e-mail headers.
1947        $email['headers'] = theme(array('webform_mail_headers_' . $node->nid, 'webform_mail_headers'), $node, $submission, $email);
1948  
1949        // Assemble the FROM string.
1950        if (isset($email['headers']['From'])) {
1951          // If a header From is already set, don't override it.
1952          $email['from'] = $email['headers']['From'];
1953          unset($email['headers']['From']);
1954        }
1955        else {
1956          $email['from'] = webform_format_email_address($email['from_address'], $email['from_name'], $node, $submission);
1957        }
1958  
1959        // Update the subject if set in the themed headers.
1960        if (isset($email['headers']['Subject'])) {
1961          $email['headers']['subject'] = $email['headers']['Subject'];
1962          unset($email['headers']['Subject']);
1963        }
1964        else {
1965          $email['subject'] = webform_format_email_subject($email['subject'], $node, $submission);
1966        }
1967  
1968        // Update the to e-mail if set in the themed headers.
1969        if (isset($email['headers']['To'])) {
1970          $email['email'] = $email['headers']['To'];
1971          unset($email['headers']['To']);
1972        }
1973  
1974        // Generate the list of addresses that this e-mail will be sent to.
1975        $addresses = array_filter(explode(',', $email['email']));
1976        $addresses_final = array();
1977        foreach ($addresses as $address) {
1978          $address = trim($address);
1979  
1980          // After filtering e-mail addresses with component values, a single value
1981          // might contain multiple addresses (such as from checkboxes or selects).
1982          $address = webform_format_email_address($address, NULL, $node, $submission, TRUE, FALSE, 'short');
1983  
1984          if (is_array($address)) {
1985            foreach ($address as $new_address) {
1986              $new_address = trim($new_address);
1987              if (valid_email_address($new_address)) {
1988                $addresses_final[] = $new_address;
1989              }
1990            }
1991          }
1992          elseif (valid_email_address($address)) {
1993            $addresses_final[] = $address;
1994          }
1995        }
1996  
1997        // Mail the webform results.
1998        foreach ($addresses_final as $address) {
1999          // Verify that this submission is not attempting to send any spam hacks.
2000          if (_webform_submission_spam_check($address, $email['subject'], $email['from'], $email['headers'])) {
2001            watchdog('webform', 'Possible spam attempt from @remote_addr' . "<br />\n" . nl2br(htmlentities($email['message'])), array('@remote_add' => ip_address()));
2002            drupal_set_message(t('Illegal information. Data not submitted.'), 'error');
2003            return FALSE;
2004          }
2005  
2006          $language = $user->uid ? user_preferred_language($user) : language_default();
2007          $mail_params = array(
2008            'message' => $email['message'],
2009            'subject' => $email['subject'],
2010            'headers' => $email['headers'],
2011            'node' => $node,
2012            'submission' => $submission,
2013          );
2014  
2015          if (module_exists('mimemail')) {
2016            // Load attachments for the e-mail.
2017            $attachments = array();
2018            if ($email['attachments']) {
2019              webform_component_include('file');
2020              foreach ($node->webform['components'] as $component) {
2021                if (webform_component_feature($component['type'], 'attachment') && !empty($submission->data[$component['cid']]['value'][0])) {
2022                  $file = webform_get_file($submission->data[$component['cid']]['value'][0]);
2023                  if ($file) {
2024                    $file->list = 1; // Needed to include in attachments.
2025                    $attachments[] = $file;
2026                  }
2027                }
2028              }
2029            }
2030  
2031            // Send the e-mail via MIME mail.
2032            mimemail($email['from'], $address, $email['subject'], $email['message'], !$email['html'], $email['headers'], $email['html'] ? NULL : $email['message'], $attachments, 'webform');
2033          }
2034          else {
2035            // Normal Drupal mailer.
2036            drupal_mail('webform', 'submission', $address, $language, $mail_params, $email['from']);
2037          }
2038        }
2039  
2040      }
2041    }
2042  
2043    // Strip out empty tags added by WYSIWYG editors if needed.
2044    $confirmation = strlen(trim(strip_tags($node->webform['confirmation']))) ? $node->webform['confirmation'] : '';
2045    $redirect_url = trim($node->webform['redirect_url']);
2046  
2047    // Remove the domain name from the redirect.
2048    $redirect_url = preg_replace('/^' . preg_quote($GLOBALS['base_url'], '/') . '\//', '', $redirect_url);
2049  
2050    // Check confirmation and redirect_url fields.
2051    $message = NULL;
2052    $external_url = FALSE;
2053    if ($is_draft) {
2054      $redirect = NULL;
2055      $message = t('Draft saved');
2056    }
2057    elseif (!empty($form_state['values']['details']['finished'])) {
2058      $redirect = NULL;
2059      $message = t('Submission updated.');
2060    }
2061    elseif (valid_url($redirect_url, TRUE)) {
2062      $redirect = $redirect_url;
2063      $external_url = TRUE;
2064     }
2065    elseif ($redirect_url && strpos($redirect_url, 'http') !== 0) {
2066      $parts = parse_url($redirect_url);
2067      $query = $parts['query'] ? ($parts['query'] . '&sid=' . $sid) : ('sid=' . $sid);
2068      $redirect = array($parts['path'], $query, $parts['fragment']);
2069    }
2070    else {
2071      $redirect = array('node/' . $node->nid . '/done', 'sid=' . $sid);
2072    }
2073  
2074    // Show a message if manually set.
2075    if (isset($message)) {
2076      drupal_set_message($message);
2077    }
2078    // If redirecting and we have a confirmation message, show it as a message.
2079    elseif (!$external_url && !empty($redirect_url) && !empty($confirmation)) {
2080      drupal_set_message(check_markup($confirmation, $node->webform['confirmation_format'], FALSE));
2081    }
2082  
2083    $form_state['redirect'] = $redirect;
2084  }
2085  
2086  /**
2087   * Post processes the submission tree with any updates from components.
2088   *
2089   * @param $node
2090   *   The full webform node.
2091   * @param $form_values
2092   *   The form values for the form.
2093   * @param $types
2094   *   Optional. Specific types to perform processing.
2095   * @param $parent
2096   *   Internal use. The current parent CID whose children are being processed.
2097   */
2098  function _webform_client_form_submit_process($node, &$form_values, $types = NULL, $parent = 0) {
2099    if (is_array($form_values)) {
2100      foreach ($form_values as $form_key => $value) {
2101        $cid = webform_get_cid($node, $form_key, $parent);
2102        if (is_array($value) && isset($node->webform['components'][$cid]['type']) && webform_component_feature($node->webform['components'][$cid]['type'], 'group')) {
2103          _webform_client_form_submit_process($node, $form_values[$form_key], $types, $cid);
2104        }
2105  
2106        if (isset($node->webform['components'][$cid])) {
2107          // Call the component process submission function.
2108          $component = $node->webform['components'][$cid];
2109          if ((!isset($types) || in_array($component['type'], $types))) {
2110            $new_value = webform_component_invoke($component['type'], 'submit', $component, $form_values[$component['form_key']]);
2111            if ($new_value !== NULL) {
2112              $form_values[$component['form_key']] = $new_value;
2113            }
2114          }
2115        }
2116      }
2117    }
2118  }
2119  
2120  /**
2121   * Flattens a submitted form back into a single array representation (rather than nested fields)
2122   */
2123  function _webform_client_form_submit_flatten($node, $fieldset, $parent = 0) {
2124    $values = array();
2125  
2126    if (is_array($fieldset)) {
2127      foreach ($fieldset as $form_key => $value) {
2128        $cid = webform_get_cid($node, $form_key, $parent);
2129  
2130        if (is_array($value) && webform_component_feature($node->webform['components'][$cid]['type'], 'group')) {
2131          $values += _webform_client_form_submit_flatten($node, $value, $cid);
2132        }
2133        else {
2134          $values[$cid] = $value;
2135        }
2136      }
2137    }
2138  
2139    return $values;
2140  }
2141  
2142  /**
2143   * Prints the confirmation message after a successful submission.
2144   */
2145  function _webform_confirmation($node) {
2146    drupal_set_title(check_plain($node->title));
2147    webform_set_breadcrumb($node);
2148    if (empty($output)) {
2149      $output = theme(array('webform_confirmation_' . $node->nid, 'webform_confirmation'), $node, $_GET['sid']);
2150    }
2151    return $output;
2152  }
2153  
2154  /**
2155   * Prepare for theming of the webform form.
2156   */
2157  function template_preprocess_webform_form(&$vars) {
2158    if (isset($vars['form']['details']['nid']['#value'])) {
2159      $vars['nid'] = $vars['form']['details']['nid']['#value'];
2160    }
2161    elseif (isset($vars['form']['submission']['#value'])) {
2162      $vars['nid'] = $vars['form']['submission']['#value']->nid;
2163    }
2164  }
2165  
2166  /**
2167   * Prepare for theming of the webform submission confirmation.
2168   */
2169  function template_preprocess_webform_confirmation(&$vars) {
2170    $confirmation = check_markup($vars['node']->webform['confirmation'], $vars['node']->webform['confirmation_format'], FALSE);
2171    // Strip out empty tags added by WYSIWYG editors if needed.
2172    $vars['confirmation_message'] = strlen(trim(strip_tags($confirmation))) ? $confirmation : '';
2173  }
2174  
2175  /**
2176   * Prepare to theme the contents of e-mails sent by webform.
2177   */
2178  function template_preprocess_webform_mail_message(&$vars) {
2179    global $user;
2180  
2181    $vars['user'] = $user;
2182    $vars['ip_address'] = ip_address();
2183  }
2184  
2185  /**
2186   * A Form API #pre_render function. Sets display based on #title_display.
2187   *
2188   * Note: this entire function may be removed in Drupal 7, which supports
2189   * #title_display natively.
2190   */
2191  function webform_element_title_display($element) {
2192    if (isset($element['#title_display']) && $element['#title_display'] == 'none') {
2193      $element['#title'] = NULL;
2194    }
2195    return $element;
2196  }
2197  
2198  /**
2199   * A Form API #post_render function. Wraps displayed elements in their label.
2200   *
2201   * Note: this entire function may be removed in Drupal 7, which supports
2202   * #theme_wrappers natively.
2203   */
2204  function webform_element_wrapper($content, $elements) {
2205    if (isset($elements['#theme_wrappers'])) {
2206      foreach ($elements['#theme_wrappers'] as $theme_wrapper) {
2207        $content = theme($theme_wrapper, $elements, $content);
2208      }
2209    }
2210    return $content;
2211  }
2212  
2213  /**
2214   * Replacement for theme_form_element().
2215   */
2216  function theme_webform_element($element, $value) {
2217    $wrapper_classes = array(
2218     'form-item',
2219     $element['#format'] == 'html' ? 'webform-display-item' : 'webform-item',
2220    );
2221    $output = '<div class="' . implode(' ', $wrapper_classes) . '" id="' . $element['#id'] . '-wrapper">' . "\n";
2222    $required = !empty($element['#required']) ? '<span class="form-required" title="' . t('This field is required.') . '">*</span>' : '';
2223  
2224    if (!empty($element['#title'])) {
2225      $title = $element['#title'];
2226      $output .= ' <label for="' . $element['#id'] . '">' . t('!title: !required', array('!title' => filter_xss_admin($title), '!required' => $required)) . "</label>\n";
2227    }
2228  
2229    $output .= '<div id="' . $element['#id'] . '">' . $value . '</div>' . "\n";
2230  
2231    if (!empty($element['#description'])) {
2232      $output .= ' <div class="description">' . $element['#description'] . "</div>\n";
2233    }
2234  
2235    $output .= "</div>\n";
2236  
2237    return $output;
2238  }
2239  
2240  /**
2241   * Output a form element in plain text format.
2242   */
2243  function theme_webform_element_text($element, $value) {
2244    $output = '';
2245    $is_group = webform_component_feature($element['#webform_component']['type'], 'group');
2246  
2247    // Output the element title.
2248    if (isset($element['#title'])) {
2249      if ($is_group) {
2250        $output .= '--' . $element['#title'] . '--';
2251      }
2252      elseif (!in_array(substr($element['#title'], -1), array('?', ':', '!', '%', ';', '@'))) {
2253        $output .= $element['#title'] . ':';
2254      }
2255      else {
2256        $output .= $element['#title'];
2257      }
2258    }
2259  
2260    // Wrap long values at 65 characters, allowing for a few fieldset indents.
2261    // It's common courtesy to wrap at 75 characters in e-mails.
2262    if ($is_group && strlen($value) > 65) {
2263      $value = wordwrap($value, 65, "\n");
2264      $lines = explode("\n", $value);
2265      foreach ($lines as $key => $line) {
2266        $lines[$key] = '  ' . $line;
2267      }
2268      $value = implode("\n", $lines);
2269    }
2270  
2271    // Add the value to the output.
2272    if ($value) {
2273      $output .= (strpos($value, "\n") === FALSE ? ' ' : "\n") . $value;
2274    }
2275  
2276    // Indent fieldsets.
2277    if ($is_group) {
2278      $lines = explode("\n", $output);
2279      foreach ($lines as $number => $line) {
2280        if (strlen($line)) {
2281          $lines[$number] = '  ' . $line;
2282        }
2283      }
2284      $output = implode("\n", $lines);
2285      $output .= "\n";
2286    }
2287  
2288    if ($output) {
2289      $output .= "\n";
2290    }
2291  
2292    return $output;
2293  }
2294  
2295  /**
2296   * Theme the headers when sending an email from webform.
2297   *
2298   * @param $node
2299   *   The complete node object for the webform.
2300   * @param $submission
2301   *   The webform submission of the user.
2302   * @param $email
2303   *   If you desire to make different e-mail headers depending on the recipient,
2304   *   you can check the $email['email'] property to output different content.
2305   *   This will be the ID of the component that is a conditional e-mail
2306   *   recipient. For the normal e-mails, it will have the value of 'default'.
2307   * @return
2308   *   An array of headers to be used when sending a webform email. If headers
2309   *   for "From", "To", or "Subject" are set, they will take precedence over
2310   *   the values set in the webform configuration.
2311   */
2312  function theme_webform_mail_headers($node, $submission, $email) {
2313    $headers = array(
2314      'X-Mailer' => 'Drupal Webform (PHP/' . phpversion() . ')',
2315    );
2316    return $headers;
2317  }
2318  
2319  /**
2320   * Check if current user has a draft of this webform, and return the sid.
2321   */
2322  function _webform_fetch_draft_sid($nid, $uid) {
2323    $result = db_query("SELECT * FROM {webform_submissions} WHERE nid = %d AND uid = %d AND is_draft = 1 ORDER BY submitted DESC", $nid, $uid);
2324    $row = db_fetch_array($result);
2325    if (isset($row['sid'])) {
2326      return (int) $row['sid'];
2327    }
2328    return FALSE;
2329  }
2330  
2331  /**
2332   * Filters all special tokens provided by webform, such as %post and %profile.
2333   *
2334   * @param $string
2335   *   The string to have its tokens replaced.
2336   * @param $node
2337   *   If replacing node-level tokens, the node for which tokens will be created.
2338   * @param $submission
2339   *   If replacing submission-level tokens, the submission for which tokens will
2340   *   be created.
2341   * @param $email
2342   *   If replacing tokens within the context of an e-mail, the Webform e-mail
2343   *   settings array.
2344   * @param $strict
2345   *   Boolean value indicating if the results should be run through check_plain.
2346   *   This is used any time the values will be output as HTML, but not in
2347   *   default values or e-mails.
2348   * @param $allow_anonymous
2349   *   Boolean value indicating if all tokens should be replaced for anonymous
2350   *   users, even if they contain sensitive user information such as %session or
2351   *   %ip_address. This is disabled by default to prevent user data from being
2352   *   preserved in the anonymous page cache and should only be used in
2353   *   non-cached situations, such as e-mails.
2354   */
2355  function _webform_filter_values($string, $node = NULL, $submission = NULL, $email = NULL, $strict = TRUE, $allow_anonymous = FALSE) {
2356    global $user;
2357    static $replacements;
2358  
2359    // Don't do any filtering if the string is empty.
2360    if (strlen(trim($string)) == 0) {
2361      return $string;
2362    }
2363  
2364    // Setup default token replacements.
2365    if (!isset($replacements)) {
2366      $replacements['unsafe'] = array();
2367      $replacements['safe']['%site'] = variable_get('site_name', 'drupal');
2368      $replacements['safe']['%date'] = format_date(time(), 'large');
2369    }
2370  
2371    // Node replacements.
2372    if (isset($node) && !array_key_exists('%title', $replacements)) {
2373      $replacements['safe']['%title'] = $node->title;
2374    }
2375  
2376    // Determine the display format.
2377    $format = isset($email['html']) && $email['html'] ? 'html' : 'text';
2378  
2379    // Submission replacements.
2380    if (isset($submission) && !isset($replacements['email'][$format])) {
2381      module_load_include('inc', 'webform', 'includes/webform.components.inc');
2382  
2383      // E-mails may be sent in two formats, keep tokens separate for each one.
2384      $replacements['email'][$format] = array();
2385  
2386      foreach ($submission->data as $cid => $value) {
2387        $component = $node->webform['components'][$cid];
2388  
2389        // Find by form key.
2390        $parents = webform_component_parent_keys($node, $component);
2391        $form_key = implode('][', $parents);
2392        $display_element = webform_component_invoke($component['type'], 'display', $component, $value['value'], $format);
2393        $replacements['email'][$format]['%email[' . $form_key . ']'] = drupal_render($display_element);
2394        $replacements['email'][$format]['%value[' . $form_key . ']'] = isset($display_element['#children']) ? $display_element['#children'] : '';
2395      }
2396  
2397      // Submission edit URL.
2398      $replacements['unsafe']['%submission_url'] = url('node/' . $node->nid . '/submission/' . $submission->sid, array('absolute' => TRUE));
2399    }
2400  
2401    // Token for the entire form tree for e-mails.
2402    if (isset($submission) && isset($email) && !isset($replacements['email'][$format]['%email_values'])) {
2403      $replacements['email'][$format]['%email_values'] = webform_submission_render($node, $submission, $email, $format);
2404    }
2405  
2406    // Provide a list of candidates for token replacement.
2407    $special_tokens = array(
2408      'safe' => array(
2409        '%get' => $_GET,
2410        '%post' => $_POST,
2411      ),
2412      'unsafe' => array(
2413        '%cookie' => $_COOKIE,
2414        '%session' => isset($_SESSION) ? $_SESSION : array(),
2415        '%request' => $_REQUEST,
2416        '%server' => $_SERVER,
2417        '%profile' => (array) $user,
2418      ),
2419    );
2420  
2421    // Replacements of global variable tokens.
2422    if (!isset($replacements['specials_set'])) {
2423      $replacements['specials_set'] = TRUE;
2424  
2425      // Load profile information if available.
2426      if ($user->uid) {
2427        $account = user_load($user->uid);
2428        $special_tokens['unsafe']['%profile'] = (array) $account;
2429      }
2430  
2431      // User replacements.
2432      if (!array_key_exists('%username', $replacements['unsafe'])) {
2433        $replacements['unsafe']['%username'] = isset($user->name) ? $user->name : '';
2434        $replacements['unsafe']['%useremail'] = isset($user->mail) ? $user->mail : '';
2435        $replacements['unsafe']['%ip_address'] = ip_address();
2436      }
2437  
2438      // Populate the replacements array with special variables.
2439      foreach ($special_tokens as $safe_state => $tokens) {
2440        foreach ($tokens as $token => $variable) {
2441          // Safety check in case $_POST or some other global has been removed
2442          // by a naughty module, in which case $variable may be NULL.
2443          if (!is_array($variable)) {
2444            continue;
2445          }
2446  
2447          foreach ($variable as $key => $value) {
2448            // This special case for profile module dates.
2449            if ($token == '%profile' && is_array($value) && isset($value['year'])) {
2450              $replacement = format_date(strtotime($value['month'] . '/' . $value['day'] . '/' . $value['year']), 'custom', 'F j, Y', '0');
2451            }
2452            else {
2453              $replacement = (!is_array($value) && !is_object($value)) ? $value : '';
2454            }
2455            $replacements[$safe_state][$token . '[' . $key . ']'] = $replacement;
2456          }
2457        }
2458      }
2459    }
2460  
2461    // Make a copy of the replacements so we don't affect the static version.
2462    $safe_replacements = $replacements['safe'];
2463  
2464    // Restrict replacements for anonymous users. Not all tokens can be used
2465    // because they may expose session or other private data to other users when
2466    // anonymous page caching is enabled.
2467    if ($user->uid || $allow_anonymous) {
2468      $safe_replacements += $replacements['unsafe'];
2469      if (isset($replacements['email'][$format])) {
2470        $safe_replacements += $replacements['email'][$format];
2471      }
2472    }
2473    else {
2474      foreach ($replacements['unsafe'] as $key => $value) {
2475        $safe_replacements[$key] = '';
2476      }
2477    }
2478  
2479    $find = array_keys($safe_replacements);
2480    $replace = array_values($safe_replacements);
2481    $string = str_replace($find, $replace, $string);
2482  
2483    // Clean up any unused tokens.
2484    foreach ($special_tokens as $safe_state => $tokens) {
2485      foreach (array_keys($tokens) as $token) {
2486        $string = preg_replace('/\\' . $token . '\[\w+\]/', '', $string);
2487      }
2488    }
2489  
2490    return $strict ? filter_xss($string) : $string;
2491  }
2492  
2493  /**
2494   * Filters all special tokens provided by webform, and allows basic layout in descriptions.
2495   */
2496  function _webform_filter_descriptions($string, $node = NULL, $submission = NULL) {
2497    return strlen($string) == 0 ? '' : check_markup(_webform_filter_values($string, $node, $submission, NULL, FALSE));
2498  }
2499  
2500  /**
2501   * Filter labels for display by running through XSS checks.
2502   */
2503  function _webform_filter_xss($string) {
2504    return filter_xss($string, array('a', 'em', 'strong', 'code'));
2505  }
2506  
2507  /**
2508   * Given a form_key and a list of form_key parents, determine the cid.
2509   *
2510   * @param $node
2511   *   A fully loaded node object.
2512   * @param $form_key
2513   *   The form key for which we're finding a cid.
2514   * @param $parent
2515   *   The cid of the parent component.
2516   */
2517  function webform_get_cid(&$node, $form_key, $pid) {
2518    foreach ($node->webform['components'] as $cid => $component) {
2519      if ($component['form_key'] == $form_key && $component['pid'] == $pid) {
2520        return $cid;
2521      }
2522    }
2523  }
2524  
2525  /**
2526   * Retreive a Drupal variable with the appropriate default value.
2527   */
2528  function webform_variable_get($variable) {
2529    switch ($variable) {
2530      case 'webform_default_from_name':
2531        $result = variable_get('webform_default_from_name', variable_get('site_name', ''));
2532        break;
2533      case 'webform_default_from_address':
2534        $result = variable_get('webform_default_from_address', variable_get('site_mail', ini_get('sendmail_from')));
2535        break;
2536      case 'webform_default_subject':
2537        $result = variable_get('webform_default_subject', t('Form submission from: %title'));
2538        break;
2539      case 'webform_node_types':
2540        $result = variable_get('webform_node_types', array('webform'));
2541        break;
2542    }
2543    return $result;
2544  }
2545  
2546  function theme_webform_token_help($node = NULL) {
2547    $basic_tokens = array(
2548      '%username',
2549      '%useremail',
2550      '%ip_address',
2551      '%site',
2552      '%date',
2553    );
2554  
2555    $special_tokens = array(
2556      '%profile[' . t('key') . ']',
2557      '%server[' . t('key') . ']',
2558      '%session[' . t('key') . ']',
2559      '%get[' . t('key') . ']',
2560      '%post[' . t('key') . ']',
2561      '%request[' . t('key') . ']',
2562    );
2563  
2564    if (isset($node)) {
2565      $submission_tokens = array(
2566        t('@submission_url - The URL for viewing the completed submission.', array('@submission_url' => '%submission_url')),
2567        t('@email_values - All included components in a hierarchical structure.', array('@email_values' => '%email_values')),
2568        t('@email_key - A formatted value and field label. Elements may be accessed such as <em>%email[fieldset_a][key_b]</em>. Do not include quotes.', array('@email_key' => '%email[key]')),
2569        t('@value_key - A value without additional formatting. Elements may be accessed such as <em>%value[fieldset_a][key_b]</em>. Do not include quotes.', array('@value_key' => '%value[key]')),
2570      );
2571    }
2572  
2573    $output = '';
2574    $output .= '<p>' . t('You may use special tokens in this field that will be replaced with dynamic values.') . '</p>';
2575  
2576    if (!empty($submission_tokens)) {
2577      $output .= theme('item_list', $submission_tokens, t('Component variables'));
2578    }
2579  
2580    $output .= theme('item_list', $basic_tokens, t('Basic variables'));
2581    $output .= theme('item_list', $special_tokens, t('Special variables'));
2582    $output .= '<p>' . t('You can use %server[key] to add any of the special PHP <a href="http://www.php.net/reserved.variables#reserved.variables.server">$_SERVER</a> variables, %session[key] to add any of the special PHP <a href="http://www.php.net/reserved.variables#reserved.variables.session">$_SESSION</a> variables and %get[key] to create prefilled forms from the <a href="http://www.php.net/reserved.variables#reserved.variables.get">URL</a>. %cookie, %request and %post also work with their respective PHP variables. For example %server[HTTP_USER_AGENT], %session[id], or %get[q].') . '</p>';
2583    if (module_exists('profile')) {
2584      $output .= '<p>' . t('If you are using the Profile module, you can also access all profile data using the syntax %profile[form_name]. If you for example have a profile value named profile_city, add the variable %profile[profile_city].') . '</p>';
2585    }
2586  
2587    $fieldset = array(
2588      '#title' => t('Token values'),
2589      '#type' => 'fieldset',
2590      '#collapsible' => TRUE,
2591      '#collapsed' => TRUE,
2592      '#children' => '<div>' . $output . '</div>',
2593    );
2594    return theme('fieldset', $fieldset);
2595  }
2596  
2597  function _webform_safe_name($name) {
2598    $new = trim($name);
2599  
2600    // If transliteration is available, use it to convert names to ASCII.
2601    if (function_exists('transliteration_get')) {
2602      $new = transliteration_get($new, '');
2603      $new = str_replace(array(' ', '-', '/'), array('_', '_', '_'), $new);
2604    }
2605    else {
2606      $new = str_replace(
2607        array(' ', '-', '/', '€', 'ƒ', 'Š', 'Ž', 'š', 'ž', 'Ÿ', '¢', '¥', 'µ', 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'à', 'á', 'â', 'ã', 'ä', 'å', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'ÿ', 'Œ',  'œ',  'Æ',  'Ð',  'Þ',  'ß',  'æ',  'ð',  'þ'),
2608        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'),
2609        $new);
2610    }
2611  
2612    $new = drupal_strtolower($new);
2613    $new = preg_replace('/[^a-z0-9_]/', '', $new);
2614    return $new;
2615  }
2616  
2617  /**
2618   * Given an email address and a name, format an e-mail address.
2619   *
2620   * @param $address
2621   *   The e-mail address.
2622   * @param $name
2623   *   The name to be used in the formatted address.
2624   * @param $node
2625   *   The webform node if replacements will be done.
2626   * @param $submission
2627   *   The webform submission values if replacements will be done.
2628   * @param $encode
2629   *   Encode the text for use in an e-mail.
2630   * @param $single
2631   *   Force a single value to be returned, even if a component expands to
2632   *   multiple addresses. This is useful to ensure a single e-mail will be
2633   *   returned for the "From" address.
2634   * @param $format
2635   *   The e-mail format, defaults to the site-wide setting. May be either "short"
2636   *   or "long".
2637   */
2638  function webform_format_email_address($address, $name, $node = NULL, $submission = NULL, $encode = TRUE, $single = TRUE, $format = NULL) {
2639    if (!isset($format)) {
2640      $format = variable_get('webform_email_address_format', 'long');
2641    }
2642  
2643    if ($name == 'default') {
2644      $name = webform_variable_get('webform_default_from_name');
2645    }
2646    elseif (is_numeric($name) && isset($node->webform['components'][$name])) {
2647      if (isset($submission->data[$name]['value'])) {
2648        $name = $submission->data[$name]['value'];
2649      }
2650      else {
2651        $name = t('Value of !component', array('!component' => $node->webform['components'][$name]['name']));
2652      }
2653    }
2654  
2655    if ($address == 'default') {
2656      $address = webform_variable_get('webform_default_from_address');
2657    }
2658    elseif (is_numeric($address) && isset($node->webform['components'][$address])) {
2659      if (isset($submission->data[$address]['value'])) {
2660        $values = $submission->data[$address]['value'];;
2661        $address = array();
2662        foreach ($values as $value) {
2663          $address = array_merge($address, explode(',', $value));
2664        }
2665      }
2666      else {
2667        $address = t('Value of "!component"', array('!component' => $node->webform['components'][$address]['name']));
2668      }
2669    }
2670  
2671    // Convert arrays into a single value for From values.
2672    if ($single) {
2673      $address = is_array($address) ? reset($address) : $address;
2674      $name = is_array($name) ? reset($name) : $name;
2675    }
2676  
2677    // Address may be an array if a component value was used on checkboxes.
2678    if (is_array($address)) {
2679      foreach ($address as $key => $individual_address) {
2680        $address[$key] = _webform_filter_values($individual_address, $node, $submission, NULL, FALSE, TRUE);
2681      }
2682    }
2683    else {
2684      $address = _webform_filter_values($address, $node, $submission, NULL, FALSE, TRUE);
2685    }
2686  
2687    if ($format == 'long' && !empty($name)) {
2688      $name = _webform_filter_values($name, $node, $submission, NULL, FALSE, TRUE);
2689      if ($encode) {
2690        $name = mime_header_encode($name);
2691      }
2692      return '"' . $name . '" <' . $address . '>';
2693    }
2694    else {
2695      return $address;
2696    }
2697  }
2698  
2699  /**
2700   * Given an email subject, format it with any needed replacements.
2701   */
2702  function webform_format_email_subject($subject, $node = NULL, $submission = NULL) {
2703    if ($subject == 'default') {
2704      $subject = webform_variable_get('webform_default_subject');
2705    }
2706    elseif (is_numeric($subject) && isset($node->webform['components'][$subject])) {
2707      $component = $node->webform['components'][$subject];
2708      if (isset($submission->data[$subject]['value'])) {
2709        $display_function = '_webform_display_' . $component['type'];
2710        $value = $submission->data[$subject]['value'];
2711  
2712        // Convert the value to a clean text representation if possible.
2713        if (function_exists($display_function)) {
2714          $display = $display_function($component, $value, 'text');
2715          $display['#theme_wrappers'] = array();
2716          $subject = str_replace("\n", ' ', drupal_render($display));
2717        }
2718        else {
2719          $subject = $value;
2720        }
2721      }
2722      else {
2723        $subject = t('Value of "!component"', array('!component' => $component['name']));
2724      }
2725    }
2726  
2727    // Convert arrays to strings (may happen if checkboxes are used as the value).
2728    if (is_array($subject)) {
2729      $subject = reset($subject);
2730    }
2731  
2732    return _webform_filter_values($subject, $node, $submission, NULL, FALSE, TRUE);
2733  }
2734  
2735  /**
2736   * Convert an array of components into a tree
2737   */
2738  function _webform_components_tree_build($src, &$tree, $parent, &$page_count) {
2739    foreach ($src as $cid => $component) {
2740      if ($component['pid'] == $parent) {
2741        _webform_components_tree_build($src, $component, $cid, $page_count);
2742        if ($component['type'] == 'pagebreak') {
2743          $page_count++;
2744        }
2745        $tree['children'][$cid] = $component;
2746        $tree['children'][$cid]['page_num'] = $page_count;
2747      }
2748    }
2749    return $tree;
2750  }
2751  
2752  /**
2753   * Flatten a component tree into a flat list.
2754   */
2755  function _webform_components_tree_flatten($tree) {
2756    $components = array();
2757    foreach ($tree as $cid => $component) {
2758      if (isset($component['children'])) {
2759        unset($component['children']);
2760        $components[$cid] = $component;
2761        // array_merge() can't be used here because the keys are numeric.
2762        $children = _webform_components_tree_flatten($tree[$cid]['children']);
2763        foreach ($children as $ccid => $ccomponent) {
2764          $components[$ccid] = $ccomponent;
2765        }
2766      }
2767      else {
2768        $components[$cid] = $component;
2769      }
2770    }
2771    return $components;
2772  }
2773  
2774  /**
2775   * Helper for the uasort in webform_tree_sort()
2776   */
2777  function _webform_components_sort($a, $b) {
2778    if ($a['weight'] == $b['weight']) {
2779      return strcasecmp($a['name'], $b['name']);
2780    }
2781    return ($a['weight'] < $b['weight']) ? -1 : 1;
2782  }
2783  
2784  /**
2785   * Sort each level of a component tree by weight and name
2786   */
2787  function _webform_components_tree_sort($tree) {
2788    if (isset($tree['children']) && is_array($tree['children'])) {
2789      $children = array();
2790      uasort($tree['children'], '_webform_components_sort');
2791      foreach ($tree['children'] as $cid => $component) {
2792        $children[$cid] = _webform_components_tree_sort($component);
2793      }
2794      $tree['children'] = $children;
2795    }
2796    return $tree;
2797  }
2798  
2799  /**
2800   * Get a list of all available component definitions.
2801   */
2802  function webform_components($include_disabled = FALSE, $reset = FALSE) {
2803    static $components, $disabled;
2804  
2805    if (!isset($components) || $reset) {
2806      $components = array();
2807      $disabled = array_flip(variable_get('webform_disabled_components', array()));
2808      foreach (module_implements('webform_component_info') as $module) {
2809        $module_components = module_invoke($module, 'webform_component_info');
2810        foreach ($module_components as $type => $info) {
2811          $module_components[$type]['module'] = $module;
2812          $module_components[$type]['enabled'] = !array_key_exists($type, $disabled);
2813        }
2814        $components += $module_components;
2815      }
2816      drupal_alter('webform_component_info', $components);
2817      ksort($components);
2818    }
2819  
2820    return $include_disabled ? $components : array_diff_key($components, $disabled);
2821  }
2822  
2823  /**
2824   * Build a list of components suitable for use as select list options.
2825   */
2826  function webform_component_options($include_disabled = FALSE) {
2827    $component_info = webform_components($include_disabled);
2828    $options = array();
2829    foreach ($component_info as $type => $info) {
2830      $options[$type] = $info['label'];
2831    }
2832    return $options;
2833  }
2834  
2835  /**
2836   * Load a component file into memory.
2837   *
2838   * @param $component_type
2839   *   The string machine name of a component.
2840   */
2841  function webform_component_include($component_type) {
2842    static $included = array();
2843  
2844    // No need to load components that have already been added once.
2845    if (!isset($included[$component_type])) {
2846      $components = webform_components(TRUE);
2847      $included[$component_type] = TRUE;
2848  
2849      if (($info = $components[$component_type]) && isset($info['file'])) {
2850        $pathinfo = pathinfo($info['file']);
2851        $basename = basename($pathinfo['basename'], '.' . $pathinfo['extension']);
2852        $path = (!empty($pathinfo['dirname']) ? $pathinfo['dirname'] . '/' : '') . $basename;
2853        module_load_include($pathinfo['extension'], $info['module'], $path);
2854      }
2855    }
2856  }
2857  
2858  /**
2859   * Invoke a component callback.
2860   *
2861   * @param $type
2862   *   The component type as a string.
2863   * @param $callback
2864   *   The callback to execute.
2865   * @param ...
2866   *   Any additional parameters required by the $callback.
2867   */
2868  function webform_component_invoke($type, $callback) {
2869    $args = func_get_args();
2870    $type = array_shift($args);
2871    $callback = array_shift($args);
2872    $function = '_webform_' . $callback . '_' . $type;
2873    webform_component_include($type);
2874    if (function_exists($function)) {
2875      return call_user_func_array($function, $args);
2876    }
2877  }
2878  
2879  /**
2880   * Disable the Drupal page cache.
2881   */
2882  function webform_disable_page_cache() {
2883    // PressFlow and Drupal 7 method.
2884    if (function_exists('drupal_page_is_cacheable')) {
2885      drupal_page_is_cacheable(FALSE);
2886    }
2887    // Drupal 6 hack to disable page cache.
2888    else {
2889      $GLOBALS['conf']['cache'] = FALSE;
2890    }
2891  }
2892  
2893  /**
2894   * Set the necessary breadcrumb for the page we are on.
2895   */
2896  function webform_set_breadcrumb($node, $submission = NULL) {
2897    $breadcrumb = drupal_get_breadcrumb();
2898  
2899    if (isset($node)) {
2900      $webform_breadcrumb = array();
2901      $webform_breadcrumb[] = array_shift($breadcrumb);
2902      $webform_breadcrumb[] = l($node->title, 'node/' . $node->nid);
2903      if (isset($submission)) {
2904        $last_link = array_shift($breadcrumb);
2905        $webform_breadcrumb[] = l(t('Submissions'), 'node/' . $node->nid . '/submissions');
2906        if (isset($last_link)) {
2907          $webform_breadcrumb[] = $last_link;
2908        }
2909      }
2910      $breadcrumb = $webform_breadcrumb;
2911    }
2912  
2913    drupal_set_breadcrumb($breadcrumb);
2914  }
2915  
2916  /**
2917   * Wrapper function for tt() if i18nstrings enabled.
2918   */
2919  function webform_tt($name, $string, $langcode = NULL, $update = FALSE) {
2920    if (function_exists('tt')) {
2921      return tt($name, $string, $langcode, $update);
2922    }
2923    else {
2924      return $string;
2925    }
2926  }
2927  
2928  /**
2929   * Implementation of hook_views_api().
2930   */
2931  function webform_views_api() {
2932    return array(
2933      'api' => 2.0,
2934      'path' => drupal_get_path('module', 'webform') .'/views',
2935    );
2936  }
2937  
2938  /**
2939   * Implementation of hook_content_extra_fields().
2940   */
2941  function webform_content_extra_fields($type_name) {
2942    $extra = array();
2943    if (in_array($type_name, webform_variable_get('webform_node_types'))) {
2944      $extra['webform'] = array(
2945        'label' => t('Webform'),
2946        'description' => t('Webform client form.'),
2947        'weight' => 10,
2948      );
2949    }
2950    return $extra;
2951  }
2952  
2953  /**
2954   * Implements hook_mollom_form_list().
2955   */
2956  function webform_mollom_form_list() {
2957    $forms = array();
2958    $webform_types = webform_variable_get('webform_node_types');
2959    if (empty($webform_types)) {
2960      return $forms;
2961    }
2962  
2963    $placeholders = db_placeholders($webform_types, 'varchar');
2964    $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);
2965  
2966    while ($node = db_fetch_object($result)) {
2967      $form_id = 'webform_client_form_' . $node->nid;
2968      $forms[$form_id] = array(
2969        'title' => t('@name form', array('@name' => $node->title)),
2970        'entity' => 'webform',
2971        'delete form' => 'webform_submission_delete_form',
2972      );
2973    }
2974    return $forms;
2975  }
2976  
2977  /**
2978   * Implements hook_mollom_form_info().
2979   */
2980  function webform_mollom_form_info($form_id) {
2981    module_load_include('inc', 'webform', 'includes/webform.components');
2982  
2983    $nid = drupal_substr($form_id, 20);
2984    $node = node_load($nid);
2985    $form_info = array(
2986      'title' => t('@name form', array('@name' => $node->title)),
2987      'mode' => MOLLOM_MODE_ANALYSIS,
2988      'bypass access' => array('edit all webform submissions', 'edit any webform content'),
2989      'entity' => 'webform',
2990      'elements' => array(),
2991      'mapping' => array(
2992        'post_id' => 'details][sid',
2993        'author_id' => 'details][uid',
2994      ),
2995    );
2996    // Add components as elements.
2997    // These components can be enabled for textual analysis (when not using a
2998    // CAPTCHA-only protection) in Mollom's form configuration.
2999    foreach ($node->webform['components'] as $cid => $component) {
3000      if (webform_component_feature($component['type'], 'spam_analysis')) {
3001        $parents = implode('][', webform_component_parent_keys($node, $component));
3002        $form_info['elements']['submitted][' . $parents] = check_plain(t($component['name']));
3003      }
3004    }
3005    // Assign field mappings based on webform configuration.
3006    // Since multiple emails can be configured, we iterate over all and take
3007    // over the assigned component for the field mapping in any email, unless
3008    // we already assigned one. We are not interested in administratively
3009    // configured static strings, only user-submitted values.
3010    foreach ($node->webform['emails'] as $email) {
3011      // Subject (post_title).
3012      if (!isset($form_info['mapping']['post_title'])) {
3013        $cid = $email['subject'];
3014        if (is_numeric($cid)) {
3015          $parents = implode('][', webform_component_parent_keys($node, $node->webform['components'][$cid]));
3016          $form_info['mapping']['post_title'] = 'submitted][' . $parents;
3017        }
3018      }
3019      // From name (author_name).
3020      if (!isset($form_info['mapping']['author_name'])) {
3021        $cid = $email['from_name'];
3022        if (is_numeric($cid)) {
3023          $parents = implode('][', webform_component_parent_keys($node, $node->webform['components'][$cid]));
3024          $form_info['mapping']['author_name'] = 'submitted][' . $parents;
3025        }
3026      }
3027      // From address (author_mail).
3028      if (!isset($form_info['mapping']['author_mail'])) {
3029        $cid = $email['from_address'];
3030        if (is_numeric($cid)) {
3031          $parents = implode('][', webform_component_parent_keys($node, $node->webform['components'][$cid]));
3032          $form_info['mapping']['author_mail'] = 'submitted][' . $parents;
3033        }
3034      }
3035    }
3036  
3037    return $form_info;
3038  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7