commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-old / webform / webform.install
1 <?php
2
3 /**
4 * @file
5 * Webform module install/schema hooks.
6 */
7
8 /**
9 * Implements hook_schema().
10 */
11 function webform_schema() {
12 $schema = array();
13
14 $schema['webform'] = array(
15 'description' => 'Table for storing additional properties for webform nodes.',
16 'fields' => array(
17 'nid' => array(
18 'description' => 'The node identifier of a webform.',
19 'type' => 'int',
20 'unsigned' => TRUE,
21 'not null' => TRUE,
22 ),
23 'next_serial' => array(
24 'description' => 'The serial number to give to the next submission to this webform.',
25 'type' => 'int',
26 'unsigned' => TRUE,
27 'not null' => TRUE,
28 'default' => 1,
29 ),
30 'confirmation' => array(
31 'description' => 'The confirmation message or URL displayed to the user after submitting a form.',
32 'type' => 'text',
33 'not null' => TRUE,
34 ),
35 'confirmation_format' => array(
36 'description' => 'The {filter_format}.format of the confirmation message.',
37 'type' => 'varchar',
38 'length' => 255,
39 'not null' => FALSE,
40 ),
41 'redirect_url' => array(
42 'description' => 'The URL a user is redirected to after submitting a form.',
43 'type' => 'varchar',
44 'length' => 2048,
45 'default' => '<confirmation>',
46 ),
47 'status' => array(
48 'description' => 'Boolean value of a webform for open (1) or closed (0).',
49 'type' => 'int',
50 'size' => 'tiny',
51 'not null' => TRUE,
52 'default' => 1,
53 ),
54 'block' => array(
55 'description' => 'Boolean value for whether this form be available as a block.',
56 'type' => 'int',
57 'size' => 'tiny',
58 'not null' => TRUE,
59 'default' => 0,
60 ),
61 'allow_draft' => array(
62 'description' => 'Boolean value for whether submissions to this form be saved as a draft.',
63 'type' => 'int',
64 'size' => 'tiny',
65 'not null' => TRUE,
66 'default' => 0,
67 ),
68 'auto_save' => array(
69 'description' => 'Boolean value for whether submissions to this form should be auto-saved between pages.',
70 'type' => 'int',
71 'size' => 'tiny',
72 'not null' => TRUE,
73 'default' => 0,
74 ),
75 'submit_notice' => array(
76 'description' => 'Boolean value for whether to show or hide the previous submissions notification.',
77 'type' => 'int',
78 'size' => 'tiny',
79 'not null' => TRUE,
80 'default' => 1,
81 ),
82 'confidential' => array(
83 'description' => 'Boolean value for whether to anonymize submissions.',
84 'type' => 'int',
85 'size' => 'tiny',
86 'not null' => TRUE,
87 'default' => 0,
88 ),
89 'submit_text' => array(
90 'description' => 'The title of the submit button on the form.',
91 'type' => 'varchar',
92 'length' => 255,
93 ),
94 'submit_limit' => array(
95 'description' => 'The number of submissions a single user is allowed to submit within an interval. -1 is unlimited.',
96 'type' => 'int',
97 'size' => 'tiny',
98 'not null' => TRUE,
99 'default' => -1,
100 ),
101 'submit_interval' => array(
102 'description' => 'The amount of time in seconds that must pass before a user can submit another submission within the set limit.',
103 'type' => 'int',
104 'not null' => TRUE,
105 'default' => -1,
106 ),
107 'total_submit_limit' => array(
108 'description' => 'The total number of submissions allowed within an interval. -1 is unlimited.',
109 'type' => 'int',
110 'not null' => TRUE,
111 'default' => -1,
112 ),
113 'total_submit_interval' => array(
114 'description' => 'The amount of time in seconds that must pass before another submission can be submitted within the set limit.',
115 'type' => 'int',
116 'not null' => TRUE,
117 'default' => -1,
118 ),
119 'progressbar_bar' => array(
120 'description' => 'Boolean value indicating if the bar should be shown as part of the progress bar.',
121 'type' => 'int',
122 'size' => 'tiny',
123 'not null' => TRUE,
124 'default' => 0,
125 ),
126 'progressbar_page_number' => array(
127 'description' => 'Boolean value indicating if the page number should be shown as part of the progress bar.',
128 'type' => 'int',
129 'size' => 'tiny',
130 'not null' => TRUE,
131 'default' => 0,
132 ),
133 'progressbar_percent' => array(
134 'description' => 'Boolean value indicating if the percentage complete should be shown as part of the progress bar.',
135 'type' => 'int',
136 'size' => 'tiny',
137 'not null' => TRUE,
138 'default' => 0,
139 ),
140 'progressbar_pagebreak_labels' => array(
141 'description' => 'Boolean value indicating if the pagebreak labels should be included as part of the progress bar.',
142 'type' => 'int',
143 'size' => 'tiny',
144 'not null' => TRUE,
145 'default' => 0,
146 ),
147 'progressbar_include_confirmation' => array(
148 'description' => 'Boolean value indicating if the confirmation page should count as a page in the progress bar.',
149 'type' => 'int',
150 'size' => 'tiny',
151 'not null' => TRUE,
152 'default' => 0,
153 ),
154 'progressbar_label_first' => array(
155 'description' => 'Label for the first page of the progress bar.',
156 'type' => 'varchar',
157 'length' => 255,
158 ),
159 'progressbar_label_confirmation' => array(
160 'description' => 'Label for the last page of the progress bar.',
161 'type' => 'varchar',
162 'length' => 255,
163 ),
164 'preview' => array(
165 'description' => 'Boolean value indicating if this form includes a page for previewing the submission.',
166 'type' => 'int',
167 'size' => 'tiny',
168 'not null' => TRUE,
169 'default' => 0,
170 ),
171 'preview_next_button_label' => array(
172 'description' => 'The text for the button that will proceed to the preview page.',
173 'type' => 'varchar',
174 'length' => 255,
175 ),
176 'preview_prev_button_label' => array(
177 'description' => 'The text for the button to go backwards from the preview page.',
178 'type' => 'varchar',
179 'length' => 255,
180 ),
181 'preview_title' => array(
182 'description' => 'The title of the preview page, as used by the progress bar.',
183 'type' => 'varchar',
184 'length' => 255,
185 ),
186 'preview_message' => array(
187 'description' => 'Text shown on the preview page of the form.',
188 'type' => 'text',
189 'not null' => TRUE,
190 ),
191 'preview_message_format' => array(
192 'description' => 'The {filter_format}.format of the preview page message.',
193 'type' => 'varchar',
194 'length' => 255,
195 ),
196 'preview_excluded_components' => array(
197 'description' => 'Comma-separated list of component IDs that should not be included in this form\'s confirmation page.',
198 'type' => 'text',
199 'not null' => TRUE,
200 ),
201 ),
202 'primary key' => array('nid'),
203 );
204
205 $schema['webform_component'] = array(
206 'description' => 'Stores information about components for webform nodes.',
207 'fields' => array(
208 'nid' => array(
209 'description' => 'The node identifier of a webform.',
210 'type' => 'int',
211 'unsigned' => TRUE,
212 'not null' => TRUE,
213 'default' => 0,
214 ),
215 'cid' => array(
216 'description' => 'The identifier for this component within this node, starts at 0 for each node.',
217 'type' => 'int',
218 'size' => 'small',
219 'unsigned' => TRUE,
220 'not null' => TRUE,
221 'default' => 0,
222 ),
223 'pid' => array(
224 'description' => 'If this component has a parent fieldset, the cid of that component.',
225 'type' => 'int',
226 'size' => 'small',
227 'unsigned' => TRUE,
228 'not null' => TRUE,
229 'default' => 0,
230 ),
231 'form_key' => array(
232 'description' => 'When the form is displayed and processed, this key can be used to reference the results.',
233 'type' => 'varchar',
234 'length' => 128,
235 ),
236 'name' => array(
237 'description' => 'The label for this component.',
238 'type' => 'text',
239 'not null' => TRUE,
240 ),
241 'type' => array(
242 'description' => 'The field type of this component (textfield, select, hidden, etc.).',
243 'type' => 'varchar',
244 'length' => 16,
245 ),
246 'value' => array(
247 'description' => 'The default value of the component when displayed to the end-user.',
248 'type' => 'text',
249 'not null' => TRUE,
250 ),
251 'extra' => array(
252 'description' => 'Additional information unique to the display or processing of this component.',
253 'type' => 'text',
254 'not null' => TRUE,
255 ),
256 'required' => array(
257 'description' => 'Boolean flag for if this component is required.',
258 'type' => 'int',
259 'size' => 'tiny',
260 'not null' => TRUE,
261 'default' => 0,
262 ),
263 'weight' => array(
264 'description' => 'Determines the position of this component in the form.',
265 'type' => 'int',
266 'size' => 'small',
267 'not null' => TRUE,
268 'default' => 0,
269 ),
270 ),
271 'primary key' => array('nid', 'cid'),
272 );
273
274 $schema['webform_conditional'] = array(
275 'description' => 'Holds information about conditional logic.',
276 'fields' => array(
277 'nid' => array(
278 'description' => 'The node identifier of a webform.',
279 'type' => 'int',
280 'unsigned' => TRUE,
281 'not null' => TRUE,
282 'default' => 0,
283 ),
284 'rgid' => array(
285 'description' => 'The rule group identifier for this group of rules.',
286 'type' => 'int',
287 'size' => 'small',
288 'unsigned' => TRUE,
289 'not null' => TRUE,
290 'default' => 0,
291 ),
292 'andor' => array(
293 'description' => 'Whether to AND or OR the actions in this group. All actions within the same rgid should have the same andor value.',
294 'type' => 'varchar',
295 'length' => 128,
296 ),
297 'weight' => array(
298 'description' => 'Determines the position of this conditional compared to others.',
299 'type' => 'int',
300 'size' => 'small',
301 'not null' => TRUE,
302 'default' => 0,
303 ),
304 ),
305 'primary key' => array('nid', 'rgid'),
306 );
307
308 $schema['webform_conditional_rules'] = array(
309 'description' => 'Holds information about conditional logic.',
310 'fields' => array(
311 'nid' => array(
312 'description' => 'The node identifier of a webform.',
313 'type' => 'int',
314 'unsigned' => TRUE,
315 'not null' => TRUE,
316 'default' => 0,
317 ),
318 'rgid' => array(
319 'description' => 'The rule group identifier for this group of rules.',
320 'type' => 'int',
321 'size' => 'small',
322 'unsigned' => TRUE,
323 'not null' => TRUE,
324 'default' => 0,
325 ),
326 'rid' => array(
327 'description' => 'The rule identifier for this conditional rule.',
328 'type' => 'int',
329 'size' => 'small',
330 'unsigned' => TRUE,
331 'not null' => TRUE,
332 'default' => 0,
333 ),
334 'source_type' => array(
335 'description' => 'The type of source on which the conditional is based. Currently always "component". Indicates what type of ID the "source" column contains.',
336 'type' => 'varchar',
337 'length' => 128,
338 ),
339 'source' => array(
340 'description' => 'The component ID being used in this condition.',
341 'type' => 'int',
342 'size' => 'small',
343 'unsigned' => TRUE,
344 'not null' => TRUE,
345 'default' => 0,
346 ),
347 'operator' => array(
348 'description' => 'Which operator (equal, contains, starts with, etc.) should be used for this comparison between the source and value?',
349 'type' => 'varchar',
350 'length' => 128,
351 ),
352 'value' => array(
353 'description' => 'The value to be compared with source.',
354 'type' => 'text',
355 ),
356 ),
357 'primary key' => array('nid', 'rgid', 'rid'),
358 );
359
360 $schema['webform_conditional_actions'] = array(
361 'description' => 'Holds information about conditional actions.',
362 'fields' => array(
363 'nid' => array(
364 'description' => 'The node identifier of a webform.',
365 'type' => 'int',
366 'unsigned' => TRUE,
367 'not null' => TRUE,
368 'default' => 0,
369 ),
370 'rgid' => array(
371 'description' => 'The rule group identifier for this group of rules.',
372 'type' => 'int',
373 'size' => 'small',
374 'unsigned' => TRUE,
375 'not null' => TRUE,
376 'default' => 0,
377 ),
378 'aid' => array(
379 'description' => 'The rule identifier for this conditional action.',
380 'type' => 'int',
381 'size' => 'small',
382 'unsigned' => TRUE,
383 'not null' => TRUE,
384 'default' => 0,
385 ),
386 'target_type' => array(
387 'description' => 'The type of target to be affected. Currently always "component". Indicates what type of ID the "target" column contains.',
388 'type' => 'varchar',
389 'length' => 128,
390 ),
391 'target' => array(
392 'description' => 'The ID of the target to be affected. Typically a component ID.',
393 'type' => 'varchar',
394 'length' => 128,
395 ),
396 'invert' => array(
397 'description' => 'If inverted, execute when rule(s) are false.',
398 'type' => 'int',
399 'size' => 'small',
400 'unsigned' => TRUE,
401 'not null' => TRUE,
402 'default' => 0,
403 ),
404 'action' => array(
405 'description' => 'The action to be performed on the target.',
406 'type' => 'varchar',
407 'length' => 128,
408 ),
409 'argument' => array(
410 'description' => 'Optional argument for action.',
411 'type' => 'text',
412 ),
413 ),
414 'primary key' => array('nid', 'rgid', 'aid'),
415 );
416
417 $schema['webform_emails'] = array(
418 'description' => 'Holds information regarding e-mails that should be sent upon submitting a webform',
419 'fields' => array(
420 'nid' => array(
421 'description' => 'The node identifier of a webform.',
422 'type' => 'int',
423 'unsigned' => TRUE,
424 'not null' => TRUE,
425 'default' => 0,
426 ),
427 'eid' => array(
428 'description' => 'The e-mail identifier for this row\'s settings.',
429 'type' => 'int',
430 'unsigned' => TRUE,
431 'size' => 'small',
432 'not null' => TRUE,
433 'default' => 0,
434 ),
435 'email' => array(
436 'description' => 'The e-mail address that will be sent to upon submission. This may be an e-mail address, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
437 'type' => 'text',
438 'not null' => FALSE,
439 ),
440 'subject' => array(
441 'description' => 'The e-mail subject that will be used. This may be a string, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
442 'type' => 'text',
443 'not null' => FALSE,
444 ),
445 'from_name' => array(
446 'description' => 'The e-mail "from" name that will be used. This may be a string, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
447 'type' => 'text',
448 'not null' => FALSE,
449 ),
450 'from_address' => array(
451 'description' => 'The e-mail "from" e-mail address that will be used. This may be a string, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
452 'type' => 'text',
453 'not null' => FALSE,
454 ),
455 'template' => array(
456 'description' => 'A template that will be used for the sent e-mail. This may be a string or the special key "default", which will use the template provided by the theming layer.',
457 'type' => 'text',
458 'not null' => FALSE,
459 ),
460 'excluded_components' => array(
461 'description' => 'A list of components that will not be included in the [submission:values] token. A list of CIDs separated by commas.',
462 'type' => 'text',
463 'not null' => TRUE,
464 ),
465 'html' => array(
466 'description' => 'Determines if the e-mail will be sent in an HTML format. Requires Mime Mail module.',
467 'type' => 'int',
468 'unsigned' => TRUE,
469 'size' => 'tiny',
470 'not null' => TRUE,
471 'default' => 0,
472 ),
473 'attachments' => array(
474 'description' => 'Determines if the e-mail will include file attachments. Requires Mime Mail module.',
475 'type' => 'int',
476 'unsigned' => TRUE,
477 'size' => 'tiny',
478 'not null' => TRUE,
479 'default' => 0,
480 ),
481 'exclude_empty' => array(
482 'description' => 'Determines if the e-mail will include component with an empty value.',
483 'type' => 'int',
484 'unsigned' => TRUE,
485 'size' => 'tiny',
486 'not null' => TRUE,
487 'default' => 0,
488 ),
489 'extra' => array(
490 'description' => 'A serialized array of additional options for the e-mail configuration, including value mapping for the TO and FROM addresses for select lists.',
491 'type' => 'text',
492 'not null' => TRUE,
493 ),
494 'status' => array(
495 'description' => 'Whether this email is enabled.',
496 'type' => 'int',
497 'unsigned' => TRUE,
498 'size' => 'tiny',
499 'not null' => TRUE,
500 'default' => 1,
501 ),
502 ),
503 'primary key' => array('nid', 'eid'),
504 );
505
506 $schema['webform_roles'] = array(
507 'description' => 'Holds access information regarding which roles are allowed to submit which webform nodes. Does not prevent access to the webform node entirely, use the {node_access} table for that purpose.',
508 'fields' => array(
509 'nid' => array(
510 'description' => 'The node identifier of a webform.',
511 'type' => 'int',
512 'unsigned' => TRUE,
513 'not null' => TRUE,
514 'default' => 0,
515 ),
516 'rid' => array(
517 'description' => 'The role identifier.',
518 'type' => 'int',
519 'unsigned' => TRUE,
520 'not null' => TRUE,
521 'default' => 0,
522 ),
523 ),
524 'primary key' => array('nid', 'rid'),
525 );
526
527 $schema['webform_submissions'] = array(
528 'description' => 'Holds general information about submissions outside of field values.',
529 'fields' => array(
530 'sid' => array(
531 'description' => 'The unique identifier for this submission.',
532 'type' => 'serial',
533 'unsigned' => TRUE,
534 'not null' => TRUE,
535 ),
536 'nid' => array(
537 'description' => 'The node identifier of a webform.',
538 'type' => 'int',
539 'unsigned' => TRUE,
540 'not null' => TRUE,
541 'default' => 0,
542 ),
543 'serial' => array(
544 'description' => 'The serial number of this submission.',
545 'type' => 'int',
546 'unsigned' => TRUE,
547 'not null' => TRUE,
548 ),
549 'uid' => array(
550 'description' => 'The id of the user that completed this submission.',
551 'type' => 'int',
552 'unsigned' => TRUE,
553 'not null' => TRUE,
554 'default' => 0,
555 ),
556 'is_draft' => array(
557 'description' => 'Is this a draft of the submission?',
558 'type' => 'int',
559 'size' => 'tiny',
560 'not null' => TRUE,
561 'default' => 0,
562 ),
563 'highest_valid_page' => array(
564 'description' => 'For drafts, the highest validated page number.',
565 'type' => 'int',
566 'size' => 'small',
567 'not null' => TRUE,
568 'default' => 0,
569 ),
570 'submitted' => array(
571 'description' => 'Timestamp when the form was first saved as draft or submitted.',
572 'type' => 'int',
573 'not null' => TRUE,
574 'default' => 0,
575 ),
576 'completed' => array(
577 'description' => 'Timestamp when the form was submitted as complete (not draft).',
578 'type' => 'int',
579 'not null' => TRUE,
580 'default' => 0,
581 ),
582 'modified' => array(
583 'description' => 'Timestamp when the form was last saved (complete or draft).',
584 'type' => 'int',
585 'not null' => TRUE,
586 'default' => 0,
587 ),
588 'remote_addr' => array(
589 'description' => 'The IP address of the user that submitted the form.',
590 'type' => 'varchar',
591 'length' => 128,
592 ),
593 ),
594 'primary key' => array('sid'),
595 'unique keys' => array(
596 'sid_nid' => array('sid', 'nid'),
597 'nid_serial' => array('nid', 'serial'),
598 ),
599 'indexes' => array(
600 'nid_uid_sid' => array('nid', 'uid', 'sid'),
601 'nid_sid' => array('nid', 'sid'),
602 ),
603 );
604
605 $schema['webform_submitted_data'] = array(
606 'description' => 'Stores all submitted field data for webform submissions.',
607 'fields' => array(
608 'nid' => array(
609 'description' => 'The node identifier of a webform.',
610 'type' => 'int',
611 'unsigned' => TRUE,
612 'not null' => TRUE,
613 'default' => 0,
614 ),
615 'sid' => array(
616 'description' => 'The unique identifier for this submission.',
617 'type' => 'int',
618 'unsigned' => TRUE,
619 'not null' => TRUE,
620 'default' => 0,
621 ),
622 'cid' => array(
623 'description' => 'The identifier for this component within this node, starts at 0 for each node.',
624 'type' => 'int',
625 'size' => 'small',
626 'unsigned' => TRUE,
627 'not null' => TRUE,
628 'default' => 0,
629 ),
630 'no' => array(
631 'description' => 'Usually this value is 0, but if a field has multiple values (such as a time or date), it may require multiple rows in the database.',
632 'type' => 'varchar',
633 'length' => 128,
634 'not null' => TRUE,
635 'default' => '0',
636 ),
637 'data' => array(
638 'description' => 'The submitted value of this field, may be serialized for some components.',
639 'type' => 'text',
640 'size' => 'medium',
641 'not null' => TRUE,
642 ),
643 ),
644 'primary key' => array('nid', 'sid', 'cid', 'no'),
645 'indexes' => array(
646 'nid' => array('nid'),
647 'sid_nid' => array('sid', 'nid'),
648 // For all but MS SQL Server databases, 64-character index is created on the data column after the schema is installed.
649 ),
650 );
651
652 $schema['webform_last_download'] = array(
653 'description' => 'Stores last submission number per user download.',
654 'fields' => array(
655 'nid' => array(
656 'description' => 'The node identifier of a webform.',
657 'type' => 'int',
658 'unsigned' => TRUE,
659 'not null' => TRUE,
660 'default' => 0,
661 ),
662 'uid' => array(
663 'description' => 'The user identifier.',
664 'type' => 'int',
665 'unsigned' => TRUE,
666 'not null' => TRUE,
667 'default' => 0,
668 ),
669 'sid' => array(
670 'description' => 'The last downloaded submission number.',
671 'type' => 'int',
672 'unsigned' => TRUE,
673 'not null' => TRUE,
674 'default' => 0,
675 ),
676 'requested' => array(
677 'description' => 'Timestamp of last download request.',
678 'type' => 'int',
679 'unsigned' => TRUE,
680 'not null' => TRUE,
681 'default' => 0,
682 ),
683 ),
684 'primary key' => array('nid', 'uid'),
685 );
686
687 return $schema;
688 }
689
690
691 /**
692 * Implements hook_requirements().
693 */
694 function webform_requirements($phase) {
695 $requirements = array();
696 $t = get_t();
697
698 // Ensure cURL exists if SimpleTest hasn't checked it already.
699 if (!class_exists('ZipArchive')) {
700 $requirements['webform_zip'] = array(
701 'title' => $t('Zip archive support'),
702 'value' => $t('Missing'),
703 'severity' => REQUIREMENT_WARNING,
704 'description' => $t('PHP does not have the zip archive extension available. Webform module requires zip support for exporting submissions to Microsoft Excel.'),
705 );
706 }
707 // Though the .info file specifies PHP version as well, this will prevent
708 // users from upgrading from 3.x if their PHP version is too old.
709 if (version_compare(phpversion(), '5.3') < 0) {
710 $requirements['webform_php'] = array(
711 'title' => $t('Webform PHP requirement'),
712 'value' => phpversion(),
713 'severity' => REQUIREMENT_ERROR,
714 'description' => $t('Webform requires PHP 5.3 or higher.'),
715 );
716 }
717 // Ensure that views is enabled as it is a new .info requirement starting
718 // with version 7.x-4.0rc1. On installation, the .info file is sufficient to
719 // cause the dependencies to be installed. On update, update.php will
720 // respect this hook_requirements implementation, but as of drush 6.3.0 and
721 // drush 7.0.0, drush updatedb will not. See:
722 // https://github.com/drush-ops/drush/issues/1427
723 if ($phase != 'install' && !module_exists('views')) {
724 $requirements['webform_views'] = array(
725 'title' => $t('Webform Views requirement'),
726 'value' => $t('Missing'),
727 'severity' => REQUIREMENT_ERROR,
728 'description' => $t('Webform requires Views, which is not installed and enabled.'),
729 );
730 }
731
732 return $requirements;
733 }
734
735 /**
736 * Implements hook_install().
737 */
738 function webform_install() {
739 module_load_include('inc', 'node', 'content_types');
740 db_update('system')
741 ->condition('name', 'webform')
742 ->condition('type', 'module')
743 ->fields(array('weight' => -1))
744 ->execute();
745
746 // Optionally create the default webform type.
747 if (variable_get('webform_install_create_content_type', TRUE)) {
748 $webform_type = array(
749 'type' => 'webform',
750 'name' => st('Webform'),
751 'base' => 'node_content',
752 'description' => st('Create a new form or questionnaire accessible to users. Submission results and statistics are recorded and accessible to privileged users.'),
753 'custom' => TRUE,
754 'modified' => TRUE,
755 'locked' => FALSE,
756 );
757 $webform_type = node_type_set_defaults($webform_type);
758 node_type_save($webform_type);
759 // Enable webform components by default on Webform nodes.
760 variable_set('webform_node_webform', TRUE);
761 // Now that a webform node type has been created, reset the cache of the
762 // node types that support webforms. This is needed for tests which will
763 // create nodes in the same execution.
764 drupal_static_reset('webform_node_types');
765 if (variable_get('webform_install_add_body_field', FALSE)) {
766 node_add_body_field($webform_type);
767 }
768 // Disable comments by default on Webform nodes.
769 variable_set('comment_webform', '0');
770 }
771 else {
772 variable_set('webform_node_types_primary', array());
773 }
774
775 // Note: MS SQL Server does not support size-limited indexes and the column
776 // type (text) is too big to fit inside index size limits.
777 if (!db_index_exists('webform_submitted_data', 'data') && db_driver() != 'sqlsrv') {
778 db_add_index('webform_submitted_data', 'data', array(array('data', 64)));
779 }
780 }
781
782 /**
783 * Implements hook_uninstall().
784 */
785 function webform_uninstall() {
786 // Unset webform variables.
787 variable_del('webform_blocks');
788 variable_del('webform_tracking_mode');
789 variable_del('webform_allowed_tags');
790 variable_del('webform_email_address_format');
791 variable_del('webform_email_address_individual');
792 variable_del('webform_default_from_name');
793 variable_del('webform_default_from_address');
794 variable_del('webform_default_subject');
795 variable_del('webform_email_replyto');
796 variable_del('webform_email_html_capable');
797 variable_del('webform_default_format');
798 variable_del('webform_format_override');
799 variable_del('webform_email_select_max');
800 variable_del('webform_node_types_primary');
801 variable_del('webform_date_type');
802 variable_del('webform_export_format');
803 variable_del('webform_csv_delimiter');
804 variable_del('webform_csv_line_ending');
805 variable_del('webform_export_wordwrap');
806 variable_del('webform_excel_legacy_exporter');
807 variable_del('webform_progressbar_style');
808 variable_del('webform_progressbar_label_first');
809 variable_del('webform_progressbar_label_confirmation');
810 variable_del('webform_table');
811 variable_del('webform_submission_access_control');
812 variable_del('webform_token_access');
813 variable_del('webform_update_batch_size');
814 variable_del('webform_disabled_components');
815
816 foreach (node_type_get_names() as $type => $name) {
817 variable_del('webform_node_' . $type);
818 }
819
820 $component_list = array();
821 $path = drupal_get_path('module', 'webform') . '/components';
822 $files = file_scan_directory($path, '/^.*\.inc$/');
823 foreach ($files as $filename => $file) {
824 variable_del('webform_enable_' . $file->name, 1);
825 }
826
827 // Delete uploaded files.
828 $filepath = file_build_uri('webform');
829 file_unmanaged_delete_recursive($filepath);
830
831 // Delete the content type "webform" if:
832 // a) there are no existing nodes of type webform and
833 // b) no additional fields have been defined for node type webform, beyond
834 // the default body field.
835 $query = new EntityFieldQuery();
836 $results = $query->entityCondition('entity_type', 'node')
837 ->entityCondition('bundle', 'webform')
838 ->range(0, 1)
839 ->execute();
840 $instances = field_info_instances('node', 'webform');
841 unset($instances['body']);
842 if (!$results && !$instances) {
843 node_type_delete('webform');
844 drupal_flush_all_caches();
845 }
846
847 }
848
849 /**
850 * Implements hook_update_dependencies().
851 */
852 function webform_update_dependencies() {
853 // Create the {filter_format} table before trying to use it.
854 $dependencies['webform'][7301] = array(
855 'filter' => 7000,
856 );
857 // Create the {file_managed} table before trying to use it.
858 $dependencies['webform'][7319] = array(
859 'system' => 7034,
860 );
861 // Create the {file_usage} table before trying to use it.
862 $dependencies['webform'][7320] = array(
863 'system' => 7059,
864 );
865 return $dependencies;
866 }
867
868 /**
869 * Set the minimum upgrade version.
870 *
871 * Currently you cannot upgrade from 2.x in Drupal 6 to 3.x in Drupal 7. However
872 * there are no database changes between the 3.x versions, so no update is
873 * needed at all to move from 3.x in Drupal 6 to Drupal 7.
874 */
875 function webform_update_last_removed() {
876 return 6313;
877 }
878
879 /**
880 * Allow the confirmation format column to have a NULL value.
881 */
882 function webform_update_7301() {
883 // These changes are modeled after user_update_7010().
884 db_change_field('webform', 'confirmation_format', 'confirmation_format', array(
885 'description' => 'The {filter_format}.format of the confirmation message.',
886 'type' => 'int',
887 'unsigned' => TRUE,
888 'not null' => FALSE,
889 ));
890 db_update('webform')
891 ->fields(array('confirmation_format' => NULL))
892 ->condition('confirmation', '')
893 ->condition('confirmation_format', 0)
894 ->execute();
895 $existing_formats = db_query("SELECT format FROM {filter_format}")->fetchCol();
896 $default_format = variable_get('filter_default_format', 1);
897
898 // Since Webform may be updated separately from Drupal core, not all format
899 // names may be numbers when running this update.
900 $numeric_formats = array();
901 foreach ($existing_formats as $format_name) {
902 if (is_numeric($format_name)) {
903 $numeric_formats[] = (int) $format_name;
904 }
905 }
906
907 $query = db_update('webform')
908 ->fields(array('confirmation_format' => $default_format))
909 ->isNotNull('confirmation_format');
910
911 if (!empty($numeric_formats)) {
912 $query->condition('confirmation_format', $numeric_formats, 'NOT IN');
913 }
914
915 $query->execute();
916 }
917
918 /**
919 * Add columns for e-mail HTML and attachment settings.
920 */
921 function webform_update_7302() {
922 if (!db_field_exists('webform_emails', 'html')) {
923 db_add_field('webform_emails', 'html', array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'default' => 0, 'not null' => TRUE));
924 db_add_field('webform_emails', 'attachments', array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'default' => 0, 'not null' => TRUE));
925 }
926 }
927
928 /**
929 * Set the default for the "submit_notice" column to 1.
930 */
931 function webform_update_7303() {
932 db_change_field('webform', 'submit_notice', 'submit_notice', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 1));
933 }
934
935 /**
936 * Add field for block feature and redirection setting.
937 */
938 function webform_update_7304() {
939 if (!db_field_exists('webform', 'block')) {
940 db_add_field('webform', 'block', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0));
941 db_change_field('webform', 'redirect_url', 'redirect_url', array('type' => 'varchar', 'length' => 255, 'default' => '<confirmation>'));
942 db_update('webform')
943 ->fields(array('redirect_url' => 'confirmation'))
944 ->condition('redirect_url', '')
945 ->execute();
946 }
947 }
948
949 /**
950 * Set additional_validate and additional_submit columns to allow NULL.
951 */
952 function webform_update_7305() {
953 if (db_field_exists('webform', 'additional_validate')) {
954 db_change_field('webform', 'additional_validate', 'additional_validate', array('type' => 'text', 'not null' => FALSE));
955 db_change_field('webform', 'additional_submit', 'additional_submit', array('type' => 'text', 'not null' => FALSE));
956 }
957 }
958
959 /**
960 * Add column for webform status (open or closed).
961 */
962 function webform_update_7306() {
963 if (!db_field_exists('webform', 'status')) {
964 db_add_field('webform', 'status', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 1));
965 }
966 }
967
968 /**
969 * Update the confirmation_format column for default text format changes.
970 */
971 function webform_update_7307() {
972 // Update removed and moved to webform_update_7301().
973 // See http://drupal.org/node/976102.
974 }
975
976 /**
977 * Update the confirmation_format column to allow it to store strings.
978 */
979 function webform_update_7308() {
980 db_change_field('webform', 'confirmation_format', 'confirmation_format', array(
981 'description' => 'The {filter_format}.format of the confirmation message.',
982 'type' => 'varchar',
983 'length' => 255,
984 'not null' => FALSE,
985 ));
986 }
987
988 /**
989 * Add the ability to auto-save as draft between pages.
990 */
991 function webform_update_7309() {
992 if (!db_field_exists('webform', 'auto_save')) {
993 db_add_field('webform', 'auto_save', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0));
994 }
995 }
996
997 /**
998 * Remove orphaned and unnecessary rows in the webform table.
999 */
1000 function webform_update_7310() {
1001 $result = db_query("SELECT nid FROM {webform} WHERE
1002 nid NOT IN
1003 (SELECT DISTINCT(w1.nid) FROM {webform} w1 INNER JOIN {webform_component} wc ON w1.nid = wc.nid)
1004 AND nid NOT IN
1005 (SELECT w2.nid FROM {webform} w2 INNER JOIN {node} n ON w2.nid = n.nid WHERE n.type = 'webform')"
1006 );
1007 $empty_nids = array();
1008 foreach ($result as $row) {
1009 $empty_nids[] = $row->nid;
1010 }
1011 if (!empty($empty_nids)) {
1012 db_delete('webform')->condition('nid', $empty_nids, 'IN')->execute();
1013 }
1014 }
1015
1016 /**
1017 * Add an index for nid_uid_sid to webform_submissions.
1018 */
1019 function webform_update_7311() {
1020 if (!db_index_exists('webform_submissions', 'nid_uid_sid')) {
1021 db_add_index('webform_submissions', 'nid_uid_sid', array('nid', 'uid', 'sid'));
1022 }
1023 }
1024
1025 /**
1026 * Remove unused Webform variables.
1027 */
1028 function webform_update_7312() {
1029 variable_del('node_types');
1030 variable_del('components');
1031 }
1032
1033 /**
1034 * Convert the Date component start and end year options to start and end date.
1035 */
1036 function webform_update_7313() {
1037 $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
1038 ->fields('wc')
1039 ->condition('type', 'date')
1040 ->execute();
1041 foreach ($result as $component) {
1042 $component['extra'] = unserialize($component['extra']);
1043 if (!isset($component['extra']['start_date']) && !isset($component['end_date'])) {
1044 foreach (array('year_start' => 'start_date', 'year_end' => 'end_date') as $key => $replacement) {
1045 $value = isset($component['extra'][$key]) ? trim($component['extra'][$key]) : '';
1046 // Relative years.
1047 if (preg_match('/[-+][ ]*[0-9]+/', $value)) {
1048 $component['extra'][$replacement] = ($value == 1) ? ($value . ' year') : ($value . ' years');
1049 }
1050 // Absolute years.
1051 elseif (is_numeric($value)) {
1052 $component['extra'][$replacement] = 'Dec 31 ' . $value;
1053 }
1054 unset($component['extra'][$key]);
1055 }
1056 $component['extra'] = serialize($component['extra']);
1057 drupal_write_record('webform_component', $component, array('nid', 'cid'));
1058 }
1059 }
1060 }
1061
1062 /**
1063 * Add webform_last_download table to store last downloaded sid per user.
1064 */
1065 function webform_update_7314() {
1066 // Safety check to prevent recreating the webform_last_download table.
1067 if (db_table_exists('webform_last_download')) {
1068 return;
1069 }
1070
1071 $schema['webform_last_download'] = array(
1072 'description' => 'Stores last submission number per user download.',
1073 'fields' => array(
1074 'nid' => array(
1075 'description' => 'The node identifier of a webform.',
1076 'type' => 'int',
1077 'unsigned' => TRUE,
1078 'not null' => TRUE,
1079 'default' => 0,
1080 ),
1081 'uid' => array(
1082 'description' => 'The user identifier.',
1083 'type' => 'int',
1084 'unsigned' => TRUE,
1085 'not null' => TRUE,
1086 'default' => 0,
1087 ),
1088 'sid' => array(
1089 'description' => 'The last downloaded submission number.',
1090 'type' => 'int',
1091 'unsigned' => TRUE,
1092 'not null' => TRUE,
1093 'default' => 0,
1094 ),
1095 ),
1096 'primary key' => array('nid', 'uid'),
1097 );
1098 db_create_table('webform_last_download', $schema['webform_last_download']);
1099 }
1100
1101 /**
1102 * Add column for timestamp of last requested CSV download.
1103 */
1104 function webform_update_7315() {
1105 if (!db_field_exists('webform_last_download', 'requested')) {
1106 db_add_field('webform_last_download', 'requested', array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0));
1107 }
1108 }
1109
1110 /**
1111 * Add additional columns for total submission limit.
1112 */
1113 function webform_update_7316() {
1114 if (!db_field_exists('webform', 'total_submit_limit')) {
1115 db_add_field('webform', 'total_submit_limit', array('type' => 'int', 'not null' => TRUE, 'default' => -1));
1116 }
1117
1118 if (!db_field_exists('webform', 'total_submit_interval')) {
1119 db_add_field('webform', 'total_submit_interval', array('type' => 'int', 'not null' => TRUE, 'default' => -1));
1120 }
1121 }
1122
1123 /**
1124 * Add an index for 'nid_sid' to webform_submissions.
1125 */
1126 function webform_update_7317() {
1127 // Even though we already have an index 'nid_uid_sid', adding the index for
1128 // 'nid_sid' saves us a tablesort on the node/x/webform-results page.
1129 if (!db_index_exists('webform_submissions', 'nid_sid')) {
1130 db_add_index('webform_submissions', 'nid_sid', array('nid', 'sid'));
1131 }
1132 }
1133
1134 /**
1135 * Upgrade file components to support the new AJAX-upload element.
1136 */
1137 function webform_update_7318() {
1138 $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
1139 ->fields('wc')
1140 ->condition('type', 'file')
1141 ->execute();
1142 foreach ($result as $component) {
1143 $component['extra'] = unserialize($component['extra']);
1144 if (!isset($component['extra']['directory'])) {
1145 $component['extra']['directory'] = $component['extra']['savelocation'];
1146 $component['extra']['scheme'] = file_default_scheme();
1147 $component['extra']['filtering']['size'] = $component['extra']['filtering']['size'] . ' KB';
1148 unset($component['extra']['savelocation']);
1149 $component['extra'] = serialize($component['extra']);
1150 drupal_write_record('webform_component', $component, array('nid', 'cid'));
1151 }
1152 }
1153
1154 return t('File components updated to support AJAX uploading.');
1155 }
1156
1157 /**
1158 * Add file usage entries for all files uploaded through Webform.
1159 */
1160 function webform_update_7319(&$sandbox) {
1161 if (!isset($sandbox['progress'])) {
1162 // Initialize batch update information.
1163 $sandbox['progress'] = 0;
1164 $sandbox['last_fid_processed'] = -1;
1165 $sandbox['max'] = db_select('file_managed')
1166 ->condition('uri', '%' . db_like('://webform/') . '%', 'LIKE')
1167 ->countQuery()
1168 ->execute()
1169 ->fetchField();
1170 }
1171
1172 // Process all files attached to a given revision during the same batch.
1173 $limit = webform_variable_get('webform_update_batch_size');
1174 $files = db_select('file_managed', 'f')
1175 ->fields('f')
1176 ->condition('uri', '%' . db_like('://webform/') . '%', 'LIKE')
1177 ->condition('fid', $sandbox['last_fid_processed'], '>')
1178 ->orderBy('fid', 'ASC')
1179 ->range(0, $limit)
1180 ->execute()
1181 ->fetchAllAssoc('fid', PDO::FETCH_ASSOC);
1182
1183 // Determine each submission with which a file is associated.
1184 if (!empty($files)) {
1185 foreach ($files as $fid => $file) {
1186 $file = (object) $file;
1187 $sids = db_query('SELECT wsd.sid FROM {webform_component} wc INNER JOIN {webform_submitted_data} wsd ON wc.nid = wsd.nid AND wc.type = :file WHERE data = :fid', array(':file' => 'file', ':fid' => $file->fid))->fetchAllAssoc('sid', PDO::FETCH_ASSOC);
1188 foreach ($sids as $sid => $row) {
1189 // We use a db_merge() instead of file_usage_add() to prevent problems
1190 // in the event this update was run twice. No file provided by Webform
1191 // should ever be in use more than once at this point.
1192 db_merge('file_usage')
1193 ->key(array(
1194 'fid' => $file->fid,
1195 'type' => 'submission',
1196 'module' => 'webform',
1197 'id' => $sid,
1198 ))
1199 ->fields(array(
1200 'count' => 1,
1201 ))
1202 ->execute();
1203 }
1204
1205 // Update our progress information for the batch update.
1206 $sandbox['progress']++;
1207 $sandbox['last_fid_processed'] = $file->fid;
1208 }
1209 }
1210
1211 // If less than limit was processed, the update process is finished.
1212 if (count($files) < $limit || $sandbox['progress'] == $sandbox['max']) {
1213 $finished = TRUE;
1214 }
1215
1216 // If there's no max value then there's nothing to update and we're finished.
1217 if (empty($sandbox['max']) || isset($finished)) {
1218 return t('Webform file entries created in the file_usage table.');
1219 }
1220 else {
1221 // Indicate our current progress to the batch update system.
1222 $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
1223 }
1224 }
1225
1226 /**
1227 * Mark files uploaded through Webform that report active usage permanent.
1228 */
1229 function webform_update_7320() {
1230 db_query("UPDATE {file_managed} SET status = 1 WHERE fid IN (SELECT fid FROM {file_usage} WHERE module = :module_name)", array(':module_name' => 'webform'));
1231 }
1232
1233 /**
1234 * Rewrite token replacement system to use D7 tokens.
1235 *
1236 * If needed, please download and install the Token module from drupal.org.
1237 * Otherwise some tokens will not be rendered.
1238 */
1239 function webform_update_7401(&$sandbox) {
1240 // Define replacements.
1241 $patterns = array(
1242 '%username',
1243 '%useremail',
1244 '%uid',
1245 '%date',
1246 '%ip_address',
1247 '%site',
1248 '%nid',
1249 '%title',
1250 '%email_values',
1251 '%submission_url',
1252 '%sid',
1253 '%server[REQUEST_URI]',
1254 '][', // Used to convert nested arrays of %value and %email.
1255 );
1256 $replacements = array(
1257 '[current-user:name]',
1258 '[current-user:mail]',
1259 '[current-user:uid]',
1260 '[submission:date:long]',
1261 '[current-user:ip-address]',
1262 '[site:name]',
1263 '[node:nid]',
1264 '[node:title]',
1265 '[submission:values]',
1266 '[submission:url]',
1267 '[submission:sid]',
1268 '[current-page:url]',
1269 ':', // Replace "][" with ":" for %value and %email.
1270 );
1271 $dpatterns = array(
1272 '/%get\[([^\]]+)\]/m',
1273 '/%email\[([^% \n\r\t]+)?\]/m',
1274 '/%value\[([^% \n\r\t]+)?\]/m',
1275 '/%profile\[([^\]]+)\]/m',
1276 );
1277 $dreplacements = array(
1278 '[current-page:query:$1]',
1279 '[submission:values:$1]',
1280 '[submission:values:$1:nolabel]',
1281 '[current-user:$1]',
1282 );
1283
1284 $limit = webform_variable_get('webform_update_batch_size');
1285 $processed_count = _webform_update_7401_batch($sandbox, $patterns, $replacements, $dpatterns, $dreplacements, $limit);
1286
1287 // If less than limit was processed, the update process is finished.
1288 if ($processed_count < $limit || $sandbox['progress'] == $sandbox['max']) {
1289 $finished = TRUE;
1290 }
1291
1292 // If there's no max value then there's nothing to update and we're finished.
1293 if (empty($sandbox['max']) || isset($finished)) {
1294 $message = t('Your existing webforms have been upgraded to use the global Drupal 7 token system.');
1295 if (!module_exists('token')) {
1296 $message .= ' <strong>' . t('Please download and install the <a href="http://drupal.org/project/token" target="_blank">Token module</a>. Otherwise some tokens will not be rendered.') . '</strong>';
1297 }
1298 return $message;
1299 }
1300 else {
1301 // Indicate our current progress to the batch update system.
1302 $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
1303 }
1304 }
1305
1306 /**
1307 * Utility function to update all the locations that use tokens.
1308 */
1309 function _webform_update_7401_batch(&$sandbox, $patterns, $replacements, $dpatterns, $dreplacements, $limit) {
1310 // Set up the initial batch process.
1311 if (!isset($sandbox['progress'])) {
1312 $sandbox['progress'] = 0;
1313 $sandbox['last_nid_processed'] = -1;
1314 $sandbox['max'] = db_select('webform')
1315 ->countQuery()
1316 ->execute()
1317 ->fetchField();
1318
1319 // Update tokens in variables.
1320 $variables = array(
1321 'webform_default_subject',
1322 'webform_default_from_name',
1323 'webform_default_from_address',
1324 );
1325 foreach ($variables as $variable) {
1326 $value = variable_get($variable, NULL);
1327 if ($value !== NULL) {
1328 $value = str_replace($patterns, $replacements, $value);
1329 $value = preg_replace($dpatterns, $dreplacements, $value);
1330 variable_set($variable, $value);
1331 }
1332 }
1333 }
1334
1335 $webforms = db_select('webform', 'w')
1336 ->fields('w')
1337 ->condition('nid', $sandbox['last_nid_processed'], '>')
1338 ->orderBy('nid', 'ASC')
1339 ->range(0, $limit)
1340 ->execute()
1341 ->fetchAllAssoc('nid', PDO::FETCH_ASSOC);
1342
1343 foreach ($webforms as $nid => $webform) {
1344 // Update the webform record itself.
1345 $original = $webform;
1346 $parts = array(
1347 'confirmation',
1348 'redirect_url',
1349 );
1350 foreach ($parts as $part) {
1351 $webform[$part] = str_replace($patterns, $replacements, $webform[$part]);
1352 $webform[$part] = preg_replace($dpatterns, $dreplacements, $webform[$part]);
1353 }
1354 if ($webform != $original) {
1355 drupal_write_record('webform', $webform, array('nid'));
1356 }
1357
1358 // Update tokens in component configurations.
1359 $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
1360 ->fields('wc')
1361 ->condition('wc.nid', $nid)
1362 ->execute();
1363 foreach ($result as $component) {
1364 $original_extra = $component['extra'];
1365 $original_value = $component['value'];
1366 $component['extra'] = unserialize($component['extra']);
1367 if (isset($component['extra']['description'])) {
1368 $description = str_replace($patterns, $replacements, $component['extra']['description']);
1369 $description = preg_replace($dpatterns, $dreplacements, $description);
1370 $component['extra']['description'] = $description;
1371 }
1372 $component['extra'] = serialize($component['extra']);
1373 $value = str_replace($patterns, $replacements, $component['value']);
1374 $value = preg_replace($dpatterns, $dreplacements, $value);
1375 $component['value'] = $value;
1376 if ($component['extra'] != $original_extra || $component['value'] != $original_value) {
1377 drupal_write_record('webform_component', $component, array('nid', 'cid'));
1378 }
1379 }
1380
1381 // Update tokens in e-mail configurations.
1382 $result = db_select('webform_emails', 'we', array('fetch' => PDO::FETCH_ASSOC))
1383 ->fields('we')
1384 ->condition('we.nid', $nid)
1385 ->execute();
1386 foreach ($result as $email) {
1387 $parts = array(
1388 'template',
1389 'subject',
1390 'from_name',
1391 );
1392 $original = $email;
1393 foreach ($parts as $part) {
1394 $email[$part] = str_replace($patterns, $replacements, $email[$part]);
1395 $email[$part] = preg_replace($dpatterns, $dreplacements, $email[$part]);
1396 }
1397 if ($email != $original) {
1398 drupal_write_record('webform_emails', $email, array('nid', 'eid'));
1399 }
1400 }
1401
1402 // Update the last processed NID.
1403 $sandbox['last_nid_processed'] = $nid;
1404 $sandbox['progress']++;
1405 }
1406 return count($webforms);
1407 }
1408
1409 /**
1410 * Add the webform_conditional database table.
1411 */
1412 function webform_update_7402() {
1413 // Sanity checks.
1414 if (db_table_exists('webform_conditional')) {
1415 // Both tables exist, so these are Webform-core provided tables.
1416 if (db_table_exists('webform_conditional_rules')) {
1417 return;
1418 }
1419 // Webform Conditional module was installed previously but not uninstalled.
1420 else {
1421 throw new DrupalUpdateException(t('The "Webform Conditional" module has previously been installed on your site. Either uninstall the module or drop the database table "webform_conditional".'));
1422 }
1423 }
1424
1425 $schema['webform_conditional'] = array(
1426 'description' => 'Holds information about conditional logic.',
1427 'fields' => array(
1428 'nid' => array(
1429 'description' => 'The node identifier of a webform.',
1430 'type' => 'int',
1431 'unsigned' => TRUE,
1432 'not null' => TRUE,
1433 'default' => 0,
1434 ),
1435 'rgid' => array(
1436 'description' => 'The rule group identifier for this group of rules.',
1437 'type' => 'int',
1438 'size' => 'small',
1439 'unsigned' => TRUE,
1440 'not null' => TRUE,
1441 'default' => 0,
1442 ),
1443 'andor' => array(
1444 'description' => 'Whether to AND or OR the actions in this group. All actions within the same crid should have the same andor value.',
1445 'type' => 'varchar',
1446 'length' => 128,
1447 ),
1448 'action' => array(
1449 'description' => 'The action to be performed on the target. Typically "show" or "hide" for targets of type "component", and "send" for targets of type "email".',
1450 'type' => 'varchar',
1451 'length' => 128,
1452 ),
1453 'target_type' => array(
1454 'description' => 'The type of target to be affected. Either "component" or "email". Indicates what type of ID the "target" column contains.',
1455 'type' => 'varchar',
1456 'length' => 128,
1457 ),
1458 'target' => array(
1459 'description' => 'The ID of the target to be affected. Typically a component ID.',
1460 'type' => 'varchar',
1461 'length' => 128,
1462 ),
1463 'weight' => array(
1464 'description' => 'Determines the position of this conditional compared to others.',
1465 'type' => 'int',
1466 'size' => 'small',
1467 'not null' => TRUE,
1468 'default' => 0,
1469 ),
1470 ),
1471 'primary key' => array('nid', 'rgid'),
1472 );
1473
1474 $schema['webform_conditional_rules'] = array(
1475 'description' => 'Holds information about conditional logic.',
1476 'fields' => array(
1477 'nid' => array(
1478 'description' => 'The node identifier of a webform.',
1479 'type' => 'int',
1480 'unsigned' => TRUE,
1481 'not null' => TRUE,
1482 'default' => 0,
1483 ),
1484 'rgid' => array(
1485 'description' => 'The rule group identifier for this group of rules.',
1486 'type' => 'int',
1487 'size' => 'small',
1488 'unsigned' => TRUE,
1489 'not null' => TRUE,
1490 'default' => 0,
1491 ),
1492 'rid' => array(
1493 'description' => 'The rule identifier for this conditional rule.',
1494 'type' => 'int',
1495 'size' => 'small',
1496 'unsigned' => TRUE,
1497 'not null' => TRUE,
1498 'default' => 0,
1499 ),
1500 'source_type' => array(
1501 'description' => 'The type of source on which the conditional is based. Currently always "component". Indicates what type of ID the "source" column contains.',
1502 'type' => 'varchar',
1503 'length' => 128,
1504 ),
1505 'source' => array(
1506 'description' => 'The component ID being used in this condition.',
1507 'type' => 'int',
1508 'size' => 'small',
1509 'unsigned' => TRUE,
1510 'not null' => TRUE,
1511 'default' => 0,
1512 ),
1513 'operator' => array(
1514 'description' => 'Which operator (equal, contains, starts with, etc.) should be used for this comparison between the source and value?',
1515 'type' => 'varchar',
1516 'length' => 128,
1517 ),
1518 'value' => array(
1519 'description' => 'The value to be compared with source.',
1520 'type' => 'text',
1521 ),
1522 ),
1523 'primary key' => array('nid', 'rgid', 'rid'),
1524 );
1525
1526 db_create_table('webform_conditional', $schema['webform_conditional']);
1527 db_create_table('webform_conditional_rules', $schema['webform_conditional_rules']);
1528 // Rebuild schema so that webform_update_7403() can use drupal_write_record().
1529 if (db_table_exists('system') && db_field_exists('system', 'status')) {
1530 drupal_get_schema(NULL, TRUE);
1531 }
1532 }
1533
1534 /**
1535 * Convert per-component conditionals to new more flexible conditional system.
1536 */
1537 function webform_update_7403(&$sandbox) {
1538 // Set up the initial batch process.
1539 if (!isset($sandbox['progress'])) {
1540 $sandbox['progress'] = 0;
1541 $sandbox['last_nid_processed'] = -1;
1542 $sandbox['converted_count'] = 0;
1543 $sandbox['max'] = db_select('webform')
1544 ->countQuery()
1545 ->execute()
1546 ->fetchField();
1547 }
1548
1549 $limit = webform_variable_get('webform_update_batch_size');
1550 $webforms = db_select('webform', 'w')
1551 ->fields('w')
1552 ->condition('nid', $sandbox['last_nid_processed'], '>')
1553 ->orderBy('nid', 'ASC')
1554 ->range(0, $limit)
1555 ->execute()
1556 ->fetchAllAssoc('nid', PDO::FETCH_ASSOC);
1557
1558 foreach ($webforms as $nid => $webform) {
1559 // Update tokens in component configurations.
1560 $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
1561 ->fields('wc')
1562 ->condition('wc.nid', $nid)
1563 ->execute();
1564 $rgid = 0;
1565 foreach ($result as $component) {
1566 // For each component, check if it has conditional properties that need
1567 // to be removed and/or migrated. Because these properties may be in any
1568 // order, copy the original extra array for comparison.
1569 $component['extra'] = unserialize($component['extra']);
1570 $original_extra = $component['extra'];
1571
1572 // Remove conditional properties if present.
1573 if (isset($component['extra']['conditional_component'])) {
1574 unset($component['extra']['conditional_component']);
1575 }
1576 if (isset($component['extra']['conditional_operator'])) {
1577 unset($component['extra']['conditional_operator']);
1578 }
1579 if (isset($component['extra']['conditional_values'])) {
1580 unset($component['extra']['conditional_values']);
1581
1582 // If the component has conditional values specified, that indicates
1583 // that this component was conditionally shown. Convert it to a new
1584 // conditional with multiple rules if needed.
1585 if (strlen(trim($original_extra['conditional_values'])) && !empty($original_extra['conditional_operator']) && !empty($original_extra['conditional_component'])) {
1586 $conditional_values = explode("\n", $original_extra['conditional_values']);
1587 $rules = array();
1588 $rule = array(
1589 'nid' => $nid,
1590 'rgid' => $rgid,
1591 'rid' => NULL,
1592 'source_type' => 'component',
1593 'source' => $original_extra['conditional_component'],
1594 'operator' => 'equal',
1595 'value' => NULL,
1596 );
1597 foreach ($conditional_values as $value) {
1598 $value = trim($value);
1599 if ($value) {
1600 $new_rule = $rule;
1601 $new_rule['rid'] = count($rules);
1602 $new_rule['value'] = $value;
1603 $rules[] = $new_rule;
1604 }
1605 }
1606 if (count($rules)) {
1607 $conditional = array(
1608 'nid' => $nid,
1609 'rgid' => $rgid,
1610 'andor' => 'or',
1611 'action' => ($original_extra['conditional_operator'] === '=') ? 'show' : 'hide',
1612 'target_type' => 'component',
1613 'target' => $component['cid'],
1614 'weight' => 0,
1615 );
1616 drupal_write_record('webform_conditional', $conditional);
1617 foreach ($rules as $rule) {
1618 drupal_write_record('webform_conditional_rules', $rule);
1619 }
1620 $sandbox['converted_count']++;
1621 $rgid++;
1622 }
1623 }
1624 }
1625
1626 // Update the component with the conditional properties removed.
1627 if ($component['extra'] != $original_extra) {
1628 $component['extra'] = serialize($component['extra']);
1629 drupal_write_record('webform_component', $component, array('nid', 'cid'));
1630 }
1631 }
1632
1633 // Update the last processed NID.
1634 $sandbox['last_nid_processed'] = $nid;
1635 $sandbox['progress']++;
1636 }
1637
1638 // If less than limit was processed, the update process is finished.
1639 if (count($webforms) < $limit || $sandbox['progress'] == $sandbox['max']) {
1640 $finished = TRUE;
1641 }
1642
1643 // If there's no max value then there's nothing to update and we're finished.
1644 if (empty($sandbox['max']) || isset($finished)) {
1645 return t('@count webforms using conditionals updated to the new conditional system.', array('@count' => $sandbox['converted_count']));
1646 }
1647 else {
1648 // Indicate our current progress to the batch update system.
1649 $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
1650 }
1651 }
1652
1653 /**
1654 * Remove files left over from deleted submissions. Such files are now deleted
1655 * automatically.
1656 */
1657 function webform_update_7404() {
1658 module_load_include('inc', 'webform', 'components/file');
1659 $fids = db_query('SELECT fid FROM {file_usage} WHERE module = \'webform\' AND type = \'submission\' AND NOT id IN(SELECT sid FROM {webform_submissions})')->fetchCol();
1660 foreach ($fids as $fid) {
1661 _webform_delete_file(NULL, array($fid));
1662 }
1663 }
1664
1665 /**
1666 * Add an "extra" column to the e-mail table for for non-queryable options.
1667 */
1668 function webform_update_7405() {
1669 $schema = array(
1670 'description' => 'A serialized array of additional options for the e-mail configuration, including excluded components and value mapping for the TO and FROM addresses for select lists.',
1671 'type' => 'text',
1672 'not null' => TRUE,
1673 'initial' => '',
1674 );
1675 if (!db_field_exists('webform_emails', 'extra')) {
1676 db_add_field('webform_emails', 'extra', $schema);
1677 }
1678 }
1679
1680 /**
1681 * Convert the "webform_use_cookies" setting to "webform_tracking_mode".
1682 */
1683 function webform_update_7406() {
1684 // Previously, we only had "strict" and "ip_address" checking. Using cookies
1685 // meant cookies in addition to IP address.
1686 $use_cookies = variable_get('webform_use_cookies');
1687 if (isset($use_cookies)) {
1688 variable_set('webform_tracking_mode', $use_cookies ? 'strict' : 'ip_address');
1689 variable_del('webform_use_cookies');
1690 }
1691 }
1692
1693 /**
1694 * Remove orphaned conditional rules.
1695 */
1696 function webform_update_7407() {
1697 // Delete entire conditions for deleted components that were the target.
1698 // This query is complicated by database compatibility since we're joining two
1699 // non-matching columns. See http://drupal.org/node/2026891.
1700 $result = db_query("SELECT rg.* FROM {webform_conditional} rg LEFT JOIN {webform_component} c ON rg.nid = c.nid AND rg.target_type = 'component' AND rg.target = RTRIM(CAST(c.cid AS CHAR(10))) WHERE c.cid IS NULL");
1701 foreach ($result as $row) {
1702 db_delete('webform_conditional')
1703 ->condition('nid', $row->nid)
1704 ->condition('rgid', $row->rgid)
1705 ->execute();
1706 db_delete('webform_conditional_rules')
1707 ->condition('nid', $row->nid)
1708 ->condition('rgid', $row->rgid)
1709 ->execute();
1710 }
1711 // Delete conditional rules for deleted components that were the source.
1712 $result = db_query("SELECT r.* FROM {webform_conditional_rules} r LEFT JOIN {webform_component} c ON r.nid = c.nid AND r.source_type = 'component' AND r.source = c.cid WHERE c.cid IS NULL");
1713 foreach ($result as $row) {
1714 db_delete('webform_conditional_rules')
1715 ->condition('nid', $row->nid)
1716 ->condition('rgid', $row->rgid)
1717 ->condition('rid', $row->rid)
1718 ->execute();
1719 }
1720 // Delete any conditions that no longer have any rules left in them.
1721 $result = db_query("SELECT rg.* FROM {webform_conditional} rg LEFT JOIN {webform_conditional_rules} r ON rg.nid = r.nid AND rg.rgid = r.rgid WHERE r.rgid IS NULL");
1722 foreach ($result as $row) {
1723 db_delete('webform_conditional')
1724 ->condition('nid', $row->nid)
1725 ->condition('rgid', $row->rgid)
1726 ->execute();
1727 }
1728 }
1729
1730 /**
1731 * Rename the "mandatory" column to "required".
1732 */
1733 function webform_update_7408() {
1734 $spec = array(
1735 'description' => 'Boolean flag for if this component is required.',
1736 'type' => 'int',
1737 'size' => 'tiny',
1738 'not null' => TRUE,
1739 'default' => 0,
1740 );
1741 if (!db_field_exists('webform_component', 'required')) {
1742 db_change_field('webform_component', 'mandatory', 'required', $spec);
1743 }
1744 }
1745
1746 /**
1747 * Add progress bar columns to the webform table.
1748 */
1749 function webform_update_7409() {
1750 if (!db_field_exists('webform', 'progressbar_bar')) {
1751 $int_schema = array(
1752 'type' => 'int',
1753 'size' => 'tiny',
1754 'not null' => TRUE,
1755 'default' => 0,
1756 );
1757 $varchar_schema = array(
1758 'type' => 'varchar',
1759 'length' => 255,
1760 );
1761
1762 $int_schema['description'] = 'Boolean value indicating if the bar should be shown as part of the progress bar.';
1763 db_add_field('webform', 'progressbar_bar', $int_schema);
1764
1765 $int_schema['description'] = 'Boolean value indicating if the page number should be shown as part of the progress bar.';
1766 db_add_field('webform', 'progressbar_page_number', $int_schema);
1767
1768 $int_schema['description'] = 'Boolean value indicating if the percentage complete should be shown as part of the progress bar.';
1769 db_add_field('webform', 'progressbar_percent', $int_schema);
1770
1771 $int_schema['description'] = 'Boolean value indicating if the pagebreak labels should be included as part of the progress bar.';
1772 db_add_field('webform', 'progressbar_pagebreak_labels', $int_schema);
1773
1774 $int_schema['description'] = 'Boolean value indicating if the confirmation page should count as a page in the progress bar.';
1775 db_add_field('webform', 'progressbar_include_confirmation', $int_schema);
1776
1777 $varchar_schema['description'] = 'Label for the first page of the progress bar.';
1778 db_add_field('webform', 'progressbar_label_first', $varchar_schema);
1779
1780 $varchar_schema['description'] = 'Label for the last page of the progress bar.';
1781 db_add_field('webform', 'progressbar_label_confirmation', $varchar_schema);
1782
1783 return t('New webform columns added.');
1784 }
1785 }
1786
1787 /**
1788 * Remove the "teaser" column from the "webform" table.
1789 */
1790 function webform_update_7410() {
1791 if (db_field_exists('webform', 'teaser')) {
1792 db_drop_field('webform', 'teaser');
1793 return t('Removed "teaser" column. All forms that had the "Show complete form in teaser" option disabled will now show forms in their teasers. Use view modes to hide the form if desired.');
1794 }
1795 }
1796
1797 /**
1798 * Remove [submission:values:x] token use of :nolabel and add :withlabel.
1799 */
1800 function webform_update_7411(&$sandbox) {
1801 // Define replacements.
1802 $patterns = array();
1803 $replacements = array();
1804 $dpatterns = array(
1805 '/\[submission:values(:(?!nolabel)[a-z_]+)+\]/m',
1806 '/\[submission:values(:([a-z_:]+))?:nolabel\]/m',
1807 );
1808 $dreplacements = array(
1809 '[submission:values$1:withlabel]',
1810 '[submission:values$1]',
1811 );
1812
1813 $limit = webform_variable_get('webform_update_batch_size');
1814 $processed_count = _webform_update_7401_batch($sandbox, $patterns, $replacements, $dpatterns, $dreplacements, $limit);
1815
1816 // If less than limit was processed, the update process is finished.
1817 if ($processed_count < $limit || $sandbox['progress'] == $sandbox['max']) {
1818 $finished = TRUE;
1819 }
1820
1821 // If there's no max value then there's nothing to update and we're finished.
1822 if (empty($sandbox['max']) || isset($finished)) {
1823 return t('Replaced tokens using [submission:values:x] with [submission:values:x:withlabel].');
1824 }
1825 else {
1826 // Indicate our current progress to the batch update system.
1827 $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
1828 }
1829 }
1830
1831 /**
1832 * Split webform_node_types as content type specific.
1833 */
1834 function webform_update_7412() {
1835 $types = variable_get('webform_node_types', array('webform'));
1836 foreach ($types as $type) {
1837 variable_set('webform_node_' . $type, TRUE);
1838 }
1839 variable_del('webform_node_types');
1840 }
1841
1842 /**
1843 * Add preview page columns to the webform table.
1844 */
1845 function webform_update_7413() {
1846 if (!db_field_exists('webform', 'preview')) {
1847 $int_schema = array(
1848 'type' => 'int',
1849 'size' => 'tiny',
1850 'not null' => TRUE,
1851 'default' => 0,
1852 );
1853 $varchar_schema = array(
1854 'type' => 'varchar',
1855 'length' => 255,
1856 );
1857 $text_schema = array(
1858 'type' => 'text',
1859 'not null' => TRUE,
1860 'initial' => '',
1861 );
1862
1863 $int_schema['description'] = 'Boolean value indicating if this form includes a page for previewing the submission.';
1864 db_add_field('webform', 'preview', $int_schema);
1865
1866 $varchar_schema['description'] = 'The text for the button that will proceed to the preview page.';
1867 db_add_field('webform', 'preview_next_button_label', $varchar_schema);
1868
1869 $varchar_schema['description'] = 'The text for the button to go backwards from the preview page.';
1870 db_add_field('webform', 'preview_prev_button_label', $varchar_schema);
1871
1872 $varchar_schema['description'] = 'The title of the preview page, as used by the progress bar.';
1873 db_add_field('webform', 'preview_title', $varchar_schema);
1874
1875 $text_schema['description'] = 'Text shown on the preview page of the form.';
1876 db_add_field('webform', 'preview_message', $text_schema);
1877
1878 $varchar_schema['description'] = 'The {filter_format}.format of the preview page message.';
1879 db_add_field('webform', 'preview_message_format', $varchar_schema);
1880
1881 $text_schema['description'] = 'Comma-separated list of component IDs that should not be included in this form\'s confirmation page.';
1882 db_add_field('webform', 'preview_excluded_components', $text_schema);
1883
1884 return t('New webform columns added.');
1885 }
1886 }
1887
1888 /**
1889 * Change email subject, from_name and from_address fields to type 'TEXT'.
1890 */
1891 function webform_update_7414() {
1892 $schema = array(
1893 'subject' => array(
1894 'description' => 'The e-mail subject that will be used. This may be a string, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
1895 'type' => 'text',
1896 'not null' => FALSE,
1897 ),
1898 'from_name' => array(
1899 'description' => 'The e-mail "from" name that will be used. This may be a string, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
1900 'type' => 'text',
1901 'not null' => FALSE,
1902 ),
1903 'from_address' => array(
1904 'description' => 'The e-mail "from" e-mail address that will be used. This may be a string, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
1905 'type' => 'text',
1906 'not null' => FALSE,
1907 ),
1908 );
1909
1910 db_change_field('webform_emails', 'subject', 'subject', $schema['subject']);
1911 db_change_field('webform_emails', 'from_name', 'from_name', $schema['from_name']);
1912 db_change_field('webform_emails', 'from_address', 'from_address', $schema['from_address']);
1913
1914 return t('Custom email columns successfully changed.');
1915 }
1916
1917 /**
1918 * Increase length of webform.redirect_url.
1919 */
1920 function webform_update_7415() {
1921 $spec = array(
1922 'description' => 'The URL a user is redirected to after submitting a form.',
1923 'type' => 'varchar',
1924 'length' => 2048,
1925 'default' => '<confirmation>',
1926 );
1927 db_change_field('webform', 'redirect_url', 'redirect_url', $spec);
1928 }
1929
1930 /**
1931 * Add columns for serial numbered submissions. Add serial numbers to existing submissions.
1932 */
1933 function webform_update_7416() {
1934 // SQL database implementations vary in how DDL statements are handled within
1935 // transactions. Since this update routine should be run in maintenance mode
1936 // without concurrent transactions, no DDL statements are executed within a
1937 // transaction.
1938
1939 // Add next_serial column to webform.
1940 $spec = array(
1941 'description' => 'The serial number to give to the next submission to this webform.',
1942 'type' => 'int',
1943 'unsigned' => TRUE,
1944 'not null' => TRUE,
1945 'default' => 1,
1946 );
1947 if (!db_field_exists('webform', 'next_serial')) {
1948 db_add_field('webform', 'next_serial', $spec);
1949 }
1950
1951 // Add serial column to webform_submissions.
1952 $spec = array(
1953 'description' => 'The serial number of this submission.',
1954 'type' => 'int',
1955 'unsigned' => TRUE,
1956 );
1957 if (!db_field_exists('webform_submissions', 'serial')) {
1958 db_add_field('webform_submissions', 'serial', $spec);
1959 }
1960
1961 // Begin a transaction for updating the serial numbers. The transaction will
1962 // commit when $txn is unset or goesout-of-scope.
1963 $txn = db_transaction();
1964
1965 // Delete stray entries from the Webform tables before adding serial numbers.
1966 db_query("DELETE FROM {webform_submissions} WHERE nid NOT IN (SELECT nid FROM {webform})");
1967 db_query("DELETE FROM {webform_submitted_data} WHERE nid NOT IN (SELECT nid FROM {webform})");
1968
1969
1970 // Add serial numbers to all submissions.
1971
1972 // Repeat for every Webform.
1973 $nids = db_select('webform', 'w')
1974 ->fields('w', array('nid'))
1975 ->execute();
1976 while ($nid = $nids->fetchColumn()) {
1977 $serial = 1;
1978 // Repeat for every submission in this Webform.
1979 $sids = db_select('webform_submissions', 'ws')
1980 ->forUpdate()
1981 ->fields('ws', array('sid'))
1982 ->condition('nid', $nid)
1983 ->orderBy('sid')
1984 ->execute();
1985 while ($sid = $sids->fetchColumn()) {
1986 // Set the serial number.
1987 db_update('webform_submissions')
1988 ->fields(array('serial' => $serial))
1989 ->condition('nid', $nid)
1990 ->condition('sid', $sid)
1991 ->execute();
1992 $serial++;
1993 }
1994 // Set the next serial number.
1995 db_update('webform')
1996 ->fields(array('next_serial' => $serial))
1997 ->condition('nid', $nid)
1998 ->execute();
1999 }
2000 // Commit the transaction
2001 unset($txn);
2002
2003 // Now that every submission has a serial number, make serial numbers required.
2004 $spec['not null'] = TRUE;
2005 $keys = array();
2006 // Create a unique index if it does not already exist.
2007 if (!db_index_exists('webform_submissions', 'nid_serial')) {
2008 $keys = array(
2009 'unique keys' => array(
2010 'nid_serial' => array('nid', 'serial'),
2011 ),
2012 );
2013 }
2014 db_change_field('webform_submissions', 'serial', 'serial', $spec, $keys);
2015
2016 return t('Columns for serial numbered submissions successfully added. Serial numbers added to existing submissions.');
2017 }
2018
2019 /**
2020 * Change webform_component.name to text to allow for longer labels.
2021 */
2022 function webform_update_7417() {
2023 // This update function was introduced in 7.x-4.0rc1, which was the first
2024 // release to require views. While the views requirement was added to the
2025 // .info file, and while subsequently hook_requirements() added a check for
2026 // views, it is possible to arrive at this update function via drush updatedb
2027 // without views enabled.
2028 $result = NULL;
2029 if (!module_exists('views')) {
2030 if (module_enable(array('views'))) {
2031 $result = t('The Views module was automatically enabled.');
2032 }
2033 else {
2034 throw new DrupalUpdateException(t('UPDATE ERROR: The Views module must be downloaded and enabled before Webform updates can proceed.'));
2035 }
2036 }
2037
2038 db_change_field('webform_component', 'name', 'name', array('type' => 'text', 'not null' => TRUE));
2039 return $result;
2040 }
2041
2042 /**
2043 * Upgrade the "extra" column in the e-mail table to be consistent with a new
2044 * webform installation.
2045 *
2046 * In version 7.x-4.0-rc6 and earlier, the extra field was added with 'not null'
2047 * FALSE, cause the schema module to report in mismatch. While functionally this
2048 * causes no problem, this update removes the schema module's warning.
2049 */
2050 function webform_update_7418() {
2051 // While there should never be any NULL values in the extra field, change them to '' to be safe.
2052 db_update('webform_emails')
2053 ->fields(array('extra' => ''))
2054 ->isNull('extra')
2055 ->execute();
2056 // Pass a complete field specification to db_change_field for cross-database compatiblity.
2057 $spec = array(
2058 'description' => 'A serialized array of additional options for the e-mail configuration, including excluded components and value mapping for the TO and FROM addresses for select lists.',
2059 'type' => 'text',
2060 'not null' => TRUE,
2061 );
2062 db_change_field('webform_emails', 'extra', 'extra', $spec);
2063 }
2064
2065 /**
2066 * Add an index on submitted data.
2067 */
2068 function webform_update_7419() {
2069 // Note: MS SQL Server does not support size-limited indexes and the column
2070 // type (text) is too big to fit inside index size limits.
2071 if (!db_index_exists('webform_submitted_data', 'data') && db_driver() != 'sqlsrv') {
2072 db_add_index('webform_submitted_data', 'data', array(array('data', 64)));
2073 }
2074 }
2075
2076 /**
2077 * Extend access to the WEBFORM tab to anyone who can access the node's EDIT tab.
2078 */
2079 function webform_update_7420() {
2080 user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('edit webform components'));
2081 }
2082
2083 /**
2084 * Set the administrator option for an HTML-capable mail system to the current automatically-detected option.
2085 */
2086 function webform_update_7421() {
2087 $capable = array_reduce(array('mandrill', 'mimemail', 'htmlmail'),
2088 function($carry, $module) { return $carry || module_exists($module); },
2089 FALSE);
2090 variable_set('webform_email_html_capable', $capable);
2091 return $capable
2092 ? t('An HTML-capable module is installed. The option to send HTML e-mail is enabled.')
2093 : t('No commonly-known HTML capable module is installed. The option to send HTML e-mail is disabled.');
2094 }
2095
2096 /**
2097 * Remove the administrator option "Include webform forms in search index" and rely on the Search Index view mode instead.
2098 */
2099 function webform_update_7422() {
2100 variable_del('webform_search_index');
2101 return module_exists('search')
2102 ? t('Webform forms will now be included in the search index only if the Webform "field" is displayed in the "Search index" view mode.')
2103 : NULL;
2104 }
2105
2106 /**
2107 * Convert conditionals to be able to support multiple actions per conditional. Backup your database before proceeding. WARNING: Sites with many, many conditionals should execute this update via drush to avoid a PHP timeout.
2108 */
2109 function webform_update_7423() {
2110 // Create webform_condtional_actions table.
2111 // The table might already exist if this update previously timed-out
2112 if (!db_table_exists('webform_conditional_actions')) {
2113 $schema['webform_conditional_actions'] = array(
2114 'description' => 'Holds information about conditional actions.',
2115 'fields' => array(
2116 'nid' => array(
2117 'description' => 'The node identifier of a webform.',
2118 'type' => 'int',
2119 'unsigned' => TRUE,
2120 'not null' => TRUE,
2121 'default' => 0,
2122 ),
2123 'rgid' => array(
2124 'description' => 'The rule group identifier for this group of rules.',
2125 'type' => 'int',
2126 'size' => 'small',
2127 'unsigned' => TRUE,
2128 'not null' => TRUE,
2129 'default' => 0,
2130 ),
2131 'aid' => array(
2132 'description' => 'The rule identifier for this conditional action.',
2133 'type' => 'int',
2134 'size' => 'small',
2135 'unsigned' => TRUE,
2136 'not null' => TRUE,
2137 'default' => 0,
2138 ),
2139 'target_type' => array(
2140 'description' => 'The type of target to be affected. Currently always "component". Indicates what type of ID the "target" column contains.',
2141 'type' => 'varchar',
2142 'length' => 128,
2143 ),
2144 'target' => array(
2145 'description' => 'The ID of the target to be affected. Typically a component ID.',
2146 'type' => 'varchar',
2147 'length' => 128,
2148 ),
2149 'invert' => array(
2150 'description' => 'If inverted, execute when rule(s) are false.',
2151 'type' => 'int',
2152 'size' => 'small',
2153 'unsigned' => TRUE,
2154 'not null' => TRUE,
2155 'default' => 0,
2156 ),
2157 'action' => array(
2158 'description' => 'The action to be performed on the target.',
2159 'type' => 'varchar',
2160 'length' => 128,
2161 ),
2162 'argument' => array(
2163 'description' => 'Optional argument for action.',
2164 'type' => 'text',
2165 ),
2166 ),
2167 'primary key' => array('nid', 'rgid', 'aid'),
2168 );
2169 db_create_table('webform_conditional_actions', $schema['webform_conditional_actions']);
2170 }
2171
2172 // In a site with many, many conditionals, the db_insert may timeout. Start a transaction to ensure atomic action.
2173 $tx = db_transaction();
2174 // Copy target information from existing webform_conditional table to new webfrom_condtional_actions table.
2175 $select = db_select('webform_conditional', 'c')
2176 ->fields('c', array('nid', 'rgid', 'action', 'target_type', 'target'))
2177 ->orderBy('nid')->orderBy('rgid');
2178 $select->addExpression("''", 'argument');
2179 db_insert('webform_conditional_actions')
2180 ->from($select)
2181 ->execute();
2182
2183 // Commit the insert
2184 unset($tx);
2185
2186 // Remove unneeded columns from webform_conditional.
2187 foreach (array('action', 'target_type', 'target') as $fieldname) {
2188 if (db_field_exists('webform_conditional', $fieldname)) {
2189 db_drop_field('webform_conditional', $fieldname);
2190 }
2191 }
2192
2193 // Rebuild the registry because this point release contains a new class: WebformConditionals.
2194 registry_rebuild();
2195
2196 return t('Webform database tables were successfully adjusted to allow more than one action for each conditional.');
2197 }
2198
2199 /**
2200 * Convert conditional actions of "hide" to "isn't shown".
2201 */
2202 function webform_update_7424() {
2203 $count = db_update('webform_conditional_actions')
2204 ->fields(array('action' => 'show', 'invert' => 1))
2205 ->condition('action', 'hide')
2206 ->execute();
2207 return format_plural($count,
2208 '1 "hide" conditional converted to "isn\'t" shown.',
2209 '@count conditionals converted to "isn\'t" shown.');
2210 }
2211
2212 /**
2213 * Add "exclude empty" option to emails.
2214 */
2215 function webform_update_7425() {
2216 // Add next_serial column to webform.
2217 $spec = array(
2218 'description' => 'Determines if the e-mail will include component with an empty value.',
2219 'type' => 'int',
2220 'unsigned' => TRUE,
2221 'size' => 'tiny',
2222 'not null' => TRUE,
2223 'default' => 0,
2224 );
2225 if (!db_field_exists('webform_emails', 'exclude_empty')) {
2226 db_add_field('webform_emails', 'exclude_empty', $spec);
2227 }
2228
2229 // Clear the views cache since this release use the webform_analysis view.
2230 cache_clear_all('*', 'cache_views', TRUE);
2231
2232 return t('Webform e-mails were sucessfully updated to add the option to exclude empty components.');
2233 }
2234
2235 /**
2236 * Add configuration to continue sending individual e-mails to multiple recipients.
2237 */
2238 function webform_update_7426() {
2239 variable_set('webform_email_address_individual', 1);
2240 }
2241
2242 /**
2243 * Add database columns for submission completed and modified timestamps. Sites with many submissions may wish to use drush to execute this update.
2244 */
2245 function webform_update_7427() {
2246 // Create new timestamp columns.
2247 $specs = array(
2248 'completed' => array(
2249 'description' => 'Timestamp when the form was submitted as complete (not draft).',
2250 'type' => 'int',
2251 'not null' => TRUE,
2252 'default' => 0,
2253 ),
2254 'modified' => array(
2255 'description' => 'Timestamp when the form was last saved (complete or draft).',
2256 'type' => 'int',
2257 'not null' => TRUE,
2258 'default' => 0,
2259 ),
2260 );
2261 foreach ($specs as $field_name => $spec) {
2262 if (!db_field_exists('webform_submissions', $field_name)) {
2263 db_add_field('webform_submissions', $field_name, $spec);
2264 }
2265 }
2266
2267 // In a site with many submissions, the db_update may timeout. Start a transaction to ensure atomic action.
2268 $tx = db_transaction();
2269 // Copy submitted to completed for non-draft submissions.
2270 db_update('webform_submissions')
2271 ->expression('completed', 'submitted')
2272 ->condition('is_draft', 0)
2273 ->execute();
2274 // Commit the update.
2275 unset($tx);
2276
2277 // Start another transaction.
2278 $tx = db_transaction();
2279 db_update('webform_submissions')
2280 ->expression('modified', 'submitted')
2281 ->execute();
2282 // Commit the update.
2283 unset($tx);
2284
2285 // Clear the views cache since to see the completed and modified views fields.
2286 cache_clear_all('*', 'cache_views', TRUE);
2287
2288 return t('Webform submissions were updated with completed and modified timestamps.');
2289 }
2290
2291 /**
2292 * Add a "confidential" option to webforms.
2293 */
2294 function webform_update_7428() {
2295 // Add confidential column to webform.
2296 if (!db_field_exists('webform', 'confidential')) {
2297 $spec = array(
2298 'description' => 'Boolean value for whether to anonymize submissions.',
2299 'type' => 'int',
2300 'size' => 'tiny',
2301 'not null' => TRUE,
2302 'default' => 0,
2303 );
2304 db_add_field('webform', 'confidential', $spec);
2305 }
2306
2307 return t('Webforms may now be configured to anonymize confidential submissions.');
2308 }
2309
2310 /**
2311 * Add a column to the submission table to store the page on which to resume a draft. Sites with many, many submissions may wish to execute this update with 'drush updatedb'.
2312 */
2313 function webform_update_7429() {
2314 // Add highest_valid_page column to webform_submissions.
2315 if (!db_field_exists('webform_submissions', 'highest_valid_page')) {
2316 $spec = array(
2317 'description' => 'For drafts, the highest validated page number.',
2318 'type' => 'int',
2319 'size' => 'small',
2320 'not null' => TRUE,
2321 'default' => 0,
2322 );
2323 db_add_field('webform_submissions', 'highest_valid_page', $spec);
2324 }
2325
2326 return t('Webforms will now resume draft submissions on the page where the submitter left off.');
2327 }
2328
2329 /**
2330 * Add a column to the emails table to allow disabling.
2331 */
2332 function webform_update_7430() {
2333 // Add status column to webform_emails.
2334 if (!db_field_exists('webform_emails', 'status')) {
2335 $spec = array(
2336 'description' => 'Whether this email is enabled.',
2337 'type' => 'int',
2338 'unsigned' => TRUE,
2339 'size' => 'tiny',
2340 'not null' => TRUE,
2341 'default' => 1,
2342 );
2343 db_add_field('webform_emails', 'status', $spec);
2344 }
2345
2346 return t('Webform emails may now be disabled.');
2347 }