webform.submissions.inc

Tracking 7.x-4.x branch
  1. drupal
    1. 6 contributions/webform/includes/webform.submissions.inc
    2. 7 contributions/webform/includes/webform.submissions.inc

This file is loaded when handling submissions, either submitting new, editing, or viewing. It also contains all CRUD functions for submissions.

@author Nathan Haug <nate@lullabot.com>

Functions & methods

NameDescription
template_preprocess_webform_submissionPreprocess function for webform-submission.tpl.php.
template_preprocess_webform_submission_informationPreprocess function for webform-submission-navigation.tpl.php.
template_preprocess_webform_submission_navigationPreprocess function for webform-submission-navigation.tpl.php.
theme_webform_submission_resendTheme the node components form. Use a table to organize the components.
webform_get_submissionFetch a specified submission for a webform node.
webform_get_submissionsReturn all the submissions for a particular node.
webform_get_submission_countReturn a count of the total number of submissions for a node.
webform_submission_dataGiven an array of submitted values, flatten it into data for a submission.
webform_submission_deleteDelete a single submission.
webform_submission_delete_formConfirm form to delete a single form submission.
webform_submission_delete_form_submit
webform_submission_insertInsert a webform submission entry in the database.
webform_submission_pageMenu callback; Present a Webform submission page for display or editing.
webform_submission_renderPrint a Webform submission for display on a page or in an e-mail.
webform_submission_resendForm to resend specific e-mails associated with a submission.
webform_submission_resend_submitSubmit handler for webform_submission_resend().
webform_submission_resend_validateValidate handler for webform_submission_resend().
webform_submission_send_mailSend related e-mails related to a submission.
webform_submission_titleMenu title callback; Return the submission number as a title.
webform_submission_total_limit_checkCheck if the total number of submissions has exceeded the limit on this form.
webform_submission_updateUpdate a webform submission entry in the database.
webform_submission_user_limit_checkCheck if the current user has exceeded the limit on this form.
_webform_submission_spam_check

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * This file is loaded when handling submissions, either submitting new,
  5. * editing, or viewing. It also contains all CRUD functions for submissions.
  6. *
  7. * @author Nathan Haug <nate@lullabot.com>
  8. */
  9. /**
  10. * Given an array of submitted values, flatten it into data for a submission.
  11. *
  12. * @param $node
  13. * The node object containing the current webform.
  14. * @param $submitted
  15. * The submitted user values from the webform.
  16. * @return
  17. * An array suitable for use in the 'data' property of a $submission object.
  18. */
  19. function webform_submission_data($node, $submitted) {
  20. $data = array();
  21. foreach ($submitted as $cid => $values) {
  22. // Don't save pagebreaks as submitted data.
  23. if ($node->webform['components'][$cid]['type'] == 'pagebreak') {
  24. continue;
  25. }
  26. if (is_array($values)) {
  27. $data[$cid] = $values;
  28. }
  29. else {
  30. $data[$cid][0] = $values;
  31. }
  32. }
  33. return $data;
  34. }
  35. /**
  36. * Update a webform submission entry in the database.
  37. *
  38. * @param $node
  39. * The node object containing the current webform.
  40. * @param $submission
  41. * The webform submission object to be saved into the database.
  42. * @return
  43. * The existing submission SID.
  44. */
  45. function webform_submission_update($node, $submission) {
  46. // Allow other modules to modify the submission before saving.
  47. foreach (module_implements('webform_submission_presave') as $module) {
  48. $function = $module . '_webform_submission_presave';
  49. $function($node, $submission);
  50. }
  51. // Update the main submission info.
  52. drupal_write_record('webform_submissions', $submission, 'sid');
  53. // If is draft, only delete data for components submitted, to
  54. // preserve any data from form pages not visited in this submission.
  55. if ($submission->is_draft) {
  56. $submitted_cids = array_keys($submission->data);
  57. if ($submitted_cids) {
  58. db_delete('webform_submitted_data')
  59. ->condition('sid', $submission->sid)
  60. ->condition('cid', $submitted_cids, 'IN')
  61. ->execute();
  62. }
  63. }
  64. else {
  65. db_delete('webform_submitted_data')
  66. ->condition('sid', $submission->sid)
  67. ->execute();
  68. }
  69. // Then re-add submission data to the database.
  70. $submission->is_new = FALSE;
  71. webform_submission_insert($node, $submission);
  72. module_invoke_all('webform_submission_update', $node, $submission);
  73. return $submission->sid;
  74. }
  75. /**
  76. * Insert a webform submission entry in the database.
  77. *
  78. * @param $node
  79. * The node object containing the current webform.
  80. * @param $submission
  81. * The webform submission object to be saved into the database.
  82. * @return
  83. * The new submission SID.
  84. */
  85. function webform_submission_insert($node, $submission) {
  86. // The submission ID may already be set if being called as an update.
  87. if (!isset($submission->sid) && (!isset($submission->is_new) || $submission->is_new == FALSE)) {
  88. // Allow other modules to modify the submission before saving.
  89. foreach (module_implements('webform_submission_presave') as $module) {
  90. $function = $module . '_webform_submission_presave';
  91. $function($node, $submission);
  92. }
  93. $submission->nid = $node->webform['nid'];
  94. drupal_write_record('webform_submissions', $submission);
  95. $is_new = TRUE;
  96. }
  97. foreach ($submission->data as $cid => $values) {
  98. foreach ($values as $delta => $value) {
  99. $data = array(
  100. 'nid' => $node->webform['nid'],
  101. 'sid' => $submission->sid,
  102. 'cid' => $cid,
  103. 'no' => $delta,
  104. 'data' => is_null($value) ? '' : $value,
  105. );
  106. drupal_write_record('webform_submitted_data', $data);
  107. }
  108. }
  109. // Invoke the insert hook after saving all the data.
  110. if (isset($is_new)) {
  111. module_invoke_all('webform_submission_insert', $node, $submission);
  112. }
  113. return $submission->sid;
  114. }
  115. /**
  116. * Delete a single submission.
  117. *
  118. * @param $nid
  119. * ID of node for which this webform was submitted.
  120. * @param $sid
  121. * ID of submission to be deleted (from webform_submitted_data).
  122. */
  123. function webform_submission_delete($node, $submission) {
  124. // Iterate through all components and let each do cleanup if necessary.
  125. foreach ($node->webform['components'] as $cid => $component) {
  126. if (isset($submission->data[$cid])) {
  127. webform_component_invoke($component['type'], 'delete', $component, $submission->data[$cid]);
  128. }
  129. }
  130. // Delete any anonymous session information.
  131. if (isset($_SESSION['webform_submission'][$submission->sid])) {
  132. unset($_SESSION['webform_submission'][$submission->sid]);
  133. }
  134. db_delete('webform_submitted_data')
  135. ->condition('nid', $node->nid)
  136. ->condition('sid', $submission->sid)
  137. ->execute();
  138. db_delete('webform_submissions')
  139. ->condition('nid', $node->nid)
  140. ->condition('sid', $submission->sid)
  141. ->execute();
  142. module_invoke_all('webform_submission_delete', $node, $submission);
  143. }
  144. /**
  145. * Send related e-mails related to a submission.
  146. *
  147. * This function is usually invoked when a submission is completed, but may be
  148. * called any time e-mails should be redelivered.
  149. *
  150. * @param $node
  151. * The node object containing the current webform.
  152. * @param $submission
  153. * The webform submission object to be used in sending e-mails.
  154. * @param $emails
  155. * (optional) An array of specific e-mail settings to be used. If omitted, all
  156. * e-mails in $node->webform['emails'] will be sent.
  157. */
  158. function webform_submission_send_mail($node, $submission, $emails = NULL) {
  159. global $user;
  160. // Get the list of e-mails we'll be sending.
  161. $emails = isset($emails) ? $emails : $node->webform['emails'];
  162. // Create a themed message for mailing.
  163. $send_count = 0;
  164. foreach ($emails as $eid => $email) {
  165. // Set the HTML property based on availablity of MIME Mail.
  166. $email['html'] = ($email['html'] && webform_email_html_capable());
  167. // Pass through the theme layer if using the default template.
  168. if ($email['template'] == 'default') {
  169. $email['message'] = theme(array('webform_mail_' . $node->nid, 'webform_mail', 'webform_mail_message'), array('node' => $node, 'submission' => $submission, 'email' => $email));
  170. }
  171. else {
  172. $email['message'] = $email['template'];
  173. }
  174. // Replace tokens in the message.
  175. $email['message'] = _webform_filter_values($email['message'], $node, $submission, $email, FALSE);
  176. // Build the e-mail headers.
  177. $email['headers'] = theme(array('webform_mail_headers_' . $node->nid, 'webform_mail_headers'), array('node' => $node, 'submission' => $submission, 'email' => $email));
  178. // Assemble the From string.
  179. if (isset($email['headers']['From'])) {
  180. // If a header From is already set, don't override it.
  181. $email['from'] = $email['headers']['From'];
  182. unset($email['headers']['From']);
  183. }
  184. else {
  185. $email['from'] = webform_format_email_address($email['from_address'], $email['from_name'], $node, $submission);
  186. }
  187. // Update the subject if set in the themed headers.
  188. if (isset($email['headers']['Subject'])) {
  189. $email['subject'] = $email['headers']['Subject'];
  190. unset($email['headers']['Subject']);
  191. }
  192. else {
  193. $email['subject'] = webform_format_email_subject($email['subject'], $node, $submission);
  194. }
  195. // Update the to e-mail if set in the themed headers.
  196. if (isset($email['headers']['To'])) {
  197. $email['email'] = $email['headers']['To'];
  198. unset($email['headers']['To']);
  199. }
  200. // Generate the list of addresses that this e-mail will be sent to.
  201. $addresses = array_filter(explode(',', $email['email']));
  202. $addresses_final = array();
  203. foreach ($addresses as $address) {
  204. $address = trim($address);
  205. // After filtering e-mail addresses with component values, a single value
  206. // might contain multiple addresses (such as from checkboxes or selects).
  207. $address = webform_format_email_address($address, NULL, $node, $submission, TRUE, FALSE, 'short');
  208. if (is_array($address)) {
  209. foreach ($address as $new_address) {
  210. $new_address = trim($new_address);
  211. if (valid_email_address($new_address)) {
  212. $addresses_final[] = $new_address;
  213. }
  214. }
  215. }
  216. elseif (valid_email_address($address)) {
  217. $addresses_final[] = $address;
  218. }
  219. }
  220. // Mail the webform results.
  221. foreach ($addresses_final as $address) {
  222. // Verify that this submission is not attempting to send any spam hacks.
  223. if (_webform_submission_spam_check($address, $email['subject'], $email['from'], $email['headers'])) {
  224. watchdog('webform', 'Possible spam attempt from @remote_addr' . "<br />\n" . nl2br(htmlentities($email['message'])), array('@remote_add' => ip_address()));
  225. drupal_set_message(t('Illegal information. Data not submitted.'), 'error');
  226. return FALSE;
  227. }
  228. $language = $user->uid ? user_preferred_language($user) : language_default();
  229. $mail_params = array(
  230. 'message' => $email['message'],
  231. 'subject' => $email['subject'],
  232. 'headers' => $email['headers'],
  233. 'node' => $node,
  234. 'submission' => $submission,
  235. 'email' => $email,
  236. );
  237. if (webform_email_html_capable()) {
  238. // Load attachments for the e-mail.
  239. $attachments = array();
  240. if ($email['attachments']) {
  241. webform_component_include('file');
  242. foreach ($node->webform['components'] as $component) {
  243. if (webform_component_feature($component['type'], 'attachment') && !empty($submission->data[$component['cid']][0])) {
  244. if (webform_component_implements($component['type'], 'attachments')) {
  245. $files = webform_component_invoke($component['type'], 'attachments', $component, $submission->data[$component['cid']]);
  246. if ($files) {
  247. $attachments = array_merge($attachments, $files);
  248. }
  249. }
  250. }
  251. }
  252. }
  253. // Add the attachments to the mail parameters.
  254. $mail_params['attachments'] = $attachments;
  255. // Set all other properties for HTML e-mail handling.
  256. $mail_params['plain'] = !$email['html'];
  257. $mail_params['plaintext'] = $email['html'] ? NULL : $email['message'];
  258. $mail_params['headers'] = $email['headers'];
  259. if ($email['html']) {
  260. // MIME Mail requires this header or it will filter all text.
  261. $mail_params['headers']['Content-Type'] = 'text/html; charset=UTF-8';
  262. }
  263. }
  264. // Mail the submission.
  265. $message = drupal_mail('webform', 'submission', $address, $language, $mail_params, $email['from']);
  266. if ($message['result']) {
  267. $send_count++;
  268. }
  269. }
  270. }
  271. return $send_count;
  272. }
  273. /**
  274. * Confirm form to delete a single form submission.
  275. *
  276. * @param $form
  277. * The new form array.
  278. * @param $form_state
  279. * The current form state.
  280. * @param $node
  281. * The node for which this webform was submitted.
  282. * @param $submission
  283. * The submission to be deleted (from webform_submitted_data).
  284. */
  285. function webform_submission_delete_form($form, $form_state, $node, $submission) {
  286. webform_set_breadcrumb($node, $submission);
  287. // Set the correct page title.
  288. drupal_set_title(webform_submission_title($node, $submission));
  289. // Keep the NID and SID in the same location as the webform_client_form().
  290. // This helps mollom identify the same fields when deleting a submission.
  291. $form['#tree'] = TRUE;
  292. $form['details']['nid'] = array(
  293. '#type' => 'value',
  294. '#value' => $node->nid,
  295. );
  296. $form['details']['sid'] = array(
  297. '#type' => 'value',
  298. '#value' => $submission->sid,
  299. );
  300. $question = t('Are you sure you want to delete this submission?');
  301. if (isset($_GET['destination'])) {
  302. $destination = $_GET['destination'];
  303. }
  304. elseif (webform_results_access($node)) {
  305. $destination = 'node/' . $node->nid . '/webform-results';
  306. }
  307. else {
  308. $destination = 'node/' . $node->nid . '/submissions';
  309. }
  310. return confirm_form($form, NULL, $destination, $question, t('Delete'), t('Cancel'));
  311. }
  312. function webform_submission_delete_form_submit($form, &$form_state) {
  313. $node = node_load($form_state['values']['details']['nid']);
  314. $submission = webform_get_submission($form_state['values']['details']['nid'], $form_state['values']['details']['sid']);
  315. webform_submission_delete($node, $submission);
  316. drupal_set_message(t('Submission deleted.'));
  317. $form_state['redirect'] = 'node/' . $node->nid . '/webform-results';
  318. }
  319. /**
  320. * Menu title callback; Return the submission number as a title.
  321. */
  322. function webform_submission_title($node, $submission) {
  323. return t('Submission #@sid', array('@sid' => $submission->sid));
  324. }
  325. /**
  326. * Menu callback; Present a Webform submission page for display or editing.
  327. */
  328. function webform_submission_page($node, $submission, $format) {
  329. global $user;
  330. // Render the admin UI breadcrumb.
  331. webform_set_breadcrumb($node, $submission);
  332. // Set the correct page title.
  333. drupal_set_title(webform_submission_title($node, $submission));
  334. if ($format == 'form') {
  335. $output = drupal_get_form('webform_client_form_' . $node->nid, $node, $submission);
  336. }
  337. else {
  338. $renderable = webform_submission_render($node, $submission, NULL, $format);
  339. $output = drupal_render($renderable);
  340. }
  341. // Determine the mode in which we're displaying this submission.
  342. $mode = ($format != 'form') ? 'display' : 'form';
  343. if (strpos(request_uri(), 'print/') !== FALSE) {
  344. $mode = 'print';
  345. }
  346. if (strpos(request_uri(), 'printpdf/') !== FALSE) {
  347. $mode = 'pdf';
  348. }
  349. // Add navigation for administrators.
  350. if (webform_results_access($node)) {
  351. $navigation = theme('webform_submission_navigation', array('node' => $node, 'submission' => $submission, 'mode' => $mode));
  352. $information = theme('webform_submission_information', array('node' => $node, 'submission' => $submission, 'mode' => $mode));
  353. }
  354. else {
  355. $navigation = NULL;
  356. $information = NULL;
  357. }
  358. // Actions may be shown to all users.
  359. $actions = theme('links', array('links' => module_invoke_all('webform_submission_actions', $node, $submission), 'attributes' => array('class' => array('links', 'inline', 'webform-submission-actions'))));
  360. // Disable the page cache for anonymous users viewing or editing submissions.
  361. if (!$user->uid) {
  362. webform_disable_page_cache();
  363. }
  364. $page = array(
  365. '#theme' => 'webform_submission_page',
  366. '#node' => $node,
  367. '#mode' => $mode,
  368. '#submission' => $submission,
  369. '#submission_content' => $output,
  370. '#submission_navigation' => $navigation,
  371. '#submission_information' => $information,
  372. '#submission_actions' => $actions,
  373. );
  374. $page['#attached']['library'][] = array('webform', 'admin');
  375. return $page;
  376. }
  377. /**
  378. * Form to resend specific e-mails associated with a submission.
  379. */
  380. function webform_submission_resend($form, $form_state, $node, $submission) {
  381. // Render the admin UI breadcrumb.
  382. webform_set_breadcrumb($node, $submission);
  383. $form['#tree'] = TRUE;
  384. $form['#node'] = $node;
  385. $form['#submission'] = $submission;
  386. foreach ($node->webform['emails'] as $eid => $email) {
  387. $email_addresses = array_filter(explode(',', check_plain($email['email'])));
  388. foreach ($email_addresses as $key => $email_address) {
  389. $email_addresses[$key] = webform_format_email_address($email_address, NULL, $node, $submission, FALSE);
  390. }
  391. $valid_email = !empty($email_addresses[0]) && valid_email_address($email_addresses[0]);
  392. $form['resend'][$eid] = array(
  393. '#type' => 'checkbox',
  394. '#default_value' => $valid_email ? TRUE : FALSE,
  395. '#disabled' => $valid_email ? FALSE : TRUE,
  396. );
  397. $form['emails'][$eid]['email'] = array(
  398. '#markup' => implode('<br />', $email_addresses),
  399. );
  400. if (!$valid_email) {
  401. $form['emails'][$eid]['email']['#value'] .= ' (' . t('empty') . ')';
  402. }
  403. $form['emails'][$eid]['subject'] = array(
  404. '#markup' => check_plain(webform_format_email_subject($email['subject'], $node, $submission)),
  405. );
  406. $form['actions'] = array('#type' => 'actions');
  407. $form['actions']['resend'] = array(
  408. '#type' => 'submit',
  409. '#value' => t('Resend e-mails'),
  410. );
  411. $form['actions']['cancel'] = array(
  412. '#type' => 'markup',
  413. '#markup' => l(t('Cancel'), isset($_GET['destination']) ? $_GET['destination'] : 'node/' . $node->nid . '/submission/' . $submission->sid),
  414. );
  415. }
  416. return $form;
  417. }
  418. /**
  419. * Validate handler for webform_submission_resend().
  420. */
  421. function webform_submission_resend_validate($form, &$form_state) {
  422. if (count(array_filter($form_state['values']['resend'])) == 0) {
  423. form_set_error('emails', t('You must select at least one email address to resend submission.'));
  424. }
  425. }
  426. /**
  427. * Submit handler for webform_submission_resend().
  428. */
  429. function webform_submission_resend_submit($form, &$form_state) {
  430. $node = $form['#node'];
  431. $submission = $form['#submission'];
  432. $emails = array();
  433. foreach ($form_state['values']['resend'] as $eid => $checked) {
  434. if ($checked) {
  435. $emails[] = $form['#node']->webform['emails'][$eid];
  436. }
  437. }
  438. $sent_count = webform_submission_send_mail($node, $submission, $emails);
  439. if ($sent_count) {
  440. drupal_set_message(format_plural($sent_count,
  441. 'Successfully re-sent submission #@sid to 1 recipient.',
  442. 'Successfully re-sent submission #@sid to @count recipients.',
  443. array('@sid' => $submission->sid)
  444. ));
  445. }
  446. else {
  447. drupal_set_message(t('No e-mails were able to be sent due to a server error.'), 'error');
  448. }
  449. }
  450. /**
  451. * Theme the node components form. Use a table to organize the components.
  452. *
  453. * @param $form
  454. * The form array.
  455. * @return
  456. * Formatted HTML form, ready for display.
  457. */
  458. function theme_webform_submission_resend($variables) {
  459. $form = $variables['form'];
  460. $header = array('', t('E-mail to'), t('Subject'));
  461. $rows = array();
  462. if (!empty($form['emails'])) {
  463. foreach (element_children($form['emails']) as $eid) {
  464. // Add each component to a table row.
  465. $rows[] = array(
  466. drupal_render($form['resend'][$eid]),
  467. drupal_render($form['emails'][$eid]['email']),
  468. drupal_render($form['emails'][$eid]['subject']),
  469. );
  470. }
  471. }
  472. else {
  473. $rows[] = array(array('data' => t('This webform is currently not setup to send emails.'), 'colspan' => 3));
  474. }
  475. $output = '';
  476. $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'webform-emails')));
  477. $output .= drupal_render_children($form);
  478. return $output;
  479. }
  480. /**
  481. * Print a Webform submission for display on a page or in an e-mail.
  482. */
  483. function webform_submission_render($node, $submission, $email, $format) {
  484. $component_tree = array();
  485. $renderable = array();
  486. $page_count = 1;
  487. $excluded_components = isset($email) ? $email['excluded_components'] : array();
  488. // Meta data that may be useful for modules implementing
  489. // hook_webform_submission_render_alter().
  490. $renderable['#node'] = $node;
  491. $renderable['#submission'] = $submission;
  492. $renderable['#email'] = $email;
  493. $renderable['#format'] = $format;
  494. // Set the theme function for submissions.
  495. $renderable['#theme'] = array('webform_submission_' . $node->nid, 'webform_submission');
  496. // Remove excluded components.
  497. $components = $node->webform['components'];
  498. foreach ($excluded_components as $cid) {
  499. unset($components[$cid]);
  500. }
  501. _webform_components_tree_build($components, $component_tree, 0, $page_count);
  502. // Make sure at least one field is available
  503. if (isset($component_tree['children'])) {
  504. // Recursively add components to the form.
  505. $input_values = $submission->data;
  506. foreach ($component_tree['children'] as $cid => $component) {
  507. if (_webform_client_form_rule_check($node, $component, $component['page_num'], $input_values, $format)) {
  508. _webform_client_form_add_component($node, $component, NULL, $renderable, $renderable, $input_values, $format);
  509. }
  510. }
  511. }
  512. drupal_alter('webform_submission_render', $renderable);
  513. return $renderable;
  514. }
  515. /**
  516. * Return all the submissions for a particular node.
  517. *
  518. * @param $filters
  519. * An array of filters to apply to this query. Usually in the format
  520. * array('nid' => $nid, 'uid' => $uid). A single integer may also be passed
  521. * in, which will be equivalent to specifying a $nid filter.
  522. * @param $header
  523. * If the results of this fetch will be used in a sortable
  524. * table, pass the array header of the table.
  525. * @param $pager_count
  526. * Optional. The number of submissions to include in the results.
  527. */
  528. function webform_get_submissions($filters = array(), $header = NULL, $pager_count = 0) {
  529. $submissions = array();
  530. if (!is_array($filters)) {
  531. $filters = array('nid' => $filters);
  532. }
  533. // UID filters need to be against a specific table.
  534. if (isset($filters['uid'])) {
  535. $filters['u.uid'] = $filters['uid'];
  536. unset($filters['uid']);
  537. }
  538. // No need to find SIDs if it was given to us.
  539. if (isset($filters['sid'])) {
  540. $sids = array($filters['sid']);
  541. }
  542. // Build the list of SIDs that need to be retrieved.
  543. else {
  544. $pager_query = db_select('webform_submissions', 'ws')->fields('ws', array('sid'));
  545. foreach ($filters as $column => $value) {
  546. $pager_query->condition($column, $value);
  547. }
  548. if (isset($filters['u.uid']) || !empty($header)) {
  549. // Join to the users table for sorting by user name.
  550. $pager_query->leftJoin('users', 'u', 'u.uid = ws.uid');
  551. }
  552. if (isset($filters['u.uid']) && $filters['u.uid'] === 0) {
  553. if (!empty($_SESSION['webform_submission'])) {
  554. $anonymous_sids = array_keys($_SESSION['webform_submission']);
  555. $pager_query->condition('sid', $anonymous_sids, 'IN');
  556. }
  557. else {
  558. $pager_query->condition('sid', 0);
  559. }
  560. }
  561. if (is_array($header)) {
  562. // Extending the query instatiates a new query object.
  563. $pager_query = $pager_query->extend('TableSort');
  564. $pager_query->orderByHeader($header);
  565. }
  566. else {
  567. $pager_query->orderBy('sid', 'ASC');
  568. }
  569. if ($pager_count) {
  570. // Extending the query instatiates a new query object.
  571. $pager_query = $pager_query->extend('PagerDefault');
  572. $pager_query->limit($pager_count);
  573. }
  574. $result = $pager_query->execute();
  575. $sids = array();
  576. foreach ($result as $row) {
  577. $sids[] = $row->sid;
  578. $submissions[$row->sid] = FALSE;
  579. }
  580. }
  581. // If there are no submissions being retrieved, return an empty array.
  582. if (empty($sids)) {
  583. return $submissions;
  584. }
  585. // Query the required submission data.
  586. $query = db_select('webform_submitted_data', 'sd');
  587. $query->leftJoin('webform_submissions', 's', 's.sid = sd.sid');
  588. $query->leftJoin('users', 'u', 'u.uid = s.uid');
  589. $query
  590. ->fields('s')
  591. ->fields('sd', array('cid', 'no', 'data'))
  592. ->fields('u', array('name'))
  593. ->condition('sd.sid', $sids, 'IN')
  594. ->orderBy('sd.sid', 'ASC')
  595. ->orderBy('sd.cid', 'ASC')
  596. ->orderBy('sd.no', 'ASC');
  597. // By adding the NID to this query we allow MySQL to use the primary key on
  598. // in webform_submitted_data for sorting (nid_sid_cid_no).
  599. if (isset($filters['nid'])) {
  600. $query->condition('sd.nid', $filters['nid']);
  601. }
  602. $result = $query->execute();
  603. // Convert the queried rows into submissions.
  604. $previous = 0;
  605. foreach ($result as $row) {
  606. if ($row->sid != $previous) {
  607. $submissions[$row->sid] = new stdClass();
  608. $submissions[$row->sid]->sid = $row->sid;
  609. $submissions[$row->sid]->nid = $row->nid;
  610. $submissions[$row->sid]->submitted = $row->submitted;
  611. $submissions[$row->sid]->remote_addr = $row->remote_addr;
  612. $submissions[$row->sid]->uid = $row->uid;
  613. $submissions[$row->sid]->name = $row->name;
  614. $submissions[$row->sid]->is_draft = $row->is_draft;
  615. $submissions[$row->sid]->data = array();
  616. }
  617. // CID may be NULL if this submission does not actually contain any data.
  618. if ($row->cid) {
  619. $submissions[$row->sid]->data[$row->cid][$row->no] = $row->data;
  620. }
  621. $previous = $row->sid;
  622. }
  623. foreach (module_implements('webform_submission_load') as $module) {
  624. $function = $module . '_webform_submission_load';
  625. $function($submissions);
  626. }
  627. return $submissions;
  628. }
  629. /**
  630. * Return a count of the total number of submissions for a node.
  631. *
  632. * @param $nid
  633. * The node ID for which submissions are being fetched.
  634. * @param $uid
  635. * Optional; the user ID to filter the submissions by.
  636. * @return
  637. * An integer value of the number of submissions.
  638. */
  639. function webform_get_submission_count($nid, $uid = NULL, $reset = FALSE) {
  640. static $counts;
  641. if (!isset($counts[$nid][$uid]) || $reset) {
  642. $query = db_select('webform_submissions', 'ws')
  643. ->addTag('webform_get_submission_count')
  644. ->condition('ws.nid', $nid)
  645. ->condition('ws.is_draft', 0);
  646. $arguments = array($nid);
  647. if ($uid !== NULL) {
  648. $query->condition('ws.uid', $uid);
  649. }
  650. if ($uid === 0) {
  651. $submissions = isset($_SESSION['webform_submission']) ? $_SESSION['webform_submission'] : NULL;
  652. if ($submissions) {
  653. $query->condition('ws.sid', $submissions, 'IN');
  654. }
  655. else {
  656. // Intentionally never match anything if the anonymous user has no
  657. // submissions.
  658. $query->condition('ws.sid', 0);
  659. }
  660. }
  661. $counts[$nid][$uid] = $query->countQuery()->execute()->fetchField();
  662. }
  663. return $counts[$nid][$uid];
  664. }
  665. /**
  666. * Fetch a specified submission for a webform node.
  667. */
  668. function webform_get_submission($nid, $sid, $reset = FALSE) {
  669. static $submissions = array();
  670. if ($reset) {
  671. $submissions = array();
  672. if (!isset($sid)) {
  673. return;
  674. }
  675. }
  676. // Load the submission if needed.
  677. if (!isset($submissions[$sid])) {
  678. $new_submissions = webform_get_submissions(array('nid' => $nid, 'sid' => $sid));
  679. $submissions[$sid] = isset($new_submissions[$sid]) ? $new_submissions[$sid] : FALSE;
  680. }
  681. return $submissions[$sid];
  682. }
  683. function _webform_submission_spam_check($to, $subject, $from, $headers = array()) {
  684. $headers = implode('\n', (array)$headers);
  685. // Check if they are attempting to spam using a bcc or content type hack.
  686. if (preg_match('/(b?cc\s?:)|(content\-type:)/i', $to . "\n" . $subject . "\n" . $from . "\n" . $headers)) {
  687. return TRUE; // Possible spam attempt.
  688. }
  689. return FALSE; // Not spam.
  690. }
  691. /**
  692. * Check if the current user has exceeded the limit on this form.
  693. *
  694. * @param $node
  695. * The webform node to be checked.
  696. * @param $account
  697. * Optional parameter. Specify the account you want to check the limit
  698. * against.
  699. *
  700. * @return
  701. * Boolean TRUE if the user has exceeded their limit. FALSE otherwise.
  702. */
  703. function webform_submission_user_limit_check($node, $account = NULL) {
  704. global $user;
  705. if (!isset($account)) {
  706. $account = $user;
  707. }
  708. // We can only check access for anonymous users through their cookies.
  709. if ($user->uid !== 0 && $account->uid === 0) {
  710. watchdog('webform', 'Unable to check anonymous user submission limit when logged in as user @uid.', array('@uid' => $user->uid), WATCHDOG_WARNING);
  711. return FALSE;
  712. }
  713. // Check if submission limiting is enabled.
  714. if ($node->webform['submit_limit'] == '-1') {
  715. return FALSE; // No check enabled.
  716. }
  717. // Retrieve submission data for this IP address or username from the database.
  718. $query = db_select('webform_submissions')
  719. ->condition('nid', $node->nid)
  720. ->condition('is_draft', 0);
  721. if ($node->webform['submit_interval'] != -1) {
  722. $query->condition('submitted', REQUEST_TIME - $node->webform['submit_interval'], '>');
  723. }
  724. if ($account->uid) {
  725. $query->condition('uid', $account->uid);
  726. }
  727. else {
  728. $query->condition('remote_addr', ip_address());
  729. }
  730. // Fetch all the entries from the database within the submit interval with this username and IP.
  731. $num_submissions_database = $query->countQuery()->execute()->fetchField();
  732. // Double check the submission history from the users machine using cookies.
  733. $num_submissions_cookie = 0;
  734. if ($account->uid === 0 && variable_get('webform_use_cookies', 0)) {
  735. $cookie_name = 'webform-' . $node->nid;
  736. if (isset($_COOKIE[$cookie_name]) && is_array($_COOKIE[$cookie_name])) {
  737. foreach ($_COOKIE[$cookie_name] as $key => $timestamp) {
  738. if ($node->webform['submit_interval'] != -1 && $timestamp <= REQUEST_TIME - $node->webform['submit_interval']) {
  739. // Remove the cookie if past the required time interval.
  740. $params = session_get_cookie_params();
  741. setcookie($cookie_name . '[' . $key . ']', '', 0, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
  742. }
  743. }
  744. // Count the number of submissions recorded in cookies.
  745. $num_submissions_cookie = count($_COOKIE[$cookie_name]);
  746. }
  747. else {
  748. $num_submissions_cookie = 0;
  749. }
  750. }
  751. if ($num_submissions_database >= $node->webform['submit_limit'] || $num_submissions_cookie >= $node->webform['submit_limit']) {
  752. // Limit exceeded.
  753. return TRUE;
  754. }
  755. // Limit not exceeded.
  756. return FALSE;
  757. }
  758. /**
  759. * Check if the total number of submissions has exceeded the limit on this form.
  760. *
  761. * @param $node
  762. * The webform node to be checked.
  763. * @return
  764. * Boolean TRUE if the form has exceeded it's limit. FALSE otherwise.
  765. */
  766. function webform_submission_total_limit_check($node) {
  767. // Check if submission limiting is enabled.
  768. if ($node->webform['total_submit_limit'] == '-1') {
  769. return FALSE; // No check enabled.
  770. }
  771. // Retrieve submission data from the database.
  772. $query = db_select('webform_submissions')
  773. ->condition('nid', $node->nid)
  774. ->condition('is_draft', 0);
  775. if ($node->webform['total_submit_interval'] != -1) {
  776. $query->condition('submitted', REQUEST_TIME - $node->webform['total_submit_interval'], '>');
  777. }
  778. // Fetch all the entries from the database within the submit interval.
  779. $num_submissions_database = $query->countQuery()->execute()->fetchField();
  780. if ($num_submissions_database >= $node->webform['total_submit_limit']) {
  781. // Limit exceeded.
  782. return TRUE;
  783. }
  784. // Limit not exceeded.
  785. return FALSE;
  786. }
  787. /**
  788. * Preprocess function for webform-submission.tpl.php.
  789. */
  790. function template_preprocess_webform_submission(&$vars) {
  791. $vars['node'] = $vars['renderable']['#node'];
  792. $vars['submission'] = $vars['renderable']['#submission'];
  793. $vars['email'] = $vars['renderable']['#email'];
  794. $vars['format'] = $vars['renderable']['#format'];
  795. }
  796. /**
  797. * Preprocess function for webform-submission-navigation.tpl.php.
  798. */
  799. function template_preprocess_webform_submission_navigation(&$vars) {
  800. $start_path = ($vars['mode'] == 'print') ? 'print/' : 'node/';
  801. $previous_query = db_select('webform_submissions')
  802. ->condition('nid', $vars['node']->nid)
  803. ->condition('sid', $vars['submission']->sid, '<');
  804. $previous_query->addExpression('MAX(sid)');
  805. $next_query = db_select('webform_submissions')
  806. ->condition('nid', $vars['node']->nid)
  807. ->condition('sid', $vars['submission']->sid, '>');
  808. $next_query->addExpression('MIN(sid)');
  809. $vars['previous'] = $previous_query->execute()->fetchField();
  810. $vars['next'] = $next_query->execute()->fetchField();
  811. $vars['previous_url'] = $start_path . $vars['node']->nid . '/submission/' . $vars['previous'] . ($vars['mode'] == 'form' ? '/edit' : '');
  812. $vars['next_url'] = $start_path . $vars['node']->nid . '/submission/' . $vars['next'] . ($vars['mode'] == 'form' ? '/edit' : '');
  813. }
  814. /**
  815. * Preprocess function for webform-submission-navigation.tpl.php.
  816. */
  817. function template_preprocess_webform_submission_information(&$vars) {
  818. $vars['account'] = user_load($vars['submission']->uid);
  819. $vars['actions'] = theme('links', module_invoke_all('webform_submission_actions', $vars['node'], $vars['submission']));
  820. }