webform.api.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994
  1. <?php
  2. /**
  3. * @file
  4. * Sample hooks demonstrating usage in Webform.
  5. */
  6. /**
  7. * @defgroup webform_hooks Webform Module Hooks
  8. * @{
  9. * Webform's hooks enable other modules to intercept events within Webform, such
  10. * as the completion of a submission or adding validation. Webform's hooks also
  11. * allow other modules to provide additional components for use within forms.
  12. */
  13. /**
  14. * Define callbacks that can be used as select list options.
  15. *
  16. * When users create a select component, they may select a pre-built list of
  17. * certain options. Webform core provides a few of these lists such as the
  18. * United States, countries of the world, and days of the week. This hook
  19. * provides additional lists that may be utilized.
  20. *
  21. * @see webform_options_example()
  22. * @see hook_webform_select_options_info_alter()
  23. *
  24. * @return
  25. * An array of callbacks that can be used for select list options. This array
  26. * should be keyed by the "name" of the pre-defined list. The values should
  27. * be an array with the following additional keys:
  28. * - title: The translated title for this list.
  29. * - options callback: The name of the function that will return the list.
  30. * - options arguments: Any additional arguments to send to the callback.
  31. * - file: Optional. The file containing the options callback, relative to
  32. * the module root.
  33. */
  34. function hook_webform_select_options_info() {
  35. $items = array();
  36. $items['days'] = array(
  37. 'title' => t('Days of the week'),
  38. 'options callback' => 'webform_options_days',
  39. 'file' => 'includes/webform.options.inc',
  40. );
  41. return $items;
  42. }
  43. /**
  44. * Alter the list of select list options provided by Webform and other modules.
  45. *
  46. * @see hook_webform_select_options_info().
  47. */
  48. function hook_webform_select_options_info_alter(&$items) {
  49. // Remove the days of the week options.
  50. unset($items['days']);
  51. }
  52. /**
  53. * This is an example function to demonstrate a webform options callback.
  54. *
  55. * This function returns a list of options that Webform may use in a select
  56. * component. In order to be called, the function name
  57. * ("webform_options_example" in this case), needs to be specified as a callback
  58. * in hook_webform_select_options_info().
  59. *
  60. * @param $component
  61. * The Webform component array for the select component being displayed.
  62. * @param $flat
  63. * Boolean value indicating whether the returned list needs to be a flat array
  64. * of key => value pairs. Select components support up to one level of
  65. * nesting, but when results are displayed, the list needs to be returned
  66. * without the nesting.
  67. * @param $filter
  68. * Boolean value indicating whether the included options should be passed
  69. * through the _webform_filter_values() function for token replacement (only)
  70. * needed if your list contains tokens).
  71. * @param $arguments
  72. * The "options arguments" specified in hook_webform_select_options_info().
  73. * @return
  74. * An array of key => value pairs suitable for a select list's #options
  75. * FormAPI property.
  76. */
  77. function webform_options_example($component, $flat, $filter, $arguments) {
  78. $options = array(
  79. 'one' => t('Pre-built option one'),
  80. 'two' => t('Pre-built option two'),
  81. 'three' => t('Pre-built option three'),
  82. );
  83. return $options;
  84. }
  85. /**
  86. * Respond to the loading of Webform submissions.
  87. *
  88. * @param $submissions
  89. * An array of Webform submissions that are being loaded, keyed by the
  90. * submission ID. Modifications to the submissions are done by reference.
  91. */
  92. function hook_webform_submission_load(&$submissions) {
  93. foreach ($submissions as $sid => $submission) {
  94. $submissions[$sid]->new_property = 'foo';
  95. }
  96. }
  97. /**
  98. * Modify a Webform submission, prior to saving it in the database.
  99. *
  100. * @param $node
  101. * The Webform node on which this submission was made.
  102. * @param $submission
  103. * The Webform submission that is about to be saved to the database.
  104. */
  105. function hook_webform_submission_presave($node, &$submission) {
  106. // Update some component's value before it is saved.
  107. $component_id = 4;
  108. $submission->data[$component_id]['value'][0] = 'foo';
  109. }
  110. /**
  111. * Respond to a Webform submission being inserted.
  112. *
  113. * Note that this hook is called after a submission has already been saved to
  114. * the database. If needing to modify the submission prior to insertion, use
  115. * hook_webform_submission_presave().
  116. *
  117. * @param $node
  118. * The Webform node on which this submission was made.
  119. * @param $submission
  120. * The Webform submission that was just inserted into the database.
  121. */
  122. function hook_webform_submission_insert($node, $submission) {
  123. // Insert a record into a 3rd-party module table when a submission is added.
  124. db_insert('mymodule_table')
  125. ->fields(array(
  126. 'nid' => $node->nid,
  127. 'sid' => $submission->sid,
  128. 'foo' => 'foo_data',
  129. ))
  130. ->execute();
  131. }
  132. /**
  133. * Respond to a Webform submission being updated.
  134. *
  135. * Note that this hook is called after a submission has already been saved to
  136. * the database. If needing to modify the submission prior to updating, use
  137. * hook_webform_submission_presave().
  138. *
  139. * @param $node
  140. * The Webform node on which this submission was made.
  141. * @param $submission
  142. * The Webform submission that was just updated in the database.
  143. */
  144. function hook_webform_submission_update($node, $submission) {
  145. // Update a record in a 3rd-party module table when a submission is updated.
  146. db_update('mymodule_table')
  147. ->fields(array(
  148. 'foo' => 'foo_data',
  149. ))
  150. ->condition('nid', $node->nid)
  151. ->condition('sid', $submission->sid)
  152. ->execute();
  153. }
  154. /**
  155. * Respond to a Webform submission being deleted.
  156. *
  157. * @param $node
  158. * The Webform node on which this submission was made.
  159. * @param $submission
  160. * The Webform submission that was just deleted from the database.
  161. */
  162. function hook_webform_submission_delete($node, $submission) {
  163. // Delete a record from a 3rd-party module table when a submission is deleted.
  164. db_delete('mymodule_table')
  165. ->condition('nid', $node->nid)
  166. ->condition('sid', $submission->sid)
  167. ->execute();
  168. }
  169. /**
  170. * Provide a list of actions that can be executed on a submission.
  171. *
  172. * Some actions are displayed in the list of submissions such as edit, view, and
  173. * delete. All other actions are displayed only when viewing the submission.
  174. * These additional actions may be specified in this hook. Examples included
  175. * directly in the Webform module include PDF, print, and resend e-mails. Other
  176. * modules may extend this list by using this hook.
  177. *
  178. * @param $node
  179. * The Webform node on which this submission was made.
  180. * @param $submission
  181. * The Webform submission on which the actions may be performed.
  182. */
  183. function hook_webform_submission_actions($node, $submission) {
  184. if (webform_results_access($node)) {
  185. $actions['myaction'] = array(
  186. 'title' => t('Do my action'),
  187. 'href' => 'node/' . $node->nid . '/submission/' . $submission->sid . '/myaction',
  188. 'query' => drupal_get_destination(),
  189. );
  190. }
  191. return $actions;
  192. }
  193. /**
  194. * Alter the display of a Webform submission.
  195. *
  196. * This function applies to both e-mails sent by Webform and normal display of
  197. * submissions when viewing through the adminsitrative interface.
  198. *
  199. * @param $renderable
  200. * The Webform submission in a renderable array, similar to FormAPI's
  201. * structure. This variable must be passed in by-reference. Important
  202. * properties of this array include #node, #submission, #email, and #format,
  203. * which can be used to find the context of the submission that is being
  204. * rendered.
  205. */
  206. function hook_webform_submission_render_alter(&$renderable) {
  207. // Remove page breaks from sent e-mails.
  208. if (isset($renderable['#email'])) {
  209. foreach (element_children($renderable) as $key) {
  210. if ($renderable[$key]['#component']['type'] == 'pagebreak') {
  211. unset($renderable[$key]);
  212. }
  213. }
  214. }
  215. }
  216. /**
  217. * Modify a loaded Webform component.
  218. *
  219. * IMPORTANT: This hook does not actually exist because components are loaded
  220. * in bulk as part of webform_node_load(). Use hook_node_load() to modify loaded
  221. * components when the node is loaded. This example is provided merely to point
  222. * to hook_node_load().
  223. *
  224. * @see hook_nodeapi()
  225. * @see webform_node_load()
  226. */
  227. function hook_webform_component_load() {
  228. // This hook does not exist. Instead use hook_node_load().
  229. }
  230. /**
  231. * Modify a Webform component before it is saved to the database.
  232. *
  233. * Note that most of the time this hook is not necessary, because Webform will
  234. * automatically add data to the component based on the component form. Using
  235. * hook_form_alter() will be sufficient in most cases.
  236. *
  237. * @see hook_form_alter()
  238. * @see webform_component_edit_form()
  239. *
  240. * @param $component
  241. * The Webform component being saved.
  242. */
  243. function hook_webform_component_presave(&$component) {
  244. $component['extra']['new_option'] = 'foo';
  245. }
  246. /**
  247. * Respond to a Webform component being inserted into the database.
  248. */
  249. function hook_webform_component_insert($component) {
  250. // Insert a record into a 3rd-party module table when a component is inserted.
  251. db_insert('mymodule_table')
  252. ->fields(array(
  253. 'nid' => $component['nid'],
  254. 'cid' => $component['cid'],
  255. 'foo' => 'foo_data',
  256. ))
  257. ->execute();
  258. }
  259. /**
  260. * Respond to a Webform component being updated in the database.
  261. */
  262. function hook_webform_component_update($component) {
  263. // Update a record in a 3rd-party module table when a component is updated.
  264. db_update('mymodule_table')
  265. ->fields(array(
  266. 'foo' => 'foo_data',
  267. ))
  268. ->condition('nid', $component['nid'])
  269. ->condition('cid', $component['cid'])
  270. ->execute();
  271. }
  272. /**
  273. * Respond to a Webform component being deleted.
  274. */
  275. function hook_webform_component_delete($component) {
  276. // Delete a record in a 3rd-party module table when a component is deleted.
  277. db_delete('mymodule_table')
  278. ->condition('nid', $component['nid'])
  279. ->condition('cid', $component['cid'])
  280. ->execute();
  281. }
  282. /**
  283. * Alter a Webform submission's header when exported.
  284. */
  285. function hook_webform_csv_header_alter(&$header, $component) {
  286. // Use the machine name for component headers, but only for the webform
  287. // with node 5 and components that are text fields.
  288. if ($component['nid'] == 5 && $component['type'] == 'textfield') {
  289. $header[2] = $component['form_key'];
  290. }
  291. }
  292. /**
  293. * Alter a Webform submission's data when exported.
  294. */
  295. function hook_webform_csv_data_alter(&$data, $component, $submission) {
  296. // If a value of a field was left blank, use the value from another
  297. // field.
  298. if ($component['cid'] == 1 && empty($data)) {
  299. $data = $submission->data[2]['value'][0];
  300. }
  301. }
  302. /**
  303. * Define components to Webform.
  304. *
  305. * @return
  306. * An array of components, keyed by machine name. Required properties are
  307. * "label" and "description". The "features" array defines which capabilities
  308. * the component has, such as being displayed in e-mails or csv downloads.
  309. * A component like "markup" for example would not show in these locations.
  310. * The possible features of a component include:
  311. *
  312. * - csv
  313. * - email
  314. * - email_address
  315. * - email_name
  316. * - required
  317. * - conditional
  318. * - spam_analysis
  319. * - group
  320. *
  321. * Note that most of these features do not indicate the default state, but
  322. * determine if the component can have this property at all. Setting
  323. * "required" to TRUE does not mean that a component's fields will always be
  324. * required, but instead give the option to the administrator to choose the
  325. * requiredness. See the example implementation for details on how these
  326. * features may be set.
  327. *
  328. * An optional "file" may be specified to be loaded when the component is
  329. * needed. A set of callbacks will be established based on the name of the
  330. * component. All components follow the pattern:
  331. *
  332. * _webform_[callback]_[component]
  333. *
  334. * Where [component] is the name of the key of the component and [callback] is
  335. * any of the following:
  336. *
  337. * - defaults
  338. * - edit
  339. * - render
  340. * - display
  341. * - submit
  342. * - delete
  343. * - help
  344. * - theme
  345. * - analysis
  346. * - table
  347. * - csv_headers
  348. * - csv_data
  349. *
  350. * See the sample component implementation for details on each one of these
  351. * callbacks.
  352. *
  353. * @see webform_components()
  354. */
  355. function hook_webform_component_info() {
  356. $components = array();
  357. $components['textfield'] = array(
  358. 'label' => t('Textfield'),
  359. 'description' => t('Basic textfield type.'),
  360. 'features' => array(
  361. // Add content to CSV downloads. Defaults to TRUE.
  362. 'csv' => TRUE,
  363. // This component supports default values. Defaults to TRUE.
  364. 'default_value' => FALSE,
  365. // This component supports a description field. Defaults to TRUE.
  366. 'description' => FALSE,
  367. // Show this component in e-mailed submissions. Defaults to TRUE.
  368. 'email' => TRUE,
  369. // Allow this component to be used as an e-mail FROM or TO address.
  370. // Defaults to FALSE.
  371. 'email_address' => FALSE,
  372. // Allow this component to be used as an e-mail SUBJECT or FROM name.
  373. // Defaults to FALSE.
  374. 'email_name' => TRUE,
  375. // This component may be toggled as required or not. Defaults to TRUE.
  376. 'required' => TRUE,
  377. // This component supports a title attribute. Defaults to TRUE.
  378. 'title' => FALSE,
  379. // This component has a title that can be toggled as displayed or not.
  380. 'title_display' => TRUE,
  381. // This component has a title that can be displayed inline.
  382. 'title_inline' => TRUE,
  383. // If this component can be used as a conditional SOURCE. All components
  384. // may always be displayed conditionally, regardless of this setting.
  385. // Defaults to TRUE.
  386. 'conditional' => TRUE,
  387. // If this component allows other components to be grouped within it
  388. // (like a fieldset or tabs). Defaults to FALSE.
  389. 'group' => FALSE,
  390. // If this component can be used for SPAM analysis, usually with Mollom.
  391. 'spam_analysis' => FALSE,
  392. // If this component saves a file that can be used as an e-mail
  393. // attachment. Defaults to FALSE.
  394. 'attachment' => FALSE,
  395. ),
  396. 'file' => 'components/textfield.inc',
  397. );
  398. return $components;
  399. }
  400. /**
  401. * Alter the list of available Webform components.
  402. *
  403. * @param $components
  404. * A list of existing components as defined by hook_webform_component_info().
  405. *
  406. * @see hook_webform_component_info()
  407. */
  408. function hook_webform_component_info_alter(&$components) {
  409. // Completely remove a component.
  410. unset($components['grid']);
  411. // Change the name of a component.
  412. $components['textarea']['label'] = t('Text box');
  413. }
  414. /**
  415. * Alter the list of Webform component default values.
  416. *
  417. * @param $defaults
  418. * A list of component defaults as defined by _webform_defaults_COMPONENT().
  419. * @param $type
  420. * The component type whose defaults are being provided.
  421. *
  422. * @see _webform_defaults_component()
  423. */
  424. function hook_webform_component_defaults_alter(&$defaults, $type) {
  425. // Alter a default for all component types.
  426. $defaults['required'] = 1;
  427. // Add a default for a new field added via hook_form_alter() or
  428. // hook_form_FORM_ID_alter() for all component types.
  429. $defaults['extra']['added_field'] = t('Added default value');
  430. // Add or alter defaults for specific component types:
  431. switch ($type) {
  432. case 'select':
  433. $defaults['extra']['optrand'] = 1;
  434. break;
  435. case 'textfield':
  436. case 'textarea':
  437. $defaults['extra']['another_added_field'] = t('Another added default value');
  438. }
  439. }
  440. /**
  441. * Alter access to a Webform submission.
  442. *
  443. * @param $node
  444. * The Webform node on which this submission was made.
  445. * @param $submission
  446. * The Webform submission.
  447. * @param $op
  448. * The operation to be performed on the submission. Possible values are:
  449. * - "view"
  450. * - "edit"
  451. * - "delete"
  452. * - "list"
  453. * @param $account
  454. * A user account object.
  455. * @return
  456. * TRUE if the current user has access to submission,
  457. * or FALSE otherwise.
  458. */
  459. function hook_webform_submission_access($node, $submission, $op = 'view', $account = NULL) {
  460. switch ($op) {
  461. case 'view':
  462. return TRUE;
  463. break;
  464. case 'edit':
  465. return FALSE;
  466. break;
  467. case 'delete':
  468. return TRUE;
  469. break;
  470. case 'list':
  471. return TRUE;
  472. break;
  473. }
  474. }
  475. /**
  476. * Determine if a user has access to see the results of a webform.
  477. *
  478. * Note in addition to the view access to the results granted here, the $account
  479. * must also have view access to the Webform node in order to see results.
  480. *
  481. * @see webform_results_access().
  482. *
  483. * @param $node
  484. * The Webform node to check access on.
  485. * @param $account
  486. * The user account to check access on.
  487. * @return
  488. * TRUE or FALSE if the user can access the webform results.
  489. */
  490. function hook_webform_results_access($node, $account) {
  491. // Let editors view results of unpublished webforms.
  492. if ($node->status == 0 && in_array('editor', $account->roles)) {
  493. return TRUE;
  494. }
  495. else {
  496. return FALSE;
  497. }
  498. }
  499. /**
  500. * Determine if a user has access to clear the results of a webform.
  501. *
  502. * Access via this hook is in addition (adds permission) to the standard
  503. * webform access (delete all webform submissions).
  504. *
  505. * @see webform_results_clear_access().
  506. *
  507. * @param $node object
  508. * The Webform node to check access on.
  509. * @param $account object
  510. * The user account to check access on.
  511. * @return boolean
  512. * TRUE or FALSE if the user can access the webform results.
  513. */
  514. function hook_webform_results_clear_access($node, $account) {
  515. return user_access('my additional access', $account);
  516. }
  517. /**
  518. * Return an array of files associated with the component.
  519. *
  520. * The output of this function will be used to attach files to e-mail messages.
  521. *
  522. * @param $component
  523. * A Webform component array.
  524. * @param $value
  525. * An array of information containing the submission result, directly
  526. * correlating to the webform_submitted_data database schema.
  527. * @return
  528. * An array of files, each file is an array with following keys:
  529. * - filepath: The relative path to the file.
  530. * - filename: The name of the file including the extension.
  531. * - filemime: The mimetype of the file.
  532. * This will result in an array looking something like this:
  533. * @code
  534. * array[0] => array(
  535. * 'filepath' => '/sites/default/files/attachment.txt',
  536. * 'filename' => 'attachment.txt',
  537. * 'filemime' => 'text/plain',
  538. * );
  539. * @endcode
  540. */
  541. function _webform_attachments_component($component, $value) {
  542. $files = array();
  543. $files[] = (array) file_load($value[0]);
  544. return $files;
  545. }
  546. /**
  547. * @}
  548. */
  549. /**
  550. * @defgroup webform_component Sample Webform Component
  551. * @{
  552. * In each of these examples, the word "component" should be replaced with the,
  553. * name of the component type (such as textfield or select). These are not
  554. * actual hooks, but instead samples of how Webform integrates with its own
  555. * built-in components.
  556. */
  557. /**
  558. * Specify the default properties of a component.
  559. *
  560. * @return
  561. * An array defining the default structure of a component.
  562. */
  563. function _webform_defaults_component() {
  564. return array(
  565. 'name' => '',
  566. 'form_key' => NULL,
  567. 'mandatory' => 0,
  568. 'pid' => 0,
  569. 'weight' => 0,
  570. 'extra' => array(
  571. 'options' => '',
  572. 'questions' => '',
  573. 'optrand' => 0,
  574. 'qrand' => 0,
  575. 'description' => '',
  576. ),
  577. );
  578. }
  579. /**
  580. * Generate the form for editing a component.
  581. *
  582. * Create a set of form elements to be displayed on the form for editing this
  583. * component. Use care naming the form items, as this correlates directly to the
  584. * database schema. The component "Name" and "Description" fields are added to
  585. * every component type and are not necessary to specify here (although they
  586. * may be overridden if desired).
  587. *
  588. * @param $component
  589. * A Webform component array.
  590. * @return
  591. * An array of form items to be displayed on the edit component page
  592. */
  593. function _webform_edit_component($component) {
  594. $form = array();
  595. // Disabling the description if not wanted.
  596. $form['description'] = array();
  597. // Most options are stored in the "extra" array, which stores any settings
  598. // unique to a particular component type.
  599. $form['extra']['options'] = array(
  600. '#type' => 'textarea',
  601. '#title' => t('Options'),
  602. '#default_value' => $component['extra']['options'],
  603. '#description' => t('Key-value pairs may be entered separated by pipes. i.e. safe_key|Some readable option') . theme('webform_token_help'),
  604. '#cols' => 60,
  605. '#rows' => 5,
  606. '#weight' => -3,
  607. '#required' => TRUE,
  608. );
  609. return $form;
  610. }
  611. /**
  612. * Render a Webform component to be part of a form.
  613. *
  614. * @param $component
  615. * A Webform component array.
  616. * @param $value
  617. * If editing an existing submission or resuming a draft, this will contain
  618. * an array of values to be shown instead of the default in the component
  619. * configuration. This value will always be an array, keyed numerically for
  620. * each value saved in this field.
  621. * @param $filter
  622. * Whether or not to filter the contents of descriptions and values when
  623. * rendering the component. Values need to be unfiltered to be editable by
  624. * Form Builder.
  625. *
  626. * @see _webform_client_form_add_component()
  627. */
  628. function _webform_render_component($component, $value = NULL, $filter = TRUE) {
  629. $form_item = array(
  630. '#type' => 'textfield',
  631. '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
  632. '#required' => $component['mandatory'],
  633. '#weight' => $component['weight'],
  634. '#description' => $filter ? _webform_filter_descriptions($component['extra']['description']) : $component['extra']['description'],
  635. '#default_value' => $filter ? _webform_filter_values($component['value']) : $component['value'],
  636. '#prefix' => '<div class="webform-component-textfield" id="webform-component-' . $component['form_key'] . '">',
  637. '#suffix' => '</div>',
  638. );
  639. if (isset($value)) {
  640. $form_item['#default_value'] = $value[0];
  641. }
  642. return $form_item;
  643. }
  644. /**
  645. * Display the result of a submission for a component.
  646. *
  647. * The output of this function will be displayed under the "Results" tab then
  648. * "Submissions". This should output the saved data in some reasonable manner.
  649. *
  650. * @param $component
  651. * A Webform component array.
  652. * @param $value
  653. * An array of information containing the submission result, directly
  654. * correlating to the webform_submitted_data database table schema.
  655. * @param $format
  656. * Either 'html' or 'text'. Defines the format that the content should be
  657. * returned as. Make sure that returned content is run through check_plain()
  658. * or other filtering functions when returning HTML.
  659. * @return
  660. * A renderable element containing at the very least these properties:
  661. * - #title
  662. * - #weight
  663. * - #component
  664. * - #format
  665. * - #value
  666. * Webform also uses #theme_wrappers to output the end result to the user,
  667. * which will properly format the label and content for use within an e-mail
  668. * (such as wrapping the text) or as HTML (ensuring consistent output).
  669. */
  670. function _webform_display_component($component, $value, $format = 'html') {
  671. return array(
  672. '#title' => $component['name'],
  673. '#weight' => $component['weight'],
  674. '#theme' => 'webform_display_textfield',
  675. '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
  676. '#post_render' => array('webform_element_wrapper'),
  677. '#field_prefix' => $component['extra']['field_prefix'],
  678. '#field_suffix' => $component['extra']['field_suffix'],
  679. '#component' => $component,
  680. '#format' => $format,
  681. '#value' => isset($value[0]) ? $value[0] : '',
  682. );
  683. }
  684. /**
  685. * A hook for changing the input values before saving to the database.
  686. *
  687. * Webform expects a component to consist of a single field, or a single array
  688. * of fields. If you have a component that requires a deeper form tree
  689. * you must flatten the data into a single array using this callback
  690. * or by setting #parents on each field to avoid data loss and/or unexpected
  691. * behavior.
  692. *
  693. * Note that Webform will save the result of this function directly into the
  694. * database.
  695. *
  696. * @param $component
  697. * A Webform component array.
  698. * @param $value
  699. * The POST data associated with the user input.
  700. * @return
  701. * An array of values to be saved into the database. Note that this should be
  702. * a numerically keyed array.
  703. */
  704. function _webform_submit_component($component, $value) {
  705. // Clean up a phone number into 123-456-7890 format.
  706. if ($component['extra']['phone_number']) {
  707. $matches = array();
  708. $number = preg_replace('[^0-9]', $value[0]);
  709. if (strlen($number) == 7) {
  710. $number = substr($number, 0, 3) . '-' . substr($number, 3, 4);
  711. }
  712. else {
  713. $number = substr($number, 0, 3) . '-' . substr($number, 3, 3) . '-' . substr($number, 6, 4);
  714. }
  715. }
  716. $value[0] = $number;
  717. return $value;
  718. }
  719. /**
  720. * Delete operation for a component or submission.
  721. *
  722. * @param $component
  723. * A Webform component array.
  724. * @param $value
  725. * An array of information containing the submission result, directly
  726. * correlating to the webform_submitted_data database schema.
  727. */
  728. function _webform_delete_component($component, $value) {
  729. // Delete corresponding files when a submission is deleted.
  730. if (!empty($value[0]) && ($file = webform_get_file($value[0]))) {
  731. file_usage_delete($file, 'webform');
  732. file_delete($file);
  733. }
  734. }
  735. /**
  736. * Module specific instance of hook_help().
  737. *
  738. * This allows each Webform component to add information into hook_help().
  739. */
  740. function _webform_help_component($section) {
  741. switch ($section) {
  742. case 'admin/config/content/webform#grid_description':
  743. return t('Allows creation of grid questions, denoted by radio buttons.');
  744. }
  745. }
  746. /**
  747. * Module specific instance of hook_theme().
  748. *
  749. * This allows each Webform component to add information into hook_theme(). If
  750. * you specify a file to include, you must define the path to the module that
  751. * this file belongs to.
  752. */
  753. function _webform_theme_component() {
  754. return array(
  755. 'webform_grid' => array(
  756. 'render element' => 'element',
  757. 'file' => 'components/grid.inc',
  758. 'path' => drupal_get_path('module', 'webform'),
  759. ),
  760. 'webform_display_grid' => array(
  761. 'render element' => 'element',
  762. 'file' => 'components/grid.inc',
  763. 'path' => drupal_get_path('module', 'webform'),
  764. ),
  765. );
  766. }
  767. /**
  768. * Calculate and returns statistics about results for this component.
  769. *
  770. * This takes into account all submissions to this webform. The output of this
  771. * function will be displayed under the "Results" tab then "Analysis".
  772. *
  773. * @param $component
  774. * An array of information describing the component, directly correlating to
  775. * the webform_component database schema.
  776. * @param $sids
  777. * An optional array of submission IDs (sid). If supplied, the analysis will
  778. * be limited to these sids.
  779. * @param $single
  780. * Boolean flag determining if the details about a single component are being
  781. * shown. May be used to provided detailed information about a single
  782. * component's analysis, such as showing "Other" options within a select list.
  783. * @return
  784. * An array of data rows, each containing a statistic for this component's
  785. * submissions.
  786. */
  787. function _webform_analysis_component($component, $sids = array(), $single = FALSE) {
  788. // Generate the list of options and questions.
  789. $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
  790. $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
  791. // Generate a lookup table of results.
  792. $query = db_select('webform_submitted_data', 'wsd')
  793. ->fields('wsd', array('no', 'data'))
  794. ->condition('nid', $component['nid'])
  795. ->condition('cid', $component['cid'])
  796. ->condition('data', '', '<>')
  797. ->groupBy('no')
  798. ->groupBy('data');
  799. $query->addExpression('COUNT(sid)', 'datacount');
  800. if (count($sids)) {
  801. $query->condition('sid', $sids, 'IN');
  802. }
  803. $result = $query->execute();
  804. $counts = array();
  805. foreach ($result as $data) {
  806. $counts[$data->no][$data->data] = $data->datacount;
  807. }
  808. // Create an entire table to be put into the returned row.
  809. $rows = array();
  810. $header = array('');
  811. // Add options as a header row.
  812. foreach ($options as $option) {
  813. $header[] = $option;
  814. }
  815. // Add questions as each row.
  816. foreach ($questions as $qkey => $question) {
  817. $row = array($question);
  818. foreach ($options as $okey => $option) {
  819. $row[] = !empty($counts[$qkey][$okey]) ? $counts[$qkey][$okey] : 0;
  820. }
  821. $rows[] = $row;
  822. }
  823. $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('class' => array('webform-grid'))));
  824. return array(array(array('data' => $output, 'colspan' => 2)));
  825. }
  826. /**
  827. * Return the result of a component value for display in a table.
  828. *
  829. * The output of this function will be displayed under the "Results" tab then
  830. * "Table".
  831. *
  832. * @param $component
  833. * A Webform component array.
  834. * @param $value
  835. * An array of information containing the submission result, directly
  836. * correlating to the webform_submitted_data database schema.
  837. * @return
  838. * Textual output formatted for human reading.
  839. */
  840. function _webform_table_component($component, $value) {
  841. $questions = array_values(_webform_component_options($component['extra']['questions']));
  842. $output = '';
  843. // Set the value as a single string.
  844. if (is_array($value)) {
  845. foreach ($value as $item => $value) {
  846. if ($value !== '') {
  847. $output .= $questions[$item] . ': ' . check_plain($value) . '<br />';
  848. }
  849. }
  850. }
  851. else {
  852. $output = check_plain(!isset($value['0']) ? '' : $value['0']);
  853. }
  854. return $output;
  855. }
  856. /**
  857. * Return the header for this component to be displayed in a CSV file.
  858. *
  859. * The output of this function will be displayed under the "Results" tab then
  860. * "Download".
  861. *
  862. * @param $component
  863. * A Webform component array.
  864. * @param $export_options
  865. * An array of options that may configure export of this field.
  866. * @return
  867. * An array of data to be displayed in the first three rows of a CSV file, not
  868. * including either prefixed or trailing commas.
  869. */
  870. function _webform_csv_headers_component($component, $export_options) {
  871. $header = array();
  872. $header[0] = array('');
  873. $header[1] = array($component['name']);
  874. $items = _webform_component_options($component['extra']['questions']);
  875. $count = 0;
  876. foreach ($items as $key => $item) {
  877. // Empty column per sub-field in main header.
  878. if ($count != 0) {
  879. $header[0][] = '';
  880. $header[1][] = '';
  881. }
  882. // The value for this option.
  883. $header[2][] = $item;
  884. $count++;
  885. }
  886. return $header;
  887. }
  888. /**
  889. * Format the submitted data of a component for CSV downloading.
  890. *
  891. * The output of this function will be displayed under the "Results" tab then
  892. * "Download".
  893. *
  894. * @param $component
  895. * A Webform component array.
  896. * @param $export_options
  897. * An array of options that may configure export of this field.
  898. * @param $value
  899. * An array of information containing the submission result, directly
  900. * correlating to the webform_submitted_data database schema.
  901. * @return
  902. * An array of items to be added to the CSV file. Each value within the array
  903. * will be another column within the file. This function is called once for
  904. * every row of data.
  905. */
  906. function _webform_csv_data_component($component, $export_options, $value) {
  907. $questions = array_keys(_webform_select_options($component['extra']['questions']));
  908. $return = array();
  909. foreach ($questions as $key => $question) {
  910. $return[] = isset($value[$key]) ? $value[$key] : '';
  911. }
  912. return $return;
  913. }
  914. /**
  915. * @}
  916. */