[ Index ]

PHP Cross Reference of Drupal 6 (yi-drupal)

title

Body

[close]

/sites/all/modules/webform/includes/ -> webform.components.inc (source)

   1  <?php
   2  
   3  /**
   4   * @file
   5   * Webform module component handling.
   6   */
   7  
   8  /**
   9   * Provides interface and database handling for editing components of a webform.
  10   *
  11   * @author Nathan Haug <nate@lullabot.com>
  12   */
  13  
  14  /**
  15   * Overview page of all components for this webform.
  16   */
  17  function webform_components_page($node) {
  18    $form = drupal_get_form('webform_components_form', $node);
  19    return theme('webform_components_page', $node, $form);
  20  }
  21  
  22  /**
  23   * Theme the output of the main components page.
  24   *
  25   * This theming provides a way to toggle between the editing modes if Form
  26   * Builder module is available.
  27   */
  28  function theme_webform_components_page($node, $form) {
  29    // Add CSS and JS. Don't preprocess because these files are used rarely.
  30    drupal_add_css(drupal_get_path('module', 'webform') . '/css/webform-admin.css', 'theme', 'all', FALSE);
  31    drupal_add_js(drupal_get_path('module', 'webform') . '/js/webform-admin.js', 'module', 'header', FALSE, TRUE, FALSE);
  32  
  33    return $form;
  34  }
  35  
  36  /**
  37   * The table-based listing of all components for this webform.
  38   */
  39  function webform_components_form($form_state, $node) {
  40    $form = array(
  41      '#tree' => TRUE,
  42      '#node' => $node,
  43      'components' => array(),
  44    );
  45  
  46    $form['nid'] = array(
  47      '#type' => 'value',
  48      '#value' => $node->nid,
  49    );
  50  
  51    $options = array();
  52    foreach ($node->webform['components'] as $cid => $component) {
  53      $options[$cid] = check_plain($component['name']);
  54      $form['components'][$cid]['cid'] = array(
  55        '#type' => 'hidden',
  56        '#default_value' => $component['cid'],
  57      );
  58      $form['components'][$cid]['pid'] = array(
  59        '#type' => 'hidden',
  60        '#default_value' => $component['pid'],
  61      );
  62      $form['components'][$cid]['weight'] = array(
  63        '#type' => 'textfield',
  64        '#size' => 4,
  65        '#title' => t('Weight'),
  66        '#default_value' => $component['weight'],
  67      );
  68      $form['components'][$cid]['mandatory'] = array(
  69        '#type' => 'checkbox',
  70        '#title' => t('Mandatory'),
  71        '#default_value' => $component['mandatory'],
  72        '#access' => webform_component_feature($component['type'], 'required'),
  73      );
  74      if (!isset($max_weight) || $component['weight'] > $max_weight) {
  75        $max_weight = $component['weight'];
  76      }
  77    }
  78  
  79    $form['add']['name'] = array(
  80      '#type' => 'textfield',
  81      '#size' => 24,
  82      '#maxlength' => 255,
  83    );
  84  
  85    $form['add']['type'] = array(
  86      '#type' => 'select',
  87      '#options' => webform_component_options(),
  88      '#weight' => 3,
  89      '#default_value' => (isset($_GET['cid']) && isset($node->webform['components'][$_GET['cid']])) ? $node->webform['components'][$_GET['cid']]['type'] : 'textfield',
  90    );
  91    $form['add']['mandatory'] = array(
  92      '#type' => 'checkbox',
  93    );
  94    $form['add']['cid'] = array(
  95      '#type' => 'hidden',
  96      '#default_value' => '',
  97    );
  98    $form['add']['pid'] = array(
  99      '#type' => 'hidden',
 100      '#default_value' => (isset($_GET['cid']) && isset($node->webform['components'][$_GET['cid']])) ? $node->webform['components'][$_GET['cid']]['pid'] : 0,
 101    );
 102    $form['add']['weight'] = array(
 103      '#type' => 'textfield',
 104      '#size' => 4,
 105      '#delta' => count($node->webform['components']) > 10 ? count($node->webform['components']) : 10,
 106    );
 107  
 108    if (isset($_GET['cid']) && isset($node->webform['components'][$_GET['cid']])) {
 109      // Make the new component appear by default directly after the one that was
 110      // just added.
 111      $form['add']['weight']['#default_value'] = $node->webform['components'][$_GET['cid']]['weight'] + 1;
 112      foreach (array_keys($node->webform['components']) as $cid) {
 113        // Adjust all later components also, to make sure none of them have the
 114        // same weight as the new component.
 115        if ($form['components'][$cid]['weight']['#default_value'] >= $form['add']['weight']['#default_value']) {
 116          $form['components'][$cid]['weight']['#default_value']++;
 117        }
 118      }
 119    }
 120    else {
 121      // If no component was just added, the new component should appear by
 122      // default at the end of the list.
 123      $form['add']['weight']['#default_value'] = isset($max_weight) ? $max_weight + 1 : 0;
 124    }
 125  
 126    $form['add']['add'] = array(
 127      '#type' => 'submit',
 128      '#value' => t('Add'),
 129      '#weight' => 45,
 130    );
 131  
 132    $form['submit'] = array(
 133      '#type' => 'submit',
 134      '#value' => t('Save'),
 135      '#weight' => 45,
 136      '#access' => count($node->webform['components']) > 0,
 137    );
 138  
 139    return $form;
 140  }
 141  
 142  /**
 143   * Theme the node components form. Use a table to organize the components.
 144   *
 145   * @param $form
 146   *   The form array.
 147   * @return
 148   *   Formatted HTML form, ready for display.
 149   */
 150  function theme_webform_components_form($form) {
 151    // Add CSS to display submission info. Don't preprocess because this CSS file is used rarely.
 152    drupal_add_css(drupal_get_path('module', 'webform') . '/css/webform-admin.css', 'theme', 'all', FALSE);
 153    drupal_add_js(drupal_get_path('module', 'webform') . '/js/webform-admin.js', 'module', 'header', FALSE, TRUE, FALSE);
 154  
 155    drupal_add_tabledrag('webform-components', 'order', 'sibling', 'webform-weight');
 156    drupal_add_tabledrag('webform-components', 'match', 'parent', 'webform-pid', 'webform-pid', 'webform-cid');
 157  
 158    $node = $form['#node'];
 159  
 160    $header = array(t('Label'), t('Type'), t('Value'), t('Mandatory'), t('Weight'), array('data' => t('Operations'), 'colspan' => 3));
 161    $rows = array();
 162  
 163    // Add a row containing form elements for a new item.
 164    unset($form['add']['name']['#title'], $form['add_type']['#description']);
 165    $form['add']['name']['#attributes']['rel'] = t('New component name');
 166    $form['add']['name']['#attributes']['class'] = 'webform-default-value';
 167    $form['add']['cid']['#attributes']['class'] = 'webform-cid';
 168    $form['add']['pid']['#attributes']['class'] = 'webform-pid';
 169    $form['add']['weight']['#attributes']['class'] = 'webform-weight';
 170    $row_data = array(
 171      drupal_render($form['add']['name']),
 172      drupal_render($form['add']['type']),
 173      '',
 174      drupal_render($form['add']['mandatory']),
 175      drupal_render($form['add']['cid']) . drupal_render($form['add']['pid']) . drupal_render($form['add']['weight']),
 176      array('colspan' => 3, 'data' => drupal_render($form['add']['add'])),
 177    );
 178    $add_form = array('data' => $row_data, 'class' => 'draggable webform-add-form');
 179    $form_rendered = FALSE;
 180  
 181    if (!empty($node->webform['components'])) {
 182      $component_tree = array();
 183      $page_count = 1;
 184      _webform_components_tree_build($node->webform['components'], $component_tree, 0, $page_count);
 185      $component_tree = _webform_components_tree_sort($component_tree);
 186      // Build the table rows.
 187      function _webform_components_form_rows($node, $cid, $component, $level, &$form, &$rows, &$add_form) {
 188        // Create presentable values.
 189        if (drupal_strlen($component['value']) > 30) {
 190          $component['value'] = drupal_substr($component['value'], 0, 30);
 191          $component['value'] .= '...';
 192        }
 193        $component['value'] = check_plain($component['value']);
 194  
 195        // Remove individual titles from the mandatory and weight fields.
 196        unset($form['components'][$cid]['mandatory']['#title']);
 197        unset($form['components'][$cid]['pid']['#title']);
 198        unset($form['components'][$cid]['weight']['#title']);
 199  
 200        // Add special classes for weight and parent fields.
 201        $form['components'][$cid]['cid']['#attributes']['class'] = 'webform-cid';
 202        $form['components'][$cid]['pid']['#attributes']['class'] = 'webform-pid';
 203        $form['components'][$cid]['weight']['#attributes']['class'] = 'webform-weight';
 204  
 205        // Build indentation for this row.
 206        $indents = '';
 207        for ($n = 1; $n <= $level; $n++) {
 208          $indents .= '<div class="indentation">&nbsp;</div>';
 209        }
 210  
 211        // Add each component to a table row.
 212        $row_data = array(
 213          $indents . filter_xss($component['name']),
 214          $form['add']['type']['#options'][$component['type']],
 215          ($component['value'] == '') ? '-' : $component['value'],
 216          drupal_render($form['components'][$cid]['mandatory']),
 217          drupal_render($form['components'][$cid]['cid']) . drupal_render($form['components'][$cid]['pid']) . drupal_render($form['components'][$cid]['weight']),
 218          l(t('Edit'), 'node/' . $node->nid . '/webform/components/' . $cid, array('query' => drupal_get_destination())),
 219          l(t('Clone'), 'node/' . $node->nid . '/webform/components/' . $cid . '/clone', array('query' => drupal_get_destination())),
 220          l(t('Delete'), 'node/' . $node->nid . '/webform/components/' . $cid . '/delete', array('query' => drupal_get_destination())),
 221        );
 222        $row_class = 'draggable';
 223        if (!webform_component_feature($component['type'], 'group')) {
 224          $row_class .= ' tabledrag-leaf';
 225        }
 226        if ($component['type'] == 'pagebreak') {
 227          $row_class .= ' tabledrag-root webform-pagebreak';
 228          $row_data[0] = array('class' => 'webform-pagebreak', 'data' => $row_data[0]);
 229        }
 230        $rows[] = array('data' => $row_data, 'class' => $row_class);
 231        if (isset($component['children']) && is_array($component['children'])) {
 232          foreach ($component['children'] as $cid => $component) {
 233            _webform_components_form_rows($node, $cid, $component, $level + 1, $form, $rows, $add_form);
 234          }
 235        }
 236  
 237        // Add the add form if this was the last edited component.
 238        if (isset($_GET['cid']) && $component['cid'] == $_GET['cid'] && $add_form) {
 239          $add_form['data'][0] = $indents . $add_form['data'][0];
 240          $rows[] = $add_form;
 241          $add_form = FALSE;
 242        }
 243      }
 244      foreach ($component_tree['children'] as $cid => $component) {
 245        _webform_components_form_rows($node, $cid, $component, 0, $form, $rows, $add_form);
 246      }
 247    }
 248    else {
 249      $rows[] = array(array('data' => t('No Components, add a component below.'), 'colspan' => 9));
 250    }
 251  
 252    // Append the add form if not already printed.
 253    if ($add_form) {
 254      $rows[] = $add_form;
 255    }
 256  
 257    $output = '';
 258    $output .= theme('table', $header, $rows, array('id' => 'webform-components'));
 259    $output .= drupal_render($form);
 260    return $output;
 261  }
 262  
 263  function webform_components_form_validate($form, &$form_state) {
 264    // Check that the entered component name is valid.
 265    if ($form_state['values']['op'] == t('Add') && drupal_strlen(trim($form_state['values']['add']['name'])) <= 0) {
 266      form_error($form['add']['name'], t('When adding a new component, the name field is required.'));
 267    }
 268  
 269    // Check that no two components end up with the same form key.
 270    $duplicates = array();
 271    $parents = array();
 272    if (isset($form_state['values']['components'])) {
 273      foreach ($form_state['values']['components'] as $cid => $component) {
 274        $form_key = $form['#node']->webform['components'][$cid]['form_key'];
 275        if (isset($parents[$component['pid']]) && ($existing = array_search($form_key, $parents[$component['pid']])) && $existing !== FALSE) {
 276          if (!isset($duplicates[$component['form_key']])) {
 277            $duplicates[$component['form_key']] = array($existing);
 278          }
 279          $duplicates[$component['form_key']][] = $cid;
 280        }
 281        $parents[$component['pid']][$cid] = $form_key;
 282      }
 283    }
 284  
 285    if (!empty($duplicates)) {
 286      $error = t('The form order failed to save because the following elements have same form keys and are under the same parent. Edit each component and give them a unique form key, then try moving them again.');
 287      $items = array();
 288      foreach ($duplicates as $form_key => $cids) {
 289        foreach ($cids as $cid) {
 290          $items[] = $form['#node']->webform['components'][$cid]['name'];
 291        }
 292      }
 293  
 294      form_error($form['components'], $error . theme('item_list', $items));
 295    }
 296  }
 297  
 298  function webform_components_form_submit($form, &$form_state) {
 299    $node = node_load($form_state['values']['nid']);
 300  
 301    // Update all mandatory and weight values.
 302    foreach ($node->webform['components'] as $cid => $component) {
 303      if ($component['pid'] != $form_state['values']['components'][$cid]['pid'] || $component['weight'] != $form_state['values']['components'][$cid]['weight'] || $component['mandatory'] != $form_state['values']['components'][$cid]['mandatory']) {
 304        $component['weight'] = $form_state['values']['components'][$cid]['weight'];
 305        $component['mandatory'] = $form_state['values']['components'][$cid]['mandatory'];
 306        $component['pid'] = $form_state['values']['components'][$cid]['pid'];
 307        $component['nid'] = $node->nid;
 308        webform_component_update($component);
 309      }
 310    }
 311  
 312    if (isset($_POST['op']) && $_POST['op'] == t('Publish')) {
 313      $node->status = 1;
 314      node_save($node);
 315      drupal_set_message(t('Your webform has been published.'));
 316      return 'node/' . $node->nid;
 317    }
 318    elseif (isset($_POST['op']) && $_POST['op'] == t('Add')) {
 319      $component = $form_state['values']['add'];
 320      $form_state['redirect'] = array('node/' . $node->nid . '/webform/components/new/' . $component['type'], 'name=' . urlencode($component['name']) . '&mandatory=' . $component['mandatory'] . '&pid=' . $component['pid'] . '&weight=' . $component['weight']);
 321    }
 322    else {
 323      drupal_set_message(t('The component positions and mandatory values have been updated.'));
 324  
 325      // Since Webform components have been updated but the node itself has not
 326      // been saved, it is necessary to explicitly clear the cache to make sure
 327      // the updated webform is visible to anonymous users.
 328      cache_clear_all();
 329  
 330      // Clear the entity cache if Entity Cache module is installed.
 331      if (module_exists('entitycache')) {
 332        cache_clear_all($node->nid, 'cache_entity_node');
 333      }
 334    }
 335  }
 336  
 337  /**
 338   * Form to configure a webform component.
 339   */
 340  function webform_component_edit_form(&$form_state, $node, $component, $clone = FALSE) {
 341    drupal_set_title(t('Edit component: @name', array('@name' => $component['name'])));
 342    drupal_add_css(drupal_get_path('module', 'webform') . '/css/webform-admin.css', 'theme', 'all', FALSE);
 343  
 344    $form['#tree'] = TRUE;
 345  
 346    // Print the correct field type specification.
 347    // We always need: name and description.
 348    $form['type'] = array(
 349      '#type' => 'value',
 350      '#value' => $component['type'],
 351    );
 352    $form['nid'] = array(
 353      '#type' => 'value',
 354      '#value' => $node->nid,
 355    );
 356    $form['cid'] = array(
 357      '#type' => 'value',
 358      '#value' => isset($component['cid']) ? $component['cid'] : NULL,
 359    );
 360    $form['clone'] = array(
 361      '#type' => 'value',
 362      '#value' => $clone,
 363    );
 364  
 365    if (webform_component_feature($component['type'], 'title')) {
 366      $form['name'] = array(
 367        '#type' => 'textfield',
 368        '#default_value' => $component['name'],
 369        '#title' => t('Label'),
 370        '#description' => t('This is used as a descriptive label when displaying this form element.'),
 371        '#required' => TRUE,
 372        '#weight' => -10,
 373        '#maxlength' => 255,
 374      );
 375    }
 376  
 377    $form['form_key'] = array(
 378      '#type' => 'textfield',
 379      '#default_value' => empty($component['form_key']) ? _webform_safe_name($component['name']) : $component['form_key'],
 380      '#title' => t('Field Key'),
 381      '#description' => t('Enter a machine readable key for this form element. May contain only alphanumeric characters and underscores. This key will be used as the name attribute of the form element. This value has no effect on the way data is saved, but may be helpful if doing custom form processing.'),
 382      '#required' => TRUE,
 383      '#weight' => -9,
 384    );
 385  
 386    $form['extra'] = array();
 387    if (webform_component_feature($component['type'], 'description')) {
 388      $form['extra']['description'] = array(
 389        '#type' => 'textarea',
 390        '#default_value' => isset($component['extra']['description']) ? $component['extra']['description'] : '',
 391        '#title' => t('Description'),
 392        '#description' => t('A short description of the field used as help for the user when he/she uses the form.') . theme('webform_token_help'),
 393        '#weight' => -1,
 394      );
 395    }
 396  
 397    // Display settings.
 398    $form['display'] = array(
 399      '#type' => 'fieldset',
 400      '#title' => t('Display'),
 401      '#collapsible' => TRUE,
 402      '#collapsed' => FALSE,
 403      '#weight' => 8,
 404    );
 405    if (webform_component_feature($component['type'], 'title_display')) {
 406      if (webform_component_feature($component['type'], 'title_inline')) {
 407        $form['display']['title_display'] = array(
 408          '#type' => 'select',
 409          '#title' => t('Label display'),
 410          '#default_value' => !empty($component['extra']['title_display']) ? $component['extra']['title_display'] : 'before',
 411          '#options' => array(
 412            'before' => t('Above'),
 413            'inline' => t('Inline'),
 414            'none' => t('None'),
 415          ),
 416          '#description' => t('Determines the placement of the component\'s label.'),
 417        );
 418      }
 419      else {
 420        $form['display']['title_display'] = array(
 421          '#type' => 'checkbox',
 422          '#title' => t('Hide label'),
 423          '#default_value' => strcmp($component['extra']['title_display'], 'none') === 0,
 424          '#return_value' => 'none',
 425          '#description' => t('Do not display the label of this component.'),
 426        );
 427      }
 428      $form['display']['title_display']['#weight'] = 8;
 429      $form['display']['title_display']['#parents'] = array('extra', 'title_display');
 430  
 431      if (webform_component_feature($component['type'], 'private')) {
 432      // May user mark fields as Private?
 433        $form['display']['private'] = array(
 434        '#type' => 'checkbox',
 435        '#title' => t('Private'),
 436        '#default_value' => ($component['extra']['private'] == '1' ? TRUE : FALSE),
 437        '#description' => t('Private fields are shown only to users with results access.'),
 438        '#weight' => 45,
 439        '#parents' => array('extra', 'private'),
 440        '#disabled' => !webform_results_access($node),
 441        );
 442      }
 443    }
 444  
 445    // Validation settings.
 446    $form['validation'] = array(
 447      '#type' => 'fieldset',
 448      '#title' => t('Validation'),
 449      '#collapsible' => TRUE,
 450      '#collapsed' => FALSE,
 451      '#weight' => 5,
 452    );
 453    if (webform_component_feature($component['type'], 'required')) {
 454      $form['validation']['mandatory'] = array(
 455        '#type' => 'checkbox',
 456        '#title' => t('Mandatory'),
 457        '#default_value' => ($component['mandatory'] == '1' ? TRUE : FALSE),
 458        '#description' => t('Check this option if the user must enter a value.'),
 459        '#weight' => -1,
 460        '#parents' => array('mandatory'),
 461      );
 462    }
 463  
 464    // Position settings, only shown if JavaScript is disabled.
 465    $form['position'] = array(
 466      '#type' => 'fieldset',
 467      '#title' => t('Position'),
 468      '#collapsible' => TRUE,
 469      '#collapsed' => TRUE,
 470      '#tree' => FALSE,
 471      '#weight' => 20,
 472      '#attributes' => array('class' => 'webform-position'),
 473    );
 474  
 475    if (variable_get('webform_enable_fieldset', TRUE) && is_array($node->webform['components'])) {
 476      $options = array('0' => t('Root'));
 477      foreach ($node->webform['components'] as $existing_cid => $value) {
 478        if (webform_component_feature($value['type'], 'group') && (!isset($component['cid']) || $existing_cid != $component['cid'])) {
 479          $options[$existing_cid] = $value['name'];
 480        }
 481      }
 482      $form['position']['pid'] = array(
 483        '#type' => 'select',
 484        '#title' => t('Parent Fieldset'),
 485        '#default_value' => $component['pid'],
 486        '#description' => t('Optional. You may organize your form by placing this component inside another fieldset.'),
 487        '#options' => $options,
 488        '#weight' => 3,
 489      );
 490    }
 491    $form['position']['weight'] = array(
 492      '#type' => 'textfield',
 493      '#size' => 4,
 494      '#title' => t('Weight'),
 495      '#default_value' => $component['weight'],
 496      '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'),
 497      '#weight' => 4,
 498    );
 499  
 500    // Add conditional fields.
 501    $conditional_components = array();
 502    $counter = 0;
 503    $last_pagebreak_slice = 0;
 504    foreach ($node->webform['components'] as $cid => $test_component) {
 505      // Only components before the pagebreak can be considered.
 506      if ($test_component['type'] == 'pagebreak') {
 507        $last_pagebreak_slice = $counter;
 508      }
 509      if (isset($component['cid']) && $cid == $component['cid']) {
 510        break;
 511      }
 512      if (webform_component_feature($test_component['type'], 'conditional')) {
 513        $conditional_components[$cid] = $test_component;
 514        $counter++;
 515      }
 516    }
 517    if ($component['type'] != 'pagebreak') {
 518      $fieldset_description = t('Create a rule to control whether or not to skip this page.');
 519    }
 520    else {
 521      $fieldset_description = t('Create a rule to control whether or not to show this form element.');
 522    }
 523    $conditional_components = array_slice($conditional_components, 0, $last_pagebreak_slice, TRUE);
 524    $form['conditional'] = array(
 525      '#weight' => 10,
 526      '#type' => 'fieldset',
 527      '#title' => t('Conditional rules'),
 528      '#collapsible' => TRUE,
 529      '#collapsed' => TRUE,
 530      '#description' => t('Create a rule to control whether or not to show this form element.'),
 531      '#tree' => FALSE,
 532    );
 533    $form['conditional']['extra'] = array(
 534      '#tree' => TRUE,
 535    );
 536    $form['conditional']['extra']['conditional_component'] = array(
 537      '#type' => 'select',
 538      '#title' => t('Component'),
 539      '#options' => webform_component_list($node, $conditional_components, FALSE, TRUE),
 540      '#description' => t('Select another component to decide whether to show or hide this component. You can only select components occurring before the most recent pagebreak.'),
 541      '#default_value' => $component['extra']['conditional_component'],
 542    );
 543    $form['conditional']['extra']['conditional_operator'] = array(
 544      '#type' => 'select',
 545      '#title' => t('Operator'),
 546      '#options' => array(
 547        '=' => t('Is one of'),
 548        '!=' => t('Is not one of')
 549      ),
 550      '#description' => t('Determines whether the list below is inclusive or exclusive.'),
 551      '#default_value' => $component['extra']['conditional_operator'],
 552    );
 553    $form['conditional']['extra']['conditional_values'] = array(
 554      '#type' => 'textarea',
 555      '#title' => t('Values'),
 556      '#description' => t('List values, one per line, that will trigger this action. If you leave this blank, this component will always display.'),
 557      '#default_value' => $component['extra']['conditional_values'],
 558    );
 559    if (empty($conditional_components)) {
 560      $form['conditional']['#access'] = FALSE;
 561    }
 562  
 563    // Add the fields specific to this component type:
 564    $additional_form_elements = (array) webform_component_invoke($component['type'], 'edit', $component);
 565    if (empty($additional_form_elements)) {
 566      drupal_set_message(t('The webform component of type @type does not have an edit function defined.', array('@type' => $component['type'])));
 567    }
 568  
 569    // Merge the additional fields with the current fields:
 570    if (isset($additional_form_elements['extra'])) {
 571      $form['extra'] = array_merge($form['extra'], $additional_form_elements['extra']);
 572      unset($additional_form_elements['extra']);
 573    }
 574    if (isset($additional_form_elements['position'])) {
 575      $form['position'] = array_merge($form['position'], $additional_form_elements['position']);
 576      unset($additional_form_elements['position']);
 577    }
 578    if (isset($additional_form_elements['display'])) {
 579      $form['display'] = array_merge($form['display'], $additional_form_elements['display']);
 580      unset($additional_form_elements['display']);
 581    }
 582    if (isset($additional_form_elements['validation'])) {
 583      $form['validation'] = array_merge($form['validation'], $additional_form_elements['validation']);
 584      unset($additional_form_elements['validation']);
 585    }
 586    elseif (count(element_children($form['validation'])) == 0) {
 587      unset($form['validation']);
 588    }
 589    $form = array_merge($form, $additional_form_elements);
 590  
 591    // Add the submit button.
 592    $form['submit'] = array(
 593      '#type' => 'submit',
 594      '#value' => t('Save component'),
 595      '#weight' => 50,
 596    );
 597  
 598    return $form;
 599  }
 600  
 601  /**
 602   * Field name validation for the webform unique key. Must be alphanumeric.
 603   */
 604  function webform_component_edit_form_validate($form, &$form_state) {
 605    $node = node_load($form_state['values']['nid']);
 606  
 607    if (!preg_match('/^[a-z0-9_]+$/i', $form_state['values']['form_key'])) {
 608      form_set_error('form_key', t('The field key %field_key is invalid. Please include only lowercase alphanumeric characters and underscores.', array('%field_key' => $form_state['values']['form_key'])));
 609    }
 610  
 611    foreach ($node->webform['components'] as $cid => $component) {
 612      if (($component['cid'] != $form_state['values']['cid'] || $form_state['values']['clone']) && ($component['pid'] == $form_state['values']['pid']) && (strcasecmp($component['form_key'], $form_state['values']['form_key']) == 0)) {
 613        form_set_error('form_key', t('The field key %field_key is already in use by the field labeled %existing_field. Please use a unique key.', array('%field_key' => $form_state['values']['form_key'], '%existing_field' => $component['name'])));
 614      }
 615    }
 616  }
 617  
 618  /**
 619   * Submit handler for webform_component_edit_form().
 620   */
 621  function webform_component_edit_form_submit($form, &$form_state) {
 622    // Ensure a webform record exists.
 623    $node = node_load($form_state['values']['nid']);
 624    webform_ensure_record($node);
 625  
 626    // Remove empty extra values.
 627    if (isset($form_state['values']['extra'])) {
 628      foreach ($form_state['values']['extra'] as $key => $value) {
 629        if ($value  === '' && !isset($form['display'][$key]['#options'][''])) {
 630          unset($form_state['values']['extra'][$key]);
 631        }
 632      }
 633    }
 634  
 635    // Remove empty attribute values.
 636    if (isset($form_state['values']['extra']['attributes'])) {
 637      foreach ($form_state['values']['extra']['attributes'] as $key => $value) {
 638        if ($value === '') {
 639          unset($form_state['values']['extra']['attributes'][$key]);
 640        }
 641      }
 642    }
 643  
 644    if ($form_state['values']['clone']) {
 645      webform_component_clone($node, $form_state['values']);
 646      drupal_set_message(t('Component %name cloned.', array('%name' => $form_state['values']['name'])));
 647    }
 648    elseif (!empty($form_state['values']['cid'])) {
 649      webform_component_update($form_state['values']);
 650      drupal_set_message(t('Component %name updated.', array('%name' => $form_state['values']['name'])));
 651    }
 652    else {
 653      $cid = webform_component_insert($form_state['values']);
 654      drupal_set_message(t('New component %name added.', array('%name' => $form_state['values']['name'])));
 655    }
 656  
 657    // Since Webform components have been updated but the node itself has not
 658    // been saved, it is necessary to explicitly clear the cache to make sure
 659    // the updated webform is visible to anonymous users.
 660    cache_clear_all();
 661  
 662    // Clear the entity cache if Entity Cache module is installed.
 663    if (module_exists('entitycache')) {
 664      cache_clear_all($node->nid, 'cache_entity_node');
 665    }
 666  
 667    $form_state['redirect'] = array('node/' . $node->nid . '/webform/components', isset($cid) ? 'cid=' . $cid : '');
 668  }
 669  
 670  /**
 671   * Form to confirm deletion of a component.
 672   */
 673  function webform_component_delete_form($form_state, $node, $component) {
 674    $cid = $component['cid'];
 675  
 676    $form = array();
 677    $form['node'] = array(
 678      '#type' => 'value',
 679      '#value' => $node,
 680    );
 681    $form['component'] = array(
 682      '#type' => 'value',
 683      '#value' => $component,
 684    );
 685  
 686    if (webform_component_feature($node->webform['components'][$cid]['type'], 'group')) {
 687      $question = t('Delete the %name fieldset?', array('%name' => $node->webform['components'][$cid]['name']));
 688      $description = t('This will immediately delete the %name fieldset and all children elements within %name from the %webform webform. This cannot be undone.', array('%name' => $node->webform['components'][$cid]['name'], '%webform' => $node->title));
 689    }
 690    else {
 691      $question = t('Delete the %name component?', array('%name' => $node->webform['components'][$cid]['name']));
 692      $description = t('This will immediately delete the %name component from the %webform webform. This cannot be undone.', array('%name' => $node->webform['components'][$cid]['name'], '%webform' => $node->title));
 693    }
 694  
 695    return confirm_form($form, $question, 'node/' . $node->nid . '/webform/components', $description, t('Delete'));
 696  }
 697  
 698  /**
 699   * Submit handler for webform_component_delete_form().
 700   */
 701  function webform_component_delete_form_submit($form, &$form_state) {
 702    // Delete the component.
 703    $node = $form_state['values']['node'];
 704    $component = $form_state['values']['component'];
 705    webform_component_delete($node, $component);
 706    drupal_set_message(t('Component %name deleted.', array('%name' => $component['name'])));
 707  
 708    // Check if this webform still contains any information.
 709    unset($node->webform['components'][$component['cid']]);
 710    webform_check_record($node);
 711  
 712    // Since Webform components have been updated but the node itself has not
 713    // been saved, it is necessary to explicitly clear the cache to make sure
 714    // the updated webform is visible to anonymous users.
 715    cache_clear_all();
 716  
 717    // Clear the entity cache if Entity Cache module is installed.
 718    if (module_exists('entitycache')) {
 719      cache_clear_all($node->nid, 'cache_entity_node');
 720    }
 721  
 722    $form_state['redirect'] = 'node/' . $node->nid . '/webform/components';
 723  }
 724  
 725  /**
 726   * Insert a new component into the database.
 727   *
 728   * @param $component
 729   *   A full component containing fields from the component form.
 730   */
 731  function webform_component_insert(&$component) {
 732    // Allow modules to modify the component before saving.
 733    foreach (module_implements('webform_component_presave') as $module) {
 734      $function = $module . '_webform_component_presave';
 735      $function($component);
 736    }
 737  
 738    if (lock_acquire('webform_component_insert_' . $component['nid'], 5)) {
 739      $component['cid'] = isset($component['cid']) ? $component['cid'] : db_result(db_query('SELECT MAX(cid) FROM {webform_component} WHERE nid = %d', $component['nid'])) + 1;
 740      $component['value'] = isset($component['value']) ? $component['value'] : NULL;
 741      $component['mandatory'] = isset($component['mandatory']) ? $component['mandatory'] : 0;
 742      $component['extra']['private'] = isset($component['extra']['private']) ? $component['extra']['private'] : 0;
 743      db_query("INSERT INTO {webform_component} (nid, cid, pid, form_key, name, type, value, extra, mandatory, weight) VALUES (%d, %d, %d, '%s', '%s', '%s', '%s', '%s', %d, %d)", $component['nid'], $component['cid'], $component['pid'], $component['form_key'], $component['name'], $component['type'], $component['value'], serialize($component['extra']), $component['mandatory'], $component['weight']);
 744      lock_release('webform_component_insert_' . $component['nid']);
 745    }
 746    else {
 747      watchdog('webform', 'A Webform component could not be saved because a timeout occurred while trying to acquire a lock for the node. Details: <pre>@component</pre>', array('@component' => print_r($component, TRUE)));
 748      return FALSE;
 749    }
 750  
 751    // Post-insert actions.
 752    module_invoke_all('webform_component_insert', $component);
 753  
 754    return $component['cid'];
 755  }
 756  
 757  /**
 758   * Update an existing component with new values.
 759   *
 760   * @param $component
 761   *   A full component containing a nid, cid, and all other fields from the
 762   *   component form. Additional properties are stored in the extra array.
 763   */
 764  function webform_component_update($component) {
 765    // Allow modules to modify the component before saving.
 766    foreach (module_implements('webform_component_presave') as $module) {
 767      $function = $module . '_webform_component_presave';
 768      $function($component);
 769    }
 770  
 771    $component['value'] = isset($component['value']) ? $component['value'] : NULL;
 772    $component['mandatory'] = isset($component['mandatory']) ? $component['mandatory'] : 0;
 773    $component['extra']['private'] = isset($component['extra']['private']) ? $component['extra']['private'] : 0;
 774    $success = db_query("UPDATE {webform_component} SET pid = %d, form_key = '%s', name = '%s', type = '%s', value = '%s', extra = '%s', mandatory = %d, weight = %d WHERE nid = %d AND cid = %d", $component['pid'], $component['form_key'], $component['name'], $component['type'], $component['value'], serialize($component['extra']), $component['mandatory'], $component['weight'], $component['nid'], $component['cid']);
 775  
 776    // Post-update actions.
 777    module_invoke_all('webform_component_update', $component);
 778  
 779    return $success;
 780  }
 781  
 782  function webform_component_delete($node, $component) {
 783    // Check if a delete function is available for this component. If so,
 784    // load all submissions and allow the component to delete each one.
 785  
 786    webform_component_include($component['type']);
 787    $delete_function = '_webform_delete_' . $component['type'];
 788    if (function_exists($delete_function)) {
 789      module_load_include('inc', 'webform', 'includes/webform.submissions');
 790      $submissions = webform_get_submissions($node->nid);
 791      foreach ($submissions as $submission) {
 792        if (isset($submission->data[$component['cid']])) {
 793          webform_component_invoke($component['type'], 'delete', $component, $submission->data[$component['cid']]['value']);
 794        }
 795      }
 796    }
 797  
 798    // Remove database entries.
 799    db_query('DELETE FROM {webform_component} WHERE nid = %d AND cid = %d', $node->nid, $component['cid']);
 800    db_query('DELETE FROM {webform_submitted_data} WHERE nid = %d AND cid = %d', $node->nid, $component['cid']);
 801  
 802    // Delete all elements under this element.
 803    $result = db_query('SELECT cid FROM {webform_component} WHERE nid = %d AND pid = %d', $node->nid, $component['cid']);
 804    while ($row = db_fetch_object($result)) {
 805      $child_component = $node->webform['components'][$row->cid];
 806      webform_component_delete($node, $child_component);
 807    }
 808  
 809    // Post-delete actions.
 810    module_invoke_all('webform_component_delete', $component);
 811  }
 812  
 813  /**
 814   * Recursively insert components into the database.
 815   *
 816   * @param $node
 817   *   The node object containing the current webform.
 818   * @param $component
 819   *   A full component containing fields from the component form.
 820   */
 821  function webform_component_clone(&$node, &$component) {
 822    $original_cid = $component['cid'];
 823    $component['cid'] = NULL;
 824    $new_cid = webform_component_insert($component);
 825    $component['cid'] = $new_cid;
 826    if (webform_component_feature($component['type'], 'group')) {
 827      foreach ($node->webform['components'] as $cid => $child_component) {
 828        if ($child_component['pid'] == $original_cid) {
 829          $child_component['pid'] = $new_cid;
 830          webform_component_clone($node, $child_component);
 831        }
 832      }
 833    }
 834    return $new_cid;
 835  }
 836  
 837  /**
 838   * Check if a component has a particular feature.
 839   *
 840   * @see hook_webform_component_info()
 841   */
 842  function webform_component_feature($type, $feature) {
 843    $components = webform_components();
 844    $defaults = array(
 845      'csv' => TRUE,
 846      'default_value' => TRUE,
 847      'description' => TRUE,
 848      'email' => TRUE,
 849      'email_address' => FALSE,
 850      'email_name' => FALSE,
 851      'required' => TRUE,
 852      'title' => TRUE,
 853      'title_display' => TRUE,
 854      'title_inline' => TRUE,
 855      'conditional' => TRUE,
 856      'spam_analysis' => FALSE,
 857      'group' => FALSE,
 858      'attachment' => FALSE,
 859      'private' => TRUE,
 860    );
 861    return isset($components[$type]['features'][$feature]) ? $components[$type]['features'][$feature] : !empty($defaults[$feature]);
 862  }
 863  
 864  /**
 865   * Create a list of components suitable for a select list.
 866   *
 867   * @param $node
 868   *   The webform node.
 869   * @param $component_filter
 870   *   Either an array of components, or a string containing a feature name (csv,
 871   *   email, required, conditional) on which this list of components will be
 872   *   restricted.
 873   * @param $indent
 874   *   Indent components placed under fieldsets with hyphens.
 875   * @param $optgroups
 876   *   Determine if pagebreaks should be converted to option groups in the
 877   *   returned list of options.
 878   */
 879  function webform_component_list($node, $component_filter = NULL, $indent = TRUE, $optgroups = FALSE) {
 880    $options = array();
 881    $page_names = array();
 882  
 883    $components = is_array($component_filter) ? $component_filter : $node->webform['components'];
 884    $feature = is_string($component_filter) ? $component_filter : NULL;
 885  
 886    foreach ($components as $cid => $component) {
 887      if (!isset($feature) || webform_component_feature($component['type'], $feature) || ($indent && webform_component_feature($component['type'], 'group'))) {
 888        $prefix = '';
 889        $page_num = $component['page_num'];
 890        $page_index = 'p' . $page_num;
 891        if ($indent && ($parent_count = count(webform_component_parent_keys($node, $component)) - 1)) {
 892          $prefix = str_repeat('-', $parent_count);
 893        }
 894        if ($optgroups && $component['type'] == 'pagebreak') {
 895          $page_names[$page_index] = $component['name'];
 896        }
 897        elseif ($optgroups && $page_num > 1) {
 898          $options[$page_index][$cid] = $prefix . $component['name'];
 899        }
 900        else {
 901          $options[$cid] = $prefix . $component['name'];
 902        }
 903      }
 904    }
 905  
 906    // Convert page breaks into optgroups.
 907    if ($optgroups) {
 908      $grouped_options = $options;
 909      $options = array();
 910      foreach ($grouped_options as $key => $values) {
 911        if (is_array($values) && isset($page_names[$key])) {
 912          $options[$page_names[$key]] = $values;
 913        }
 914        else {
 915          $options[$key] = $values;
 916        }
 917      }
 918    }
 919  
 920    return $options;
 921  }
 922  
 923  /**
 924   * A Form API process function to expand a component list into checkboxes.
 925   */
 926  function webform_component_select($element) {
 927    // Split the select list into checkboxes.
 928    foreach ($element['#options'] as $key => $label) {
 929      $label_length = strlen($label);
 930      $label = preg_replace('/^(\-)+/', '', $label);
 931      $indents = $label_length - strlen($label);
 932      $element[$key] = array(
 933        '#title' => $label,
 934        '#type' => 'checkbox',
 935        '#default_value' => array_search($key, $element['#value']) !== FALSE,
 936        '#return_value' => $key,
 937        '#parents' => array_merge($element['#parents'], array($key)),
 938        '#indent' => $indents,
 939      );
 940    }
 941  
 942    $element['#type'] = 'webform_component_select';
 943    $element['#theme'] = 'webform_component_select';
 944  
 945    return $element;
 946  }
 947  
 948  /**
 949   * Theme the contents of a Webform component select element.
 950   */
 951  function theme_webform_component_select($element) {
 952    drupal_add_js('misc/tableselect.js');
 953    drupal_add_js(drupal_get_path('module', 'webform') . '/js/webform-admin.js', 'module', 'header', FALSE, TRUE, FALSE);
 954  
 955    $rows = array();
 956    $header = array();
 957    if (!isset($element['#all_checkbox']) || $element['#all_checkbox']) {
 958      $header = array(array('class' => 'select-all', 'data' => ' ' . t('Include all components')));
 959    }
 960    foreach (element_children($element) as $key) {
 961      $rows[] = array(
 962        theme('indentation', $element[$key]['#indent']) . drupal_render($element[$key]),
 963      );
 964    }
 965  
 966    $element['#collapsible'] = isset($element['#collapsible']) ? $element['#collapsible'] : TRUE;
 967    $element['#collapsed'] = isset($element['#collapsed']) ? $element['#collapsed'] : TRUE;
 968  
 969    $element['#attributes']['class'] = 'webform-component-select-table';
 970    if (empty($rows)) {
 971      $element['#children'] = t('No available components.');
 972    }
 973    else {
 974      $element['#children'] = '<div class="webform-component-select-wrapper">' . theme('table', $header, $rows) . '</div>';
 975    }
 976  
 977    $element['#value'] = NULL;
 978  
 979    return theme('fieldset', $element);
 980  }
 981  
 982  /**
 983   * Find a components parents within a node.
 984   */
 985  function webform_component_parent_keys($node, $component) {
 986    $parents = array($component['form_key']);
 987    $pid = $component['pid'];
 988    while ($pid) {
 989      $parents[] = $node->webform['components'][$pid]['form_key'];
 990      $pid = $node->webform['components'][$pid]['pid'];
 991    }
 992    return array_reverse($parents);
 993  }
 994  
 995  /**
 996   * Populate a component with the defaults for that type.
 997   */
 998  function webform_component_defaults(&$component) {
 999    if ($defaults = webform_component_invoke($component['type'], 'defaults')) {
1000      foreach ($defaults as $key => $default) {
1001        if (!isset($component[$key])) {
1002          $component[$key] = $default;
1003        }
1004      }
1005      foreach ($defaults['extra'] as $extra => $default) {
1006        if (!isset($component['extra'][$extra])) {
1007          $component['extra'][$extra] = $default;
1008        }
1009      }
1010      $component['extra'] += array(
1011        'conditional_component' => '',
1012        'conditional_operator' => '=',
1013        'conditional_values' => '',
1014      );
1015    }
1016  }
1017  
1018  /**
1019   * Validate an element value is unique with no duplicates in the database.
1020   */
1021  function webform_validate_unique($element, $form_state) {
1022    if ($element['#value'] !== '') {
1023      $nid = $form_state['values']['details']['nid'];
1024      $sid = empty($form_state['values']['details']['sid']) ? 0 : $form_state['values']['details']['sid'];
1025      $count = db_result(db_query("SELECT count(*) FROM {webform_submitted_data} WHERE nid = %d AND cid = %d AND sid <> %d AND LOWER(data) = '%s'", $nid, $element['#webform_component']['cid'], $sid, $element['#value']));
1026      if ($count) {
1027        form_error($element, t('The value %value has already been submitted once for the %title field. You may have already submitted this form, or you need to use a different value.', array('%value' => $element['#value'], '%title' => $element['#title'])));
1028      }
1029    }
1030  }


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