commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-new / webform / webform.api.php
1 <?php
2
3 /**
4 * @file
5 * Sample hooks demonstrating usage in Webform.
6 */
7
8 /**
9 * @defgroup webform_hooks Webform Module Hooks
10 * @{
11 * Webform's hooks enable other modules to intercept events within Webform, such
12 * as the completion of a submission or adding validation. Webform's hooks also
13 * allow other modules to provide additional components for use within forms.
14 */
15
16 /**
17 * Define callbacks that can be used as select list options.
18 *
19 * When users create a select component, they may select a pre-built list of
20 * certain options. Webform core provides a few of these lists such as the
21 * United States, countries of the world, and days of the week. This hook
22 * provides additional lists that may be utilized.
23 *
24 * @see webform_options_example()
25 * @see hook_webform_select_options_info_alter()
26 *
27 * @return
28 * An array of callbacks that can be used for select list options. This array
29 * should be keyed by the "name" of the pre-defined list. The values should
30 * be an array with the following additional keys:
31 * - title: The translated title for this list.
32 * - options callback: The name of the function that will return the list.
33 * - options arguments: Any additional arguments to send to the callback.
34 * - file: Optional. The file containing the options callback, relative to
35 * the module root.
36 */
37 function hook_webform_select_options_info() {
38 $items = array();
39
40 $items['days'] = array(
41 'title' => t('Days of the week'),
42 'options callback' => 'webform_options_days',
43 'file' => 'includes/webform.options.inc',
44 );
45
46 return $items;
47 }
48
49 /**
50 * Alter the list of select list options provided by Webform and other modules.
51 *
52 * @see hook_webform_select_options_info().
53 */
54 function hook_webform_select_options_info_alter(&$items) {
55 // Remove the days of the week options.
56 unset($items['days']);
57 }
58
59 /**
60 * This is an example function to demonstrate a webform options callback.
61 *
62 * This function returns a list of options that Webform may use in a select
63 * component. In order to be called, the function name
64 * ("webform_options_example" in this case), needs to be specified as a callback
65 * in hook_webform_select_options_info().
66 *
67 * @param $component
68 * The Webform component array for the select component being displayed.
69 * @param $flat
70 * Boolean value indicating whether the returned list needs to be a flat array
71 * of key => value pairs. Select components support up to one level of
72 * nesting, but when results are displayed, the list needs to be returned
73 * without the nesting.
74 * @param $arguments
75 * The "options arguments" specified in hook_webform_select_options_info().
76 * @return
77 * An array of key => value pairs suitable for a select list's #options
78 * FormAPI property.
79 */
80 function webform_options_example($component, $flat, $arguments) {
81 $options = array(
82 'one' => t('Pre-built option one'),
83 'two' => t('Pre-built option two'),
84 'three' => t('Pre-built option three'),
85 );
86
87 return $options;
88 }
89
90 /**
91 * Respond to the loading of Webform submissions.
92 *
93 * @param $submissions
94 * An array of Webform submissions that are being loaded, keyed by the
95 * submission ID. Modifications to the submissions are done by reference.
96 */
97 function hook_webform_submission_load(&$submissions) {
98 foreach ($submissions as $sid => $submission) {
99 $submissions[$sid]->new_property = 'foo';
100 }
101 }
102
103 /**
104 * Respond to the creation of a new submission from form values.
105 *
106 * This hook is called when a user has completed a submission to initialize the
107 * submission object. After this object has its values populated, it will be
108 * saved by webform_submission_insert(). Note that this hook is only called for
109 * new submissions, not for submissions being edited. If responding to the
110 * saving of all submissions, it's recommended to use
111 * hook_webform_submission_presave().
112 *
113 * @param $submission
114 * The submission object that has been created.
115 * @param $node
116 * The Webform node for which this submission is being saved.
117 * @param $account
118 * The user account that is creating the submission.
119 * @param $form_state
120 * The contents of form state that is the basis for this submission.
121 *
122 * @see webform_submission_create()
123 */
124 function hook_webform_submission_create_alter(&$submission, &$node, &$account, &$form_state) {
125 $submission->new_property = TRUE;
126 }
127
128 /**
129 * Modify a Webform submission, prior to saving it in the database.
130 *
131 * @param $node
132 * The Webform node on which this submission was made.
133 * @param $submission
134 * The Webform submission that is about to be saved to the database.
135 */
136 function hook_webform_submission_presave($node, &$submission) {
137 // Update some component's value before it is saved.
138 $component_id = 4;
139 $submission->data[$component_id][0] = 'foo';
140 }
141
142 /**
143 * Respond to a Webform submission being inserted.
144 *
145 * Note that this hook is called after a submission has already been saved to
146 * the database. If needing to modify the submission prior to insertion, use
147 * hook_webform_submission_presave().
148 *
149 * @param $node
150 * The Webform node on which this submission was made.
151 * @param $submission
152 * The Webform submission that was just inserted into the database.
153 */
154 function hook_webform_submission_insert($node, $submission) {
155 // Insert a record into a 3rd-party module table when a submission is added.
156 db_insert('mymodule_table')
157 ->fields(array(
158 'nid' => $node->nid,
159 'sid' => $submission->sid,
160 'foo' => 'foo_data',
161 ))
162 ->execute();
163 }
164
165 /**
166 * Respond to a Webform submission being updated.
167 *
168 * Note that this hook is called after a submission has already been saved to
169 * the database. If needing to modify the submission prior to updating, use
170 * hook_webform_submission_presave().
171 *
172 * @param $node
173 * The Webform node on which this submission was made.
174 * @param $submission
175 * The Webform submission that was just updated in the database.
176 */
177 function hook_webform_submission_update($node, $submission) {
178 // Update a record in a 3rd-party module table when a submission is updated.
179 db_update('mymodule_table')
180 ->fields(array(
181 'foo' => 'foo_data',
182 ))
183 ->condition('nid', $node->nid)
184 ->condition('sid', $submission->sid)
185 ->execute();
186 }
187
188 /**
189 * Respond to a Webform submission being deleted.
190 *
191 * @param $node
192 * The Webform node on which this submission was made.
193 * @param $submission
194 * The Webform submission that was just deleted from the database.
195 */
196 function hook_webform_submission_delete($node, $submission) {
197 // Delete a record from a 3rd-party module table when a submission is deleted.
198 db_delete('mymodule_table')
199 ->condition('nid', $node->nid)
200 ->condition('sid', $submission->sid)
201 ->execute();
202 }
203
204 /**
205 * Provide a list of actions that can be executed on a submission.
206 *
207 * Some actions are displayed in the list of submissions such as edit, view, and
208 * delete. All other actions are displayed only when viewing the submission.
209 * These additional actions may be specified in this hook. Examples included
210 * directly in the Webform module include PDF, print, and resend e-mails. Other
211 * modules may extend this list by using this hook.
212 *
213 * @param $node
214 * The Webform node on which this submission was made.
215 * @param $submission
216 * The Webform submission on which the actions may be performed.
217 */
218 function hook_webform_submission_actions($node, $submission) {
219 $actions= array();
220
221 if (webform_results_access($node)) {
222 $actions['myaction'] = array(
223 'title' => t('Do my action'),
224 'href' => 'node/' . $node->nid . '/submission/' . $submission->sid . '/myaction',
225 'query' => drupal_get_destination(),
226 );
227 }
228
229 return $actions;
230 }
231
232 /**
233 * Modify the draft to be presented for editing.
234 *
235 * When drafts are enabled for the webform, by default, a pre-existig draft is
236 * presented when the webform is displayed to that user. To allow multiple
237 * drafts, implement this alter function to set the $sid to NULL, or use your
238 * application's business logic to determine whether a new draft or which of
239 * he pre-existing drafts should be presented.
240 *
241 * @param integer $sid
242 * The id of the most recent submission to be presented for editing. Change
243 * to a different draft's sid or set to NULL for a new draft.
244 * @param array $context
245 * Array of context with indices 'nid' and 'uid'.
246 */
247 function hook_webform_draft_alter(&$sid, $context) {
248 if ($_GET['newdraft']) {
249 $sid = NULL;
250 }
251 }
252
253 /**
254 * Alter the display of a Webform submission.
255 *
256 * This function applies to both e-mails sent by Webform and normal display of
257 * submissions when viewing through the adminsitrative interface.
258 *
259 * @param $renderable
260 * The Webform submission in a renderable array, similar to FormAPI's
261 * structure. This variable must be passed in by-reference. Important
262 * properties of this array include #node, #submission, #email, and #format,
263 * which can be used to find the context of the submission that is being
264 * rendered.
265 */
266 function hook_webform_submission_render_alter(&$renderable) {
267 // Remove page breaks from sent e-mails.
268 if (isset($renderable['#email'])) {
269 foreach (element_children($renderable) as $key) {
270 if ($renderable[$key]['#component']['type'] == 'pagebreak') {
271 unset($renderable[$key]);
272 }
273 }
274 }
275 }
276
277 /**
278 * Modify a loaded Webform component.
279 *
280 * IMPORTANT: This hook does not actually exist because components are loaded
281 * in bulk as part of webform_node_load(). Use hook_node_load() to modify loaded
282 * components when the node is loaded. This example is provided merely to point
283 * to hook_node_load().
284 *
285 * @see hook_nodeapi()
286 * @see webform_node_load()
287 */
288 function hook_webform_component_load() {
289 // This hook does not exist. Instead use hook_node_load().
290 }
291
292 /**
293 * Modify a Webform component before it is saved to the database.
294 *
295 * Note that most of the time this hook is not necessary, because Webform will
296 * automatically add data to the component based on the component form. Using
297 * hook_form_alter() will be sufficient in most cases.
298 *
299 * @see hook_form_alter()
300 * @see webform_component_edit_form()
301 *
302 * @param $component
303 * The Webform component being saved.
304 */
305 function hook_webform_component_presave(&$component) {
306 $component['extra']['new_option'] = 'foo';
307 }
308
309 /**
310 * Respond to a Webform component being inserted into the database.
311 */
312 function hook_webform_component_insert($component) {
313 // Insert a record into a 3rd-party module table when a component is inserted.
314 db_insert('mymodule_table')
315 ->fields(array(
316 'nid' => $component['nid'],
317 'cid' => $component['cid'],
318 'foo' => 'foo_data',
319 ))
320 ->execute();
321 }
322
323 /**
324 * Respond to a Webform component being updated in the database.
325 */
326 function hook_webform_component_update($component) {
327 // Update a record in a 3rd-party module table when a component is updated.
328 db_update('mymodule_table')
329 ->fields(array(
330 'foo' => 'foo_data',
331 ))
332 ->condition('nid', $component['nid'])
333 ->condition('cid', $component['cid'])
334 ->execute();
335 }
336
337 /**
338 * Respond to a Webform component being deleted.
339 */
340 function hook_webform_component_delete($component) {
341 // Delete a record in a 3rd-party module table when a component is deleted.
342 db_delete('mymodule_table')
343 ->condition('nid', $component['nid'])
344 ->condition('cid', $component['cid'])
345 ->execute();
346 }
347
348 /**
349 * Alter the entire analysis before rendering to the page on the Analysis tab.
350 *
351 * This alter hook allows modification of the entire analysis of a node's
352 * Webform results. The resulting analysis is displayed on the Results ->
353 * Analysis tab on the Webform.
354 *
355 * @param array $analysis
356 * A Drupal renderable array, passed by reference, containing the entire
357 * contents of the analysis page. This typically will contain the following
358 * two major keys:
359 * - form: The form for configuring the shown analysis.
360 * - components: The list of analyses for each analysis-enabled component
361 * for the node. Each keyed by its component ID.
362 */
363 function hook_webform_analysis_alter(&$analysis) {
364 $node = $analysis['#node'];
365
366 // Add an additional piece of information to every component's analysis:
367 foreach (element_children($analysis['components']) as $cid) {
368 $component = $node->components[$cid];
369 $analysis['components'][$cid]['chart'] = array(
370 '#markup' => t('Chart for the @name component', array('@name' => $component['name'])),
371 );
372 }
373 }
374 /**
375 * Alter data when displaying an analysis on that component.
376 *
377 * This hook modifies the data from an individual component's analysis results.
378 * It can be used to add additional analysis, or to modify the existing results.
379 * If needing to alter the entire set of analyses rather than an individual
380 * component, hook_webform_analysis_alter() may be used instead.
381 *
382 * @param array $data
383 * An array containing the result of a components analysis hook, passed by
384 * reference. This is passed directly from a component's
385 * _webform_analysis_component() function. See that hook for more information
386 * on this value.
387 * @param object $node
388 * The node object that contains the component being analyzed.
389 * @param array $component
390 * The Webform component array whose analysis results are being displayed.
391 *
392 * @see _webform_analysis_component()
393 * @see hook_webform_analysis_alter()
394 */
395 function hook_webform_analysis_component_data_alter(&$data, $node, $component) {
396 if ($component['type'] === 'textfield') {
397 // Do not display rows that contain a zero value.
398 foreach ($data as $row_number => $row_data) {
399 if ($row_data[1] === 0) {
400 unset($data[$row_number]);
401 }
402 }
403 }
404 }
405
406 /**
407 * Alter a Webform submission's header when exported.
408 */
409 function hook_webform_csv_header_alter(&$header, $component) {
410 // Use the machine name for component headers, but only for the webform
411 // with node 5 and components that are text fields.
412 if ($component['nid'] == 5 && $component['type'] == 'textfield') {
413 $header[2] = $component['form_key'];
414 }
415 }
416
417 /**
418 * Alter a Webform submission's data when exported.
419 */
420 function hook_webform_csv_data_alter(&$data, $component, $submission) {
421 // If a value of a field was left blank, use the value from another
422 // field.
423 if ($component['cid'] == 1 && empty($data)) {
424 $data = $submission->data[2]['value'][0];
425 }
426 }
427
428 /**
429 * Define components to Webform.
430 *
431 * @return
432 * An array of components, keyed by machine name. Required properties are
433 * "label" and "description". The "features" array defines which capabilities
434 * the component has, such as being displayed in e-mails or csv downloads.
435 * A component like "markup" for example would not show in these locations.
436 * The possible features of a component include:
437 *
438 * - csv
439 * - email
440 * - email_address
441 * - email_name
442 * - required
443 * - conditional
444 * - spam_analysis
445 * - group
446 *
447 * Note that most of these features do not indicate the default state, but
448 * determine if the component can have this property at all. Setting
449 * "required" to TRUE does not mean that a component's fields will always be
450 * required, but instead give the option to the administrator to choose the
451 * requiredness. See the example implementation for details on how these
452 * features may be set.
453 *
454 * An optional "file" may be specified to be loaded when the component is
455 * needed. A set of callbacks will be established based on the name of the
456 * component. All components follow the pattern:
457 *
458 * _webform_[callback]_[component]
459 *
460 * Where [component] is the name of the key of the component and [callback] is
461 * any of the following:
462 *
463 * - defaults
464 * - edit
465 * - render
466 * - display
467 * - submit
468 * - delete
469 * - help
470 * - theme
471 * - analysis
472 * - table
473 * - csv_headers
474 * - csv_data
475 *
476 * See the sample component implementation for details on each one of these
477 * callbacks.
478 *
479 * @see webform_components()
480 */
481 function hook_webform_component_info() {
482 $components = array();
483
484 $components['textfield'] = array(
485 'label' => t('Textfield'),
486 'description' => t('Basic textfield type.'),
487 'features' => array(
488 // This component includes an analysis callback. Defaults to TRUE.
489 'analysis' => TRUE,
490
491 // Add content to CSV downloads. Defaults to TRUE.
492 'csv' => TRUE,
493
494 // This component supports default values. Defaults to TRUE.
495 'default_value' => FALSE,
496
497 // This component supports a description field. Defaults to TRUE.
498 'description' => FALSE,
499
500 // Show this component in e-mailed submissions. Defaults to TRUE.
501 'email' => TRUE,
502
503 // Allow this component to be used as an e-mail FROM or TO address.
504 // Defaults to FALSE.
505 'email_address' => FALSE,
506
507 // Allow this component to be used as an e-mail SUBJECT or FROM name.
508 // Defaults to FALSE.
509 'email_name' => TRUE,
510
511 // This component may be toggled as required or not. Defaults to TRUE.
512 'required' => TRUE,
513
514 // This component supports a title attribute. Defaults to TRUE.
515 'title' => FALSE,
516
517 // This component has a title that can be toggled as displayed or not.
518 'title_display' => TRUE,
519
520 // This component has a title that can be displayed inline.
521 'title_inline' => TRUE,
522
523 // If this component can be used as a conditional SOURCE. All components
524 // may always be displayed conditionally, regardless of this setting.
525 // Defaults to TRUE.
526 'conditional' => TRUE,
527
528 // If this component allows other components to be grouped within it
529 // (like a fieldset or tabs). Defaults to FALSE.
530 'group' => FALSE,
531
532 // If this component can be used for SPAM analysis, usually with Mollom.
533 'spam_analysis' => FALSE,
534
535 // If this component saves a file that can be used as an e-mail
536 // attachment. Defaults to FALSE.
537 'attachment' => FALSE,
538
539 // If this component reflects a time range and should use labels such as
540 // "Before" and "After" when exposed as filters in Views module.
541 'views_range' => FALSE,
542 ),
543
544 // Specify the conditional behaviour of this component.
545 // Examples are 'string', 'date', 'time', 'numeric', 'select'.
546 // Defaults to 'string'.
547 'conditional_type' => 'string',
548
549 'file' => 'components/textfield.inc',
550 );
551
552 return $components;
553 }
554
555 /**
556 * Alter the list of available Webform components.
557 *
558 * @param $components
559 * A list of existing components as defined by hook_webform_component_info().
560 *
561 * @see hook_webform_component_info()
562 */
563 function hook_webform_component_info_alter(&$components) {
564 // Completely remove a component.
565 unset($components['grid']);
566
567 // Change the name of a component.
568 $components['textarea']['label'] = t('Text box');
569 }
570
571 /**
572 * Alter the list of Webform component default values.
573 *
574 * @param $defaults
575 * A list of component defaults as defined by _webform_defaults_COMPONENT().
576 * @param $type
577 * The component type whose defaults are being provided.
578 *
579 * @see _webform_defaults_component()
580 */
581 function hook_webform_component_defaults_alter(&$defaults, $type) {
582 // Alter a default for all component types.
583 $defaults['required'] = 1;
584
585 // Add a default for a new field added via hook_form_alter() or
586 // hook_form_FORM_ID_alter() for all component types.
587 $defaults['extra']['added_field'] = t('Added default value');
588
589 // Add or alter defaults for specific component types:
590 switch ($type) {
591 case 'select':
592 $defaults['extra']['optrand'] = 1;
593 break;
594
595 case 'textfield':
596 case 'textarea':
597 $defaults['extra']['another_added_field'] = t('Another added default value');
598 }
599 }
600
601 /**
602 * Alter access to a Webform submission.
603 *
604 * @param $node
605 * The Webform node on which this submission was made.
606 * @param $submission
607 * The Webform submission.
608 * @param $op
609 * The operation to be performed on the submission. Possible values are:
610 * - "view"
611 * - "edit"
612 * - "delete"
613 * - "list"
614 * @param $account
615 * A user account object.
616 * @return
617 * TRUE if the current user has access to submission,
618 * or FALSE otherwise.
619 */
620 function hook_webform_submission_access($node, $submission, $op = 'view', $account = NULL) {
621 switch ($op) {
622 case 'view':
623 return TRUE;
624 break;
625 case 'edit':
626 return FALSE;
627 break;
628 case 'delete':
629 return TRUE;
630 break;
631 case 'list':
632 return TRUE;
633 break;
634 }
635 }
636
637 /**
638 * Determine if a user has access to see the results of a webform.
639 *
640 * Note in addition to the view access to the results granted here, the $account
641 * must also have view access to the Webform node in order to see results.
642 * Access via this hook is in addition (adds permission) to the standard
643 * webform access.
644 *
645 * @see webform_results_access().
646 *
647 * @param $node
648 * The Webform node to check access on.
649 * @param $account
650 * The user account to check access on.
651 * @return
652 * TRUE or FALSE if the user can access the webform results.
653 */
654 function hook_webform_results_access($node, $account) {
655 // Let editors view results of unpublished webforms.
656 if ($node->status == 0 && in_array('editor', $account->roles)) {
657 return TRUE;
658 }
659 else {
660 return FALSE;
661 }
662 }
663
664 /**
665 * Determine if a user has access to clear the results of a webform.
666 *
667 * Access via this hook is in addition (adds permission) to the standard
668 * webform access (delete all webform submissions).
669 *
670 * @see webform_results_clear_access().
671 *
672 * @param object $node
673 * The Webform node to check access on.
674 * @param object $account
675 * The user account to check access on.
676 * @return boolean
677 * TRUE or FALSE if the user can access the webform results.
678 */
679 function hook_webform_results_clear_access($node, $account) {
680 return user_access('my additional access', $account);
681 }
682
683 /**
684 * Overrides the node_access and user_access permission to access and edit
685 * webform components, e-mails, conditions, and form settings.
686 *
687 * Return NULL to defer to other modules. If all implementations defer, then
688 * access to the node's EDIT tab plus 'edit webform components' permission
689 * determines access. To grant access, return TRUE; to deny access, return
690 * FALSE. If more than one implementation return TRUE/FALSE, all must be TRUE
691 * to grant access.
692 *
693 * In this way, access to the EDIT tab of the node may be decoupled from
694 * access to the WEBFORM tab. When returning TRUE, consider all aspects of
695 * access as this will be the only test. For example, 'return TRUE;' would grant
696 * annonymous access to creating webform components, which seldom be desired.
697 *
698 * @see webform_node_update_access().
699 *
700 * @param object $node
701 * The Webform node to check access on.
702 * @param object $account
703 * The user account to check access on.
704 * @return boolean|NULL
705 * TRUE or FALSE if the user can access the webform results, or NULL if
706 * access should be deferred to other implementations of this hook or
707 * node_access('update') plus user_access('edit webform components').
708 */
709 function hook_webform_update_access($node, $account) {
710 // Allow anyone who can see webform_editable_by_user nodes and who has
711 // 'my webform component edit access' permission to see, edit, and delete the
712 // webform components, e-mails, conditionals, and form settings.
713 if ($node->type == 'webform_editable_by_user') {
714 return node_access('view', $node, $account) && user_access('my webform component edit access', $account);
715 }
716 }
717
718
719 /**
720 * Return an array of files associated with the component.
721 *
722 * The output of this function will be used to attach files to e-mail messages.
723 *
724 * @param $component
725 * A Webform component array.
726 * @param $value
727 * An array of information containing the submission result, directly
728 * correlating to the webform_submitted_data database schema.
729 * @return
730 * An array of files, each file is an array with following keys:
731 * - filepath: The relative path to the file.
732 * - filename: The name of the file including the extension.
733 * - filemime: The mimetype of the file.
734 * This will result in an array looking something like this:
735 * @code
736 * array[0] => array(
737 * 'filepath' => '/sites/default/files/attachment.txt',
738 * 'filename' => 'attachment.txt',
739 * 'filemime' => 'text/plain',
740 * );
741 * @endcode
742 */
743 function _webform_attachments_component($component, $value) {
744 $files = array();
745 $files[] = (array) file_load($value[0]);
746 return $files;
747 }
748
749
750 /**
751 * Alter default settings for a newly created webform node.
752 *
753 * @param array $defaults
754 * Default settings for a newly created webform node as defined by webform_node_defaults().
755 *
756 * @see webform_node_defaults()
757 */
758 function hook_webform_node_defaults_alter(&$defaults) {
759 $defaults['allow_draft'] = '1';
760 }
761
762 /**
763 * Add additional fields to submission data downloads.
764 *
765 * @return
766 * Keys and titles for default submission information.
767 *
768 * @see hook_webform_results_download_submission_information_data()
769 */
770 function hook_webform_results_download_submission_information_info() {
771 return array(
772 'field_key_1' => t('Field Title 1'),
773 'field_key_2' => t('Field Title 2'),
774 );
775 }
776
777 /**
778 * Return values for submission data download fields.
779 *
780 * @param $token
781 * The name of the token being replaced.
782 * @param $submission
783 * The data for an individual submission from webform_get_submissions().
784 * @param $options
785 * A list of options that define the output format. These are generally passed
786 * through from the GUI interface.
787 * @param $serial_start
788 * The starting position for the Serial column in the output.
789 * @param $row_count
790 * The number of the row being generated.
791 *
792 * @return
793 * Value for requested submission information field.
794 *
795 * @see hook_webform_results_download_submission_information_info()
796 */
797 function hook_webform_results_download_submission_information_data($token, $submission, array $options, $serial_start, $row_count) {
798 switch ($token) {
799 case 'field_key_1':
800 return 'Field Value 1';
801 case 'field_key_2':
802 return 'Field Value 2';
803 }
804 }
805
806 /**
807 * @}
808 */
809
810 /**
811 * @defgroup webform_component Sample Webform Component
812 * @{
813 * In each of these examples, the word "component" should be replaced with the,
814 * name of the component type (such as textfield or select). These are not
815 * actual hooks, but instead samples of how Webform integrates with its own
816 * built-in components.
817 */
818
819 /**
820 * Specify the default properties of a component.
821 *
822 * @return
823 * An array defining the default structure of a component.
824 */
825 function _webform_defaults_component() {
826 return array(
827 'name' => '',
828 'form_key' => NULL,
829 'required' => 0,
830 'pid' => 0,
831 'weight' => 0,
832 'extra' => array(
833 'options' => '',
834 'questions' => '',
835 'optrand' => 0,
836 'qrand' => 0,
837 'description' => '',
838 'description_above' => FALSE,
839 'private' => FALSE,
840 'analysis' => TRUE,
841 ),
842 );
843 }
844
845 /**
846 * Generate the form for editing a component.
847 *
848 * Create a set of form elements to be displayed on the form for editing this
849 * component. Use care naming the form items, as this correlates directly to the
850 * database schema. The component "Name" and "Description" fields are added to
851 * every component type and are not necessary to specify here (although they
852 * may be overridden if desired).
853 *
854 * @param $component
855 * A Webform component array.
856 * @return
857 * An array of form items to be displayed on the edit component page
858 */
859 function _webform_edit_component($component) {
860 $form = array();
861
862 // Disabling the description if not wanted.
863 $form['description'] = array();
864
865 // Most options are stored in the "extra" array, which stores any settings
866 // unique to a particular component type.
867 $form['extra']['options'] = array(
868 '#type' => 'textarea',
869 '#title' => t('Options'),
870 '#default_value' => $component['extra']['options'],
871 '#description' => t('Key-value pairs may be entered separated by pipes. i.e. safe_key|Some readable option') . ' ' . theme('webform_token_help'),
872 '#cols' => 60,
873 '#rows' => 5,
874 '#weight' => -3,
875 '#required' => TRUE,
876 );
877 return $form;
878 }
879
880 /**
881 * Render a Webform component to be part of a form.
882 *
883 * @param $component
884 * A Webform component array.
885 * @param $value
886 * If editing an existing submission or resuming a draft, this will contain
887 * an array of values to be shown instead of the default in the component
888 * configuration. This value will always be an array, keyed numerically for
889 * each value saved in this field.
890 * @param $filter
891 * Whether or not to filter the contents of descriptions and values when
892 * rendering the component. Values need to be unfiltered to be editable by
893 * Form Builder.
894 * @param $submission
895 * The submission from which this component is being rendered. Usually not
896 * needed. Used by _webform_render_date() to validate using the submission's
897 * completion date.
898 *
899 * @see _webform_client_form_add_component()
900 */
901 function _webform_render_component($component, $value = NULL, $filter = TRUE, $submission = NULL) {
902 $form_item = array(
903 '#type' => 'textfield',
904 '#title' => $filter ? webform_filter_xss($component['name']) : $component['name'],
905 '#required' => $component['required'],
906 '#weight' => $component['weight'],
907 '#description' => $filter ? webform_filter_descriptions($component['extra']['description']) : $component['extra']['description'],
908 '#default_value' => $filter ? webform_replace_tokens($component['value']) : $component['value'],
909 '#theme_wrappers' => array('webform_element'),
910 );
911
912 if (isset($value)) {
913 $form_item['#default_value'] = $value[0];
914 }
915
916 return $form_item;
917 }
918
919 /**
920 * Allow modules to modify a webform component that is going to be rendered in a form.
921 *
922 * @param array $element
923 * The display element as returned by _webform_render_component().
924 * @param array $component
925 * A Webform component array.
926 *
927 * @see _webform_render_component()
928 */
929 function hook_webform_component_render_alter(&$element, &$component) {
930 if ($component['cid'] == 10) {
931 $element['#title'] = 'My custom title';
932 $element['#default_value'] = 42;
933 }
934 }
935
936 /**
937 * Display the result of a submission for a component.
938 *
939 * The output of this function will be displayed under the "Results" tab then
940 * "Submissions". This should output the saved data in some reasonable manner.
941 *
942 * @param $component
943 * A Webform component array.
944 * @param $value
945 * An array of information containing the submission result, directly
946 * correlating to the webform_submitted_data database table schema.
947 * @param $format
948 * Either 'html' or 'text'. Defines the format that the content should be
949 * returned as. Make sure that returned content is run through check_plain()
950 * or other filtering functions when returning HTML.
951 * @param $submission
952 * The submission. Used to generate tokens.
953 * @return
954 * A renderable element containing at the very least these properties:
955 * - #title
956 * - #weight
957 * - #component
958 * - #format
959 * - #value
960 * Webform also uses #theme_wrappers to output the end result to the user,
961 * which will properly format the label and content for use within an e-mail
962 * (such as wrapping the text) or as HTML (ensuring consistent output).
963 */
964 function _webform_display_component($component, $value, $format = 'html', $submission = array()) {
965 return array(
966 '#title' => $component['name'],
967 '#weight' => $component['weight'],
968 '#theme' => 'webform_display_textfield',
969 '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
970 '#post_render' => array('webform_element_wrapper'),
971 '#field_prefix' => $component['extra']['field_prefix'],
972 '#field_suffix' => $component['extra']['field_suffix'],
973 '#component' => $component,
974 '#format' => $format,
975 '#value' => isset($value[0]) ? $value[0] : '',
976 );
977 }
978
979 /**
980 * Allow modules to modify a "display only" webform component.
981 *
982 * @param array $element
983 * The display element as returned by _webform_display_component().
984 * @param array $component
985 * A Webform component array.
986 *
987 * @see _webform_display_component()
988 */
989 function hook_webform_component_display_alter(&$element, &$component) {
990 if ($component['cid'] == 10) {
991 $element['#title'] = 'My custom title';
992 $element['#default_value'] = 42;
993 }
994 }
995
996 /**
997 * Performs the conditional action set on an implemented component.
998 *
999 * Setting the form element allows form validation functions to see the value
1000 * that webform has set for the given component.
1001 *
1002 * @param array $component
1003 * The webform component array whose value is being set for the currently-
1004 * edited submission.
1005 * @param array $element
1006 * The form element currently being set.
1007 * @param array $form_state
1008 * The form's state.
1009 * @param string $value
1010 * The value to be set, as defined in the conditional action.
1011 */
1012 function _webform_action_set_component($component, &$element, &$form_state, $value) {
1013 $element['#value'] = $value;
1014 form_set_value($element, $value, $form_state);
1015 }
1016
1017 /**
1018 * A hook for changing the input values before saving to the database.
1019 *
1020 * Webform expects a component to consist of a single field, or a single array
1021 * of fields. If you have a component that requires a deeper form tree
1022 * you must flatten the data into a single array using this callback
1023 * or by setting #parents on each field to avoid data loss and/or unexpected
1024 * behavior.
1025 *
1026 * Note that Webform will save the result of this function directly into the
1027 * database.
1028 *
1029 * @param $component
1030 * A Webform component array.
1031 * @param $value
1032 * The POST data associated with the user input.
1033 * @return
1034 * An array of values to be saved into the database. Note that this should be
1035 * a numerically keyed array.
1036 */
1037 function _webform_submit_component($component, $value) {
1038 // Clean up a phone number into 123-456-7890 format.
1039 if ($component['extra']['phone_number']) {
1040 $number = preg_replace('/[^0-9]/', '', $value[0]);
1041 if (strlen($number) == 7) {
1042 $number = substr($number, 0, 3) . '-' . substr($number, 3, 4);
1043 }
1044 else {
1045 $number = substr($number, 0, 3) . '-' . substr($number, 3, 3) . '-' . substr($number, 6, 4);
1046 }
1047 }
1048
1049 $value[0] = $number;
1050 return $value;
1051 }
1052
1053 /**
1054 * Delete operation for a component or submission.
1055 *
1056 * @param $component
1057 * A Webform component array.
1058 * @param $value
1059 * An array of information containing the submission result, directly
1060 * correlating to the webform_submitted_data database schema.
1061 */
1062 function _webform_delete_component($component, $value) {
1063 // Delete corresponding files when a submission is deleted.
1064 if (!empty($value[0]) && ($file = webform_get_file($value[0]))) {
1065 file_usage_delete($file, 'webform');
1066 file_delete($file);
1067 }
1068 }
1069
1070 /**
1071 * Module specific instance of hook_help().
1072 *
1073 * This allows each Webform component to add information into hook_help().
1074 */
1075 function _webform_help_component($section) {
1076 switch ($section) {
1077 case 'admin/config/content/webform#grid_description':
1078 return t('Allows creation of grid questions, denoted by radio buttons.');
1079 }
1080 }
1081
1082 /**
1083 * Module specific instance of hook_theme().
1084 *
1085 * This allows each Webform component to add information into hook_theme(). If
1086 * you specify a file to include, you must define the path to the module that
1087 * this file belongs to.
1088 */
1089 function _webform_theme_component() {
1090 return array(
1091 'webform_grid' => array(
1092 'render element' => 'element',
1093 'file' => 'components/grid.inc',
1094 'path' => drupal_get_path('module', 'webform'),
1095 ),
1096 'webform_display_grid' => array(
1097 'render element' => 'element',
1098 'file' => 'components/grid.inc',
1099 'path' => drupal_get_path('module', 'webform'),
1100 ),
1101 );
1102 }
1103
1104 /**
1105 * Calculate and returns statistics about results for this component.
1106 *
1107 * This takes into account all submissions to this webform. The output of this
1108 * function will be displayed under the "Results" tab then "Analysis".
1109 *
1110 * @param $component
1111 * An array of information describing the component, directly correlating to
1112 * the webform_component database schema.
1113 * @param $sids
1114 * An optional array of submission IDs (sid). If supplied, the analysis will
1115 * be limited to these sids.
1116 * @param $single
1117 * Boolean flag determining if the details about a single component are being
1118 * shown. May be used to provided detailed information about a single
1119 * component's analysis, such as showing "Other" options within a select list.
1120 * @param $join
1121 * An optional SelectQuery object to be used to join with the submissions
1122 * table to restrict the submissions being analyzed.
1123 * @return
1124 * An array containing one or more of the following keys:
1125 * - table_rows: If this component has numeric data that can be represented in
1126 * a grid, return the values here. This array assumes a 2-dimensional
1127 * structure, with the first value being a label and subsequent values
1128 * containing a decimal or integer.
1129 * - table_header: If this component has more than a single set of values,
1130 * include a table header so each column can be labeled.
1131 * - other_data: If your component has non-numeric data to include, such as
1132 * a description or link, include that in the other_data array. Each item
1133 * may be a string or an array of values that matches the number of columns
1134 * in the table_header property.
1135 * At the very least, either table_rows or other_data should be provided.
1136 * Note that if you want your component's analysis to be available by default
1137 * without the user specifically enabling it, you must set
1138 * $component['extra']['analysis'] = TRUE in your
1139 * _webform_defaults_component() callback.
1140 *
1141 * @see _webform_defaults_component()
1142 */
1143 function _webform_analysis_component($component, $sids = array(), $single = FALSE, $join = NULL) {
1144 // Generate the list of options and questions.
1145 $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
1146 $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
1147
1148 // Generate a lookup table of results.
1149 $query = db_select('webform_submitted_data', 'wsd')
1150 ->fields('wsd', array('no', 'data'))
1151 ->condition('nid', $component['nid'])
1152 ->condition('cid', $component['cid'])
1153 ->condition('data', '', '<>')
1154 ->groupBy('no')
1155 ->groupBy('data');
1156 $query->addExpression('COUNT(sid)', 'datacount');
1157
1158 if (count($sids)) {
1159 $query->condition('sid', $sids, 'IN');
1160 }
1161
1162 if ($join) {
1163 $query->innerJoin($join, 'ws2_', 'wsd.sid = ws2_.sid');
1164 }
1165
1166 $result = $query->execute();
1167 $counts = array();
1168 foreach ($result as $data) {
1169 $counts[$data->no][$data->data] = $data->datacount;
1170 }
1171
1172 // Create an entire table to be put into the returned row.
1173 $rows = array();
1174 $header = array('');
1175
1176 // Add options as a header row.
1177 foreach ($options as $option) {
1178 $header[] = $option;
1179 }
1180
1181 // Add questions as each row.
1182 foreach ($questions as $qkey => $question) {
1183 $row = array($question);
1184 foreach ($options as $okey => $option) {
1185 $row[] = !empty($counts[$qkey][$okey]) ? $counts[$qkey][$okey] : 0;
1186 }
1187 $rows[] = $row;
1188 }
1189
1190 $other = array();
1191 $other[] = l(t('More information'), 'node/' . $component['nid'] . '/webform-results/analysis/' . $component['cid']);
1192
1193 return array(
1194 'table_header' => $header,
1195 'table_rows' => $rows,
1196 'other_data' => $other,
1197 );
1198 }
1199
1200 /**
1201 * Return the result of a component value for display in a table.
1202 *
1203 * The output of this function will be displayed under the "Results" tab then
1204 * "Table".
1205 *
1206 * @param $component
1207 * A Webform component array.
1208 * @param $value
1209 * An array of information containing the submission result, directly
1210 * correlating to the webform_submitted_data database schema.
1211 * @return
1212 * Textual output formatted for human reading.
1213 */
1214 function _webform_table_component($component, $value) {
1215 $questions = array_values(_webform_component_options($component['extra']['questions']));
1216 $output = '';
1217 // Set the value as a single string.
1218 if (is_array($value)) {
1219 foreach ($value as $item => $value) {
1220 if ($value !== '') {
1221 $output .= $questions[$item] . ': ' . check_plain($value) . '<br />';
1222 }
1223 }
1224 }
1225 else {
1226 $output = check_plain(!isset($value['0']) ? '' : $value['0']);
1227 }
1228 return $output;
1229 }
1230
1231 /**
1232 * Return the header for this component to be displayed in a CSV file.
1233 *
1234 * The output of this function will be displayed under the "Results" tab then
1235 * "Download".
1236 *
1237 * @param $component
1238 * A Webform component array.
1239 * @param $export_options
1240 * An array of options that may configure export of this field.
1241 * @return
1242 * An array of data to be displayed in the first three rows of a CSV file, not
1243 * including either prefixed or trailing commas.
1244 */
1245 function _webform_csv_headers_component($component, $export_options) {
1246 $header = array();
1247 $header[0] = array('');
1248 $header[1] = array($export_options['header_keys'] ? $component['form_key'] : $component['name']);
1249 $items = _webform_component_options($component['extra']['questions']);
1250 $count = 0;
1251 foreach ($items as $key => $item) {
1252 // Empty column per sub-field in main header.
1253 if ($count != 0) {
1254 $header[0][] = '';
1255 $header[1][] = '';
1256 }
1257 // The value for this option.
1258 $header[2][] = $item;
1259 $count++;
1260 }
1261
1262 return $header;
1263 }
1264
1265 /**
1266 * Format the submitted data of a component for CSV downloading.
1267 *
1268 * The output of this function will be displayed under the "Results" tab then
1269 * "Download".
1270 *
1271 * @param $component
1272 * A Webform component array.
1273 * @param $export_options
1274 * An array of options that may configure export of this field.
1275 * @param $value
1276 * An array of information containing the submission result, directly
1277 * correlating to the webform_submitted_data database schema.
1278 * @return
1279 * An array of items to be added to the CSV file. Each value within the array
1280 * will be another column within the file. This function is called once for
1281 * every row of data.
1282 */
1283 function _webform_csv_data_component($component, $export_options, $value) {
1284 $questions = array_keys(_webform_select_options($component['extra']['questions']));
1285 $return = array();
1286 foreach ($questions as $key => $question) {
1287 $return[] = isset($value[$key]) ? $value[$key] : '';
1288 }
1289 return $return;
1290 }
1291
1292 /**
1293 * Adjusts the view field(s) that are automatically generated for number
1294 * components.
1295 *
1296 * Provides each component the opportunity to adjust how this component is
1297 * displayed in a view as a field in a view table. For example, a component may
1298 * modify how it responds to click-sorting. Or it may add additional fields,
1299 * such as a grid component having a column for each question.
1300 *
1301 * @param array $component
1302 * A Webform component array
1303 * @param array $fields
1304 * An array of field-definition arrays. Will be passed one field definition,
1305 * which may be modified. Additional fields may be added to the array.
1306 * @return array
1307 * The modified $fields array.
1308 */
1309 function _webform_view_field_component($component, $fields) {
1310 foreach ($fields as &$field) {
1311 $field['webform_datatype'] = 'number';
1312 }
1313 return $fields;
1314 }
1315
1316 /**
1317 * Modify the how a view was expanded to show all the components.
1318 *
1319 * This alter function is only called when the view is actually modified. It
1320 * provides modules an opportunity to alter the changes that webform made to
1321 * the view.
1322 *
1323 * This hook is called from webform_views_pre_view. If another module also
1324 * changes views by implementing this same views hook, the relative order of
1325 * execution of the two implementations will depend upon the module weights of
1326 * the two modules. Using hook_webform_view_alter instead guarantees an
1327 * opportuinty to modify the view AFTER webform.
1328 *
1329 * @param object $view
1330 * The view object.
1331 * @param string $display_id
1332 * The display_id that was expanded by webform.
1333 * @param array $args
1334 * The argumentst that were passed to the view.
1335 */
1336 function hook_webform_view_alter($view, $display_id, $args) {
1337 // Don't show component with cid == 4
1338 $fields = $view->get_items('field', $display_id);
1339 foreach ($fields as $id => $field) {
1340 if (isset($field['webform_cid']) && $field['webform_cid'] == 4) {
1341 unset($fields[$id]);
1342 }
1343 }
1344 $view->display[$display_id]->handler->set_option('fields', $fields);
1345 }
1346
1347 /**
1348 * Modify the list of mail systems that are capable of sending HTML email.
1349 *
1350 * @param array &$systems
1351 * An array of mail system class names.
1352 */
1353 function hook_webform_html_capable_mail_systems_alter(&$systems) {
1354 if (module_exists('my_module')) {
1355 $systems[] = 'MyModuleMailSystem';
1356 }
1357 }
1358
1359 /**
1360 * Define a list of webform exporters.
1361 *
1362 * @return array
1363 * A list of the available exporters provided by the module.
1364 *
1365 * @see webform_webform_exporters()
1366 */
1367 function hook_webform_exporters() {
1368 $exporters = array(
1369 'webform_exporter_custom' => array(
1370 'title' => t('Webform exporter name'),
1371 'description' => t('The description for this exporter.'),
1372 'handler' => 'webform_exporter_custom',
1373 'file' => drupal_get_path('module', 'yourmodule') . '/includes/webform_exporter_custom.inc',
1374 'weight' => 10,
1375 ),
1376 );
1377
1378 return $exporters;
1379 }
1380
1381 /**
1382 * Modify the list of webform exporters definitions.
1383 *
1384 * @param array &$exporters
1385 * A list of all available webform exporters.
1386 */
1387 function hook_webform_exporters_alter(&$exporters) {
1388 $exporters['excel']['handler'] = 'customized_excel_exporter';
1389 $exporters['excel']['file'] = drupal_get_path('module', 'yourmodule') . '/includes/customized_excel_exporter.inc';
1390 }
1391
1392 /**
1393 * @}
1394 */