source: sipes/modules_contrib/webform/webform.module @ 3ce658b

stableversion-3.0
Last change on this file since 3ce658b was 32a0eb4, checked in by José Gregorio Puentes <jpuentes@…>, 8 años ago

se agregaron los archivos de la nueva version del modulo

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