webform.submissions.inc 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922
  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]['value'] = $values;
  28. }
  29. else {
  30. $data[$cid]['value'][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['value'] 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 $node
  119. * The node object containing the current webform.
  120. * @param $submission
  121. * The webform submission object to be deleted from the database.
  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]['value']);
  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, TRUE);
  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']]['value'][0])) {
  244. if (webform_component_implements($component['type'], 'attachments')) {
  245. $files = webform_component_invoke($component['type'], 'attachments', $component, $submission->data[$component['cid']]['value']);
  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. $output = webform_submission_render($node, $submission, NULL, $format);
  339. }
  340. // Determine the mode in which we're displaying this submission.
  341. $mode = ($format != 'form') ? 'display' : 'form';
  342. if (strpos(request_uri(), 'print/') !== FALSE) {
  343. $mode = 'print';
  344. }
  345. if (strpos(request_uri(), 'printpdf/') !== FALSE) {
  346. $mode = 'pdf';
  347. }
  348. // Add navigation for administrators.
  349. if (webform_results_access($node)) {
  350. $navigation = theme('webform_submission_navigation', array('node' => $node, 'submission' => $submission, 'mode' => $mode));
  351. $information = theme('webform_submission_information', array('node' => $node, 'submission' => $submission, 'mode' => $mode));
  352. }
  353. else {
  354. $navigation = NULL;
  355. $information = NULL;
  356. }
  357. // Actions may be shown to all users.
  358. $actions = theme('links', array('links' => module_invoke_all('webform_submission_actions', $node, $submission), 'attributes' => array('class' => array('links', 'inline', 'webform-submission-actions'))));
  359. // Disable the page cache for anonymous users viewing or editing submissions.
  360. if (!$user->uid) {
  361. webform_disable_page_cache();
  362. }
  363. $page = array(
  364. '#theme' => 'webform_submission_page',
  365. '#node' => $node,
  366. '#mode' => $mode,
  367. '#submission' => $submission,
  368. '#submission_content' => $output,
  369. '#submission_navigation' => $navigation,
  370. '#submission_information' => $information,
  371. '#submission_actions' => $actions,
  372. );
  373. $page['#attached']['library'][] = array('webform', 'admin');
  374. return $page;
  375. }
  376. /**
  377. * Form to resend specific e-mails associated with a submission.
  378. */
  379. function webform_submission_resend($form, $form_state, $node, $submission) {
  380. // Render the admin UI breadcrumb.
  381. webform_set_breadcrumb($node, $submission);
  382. $form['#tree'] = TRUE;
  383. $form['#node'] = $node;
  384. $form['#submission'] = $submission;
  385. foreach ($node->webform['emails'] as $eid => $email) {
  386. $email_addresses = array_filter(explode(',', check_plain($email['email'])));
  387. foreach ($email_addresses as $key => $email_address) {
  388. $email_addresses[$key] = webform_format_email_address($email_address, NULL, $node, $submission, FALSE);
  389. }
  390. $valid_email = !empty($email_addresses[0]) && valid_email_address($email_addresses[0]);
  391. $form['resend'][$eid] = array(
  392. '#type' => 'checkbox',
  393. '#default_value' => $valid_email ? TRUE : FALSE,
  394. '#disabled' => $valid_email ? FALSE : TRUE,
  395. );
  396. $form['emails'][$eid]['email'] = array(
  397. '#markup' => implode('<br />', $email_addresses),
  398. );
  399. if (!$valid_email) {
  400. $form['emails'][$eid]['email']['#markup'] .= ' (' . t('empty') . ')';
  401. }
  402. $form['emails'][$eid]['subject'] = array(
  403. '#markup' => check_plain(webform_format_email_subject($email['subject'], $node, $submission)),
  404. );
  405. $form['actions'] = array('#type' => 'actions');
  406. $form['actions']['submit'] = array(
  407. '#type' => 'submit',
  408. '#value' => t('Resend e-mails'),
  409. );
  410. $form['actions']['cancel'] = array(
  411. '#type' => 'markup',
  412. '#markup' => l(t('Cancel'), isset($_GET['destination']) ? $_GET['destination'] : 'node/' . $node->nid . '/submission/' . $submission->sid),
  413. );
  414. }
  415. return $form;
  416. }
  417. /**
  418. * Validate handler for webform_submission_resend().
  419. */
  420. function webform_submission_resend_validate($form, &$form_state) {
  421. if (count(array_filter($form_state['values']['resend'])) == 0) {
  422. form_set_error('emails', t('You must select at least one email address to resend submission.'));
  423. }
  424. }
  425. /**
  426. * Submit handler for webform_submission_resend().
  427. */
  428. function webform_submission_resend_submit($form, &$form_state) {
  429. $node = $form['#node'];
  430. $submission = $form['#submission'];
  431. $emails = array();
  432. foreach ($form_state['values']['resend'] as $eid => $checked) {
  433. if ($checked) {
  434. $emails[] = $form['#node']->webform['emails'][$eid];
  435. }
  436. }
  437. $sent_count = webform_submission_send_mail($node, $submission, $emails);
  438. if ($sent_count) {
  439. drupal_set_message(format_plural($sent_count,
  440. 'Successfully re-sent submission #@sid to 1 recipient.',
  441. 'Successfully re-sent submission #@sid to @count recipients.',
  442. array('@sid' => $submission->sid)
  443. ));
  444. }
  445. else {
  446. drupal_set_message(t('No e-mails were able to be sent due to a server error.'), 'error');
  447. }
  448. }
  449. /**
  450. * Theme the node components form. Use a table to organize the components.
  451. *
  452. * @param $form
  453. * The form array.
  454. * @return
  455. * Formatted HTML form, ready for display.
  456. */
  457. function theme_webform_submission_resend($variables) {
  458. $form = $variables['form'];
  459. $header = array('', t('E-mail to'), t('Subject'));
  460. $rows = array();
  461. if (!empty($form['emails'])) {
  462. foreach (element_children($form['emails']) as $eid) {
  463. // Add each component to a table row.
  464. $rows[] = array(
  465. drupal_render($form['resend'][$eid]),
  466. drupal_render($form['emails'][$eid]['email']),
  467. drupal_render($form['emails'][$eid]['subject']),
  468. );
  469. }
  470. }
  471. else {
  472. $rows[] = array(array('data' => t('This webform is currently not setup to send emails.'), 'colspan' => 3));
  473. }
  474. $output = '';
  475. $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'webform-emails')));
  476. $output .= drupal_render_children($form);
  477. return $output;
  478. }
  479. /**
  480. * Print a Webform submission for display on a page or in an e-mail.
  481. */
  482. function webform_submission_render($node, $submission, $email, $format) {
  483. $component_tree = array();
  484. $renderable = array();
  485. $page_count = 1;
  486. $excluded_components = isset($email) ? $email['excluded_components'] : array();
  487. // Meta data that may be useful for modules implementing
  488. // hook_webform_submission_render_alter().
  489. $renderable['#node'] = $node;
  490. $renderable['#submission'] = $submission;
  491. $renderable['#email'] = $email;
  492. $renderable['#format'] = $format;
  493. // Set the theme function for submissions.
  494. $renderable['#theme'] = array('webform_submission_' . $node->nid, 'webform_submission');
  495. // Remove excluded components.
  496. $components = $node->webform['components'];
  497. foreach ($excluded_components as $cid) {
  498. unset($components[$cid]);
  499. }
  500. _webform_components_tree_build($components, $component_tree, 0, $page_count);
  501. // Make sure at least one field is available
  502. if (isset($component_tree['children'])) {
  503. // Recursively add components to the form.
  504. foreach ($component_tree['children'] as $cid => $component) {
  505. if (_webform_client_form_rule_check($node, $component, $component['page_num'], NULL, $submission)) {
  506. _webform_client_form_add_component($node, $component, NULL, $renderable, $renderable, NULL, $submission, $format);
  507. }
  508. }
  509. }
  510. drupal_alter('webform_submission_render', $renderable);
  511. return drupal_render($renderable);
  512. }
  513. /**
  514. * Return all the submissions for a particular node.
  515. *
  516. * @param $filters
  517. * An array of filters to apply to this query. Usually in the format
  518. * array('nid' => $nid, 'uid' => $uid). A single integer may also be passed
  519. * in, which will be equivalent to specifying a $nid filter.
  520. * @param $header
  521. * If the results of this fetch will be used in a sortable
  522. * table, pass the array header of the table.
  523. * @param $pager_count
  524. * Optional. The number of submissions to include in the results.
  525. */
  526. function webform_get_submissions($filters = array(), $header = NULL, $pager_count = 0) {
  527. $submissions = array();
  528. if (!is_array($filters)) {
  529. $filters = array('nid' => $filters);
  530. }
  531. // UID filters need to be against a specific table.
  532. if (isset($filters['uid'])) {
  533. $filters['u.uid'] = $filters['uid'];
  534. unset($filters['uid']);
  535. }
  536. // No need to find SIDs if it was given to us.
  537. if (isset($filters['sid'])) {
  538. $sids = array($filters['sid']);
  539. }
  540. // Build the list of SIDs that need to be retrieved.
  541. else {
  542. $pager_query = db_select('webform_submissions', 'ws')->fields('ws', array('sid'));
  543. foreach ($filters as $column => $value) {
  544. $pager_query->condition($column, $value);
  545. }
  546. if (isset($filters['u.uid']) || !empty($header)) {
  547. // Join to the users table for sorting by user name.
  548. $pager_query->leftJoin('users', 'u', 'u.uid = ws.uid');
  549. }
  550. if (isset($filters['u.uid']) && $filters['u.uid'] === 0) {
  551. if (!empty($_SESSION['webform_submission'])) {
  552. $anonymous_sids = array_keys($_SESSION['webform_submission']);
  553. $pager_query->condition('sid', $anonymous_sids, 'IN');
  554. }
  555. else {
  556. $pager_query->condition('sid', 0);
  557. }
  558. }
  559. if (is_array($header)) {
  560. // Extending the query instatiates a new query object.
  561. $pager_query = $pager_query->extend('TableSort');
  562. $pager_query->orderByHeader($header);
  563. }
  564. else {
  565. $pager_query->orderBy('sid', 'ASC');
  566. }
  567. if ($pager_count) {
  568. // Extending the query instatiates a new query object.
  569. $pager_query = $pager_query->extend('PagerDefault');
  570. $pager_query->limit($pager_count);
  571. }
  572. $result = $pager_query->execute();
  573. $sids = array();
  574. foreach ($result as $row) {
  575. $sids[] = $row->sid;
  576. $submissions[$row->sid] = FALSE;
  577. }
  578. }
  579. // If there are no submissions being retrieved, return an empty array.
  580. if (empty($sids)) {
  581. return $submissions;
  582. }
  583. // Query the required submission data.
  584. $query = db_select('webform_submissions', 's');
  585. $query->leftJoin('users', 'u', 'u.uid = s.uid');
  586. $query
  587. ->fields('s')
  588. ->fields('u', array('name'))
  589. ->condition('s.sid', $sids, 'IN');
  590. $submissions = $query->execute()->fetchAllAssoc('sid');
  591. foreach ($submissions as $sid => $submission) {
  592. $submissions[$sid]->data = array();
  593. }
  594. // Query the required submission data.
  595. $query = db_select('webform_submitted_data', 'sd');
  596. $query
  597. ->fields('sd', array('sid', 'cid', 'no', 'data'))
  598. ->condition('sd.sid', array_keys($submissions), 'IN')
  599. ->orderBy('sd.sid', 'ASC')
  600. ->orderBy('sd.cid', 'ASC')
  601. ->orderBy('sd.no', 'ASC');
  602. // By adding the NID to this query we allow MySQL to use the primary key on
  603. // in webform_submitted_data for sorting (nid_sid_cid_no).
  604. if (isset($filters['nid'])) {
  605. $query->condition('sd.nid', $filters['nid']);
  606. }
  607. if ($submissions) {
  608. $result = $query->execute();
  609. // Convert the queried rows into submission data.
  610. foreach ($result as $row) {
  611. $submissions[$row->sid]->data[$row->cid]['value'][$row->no] = $row->data;
  612. }
  613. }
  614. foreach (module_implements('webform_submission_load') as $module) {
  615. $function = $module . '_webform_submission_load';
  616. $function($submissions);
  617. }
  618. return $submissions;
  619. }
  620. /**
  621. * Return a count of the total number of submissions for a node.
  622. *
  623. * @param $nid
  624. * The node ID for which submissions are being fetched.
  625. * @param $uid
  626. * Optional; the user ID to filter the submissions by.
  627. * @return
  628. * An integer value of the number of submissions.
  629. */
  630. function webform_get_submission_count($nid, $uid = NULL, $reset = FALSE) {
  631. static $counts;
  632. if (!isset($counts[$nid][$uid]) || $reset) {
  633. $query = db_select('webform_submissions', 'ws')
  634. ->addTag('webform_get_submission_count')
  635. ->condition('ws.nid', $nid)
  636. ->condition('ws.is_draft', 0);
  637. $arguments = array($nid);
  638. if ($uid !== NULL) {
  639. $query->condition('ws.uid', $uid);
  640. }
  641. if ($uid === 0) {
  642. $submissions = isset($_SESSION['webform_submission']) ? $_SESSION['webform_submission'] : NULL;
  643. if ($submissions) {
  644. $query->condition('ws.sid', $submissions, 'IN');
  645. }
  646. else {
  647. // Intentionally never match anything if the anonymous user has no
  648. // submissions.
  649. $query->condition('ws.sid', 0);
  650. }
  651. }
  652. $counts[$nid][$uid] = $query->countQuery()->execute()->fetchField();
  653. }
  654. return $counts[$nid][$uid];
  655. }
  656. /**
  657. * Fetch a specified submission for a webform node.
  658. */
  659. function webform_get_submission($nid, $sid, $reset = FALSE) {
  660. static $submissions = array();
  661. if ($reset) {
  662. $submissions = array();
  663. if (!isset($sid)) {
  664. return;
  665. }
  666. }
  667. // Load the submission if needed.
  668. if (!isset($submissions[$sid])) {
  669. $new_submissions = webform_get_submissions(array('nid' => $nid, 'sid' => $sid));
  670. $submissions[$sid] = isset($new_submissions[$sid]) ? $new_submissions[$sid] : FALSE;
  671. }
  672. return $submissions[$sid];
  673. }
  674. function _webform_submission_spam_check($to, $subject, $from, $headers = array()) {
  675. $headers = implode('\n', (array)$headers);
  676. // Check if they are attempting to spam using a bcc or content type hack.
  677. if (preg_match('/(b?cc\s?:)|(content\-type:)/i', $to . "\n" . $subject . "\n" . $from . "\n" . $headers)) {
  678. return TRUE; // Possible spam attempt.
  679. }
  680. return FALSE; // Not spam.
  681. }
  682. /**
  683. * Check if the current user has exceeded the limit on this form.
  684. *
  685. * @param $node
  686. * The webform node to be checked.
  687. * @return
  688. * Boolean TRUE if the user has exceeded their limit. FALSE otherwise.
  689. */
  690. function _webform_submission_user_limit_check($node) {
  691. global $user;
  692. // Check if submission limiting is enabled.
  693. if ($node->webform['submit_limit'] == '-1') {
  694. return FALSE; // No check enabled.
  695. }
  696. // Retrieve submission data for this IP address or username from the database.
  697. $query = db_select('webform_submissions')
  698. ->addTag('webform_submission_user_limit_check')
  699. ->condition('nid', $node->nid)
  700. ->condition('is_draft', 0);
  701. if ($node->webform['submit_interval'] != -1) {
  702. $query->condition('submitted', REQUEST_TIME - $node->webform['submit_interval'], '>');
  703. }
  704. if ($user->uid) {
  705. $query->condition('uid', $user->uid);
  706. }
  707. else {
  708. $query->condition('remote_addr', ip_address());
  709. }
  710. // Fetch all the entries from the database within the submit interval with this username and IP.
  711. $num_submissions_database = $query->countQuery()->execute()->fetchField();
  712. // Double check the submission history from the users machine using cookies.
  713. $num_submissions_cookie = 0;
  714. if ($user->uid == 0 && variable_get('webform_use_cookies', 0)) {
  715. $cookie_name = 'webform-' . $node->nid;
  716. if (isset($_COOKIE[$cookie_name]) && is_array($_COOKIE[$cookie_name])) {
  717. foreach ($_COOKIE[$cookie_name] as $key => $timestamp) {
  718. if ($node->webform['submit_interval'] != -1 && $timestamp <= REQUEST_TIME - $node->webform['submit_interval']) {
  719. // Remove the cookie if past the required time interval.
  720. $params = session_get_cookie_params();
  721. setcookie($cookie_name . '[' . $key . ']', '', 0, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
  722. }
  723. }
  724. // Count the number of submissions recorded in cookies.
  725. $num_submissions_cookie = count($_COOKIE[$cookie_name]);
  726. }
  727. else {
  728. $num_submissions_cookie = 0;
  729. }
  730. }
  731. if ($num_submissions_database >= $node->webform['submit_limit'] || $num_submissions_cookie >= $node->webform['submit_limit']) {
  732. // Limit exceeded.
  733. return TRUE;
  734. }
  735. // Limit not exceeded.
  736. return FALSE;
  737. }
  738. /**
  739. * Check if the total number of submissions has exceeded the limit on this form.
  740. *
  741. * @param $node
  742. * The webform node to be checked.
  743. * @return
  744. * Boolean TRUE if the form has exceeded it's limit. FALSE otherwise.
  745. */
  746. function _webform_submission_total_limit_check($node) {
  747. // Check if submission limiting is enabled.
  748. if ($node->webform['total_submit_limit'] == '-1') {
  749. return FALSE; // No check enabled.
  750. }
  751. // Retrieve submission data from the database.
  752. $query = db_select('webform_submissions')
  753. ->addTag('webform_submission_total_limit_check')
  754. ->condition('nid', $node->nid)
  755. ->condition('is_draft', 0);
  756. if ($node->webform['total_submit_interval'] != -1) {
  757. $query->condition('submitted', REQUEST_TIME - $node->webform['total_submit_interval'], '>');
  758. }
  759. // Fetch all the entries from the database within the submit interval.
  760. $num_submissions_database = $query->countQuery()->execute()->fetchField();
  761. if ($num_submissions_database >= $node->webform['total_submit_limit']) {
  762. // Limit exceeded.
  763. return TRUE;
  764. }
  765. // Limit not exceeded.
  766. return FALSE;
  767. }
  768. /**
  769. * Preprocess function for webform-submission.tpl.php.
  770. */
  771. function template_preprocess_webform_submission(&$vars) {
  772. $vars['node'] = $vars['renderable']['#node'];
  773. $vars['submission'] = $vars['renderable']['#submission'];
  774. $vars['email'] = $vars['renderable']['#email'];
  775. $vars['format'] = $vars['renderable']['#format'];
  776. }
  777. /**
  778. * Preprocess function for webform-submission-navigation.tpl.php.
  779. */
  780. function template_preprocess_webform_submission_navigation(&$vars) {
  781. $start_path = ($vars['mode'] == 'print') ? 'print/' : 'node/';
  782. $previous_query = db_select('webform_submissions')
  783. ->condition('nid', $vars['node']->nid)
  784. ->condition('sid', $vars['submission']->sid, '<');
  785. $previous_query->addExpression('MAX(sid)');
  786. $next_query = db_select('webform_submissions')
  787. ->condition('nid', $vars['node']->nid)
  788. ->condition('sid', $vars['submission']->sid, '>');
  789. $next_query->addExpression('MIN(sid)');
  790. $vars['previous'] = $previous_query->execute()->fetchField();
  791. $vars['next'] = $next_query->execute()->fetchField();
  792. $vars['previous_url'] = $start_path . $vars['node']->nid . '/submission/' . $vars['previous'] . ($vars['mode'] == 'form' ? '/edit' : '');
  793. $vars['next_url'] = $start_path . $vars['node']->nid . '/submission/' . $vars['next'] . ($vars['mode'] == 'form' ? '/edit' : '');
  794. }
  795. /**
  796. * Preprocess function for webform-submission-navigation.tpl.php.
  797. */
  798. function template_preprocess_webform_submission_information(&$vars) {
  799. $vars['account'] = user_load($vars['submission']->uid);
  800. $vars['actions'] = theme('links', module_invoke_all('webform_submission_actions', $vars['node'], $vars['submission']));
  801. }