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