| [ Index ] |
PHP Cross Reference of Drupal 6 (yi-drupal) |
[Summary view] [Print] [Text view]
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"> </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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Mon Jul 9 18:01:44 2012 | Cross-referenced by PHPXref 0.7 |