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