<?php

// $Id: webform.module,v 1.100 2006/09/17 23:45:34 quicksketch Exp $

/** This module provides a simple way to create forms and questionnaires 
 * for your website.
 * The development of this module was sponsered by &Aring;F Industri AB,
 * Open Source City and Karlstad University Library.
 *
 * @author Pontus Ullgren <ullgren@user.sourceforge.net>
 * @package module_webform
 * @copyright Pontus Ullgren 2003-2005
 * 
 **/

/** 
 * Hook Help - used to retrieve help texts and general infomation about the module
 * @param section specifies the section to display help for.
 * @return a formated string containing the help output.
 **/
function webform_help($section= "admin/help#webform") {
  $output= "";
  switch ($section) {
    case 'admin/settings/webform' :
      $output= t("Webforms are forms and questionnaires. To add one select <strong>create content -&gt; webform</strong>. Below you can set different security and debug options.");
      break;
    case 'admin/help#webform' :
      $output= t("<p>This module lets you create forms or questionnaires and define there content. Submissions from these forms are stored in the database and optionaly also send by e-mail to a predefined address.</p>
      <p>Here is how to create one:</p>
      <ul>
       <li>Go to Create Content and add a webform</li>
       <li>Add a description that is displayed as a teaser and above the actual form.</li>
       <li>Add a confirmation message or redirect node that is to be displayed after a successful submission.</li>
       <li>Add one or more components to your form.</li>
       <li>Optionaly add a email address to which submissions will be send. If no email address is specified no email will be send when submissions are made through the form.</li>
       <li>Optionally select an email (or hidden) component that will be used to populate the return email address on any sent email.</ul>
       <li>Optionally select a textfield (or hidden) component that will be used to populate the subject email field on any sent email.</ul>
      </ul>
      <p>Help on adding and how to configure the components will be shown after adding your first component.</p>
      <p>On user submissions the form content is stored in the database table <i>webform_submitted_data</i> as key-value pairs.</p>
      ");
      break;
    case 'admin/modules#description' :
      $output= t("Enables the creation of forms and questionnaires.");
      break;
    case 'node/add#webform' :
      $output= t("A webform can be a questionnaires, contact or request forms. It can be used to let visitors make contact, register for a event or to enable a complex survey.");
      break;
    case 'webform/helptext#variables' :
      $output= t('Available variables are: %username, %useremail, %site, %date.');
      $output .= ' '.t('You can also use %server[key] to add any of the special PHP <a href="http://www.php.net/reserved.variables#reserved.variables.server">$_SERVER</a> variables and %get[key] to create prefilled forms from from the <a href="http://www.php.net/reserved.variables#reserved.variables.get">URL</a>. (e.g. %server[HTTP_USER_AGENT] or %get[id])');
      if (module_exist('profile')) {
        $output .= t('If you are using the profiles 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 varible %profile[profile_city].');
      }
      break;
  }
  // Call help hooks in plugins:
  $components = _webform_load_components();
  foreach ($components as $component) {
    $helpFunction = "_webform_help_".$component;
    if (function_exists($helpFunction)) {
      $output .= $helpFunction($section);
    }
  }
  return $output;
}

function webform_perm() {
  return array ("create webforms", "edit own webforms", "edit webforms", "access webform results", "clear webform results","use PHP for additional processing");
}

/**
 * Define the human-readable name of a node type.
 */
function webform_node_info() {
  return array ('webform' => array ('name' => 'webform', 'base' => 'webform'));
}

function webform_access($op, $node) {
  global $user;
  
  switch ($op) {
    case "create":
      return user_access("create webforms");
    case "update":
    case "delete":
      if ((user_access("edit own webforms") && ($user->uid == $node->uid)) ||
        (user_access("edit webforms"))) {
        return true;
      }
  }
}

function webform_insert($node) {
  global $user;
  // Correctly set the submission limits
  if ($_POST['edit']['enforce_limit'] === 'no') {
    $node->submit_limit = '-1';
    $node->submit_interval = '157784630'; // 5 years, close enough to 'ever'
  }
  
  // Insert the Webform
  db_query("INSERT INTO {webform} (nid, confirmation, redirect_post, submit_limit, submit_interval, email, email_from, email_subject, additional_validate, additional_submit) VALUES (%d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $node->nid, $node->confirmation, $node->redirect_post, $node->submit_limit, $node->submit_interval, $node->email, $node->email_from, $node->email_subject, $node->additional_validate, $node->additional_submit);
  
  // Re-add existing components from the POST array
  if (isset($_POST['edit']['webformcomponents'])) {
    unset($node->webformcomponents);
    $node->webformcomponents = _webform_components_decode($_POST['edit']['webformcomponents']);
  }

  // Insert the components into the database
  if( is_array($node->webformcomponents) && !empty($node->webformcomponents)) {
    foreach($node->webformcomponents as $cid => $component) {   
      db_query("INSERT INTO {webform_component} (nid, cid, pid, name, type, value, extra, mandatory, weight) ".
               " VALUES (%d, %d, %d, '%s', '%s', '%s', '%s', %d, %d)",
               $node->nid,
               $cid,
               $component['parent'],
               $component['name'], 
               $component['type'],
               $component['value'], 
               serialize($component['extra']), 
               ($component['mandatory']?1:0), 
               $component['weight']
              );
    }
  }
}

function webform_update($node) {  
  // Update the webform by deleting existing data and replacing with the new
  db_query("DELETE FROM {webform} WHERE nid = %d", $node->nid);
  db_query("DELETE FROM {webform_component} WHERE nid = %d", $node->nid);
  webform_insert($node);
}

function webform_delete(& $node) {
  // delete 
  db_query("DELETE FROM {webform} WHERE nid = %d", $node->nid);
  db_query("DELETE FROM {webform_component} WHERE nid = %d", $node->nid);
  watchdog('webform', 'webform "'.$node->title.'" deleted', WATCHDOG_NOTICE);
}

function webform_load($node) {
  // load 
  $page= db_fetch_object(db_query("SELECT * FROM {webform} WHERE nid = %d", $node->nid));
  $result= db_query('SELECT * FROM {webform_component} WHERE nid = %d ORDER BY weight, name', $node->nid);
  while ($c= db_fetch_array($result)) {
    $page->webformcomponents[$c['cid']]['cid']= $c['cid'];
    $page->webformcomponents[$c['cid']]['name']= $c['name'];
    $page->webformcomponents[$c['cid']]['type']= $c['type'];
    $page->webformcomponents[$c['cid']]['value']= $c['value'];
    $page->webformcomponents[$c['cid']]['extra']= unserialize($c['extra']);
    $page->webformcomponents[$c['cid']]['mandatory']= $c['mandatory'];
    $page->webformcomponents[$c['cid']]['parent']= $c['pid'];
    $page->webformcomponents[$c['cid']]['weight']= $c['weight'];
  }
  return $page;
}

function webform_settings() {

  $form['components'] = array (
    '#type' => 'fieldset',
    '#title' => t('Available Components'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
    '#description' => t('Below is a list of supported field types available for webform. You may disable any of these components by unchecking its corresponding box. Only checked components will be available in existing or new webforms.'),
  );
  
  // Add each component to the form:
  $component_types = _webform_load_components(true);
  foreach ($component_types as $componentName => $componentTransName) {
    $form['components']['webform_enable_'.$componentName] = array (
      '#title' => $componentTransName,
      '#description' => module_invoke('webform', 'help', 'admin/settings/webform#'.$componentName.'_description'),
      '#type' => 'checkbox',
      '#checked_value' => 1,
      '#default_value' => variable_get('webform_enable_'.$componentName,1),
    );
  }
  
  $form['advanced'] = array (
    '#type' => 'fieldset',
    '#title' => t('Advanced Options'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  
  $form['advanced']['webform_use_cookies']  = array(
    '#type' => 'checkbox',
    '#checked_value' => 1,
    '#title' => t("Allow Cookies for Tracking Submissions"),
    '#default_value' => variable_get("webform_use_cookies", 0), 
    '#description' => t('<a href="http://www.wikipedia.org/wiki/HTTP_cookie">Cookies</a> can be used to help prevent the same user from repeatedly submitting a webform. This feature is not needed for limiting submissions per user, though it can increase accuracy in some situations. Besides cookies, webform also uses IP addresses and site usernames to prevent repeated submissions.')
  );

  $form['advanced']['webform_debug']  = array(
    '#type' => 'select',
    '#title' => t("Webforms Debug"),
    '#default_value' => variable_get("webform_debug", 0), 
    '#options' => array (0 => "OFF", 1 => "Log submissions", 2 => "Full debug"), 
    '#description' => t('Set this option to "Log submissions" to log all submissions in the watchdog. Set it to "Full debug" to print debug info on submission. You probably want to leave this option on "OFF".')
  );
  return $form;
} // end function webform_settings

function theme_webform_settings_form ($form) {
  // Format the components into a table
  foreach (element_children($form['components']) as $key) {  
    $row = array();
    $row[] = $form['components'][$key]['#title'];
    $row[] = $form['components'][$key]['#description'];
    unset($form['components'][$key]['#title']);
    unset($form['components'][$key]['#description']);
    $row[] = array('data' => form_render($form['components'][$key]), 'align' => 'center');
    $rows[] = $row;
  }
  $header = array(t('Name'), t('Description'), t('Enabled'));
  
  // Create the table inside the form
  $form['components']['table'] = array (
    '#value' => theme('table', $header, $rows)
  );
  
  $output = form_render($form);
  return $output;
}

/**
 * Implementation of hook_menu().
 */
function webform_menu($may_cache) {
  global $user;
  
  $items = array ();
  if ($may_cache) {
    $items[]= array ('path' => 'node/add/webform', 'title' => t('webform'),
      'access' => user_access('create webforms'));
    
    // Submissions listing 
    $items[]= array ('path' => 'admin/webform', 'title' => t('webform'),
      'callback' => 'webform_page',
      'access' => user_access('access webform results'),
      'type' => MENU_NORMAL_ITEM);
        
    return $items;
  }

  if (arg(0) == 'node' && is_numeric(arg(1))) {
    $node = node_load(arg(1));
    if ($node->nid && $node->type == 'webform') {
      $items[]= array ('path' => 'node/'.$node->nid.'/done', 'title' => t('webform'),
                       'callback' => '_webform_confirmation', 'callback arguments' => array(arg(1)),
                       'access' => true,
                       'type' => MENU_CALLBACK); 
      $items[]= array ('path' => 'node/'.$node->nid.'/results', 'title' => t('results'), 
                       'callback' => 'webform_results',
                       'access' => user_access('access webform results'), 
                       'type' => MENU_LOCAL_TASK, 'weight' => 2);
      $items[]= array ('path' => 'node/'.$node->nid.'/results/submissions', 'title' => t('submissions'),
                       'callback' => 'webform_results',
                       'access' => user_access('access webform results'),
                       'weight' => 4,
                       'type' => MENU_DEFAULT_LOCAL_TASK);
      $items[]= array ('path' => 'node/'.$node->nid.'/results/analysis', 'title' => t('analysis'),
                       'callback' => 'webform_results',
                       'access' => user_access('access webform results'),
                       'weight' => 5,
                       'type' => MENU_LOCAL_TASK);
      $items[]= array ('path' => 'node/'.$node->nid.'/results/table', 'title' => t('table'), 
                       'callback' => 'webform_results',
                       'access' => user_access('access webform results'), 
                       'weight' => 6,
                       'type' => MENU_LOCAL_TASK);
      $items[]= array ('path' => 'node/'.$node->nid.'/results/download', 'title' => t('download'), 
                       'callback' => 'webform_results',
                       'access' => user_access('access webform results'),
                       'weight' => 7,
                       'type' => MENU_LOCAL_TASK);
      $items[]= array ('path' => 'node/'.$node->nid.'/results/clear', 'title' => t('clear'), 
                       'callback' => 'webform_results',
                       'access' => user_access('clear webform results'),
                       'weight' => 8,
                       'type' => MENU_LOCAL_TASK);
      $items[]= array ('path' => 'node/'.$node->nid.'/results/delete', 'title' => t('webform'),
                       'callback' => '_webform_confirmation',
                       'access' => user_access('clear webform results'),
                       'type' => MENU_CALLBACK); 
    }
  }

  return $items;
}


/** 
 * Implementation of hook_link().
 * Always add a "view form" link
 **/
function webform_link($type, $node= 0, $main= 0) {
  if ($node->type == 'webform') {
    if ($main == 1) {
      $links[]= l(t('go to form'), "node/$node->nid", array ('title' => t('View this form.'), 'class' => 'read-more'));
    }
  }
  return $links;
}

/** 
 * Implementation of hook_prepare().
 * This function is called before the display of webform_form(). Rather than
 * a typical usage of hook_prepare, in webform it is used to update the contents
 * of the $node object after changes have been made to the node, such as adding a
 * new component or deleting an existing component. The node is altered as necessary,
 * then the user is returned to the display of webform_form() with the changes visible.
 * The changes to the node are not permanent until the user submits the form.
 **/
function webform_prepare (&$node) {

  $op = $_POST['op'];

  switch ($op) {
    case t('Delete Selected'):
      // Re-add existing components
      if (isset($_POST['edit']['webformcomponents'])) {
        $node->webformcomponents = _webform_components_decode($_POST['edit']['webformcomponents']);
      }
      // Delete an existing component
      $cid = $_POST['edit']['selected_component'];
      unset($node->webformcomponents[$cid]);
      break;
      
    case t('Done'):
      // Overwrite the database components with any saved in the POST array
      if (isset($_POST['edit']['node']['webformcomponents'])) {
        $node->webformcomponents = _webform_components_decode($_POST['edit']['node']['webformcomponents']);
        unset($_POST['edit']['node']['webformcomponents']);
      }
      
      // Add the component changes/additions to the node components
      $cid = $_POST['edit']['field']['key'];
      if (isset($_POST['edit']['field']) && is_array($_POST['edit']['field'])) {
        unset($node->webformcomponents[$cid]);
        foreach ($_POST['edit']['field'] as $key => $value) {
          $node->webformcomponents[$cid][$key] = $value;
        }
        unset($_POST['edit']['field']);
      }
  
      // Add saved values of the node
      $old_edit = (array)$node;
      $new_edit = (array)$_POST['edit']['node'];
      
      $edit = array_merge($old_edit, $new_edit);
      $node = (object)$edit;
      
      // Restore the node form's saved POST values
      $_POST['edit'] = $_POST['edit']['node'];

      // Display editted message
      drupal_set_message(t('The form component has been changed. Remember to press Submit on the bottom of this form to save your changes.'));
      break;
    case t('Preview'):
      if (isset($_POST['edit']['webformcomponents'])) {
        $node->webformcomponents = _webform_components_decode($_POST['edit']['webformcomponents']);
      }
      break;
  } // end switch
  
  // Make sure the submission limiter is correctly set
  if ($_POST['edit']['enforce_limit'] === 'no') {
    $node->submit_limit = '-1';
    $node->submit_interval = '157784630'; // 5 years, close enough to 'ever'
  }
}

/** 
 * Implementation of hook_validate().
 * This function is called after the user clicks any button displayed in webform_form().
 * Rather than a typical usage of validation, in webform this is used to perform various
 * actions without kicking the user out of the 'edit' tab. For instance, webform detects
 * the when the buttons "Add" or "Edit Selected" are clicked, and then direct the user
 * to a form for editing a component.
 **/
function webform_validate (&$node) {

  $op = $_POST['op'];
  $node->webformcomponents = _webform_components_decode ($_POST['edit']['webformcomponents']);

  switch ($op) {
    // Intercept these buttons and redirect:
    case t('Add'):
      $errors = form_get_errors();
      if (empty($errors)) {
        $form = webform_edit_field_form($node);
        $output = drupal_get_form('webform_edit_'.$_POST['edit']['webform_newfield_type'],$form);
        print theme('page',$output);
        exit(0);
      }
      break;
    case t('Edit Selected'):
      $errors = form_get_errors();
      if (empty($errors)) {
        $form = webform_edit_field_form($node);
        $component = $node->webformcomponents[$_POST['edit']['selected_component']];
        $output = drupal_get_form('webform_edit_'.$component['type'],$form);
        print theme('page',$output);
        exit(0);
      }
      break;
    case t('Delete Selected'):
      // We have to throw a form error for Drupal to stop form processing
      // The 'selected_component' field item is a radio button, which shouldn't
      // show a red border of any sort when an error is flagged on it
      if ($_POST['edit']['selected_component']) {
        form_set_error('selected_component',t("Field deleted, form must be submitted to save changes"));
      }
      else {
        form_set_error('selected_component',t("A component must be selected to delete"));
      }
      break;
    // Standard form checking:
    case t('Submit'):
    case t('Preview'):
      // Make sure the submission limiter is correctly set
      if ($_POST['edit']['enforce_limit'] === 'yes') {
        if (!is_numeric($_POST['edit']['submit_limit'])) {
          form_set_error('submit_limit',t('Submission limit must be a number'));
        }
      }
      break;
  }
}

/** 
 * Implementation of hook_form()
 * Creates the standard form for editing or creating a webform.
 **/
function webform_form (&$node, &$param) {
/* Start Edit Form */

  $form['webform'] = array(
    '#type' => 'fieldset',
    '#title' => t('Webform Settings'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
    '#weight' => -2);
  
  $form['webform']['title'] = array(
    '#type' => 'textfield',
    '#title' => t('Title'),
    '#default_value' => $node->title,
    '#maxlength' => 128,
    '#required' => TRUE,
  );
  
  $form['webform']['body'] = array(
    '#type' => 'textarea',
    '#title' => t('Description'),
    '#description' => t('Text to be shown as teaser and before the form.'),
    '#default_value' => $node->body,
    '#cols' => 40,
    '#rows' => 10,
    '#required' => TRUE,
  );
  
  $form['webform']['confirmation'] = array(
    '#type' => 'textarea',
    '#title' => t("Confirmation message or redirect URL"),
    '#description' => t("Message to be shown upon successful submission or an absolute path to a redirect page (must start with http://)"),
    '#default_value' => $node->confirmation,
    '#cols' => 40,
    '#rows' => 10,
    '#required' => TRUE,
  );
  
  $form['webform']['format'] = filter_form($node->format);
 
  /* End Edit Form */
  
  /* Start Components Form */
  $form['components'] = array(
    '#type' => 'fieldset',
    '#title' => t('Components'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
    '#weight' => -1);
  
  if (is_array($node->webformcomponents) && !empty($node->webformcomponents)) {
    $options = array();
    foreach($node->webformcomponents as $cid => $component) {  
      $options[$cid] = htmlspecialchars($component['name'], ENT_QUOTES);
    }
    
    if ($node->selected_component > 0) {
        $defaultValue = $node->selected_component;
    }
    else {
        $compKeys = array_keys($node->webformcomponents);
        $defaultValue = array_shift($compKeys);
    }
    
    $form['components']['selected_component'] = array(
      '#type' => 'radios',
      '#options' => $options,
      '#default_value' => $defaultValue,
      '#DANGEROUS_SKIP_CHECK' => TRUE,
    );
    $form['components']['delete_component'] = array(
      '#type' => 'submit',
      '#value' => t('Delete Selected'),
      '#weight' => 2,
    );
    $form['components']['edit_component'] = array(
      '#type' => 'submit',
      '#value' => t('Edit Selected'),
      '#weight' => 2,
    );
  }

  $component_types =  _webform_load_components();
  $form['components']['webform_newfield_type'] = array(
    '#type' => 'select',
    '#title' => t('Add a new component'),
    '#default_value' => $node->webform_newfield_type,
    '#options' => $component_types,
    '#description' => t('Each component adds a new field to the form. Any number components (even of the same type) may be added to the form. Select a component type to add above.'),
    '#weight' => 3,
  );
  $form['components']['add_component'] = array(
    '#type' => 'submit',
    '#value' => t('Add'),
    '#weight' => 4,
  );
  
  /* End Components Form */
  
  /* Start Email Settings Form */
  $form['mailsettings'] = array(
    '#type' => 'fieldset',
    '#title' => t('Mail Settings'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
    '#weight' => 0,
  );
  
  $form['mailsettings']['email'] = array(
    '#type' => 'textfield',
    '#title' =>t("E-mail to address"),
    '#default_value' => $node->email,
    '#description' => t('Form submissions will be e-mailed to this address. Leave blank for none.'),
  );
  
  // Build arrays of possible return email addresses and email subject lines from elements on the form
  $possible_email_from = array ('Default' => t('Default'));
  $possible_email_subject = array ('Default' => t('Default'));
  
  if (is_array($node->webformcomponents) && !empty ($node->webformcomponents)) {
    foreach ($node->webformcomponents as $cid => $component) {
      $type = $component['type'];
      if ($type == 'email' || $type == 'hidden') {
        $possible_email_from[$component['name']] = $component['name'];
      }
      if ($type == 'textfield' || $type == 'hidden') {
        $possible_email_subject[$component['name']] = $component['name'];
      }
    }
  }
  
  $form['mailsettings']['email_from'] = array(
    '#type' => 'select',
    '#title' => t('E-mail from address'),
    '#default_value' => $node->email_from,
    '#options' => $possible_email_from,
    '#description' => t('After adding components to this form, any email or hidden form element may be selected as the sender for e-mails.'),
    '#weight' => 7,
    '#DANGEROUS_SKIP_CHECK' => TRUE,
  );
  $form['mailsettings']['email_subject'] = array(
    '#type' => 'select',
    '#title' => t('E-mail subject'),
    '#default_value' => $node->email_subject,
    '#options' => $possible_email_subject,
    '#description' => t('After adding components to this form, any textfield or hidden form element may be selected as the subject line for e-mails.'),
    '#weight' => 7,
    '#DANGEROUS_SKIP_CHECK' => TRUE,
  );
  
  /* End mail settings form */
  
  /* Start advanced settings form */
  $form['advanced'] = array(
    '#type' => 'fieldset',
    '#title' => t('Advanced Settings'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#weight' => 1);
  
  
  $form['advanced']['submitlimit'] = array(
    '#type' => 'fieldset',
    '#title' => t('Limit the number of submission a user may send within a specified time period.'),
    '#prefix' => '<div class="container-inline">',
    '#suffix' => '</div>',
  );
  
  $form['advanced']['submitlimit']['unlimited'] = array(
    '#name' => 'edit[enforce_limit]', // Override the naming scheme to force these radios into the same group
    '#type' => 'radio',
    '#return_value' => 'no',
    '#title' => 'Unlimited',
    '#default_value' => ($node->submit_limit > 0 ? false : 'no'),
    '#parents' => array('advanced','submitlimit'), // If we don't specify the parent, the forms API will think this is an illegal posting
  );

  $form['advanced']['submitlimit']['limited'] = array(
    '#name' => 'edit[enforce_limit]', // Override the naming scheme to force these radios into the same group
    '#type' => 'radio',
    '#return_value' => 'yes',
    '#prefix' => '<br />',
    '#suffix' => t('Limit to '),
    '#default_value' => ($node->submit_limit > 0 ? 'yes' : false),
    '#parents' => array('advanced','submitlimit'), // If we don't specify the parent, the forms API will think this is an illegal posting
  );
  $form['advanced']['submitlimit']['submit_limit'] = array(
    '#type' => 'textfield',
    '#maxlength' => 2,
    '#size' => 2,
    '#suffix' => ' '.t('submission(s)').' ',
    '#default_value' => ($node->submit_limit > 0 ? $node->submit_limit : ""),
    '#attributes' => array ('style' => 'width: 2em; display: inline;',
                            'onchange' => "javascript: document.getElementsByName('edit[enforce_limit]').item(1).checked = true;",
                            'onclick' => "javascript: document.getElementsByName('edit[enforce_limit]').item(1).checked = true;"
                           ),
  );
  
  $form['advanced']['submitlimit']['submit_interval'] = array(
    '#type' => 'select',
    '#options' => array (
                   '157784630' => t('ever'), // 5 years
                   '1600'   => t('every hour'),
                   '86400'  => t('every day'),
                   '604800' => t('every week'),
                 ),
    '#default_value' => $node->submit_interval,
    '#attributes' => array ('onchange' => "javascript: document.getElementsByName('edit[enforce_limit]').item(1).checked = true;",
                            'onclick' => "javascript: document.getElementsByName('edit[enforce_limit]').item(1).checked = true;"
                           ),
  );
  
  if (user_access('use PHP for additional processing')) {
    $form['advanced']['additional_validate'] = array(
      '#type' => 'textarea',
      '#title' => 'Additional Validation',
      '#description' => t('Enter PHP code to preform additional validation for this form. Include the &lt;?php ?&gt; tags. $form_id and $form_values are available variables. If validation fails, use the form_set_error function to prevent the form from being submitted. Use the same syntax as a _validate function used in the <a href="http://api.drupal.org/api/4.7/file/developer/topics/forms_api.html">Forms API</a>.'),
      '#default_value' => $node->additional_validate,
      '#cols' => 40,
      '#rows' => 10,
    );
    
    $form['advanced']['additional_submit'] = array(
      '#type' => 'textarea',
      '#title' => 'Additional Processing',
      '#description' => t('Enter PHP code to preform additional processing for this form (after the validation). Include the &lt;?php ?&gt; tags. $form_id and $form_values are available variables, use the same syntax as a _submit function used in the <a href="http://api.drupal.org/api/4.7/file/developer/topics/forms_api.html">Forms API</a>.'),
      '#default_value' => $node->additional_submit,
      '#cols' => 40,
      '#rows' => 10,
    );
  }
  $form['advanced']['redirect_post'] = array(
    '#type' => 'checkbox',
    '#title' => t("Redirect POST Values"),
    '#description' => t("Forward the contents of the POST array to the redirect URL. Use this option for custom processing of the form information. No processing will be done by webform. The confirmation option above MUST be a full redirect URL for this option to have an effect."),
    '#default_value' => $node->redirect_post,
  );
  
  /* End Advanced Settings Form */
  
  // Add hidden form elements containing the contents of the components
  if (is_array($node->webformcomponents) && !empty($node->webformcomponents)) {
    $form['webformcomponents'] = array (
      '#type' => 'fieldset',
      '#tree' => TRUE,
      '#attributes' => array('style' => 'display: none;'),
    );
    foreach ($node->webformcomponents as $cid => $component) {
    // Create a hidden field with the component's values
      $form['webformcomponents'][$cid] = array(
        '#type' => 'hidden',
        '#value' => base64_encode(serialize($component)),
      );
    }
  }
  
  // Create our forms and display the output:
  return $form;
}
/**
 * Theme the node form. Use a standard Drupal table to organize the components fieldset
 *
 * @param array $form
 * @return string Formatted HTML form, ready for display
 */
function theme_webform_node_form ($form) {
  $node = $form['#node'];
  if (is_array($node->webformcomponents) && !empty($node->webformcomponents)) {
    $componentTree = array();
    _webform_components_tree_build($node->webformcomponents, $componentTree);
    $componentTree = _webform_components_tree_sort($componentTree);
    // Build the table rows
    function _webform_add_rows($cid, $component, $level, &$form, &$rows) { 
      // Create presentable values
      if (strlen($component['value']) > 30) {
        $component['value'] = htmlspecialchars(substr($component['value'],0,30), ENT_QUOTES);
        $component['value'] .= "...";
      }
      
      // Add padding to the radio label
      $form['components']['selected_component'][$cid]['#title'] = '<span style="padding-left: '.($level*15).'px; padding-right: 20px;">'.$form['components']['selected_component'][$cid]['#title'].'</span>';
      
      // Add each component to a table row
      $rows[] = array(
        form_render($form['components']['selected_component'][$cid]),
        $component['type'],
        ($component['value'] == "") ? "-" : $component['value'],
        ($component['mandatory'] == 1) ? "Y" : "N" ,
        $component['weight'],
      );
      if (is_array($component['children'])) {
        foreach ($component['children'] as $cid => $component) {
          _webform_add_rows($cid, $component, $level+1, $form, $rows);
        }
      }
    }
    foreach ($componentTree['children'] as $cid => $component) {
      _webform_add_rows($cid, $component, 0, $form, $rows);
    }
  }
  else {
    $rows[] = array(NULL,array('data' => "No Components, add a component below.", 'colspan' => 5));
  }

  $headers = array(
    array('data' => t('Name'), 'style' => 'padding-left: 30px'),
    t('Type'),
    t('Value'),
    t('Mandatory'),
    t('Weight'),
  );
  $componentTable .= theme('table', $headers, $rows);

  $form['components']['table'] = array (
    '#value' => $componentTable,
  );
  return form_render($form);
}

function webform_edit_field_form (&$node) {
  // we have to do what the framework would do for us so we don't lose information.
  if(!isset($_POST['edit'])) {
    // We are calling this page in the wrong way ... 
    // Do some clean up ??
    drupal_set_message(t('Page called out of order'),'error');
    drupal_set_title("Webform Error");
    return array();
  }
  
  // This is the information about the current field.
  $currfield = array();

  if ( $_POST['op'] == t('Edit Selected') ) {
    // Check to make sure a valid component id was selected
    $cid = $_POST['edit']['selected_component'];
    $component = $node->webformcomponents[$cid];
    if (empty($component)) {
      drupal_set_message(t('Component not found'),'error');
      drupal_set_title("Webform Error");
      return array();
    }
    // We are editing a existing field.
    // Fetch all filed data into the $currfield object.
    $currfield['key'] = $cid;
    $currfield['type'] = $component['type'];
    $currfield['name'] = $component['name'];
    $currfield['default'] = $component['value'];
    $currfield['parent'] = $component['parent'];
    $currfield['weight'] = $component['weight'];
    $currfield['mandatory'] = $component['mandatory'];
    $currfield['extra'] = $component['extra'];
    drupal_set_title("Edit component: ".$currfield['name']." (".$currfield['type'].")");
  }
  else {
    // Check to make sure a valid component type was selected
    $component_types =  _webform_load_components();
    $new_component_type = $_POST['edit']['webform_newfield_type'];
    if (empty($new_component_type) || !key_exists($new_component_type,$component_types)) {
      drupal_set_message(t('Unknown component type \''.$new_component_type.'\''),'error');
      drupal_set_title("Webform Error");
      print theme('page', "");
      return;
    }
    // We are editing a new node
    $currfield['key'] = time();
    $currfield['type'] = $_POST['edit']['webform_newfield_type'];
    $currfield['name'] = $currfield['type']."_".$currfield['key'];
    drupal_set_title("Add new component (".$currfield['type'].")");
  }
  
  // Construct the form:
  $form = array();
  
  // Print the correct field type specification
  // We always need: name and description.
  $form['field'] = array(
    '#type' => 'fieldset',
    '#title' => t('Field Details'),
    '#collapsible' => FALSE,
    '#collapsed' => FALSE,
    '#weight' => 5,
    '#tree' => TRUE,
  );
  
  
  $form['field']['type'] = array(
    '#type' => 'hidden',
    '#value' => $currfield['type'],
  );
  $form['field']['key'] = array(
    '#type' => 'hidden',
    '#value' => $currfield['key'],
  );
  $form['field']['name'] = array(
    '#type' => 'textfield',
    '#default_value' => $currfield['name'],
    '#title' => t("Name"),
    '#description' => t('Name of the field. This is used as a descriptive label and as the key when saving data in the database.'),
    '#required' => TRUE,
    '#weight' => -1,
  );
  $form['field']['extra']['description'] = array(
    '#type' => 'textfield',
    '#default_value' => $currfield['extra']['description'],
    '#title' => t("Description"),
    '#description' => t('A short description of the field used as help for the user when he/she uses the form.').'<br />'.
                          webform_help('webform/helptext#variables'),
    '#weight' => -1,
  );
  
  $form['field']['mandatory'] = array (
    '#type' => 'checkbox',
    '#title' => t("Mandatory"),
    '#default_value' => ($currfield['mandatory']=='1'?TRUE:FALSE),
    '#description' => t('Check this option if the user must enter a value.'),
    '#weight' => 2,
  );
  
  if (variable_get('webform_enable_fieldset',true) && is_array($node->webformcomponents)) {
    $options = array('0' => 'Root');
    foreach ($node->webformcomponents as $thiscid => $value) {
      if ($value['type'] == 'fieldset' && $thiscid != $cid) {
        $options[$thiscid] = htmlspecialchars($value['name'], ENT_QUOTES);
      }
    }
    $form['field']['parent'] = array (
      '#type' => 'select',
      '#title' => t("Parent Fieldset"),
      '#default_value' => $currfield['parent'],
      '#description' => t('Optional. You may organize your form by placing this component inside inside another fieldset.'),
      '#options' => $options,
      '#weight' => 2,
    );
  }
  
  $form['field']['weight'] = array (
    '#type' => 'weight',
    '#delta' => (count($_POST['edit']['webformcomponents'])>10?count($_POST['edit']['webformcomponents']):10),
    '#title' => t("Weight"),
    '#default_value' => $currfield['weight'],
    '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'),
    '#weight' => 2,
  );

  // Add the fields specific to this component type:  
  _webform_load_components(); // Load all component types
  $editFunction = "_webform_edit_".$currfield['type'];
  if (function_exists($editFunction)) {
    $additionalFormElements = $editFunction($currfield); // Call the component render function
  }
  else {
    drupal_set_message(t("The webform component")." '".$currfield['type']."' ".t(" does not have an edit function defined"));
  }

  // Merge the additional fields with the current fields:
  $extraFieldsCopy = $form['field']['extra'];
  $form['field'] = array_merge($form['field'],$additionalFormElements);
  $form['field']['extra'] = array_merge((array)$extraFieldsCopy,(array)$additionalFormElements['extra']);
  
  // Add the submit button
  $form['field']['submit'] = array (
    '#type' => 'submit',
    '#value' => t("Done"),
    '#weight' => 3,
  );

  // Create hidden form elements to restore all the settings on the node edit form
  $form['node'] = array (
    '#tree' => TRUE,
  );

  // Recursively create hidden form elements for all fields on the node form
  function _webform_edit_field_form_hiddens (&$form, $key, $value) {
    if (is_array($value)) {
      foreach ($value as $k => $v) {
        _webform_edit_field_form_hiddens($form[$key], $k, $v);
      }
    }
    else {
      $form[$key] = array (
        '#type' => 'hidden',
        '#value' => $value
      );
    }
  }
  _webform_edit_field_form_hiddens($form, 'node', $_POST['edit']);

  return $form;
}

function webform_view(&$node, $teaser = 0, $page = 0) {
  if (arg(0) == "comment" || $teaser) {
    // Do not render the form for comments or on teasers
    return;
  }
  
  include_once (drupal_get_path('module', 'webform')."/webform.inc");
  _webform_load_components(); // Load all the components
  
  global $user, $sid_to_display, $submission, $preview;
  $sid_to_display = check_plain($_GET['sid']); // Will be NULL if no sid
  $submission = array();
  $preview = false;
  
  if ($_POST['op'] == t('Preview')) {
    $preview = true;
    webform_prepare($node);
  }
  
  if (module_exist('profile')) {
    profile_load_profile($user);
  }

  if ($sid_to_display && is_numeric($sid_to_display)) {
    if (user_access('access webform results')) {
      drupal_set_title('Submission #'.$sid_to_display);
      $node->body = "";
      $submission = _webform_fetch_submission($sid_to_display);
    } else {
      $sid_to_display = NULL; // This user is not allowed to access a submitted node. Deny the attempt without alert.
      watchdog('webform', t('Unauthorized webform access attempt', array ('%name' => "<em>$user->name</em>")), WATCHDOG_WARNING); // and log the attempt
    }
  }

  if ($node->redirect_post && valid_url(trim($node->confirmation), true)) {
    $form['#action'] = trim($node->confirmation);
  }
  
  // Add a theme function for this form
  $form['#theme'] = 'webform_form_'.$node->nid;
  // Set the encoding type (necessary for file uploads)
  $form['#attributes']['enctype'] = 'multipart/form-data';
  
  if (is_array($node->webformcomponents) && !empty ($node->webformcomponents)) {
    // Prepare a new form array
    $form['submitted'] = array(
      '#tree' => TRUE
    );
    $form['details'] = array (
      '#tree' => true,
    );
    
    // Put the components into a tree structure
    $componentTree = array();
    _webform_components_tree_build($node->webformcomponents,$componentTree);
    // Recursively add components to the form
    foreach ($componentTree['children'] as $cid => $component) {
      _webform_view_add_component($cid, $component, $form['submitted'], $form);
    }
    // Do not display the submit button if this is a preview or submission view
    if (!$preview && empty($sid_to_display)) {
      // Additional hidden elements   
      // Email subject and sender
      $form['details']['email_subject'] = array (
        '#type'      => 'hidden',
        '#value'     => $node->email_subject,
      );
      $form['details']['email_from'] = array (
        '#type'      => 'hidden',
        '#value'     => $node->email_from,
      );
      $form['details']['nid'] = array (
        '#type'      => 'value',
        '#value'     => $node->nid,
      );
      
      // Add the submit button
      $form['submitbutton'] = array(
        '#type' => 'submit',
        '#value' => t('Submit'),
        '#weight' => 1000,
      );
    }
  }
  
  // Note that the form name is defined here ('webform_client'). This in turn will
  // use the From API to call the 'webform_client_validate' and 'webform_client_submit' functions
  $output = drupal_get_form('webform_client',$form);
  
  // Remove the surrounding <form> tag if this is a preview
  if ($preview) {
    $output = preg_replace('/<\/?form[^>]*>/','',$output);
  }
  
  $node->body = check_markup($node->body, $node->format, FALSE);
  $node->body .= $output;
} // end webform_view


function _webform_view_add_component ($cid, $component, &$parent_fieldset, &$form) {
  global $sid_to_display, $submission, $preview;
  
  // Load with submission information if necessary
  if($sid_to_display){
    // This component is display only, with the value set according information
    // previously submitted in the submission numbered $sid_to_display
    $displayFunction = "_webform_submission_display_".$component['type'];
    if (function_exists($displayFunction)) {
      $parent_fieldset[$cid] = $displayFunction($submission['data'][$cid],$component);
    }
  } else {
    // Add this user-defined field to the form (with all the values that are always available)      
    $renderFunction = "_webform_render_".$component['type'];
    if (function_exists($renderFunction)) {
      $parent_fieldset[$cid] = $renderFunction($component); // Call the component render function
    } else {
      drupal_set_message(t("The webform component")." '".$component['type']."' ".t("is not able to be displayed"));
    }
  }
  
  if (is_array($component['children'])) {
    foreach ($component['children'] as $scid => $subcomponent) {
      _webform_view_add_component ($scid, $subcomponent, $parent_fieldset[$cid], $form);
    }
  }
}

function webform_client_validate($form_id, $form_values) {
  global $user, $base_url;
  include_once (drupal_get_path('module', 'webform')."/webform.inc");
  $node = node_load(array('nid'=>$form_values['details']['nid']));

  // Flatten trees within the submission
  $form_values['submitted_tree'] = $form_values['submitted'];
  _webform_client_submit_flatten($node, $form_values['submitted'], $form_values['submitted']);
  
  // Verify that this submission is within the submission limits on this form
  if ($violationCount = _webform_submission_limit_check ($node, $form_values)) {
    // If the webform is being swamped by repeated entries, limit the messages in watchdog
    if ($violationCount < 21) {
      if ($user->uid > 0) {
        watchdog('webform', t('The authenticated user <a href="%userURL">%username</a> attempted to submit more entries than allowed on the <a href="%webformURL">%webformTitle</a> webform',
                              array(
                                '%userURL' => url('user/'.$user->uid),
                                '%username' => $user->name,
                                '%webformURL' => url('node/'.$node->nid),
                                '%webformTitle' => $node->title,
                              )), WATCHDOG_WARNING);
      } else {
        watchdog('webform', t('An anonymous user with IP address %ip attempted to submit more entries than allowed on the <a href="%webformURL">%webformTitle</a> webform',
                              array(
                                '%ip' => $_SERVER['REMOTE_ADDR'],
                                '%webformURL' => url('node/'.$node->nid),
                                '%webformTitle' => $node->title,
                              )), WATCHDOG_WARNING);
      }
    }
    form_set_error('',t("You have submitted the maximum number of entries. Check submission guidelines."));
  }
  
  if (trim($node->additional_validate)) {
    // We use eval here (rather than drupal_eval) because the user needs access to local variables
    eval("?>".$node->additional_validate);
  }
}

function webform_client_submit($form_id, $form_values) {
  global $user, $base_url;
  include_once (drupal_get_path('module', 'webform')."/webform.inc");
  $node = node_load(array('nid'=>$form_values['details']['nid']));

  // Flatten trees within the submission
  $form_values['submitted_tree'] = $form_values['submitted'];
  _webform_client_submit_flatten($node, $form_values['submitted'], $form_values['submitted']);
  
  // Process post-submission data if necessary in the components
  _webform_load_components();
  foreach ($node->webformcomponents as $cid => $component) {
    $submitFunction = "_webform_submit_".$component['type'];
    if (function_exists($submitFunction)) {
      $submitFunction($form_values['submitted'][$cid], $component); // Call the component process submission function
    }
  }
  
  // Save the submission to the database
  $sid = _webform_save_submission($node, $form_values['submitted']);
  
  // Check if this form is sending an email
  if (isset ($node->email)) {
    $node->email = strip_tags($node->email);
    if (!empty ($node->email)) {
    
      // Create a themed message for mailing
      // Check for a node-specific message:
      $message = theme("webform_create_mailmessage_$node->nid",$form_values, $node, $sid);
      if (!$message) {
        // Otherwise use the generic form:
        $message = theme("webform_create_mailmessage",$form_values, $node, $sid);
      }
      
      // Build arrays of possible return email addresses and email subject lines from elements on the form
      
      // Default values:
      $email_from_string= "From: ".mime_header_encode(variable_get('site_mail', "webmail@{$_SERVER['SERVER_NAME']}"))."\r\n";
      $email_subject_string= t('Form submission from: ').' '.$node->title;

      // Search for user set FROM and SUBJECT fields
      if (is_array($node->webformcomponents) && !empty ($node->webformcomponents)) {
        foreach ($node->webformcomponents as $cid => $component) {
          $type= $component['type'];
          // Find and set a custom FROM: field
          if ($type == 'email' || $type == 'hidden') {
            if ($component['name'] == $node->email_from) {
              $email_from_string= "From: ".mime_header_encode(strip_tags($form_values['submitted'][$cid]))."\r\n";
            }
          }
          // Find and set a custom SUBJECT: field
          if ($type == 'textfield' || $type == 'hidden') {
            if ($component['name'] == $node->email_subject) {
              $email_subject_string= strip_tags($form_values['submitted'][$cid]);
            }
          }
          if ($type == 'email' && $component['extra']['carboncopy'] == 'Y' ) {
            $headers  .= "CC: ".$form_values['submitted'][$cid]."\r\n";
          }
        }
      }
      
      // Verify that this submission is not attempting to send any spam hacks
      if (_webform_submission_spam_check ($node->email, $email_from_string, $email_subject_string, $message)) {
        watchdog('webform', t('Possible spam attempt from ').$_SERVER['REMOTE_ADDR']."<br />\n".nl2br(htmlentities($message)), WATCHDOG_WARNING);
        drupal_set_message(t("Illegal information. Data not submitted."), 'error');
        return false;
      }
      // Additional headers
      $headers .= $email_from_string;
      $headers .= $email_subject_string;
      $headers .= "Date: ".date("r")."\r\n";
      $headers .= "X-Mailer: Drupal Webform (PHP/" . phpversion().")";
    
      // Mail the webform results
      user_mail($node->email, $email_subject_string, $message, $headers);
      
      // Debugging output
      if (variable_get('webform_debug',0) >= 2) {
        drupal_set_message("E-mail Headers: <pre>".$headers."</pre>E-mail Body: <pre>".$message."</pre>");
      }
    }
  }
   
  if (variable_get('webform_debug', 0) >= 1) {
    watchdog('webform', t('Submission posted to ')."'$node->title' ".l(t('Results'),'node/'.$node->nid,NULL,'sid='.$sid)."<br />\n".nl2br(htmlentities($message)), WATCHDOG_NOTICE);
  }

  if (trim($node->additional_submit)) {
    // We use eval here (rather than drupal_eval) because the user needs access to local variables
    eval("?>".$node->additional_submit);
  }
  
  // Check confirmation field to see if redirect should be to another node or a message
  if (valid_url(trim($node->confirmation), true)) {
    header("Location: ".trim($node->confirmation));
    exit();
  } else {
    drupal_goto('node/'.$node->nid.'/done');
  }
} // end webform_client_submit

/**
 * Flattens a submitted form back into a single array representation (rather than nested fields)
 */
function _webform_client_submit_flatten($node, $fieldset, &$form) {
  foreach ($fieldset as $key => $value) {
    if (is_array($value) && $node->webformcomponents[$key]['type'] == 'fieldset') {
      _webform_client_submit_flatten($node, $value,$form);
      $form[$key] = ""; // Set the fieldset value to a blank
      unset($fieldset[$key]);
    } else {
      $form[$key] = $value;
    }
  }
}

/**
 * Prints the confirmation message after a successful submission.
 */
function _webform_confirmation($nid) {
  if ($node = node_load(array ('nid' => $nid))) {
    if (node_access('view', $node)) {
      drupal_set_title($node->title);
      return theme('webform_confirmation',$node);
    } else {
      drupal_access_denied();
    }
  } else {
    drupal_set_message(t("No node with the id '%nid' could be found",array('%nid'=>$nid)));
    drupal_not_found();
  }
} // end _webform_confirmation

/**
 * Themable function for webform submission confirmation
 */
function theme_webform_confirmation($node) {
  $node->body = check_markup($node->confirmation, $node->format, FALSE);
  $node->links[] = l(t('Go back to the form'), 'node/'.$node->nid);
  return theme('node', $node, FALSE, TRUE);
}

/** 
 * Filters all special chars
 **/
function _webform_filtervalues($string, $strict = TRUE) {
  global $user;

  $find= array ('%username', '%useremail', '%site', '%date');
  $replace= array ($user->name, $user->mail, variable_get('site_name', 'drupal'), format_date(time(), 'large'));

  if (module_exist('profile')) {
    foreach ($user as $k => $v) {
      $find[]= "%profile[$k]";
      $replace[]= $v;
    }
  }
  foreach ($_SERVER as $k => $v) {
    $find[]= "%server[$k]";
    $replace[]= $v;
  }
  foreach ($_GET as $k => $v) {
    $find[]= "%get[$k]";
    $replace[]= $v;
  }
  foreach ($_POST as $k => $v) {
    $find[]= "%post[$k]";
    $replace[]= $v;
  }
  $string= str_replace($find, $replace, $string);

  if (module_exist('profile')) {
    // Clean up any unused %profile stuff
    $string= preg_replace('/\%profile\[\w+\]/', '', $string);
  }
  // Clean up any unused %post and %get stuff
  $string= preg_replace('/\%get\[\w+\]/', '', $string);
  $string= preg_replace('/\%post\[\w+\]/', '', $string);
  // Clean up any unused %system stuff
  $string= preg_replace('/\%server\[\w+\]/', '', $string);

  if ($strict) {
  	return filter_xss($string);
  } else {
    return $string;
  }
} // end _webform_filtervalues

function _webform_save_submission($node, $submitted) {
  global $user;

  $sid = db_next_id('{webform_submissions}_sid');
  
  // 'user' is a reserved keyword in Postgres
  $user_col = $GLOBALS['db_type'] == 'pgsql' ? '"user"' : 'user';
  db_query("INSERT INTO {webform_submissions} (nid, sid, submitted, $user_col, remote_addr) "." VALUES (%d, %d, %d, '%s', '%s')", $node->nid, $sid, time(), $user->name, $_SERVER['REMOTE_ADDR']);

  foreach ($submitted as $cid => $value) {
    if (is_array($value)) {
      $key = 0;
      foreach ($value as $k => $v) {
        db_query("INSERT INTO {webform_submitted_data} (nid, sid, cid, no, data) "."VALUES (%d, %d, %d, %d, '%s')", $node->nid, $sid, $cid, $key, $v);
        $key++;
      }
    }
    else {
      db_query("INSERT INTO {webform_submitted_data} (nid, sid, cid, no, data) "."VALUES (%d, %d, %d, %d, '%s')", $node->nid, $sid, $cid, 0, $value);
    }
  }
  
  return $sid;
}

function webform_page() {
  include_once (drupal_get_path('module', 'webform')."/webform.inc");
  return _webform_page();
}

/**
 * function webform_results() is an allocator function that builds the page under the 'results' sub menu
 * The function uses the URL tail to nominate internal content.
 */
function webform_results() {
  include_once (drupal_get_path('module', 'webform')."/webform.inc");
  include_once (drupal_get_path('module', 'webform')."/webform_report.inc");

  $nid= arg(1);
  $node= node_load(array ('nid' => $nid));

  $title= $node->title;
  drupal_set_title($title);

  if (arg(2) == 'results') {

    switch (arg(3)) {
      case 'analysis' :
        $content= _webform_results_analysis($nid);
        break;
      case 'clear' :
        $content= _webform_results_clear($nid);
        break;
      case 'delete' :
        $sid= arg(4);
        $content= _webform_submission_delete($nid, $sid);
        break;
      case 'table' :
        $content= _webform_results_table($nid);
        break;
      case 'download' :
        $content= _webform_results_download($nid);
        break;
      case 'submissions' :
      default :
        $content= _webform_results_submissions($nid);
        break;
    }

    return $content;
  }
}

/**
 * Returns the version of this release of the webform module.
 *
 * @return array An array with keys 'text' and 'build' containing the
 *         text version and build ID of this release, respectively.
 */
function _webform_version() {
  /* Why text and an ID?  Well, the text is easier for the user to
   * read and understand while the build ID, being a number (a date
   * with a serial, specifically), is easier for the developer to use
   * to determine newer/older versions for upgrade and installation
   * purposes.
   */

  return array ("text" => "4.7.1", "build" => 471);
} // function _webform_version

function _webform_safe_name($name) {
  $name = drupal_strtolower(trim($name));
  $name = str_replace(' ', '_', $name);
  $name = preg_replace('/[^a-z0-9_]/', '', $name);
  return $name;
}

function _webform_components_decode ($components) {
  if (is_array($components)) {
    foreach ($components as $cid => $value) {
      if (is_string($value)) {
        $components[$cid] = unserialize(base64_decode($value));
      }
    }
  }
  return $components;
}

/**
 * Convert an array of components into a tree
 */
function _webform_components_tree_build ($src, &$tree, $parent = 0) {
  foreach ($src as $cid => $component) {
    if ($component['parent'] == $parent) {
      _webform_components_tree_build($src, $component, $cid);
      $tree['children'][$cid] = $component;
    }
  }
  return $tree;
}

/**
 * Helper for the uasort in webform_tree_sort()
 */
function _webform_components_sort($a, $b) {
  if ($a['weight'] == $b['weight']) {
    return strcasecmp($a['name'],$b['name']);
  }
  return ($a['weight'] < $b['weight']) ? -1 : 1;
}


/**
 * Sort each level of a component tree by weight and name
 */
function _webform_components_tree_sort ($tree) {
  if (is_array($tree['children'])) {
    uasort($tree['children'],"_webform_components_sort");
    foreach($tree['children'] as $cid => $component) {
      $return[$cid] = _webform_components_tree_sort($component);
    }
    $tree['children'] = $return;
  }
  return $tree;
}

function _webform_load_components ($returnAll = false) {
  static $componentList;
  
  if (!isset($componentList)) {
    $componentList = array();
    $path = drupal_get_path('module', 'webform')."/components";
    $files = file_scan_directory($path, '^.*\.inc$');
    foreach ($files as $filename => $file) {
      if ($returnAll || variable_get('webform_enable_'.$file->name,1)) {
        include_once($filename);
        $componentList[$file->name] = t($file->name);
      }
    }
  }
  return $componentList;
}
