Project

General

Profile

Paste
Download (82.3 KB) Statistics
| Branch: | Revision:

root / drupal7 / sites / all / modules / webform / webform.install @ 389fb945

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
649
      // the data column after the schema is installed.
650
    ),
651
  );
652

    
653
  $schema['webform_last_download'] = array(
654
    'description' => 'Stores last submission number per user download.',
655
    'fields' => array(
656
      'nid' => array(
657
        'description' => 'The node identifier of a webform.',
658
        'type' => 'int',
659
        'unsigned' => TRUE,
660
        'not null' => TRUE,
661
        'default' => 0,
662
      ),
663
      'uid' => array(
664
        'description' => 'The user identifier.',
665
        'type' => 'int',
666
        'unsigned' => TRUE,
667
        'not null' => TRUE,
668
        'default' => 0,
669
      ),
670
      'sid' => array(
671
        'description' => 'The last downloaded submission number.',
672
        'type' => 'int',
673
        'unsigned' => TRUE,
674
        'not null' => TRUE,
675
        'default' => 0,
676
      ),
677
      'requested' => array(
678
        'description' => 'Timestamp of last download request.',
679
        'type' => 'int',
680
        'unsigned' => TRUE,
681
        'not null' => TRUE,
682
        'default' => 0,
683
      ),
684
    ),
685
    'primary key' => array('nid', 'uid'),
686
  );
687

    
688
  return $schema;
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_INFO,
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_fieldset_wrap');
801
  variable_del('webform_node_types_primary');
802
  variable_del('webform_date_type');
803
  variable_del('webform_export_format');
804
  variable_del('webform_csv_delimiter');
805
  variable_del('webform_csv_line_ending');
806
  variable_del('webform_export_wordwrap');
807
  variable_del('webform_excel_legacy_exporter');
808
  variable_del('webform_progressbar_style');
809
  variable_del('webform_progressbar_label_first');
810
  variable_del('webform_progressbar_label_confirmation');
811
  variable_del('webform_table');
812
  variable_del('webform_submission_access_control');
813
  variable_del('webform_token_access');
814
  variable_del('webform_update_batch_size');
815
  variable_del('webform_disabled_components');
816

    
817
  foreach (node_type_get_names() as $type => $name) {
818
    variable_del('webform_node_' . $type);
819
  }
820

    
821
  $component_list = array();
822
  $path = drupal_get_path('module', 'webform') . '/components';
823
  $files = file_scan_directory($path, '/^.*\.inc$/');
824
  foreach ($files as $filename => $file) {
825
    variable_del('webform_enable_' . $file->name, 1);
826
  }
827

    
828
  // Delete uploaded files.
829
  $filepath = file_build_uri('webform');
830
  file_unmanaged_delete_recursive($filepath);
831

    
832
  // Delete the content type "webform" if:
833
  // 1. there are no existing nodes of type webform and
834
  // 2. no additional fields have been defined for node type webform, beyond the
835
  //    default body field.
836
  $query = new EntityFieldQuery();
837
  $results = $query->entityCondition('entity_type', 'node')
838
    ->entityCondition('bundle', 'webform')
839
    ->range(0, 1)
840
    ->execute();
841
  $instances = field_info_instances('node', 'webform');
842
  unset($instances['body']);
843
  if (!$results && !$instances) {
844
    node_type_delete('webform');
845
    drupal_flush_all_caches();
846
  }
847

    
848
}
849

    
850
/**
851
 * Implements hook_update_dependencies().
852
 */
853
function webform_update_dependencies() {
854
  // Create the {filter_format} table before trying to use it.
855
  $dependencies['webform'][7301] = array(
856
    'filter' => 7000,
857
  );
858
  // Create the {file_managed} table before trying to use it.
859
  $dependencies['webform'][7319] = array(
860
    'system' => 7034,
861
  );
862
  // Create the {file_usage} table before trying to use it.
863
  $dependencies['webform'][7320] = array(
864
    'system' => 7059,
865
  );
866
  if (module_exists('rules')) {
867
    // Ensure rules tables and fields exist before trying to use it.
868
    $dependencies['webform'][7420] = array(
869
      'rules' => 7213,
870
    );
871
  }
872
  return $dependencies;
873
}
874

    
875
/**
876
 * Set the minimum upgrade version.
877
 *
878
 * Currently you cannot upgrade from 2.x in Drupal 6 to 3.x in Drupal 7. However
879
 * there are no database changes between the 3.x versions, so no update is
880
 * needed at all to move from 3.x in Drupal 6 to Drupal 7.
881
 */
882
function webform_update_last_removed() {
883
  return 6313;
884
}
885

    
886
/**
887
 * Allow the confirmation format column to have a NULL value.
888
 */
889
function webform_update_7301() {
890
  // These changes are modeled after user_update_7010().
891
  db_change_field('webform', 'confirmation_format', 'confirmation_format', array(
892
    'description' => 'The {filter_format}.format of the confirmation message.',
893
    'type' => 'int',
894
    'unsigned' => TRUE,
895
    'not null' => FALSE,
896
  ));
897
  db_update('webform')
898
    ->fields(array('confirmation_format' => NULL))
899
    ->condition('confirmation', '')
900
    ->condition('confirmation_format', 0)
901
    ->execute();
902
  $existing_formats = db_query("SELECT format FROM {filter_format}")->fetchCol();
903
  $default_format = variable_get('filter_default_format', 1);
904

    
905
  // Since Webform may be updated separately from Drupal core, not all format
906
  // names may be numbers when running this update.
907
  $numeric_formats = array();
908
  foreach ($existing_formats as $format_name) {
909
    if (is_numeric($format_name)) {
910
      $numeric_formats[] = (int) $format_name;
911
    }
912
  }
913

    
914
  $query = db_update('webform')
915
    ->fields(array('confirmation_format' => $default_format))
916
    ->isNotNull('confirmation_format');
917

    
918
  if (!empty($numeric_formats)) {
919
    $query->condition('confirmation_format', $numeric_formats, 'NOT IN');
920
  }
921

    
922
  $query->execute();
923
}
924

    
925
/**
926
 * Add columns for e-mail HTML and attachment settings.
927
 */
928
function webform_update_7302() {
929
  if (!db_field_exists('webform_emails', 'html')) {
930
    db_add_field('webform_emails', 'html', array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'default' => 0, 'not null' => TRUE));
931
    db_add_field('webform_emails', 'attachments', array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'default' => 0, 'not null' => TRUE));
932
  }
933
}
934

    
935
/**
936
 * Set the default for the "submit_notice" column to 1.
937
 */
938
function webform_update_7303() {
939
  db_change_field('webform', 'submit_notice', 'submit_notice', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 1));
940
}
941

    
942
/**
943
 * Add field for block feature and redirection setting.
944
 */
945
function webform_update_7304() {
946
  if (!db_field_exists('webform', 'block')) {
947
    db_add_field('webform', 'block', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0));
948
    db_change_field('webform', 'redirect_url', 'redirect_url', array('type' => 'varchar', 'length' => 255, 'default' => '<confirmation>'));
949
    db_update('webform')
950
      ->fields(array('redirect_url' => 'confirmation'))
951
      ->condition('redirect_url', '')
952
      ->execute();
953
  }
954
}
955

    
956
/**
957
 * Set additional_validate and additional_submit columns to allow NULL.
958
 */
959
function webform_update_7305() {
960
  if (db_field_exists('webform', 'additional_validate')) {
961
    db_change_field('webform', 'additional_validate', 'additional_validate', array('type' => 'text', 'not null' => FALSE));
962
    db_change_field('webform', 'additional_submit', 'additional_submit', array('type' => 'text', 'not null' => FALSE));
963
  }
964
}
965

    
966
/**
967
 * Add column for webform status (open or closed).
968
 */
969
function webform_update_7306() {
970
  if (!db_field_exists('webform', 'status')) {
971
    db_add_field('webform', 'status', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 1));
972
  }
973
}
974

    
975
/**
976
 * Update the confirmation_format column for default text format changes.
977
 */
978
function webform_update_7307() {
979
  // Update removed and moved to webform_update_7301().
980
  // See http://drupal.org/node/976102.
981
}
982

    
983
/**
984
 * Update the confirmation_format column to allow it to store strings.
985
 */
986
function webform_update_7308() {
987
  db_change_field('webform', 'confirmation_format', 'confirmation_format', array(
988
    'description' => 'The {filter_format}.format of the confirmation message.',
989
    'type' => 'varchar',
990
    'length' => 255,
991
    'not null' => FALSE,
992
  ));
993
}
994

    
995
/**
996
 * Add the ability to auto-save as draft between pages.
997
 */
998
function webform_update_7309() {
999
  if (!db_field_exists('webform', 'auto_save')) {
1000
    db_add_field('webform', 'auto_save', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0));
1001
  }
1002
}
1003

    
1004
/**
1005
 * Remove orphaned and unnecessary rows in the webform table.
1006
 */
1007
function webform_update_7310() {
1008
  $result = db_query("SELECT nid FROM {webform} WHERE
1009
    nid NOT IN
1010
    (SELECT DISTINCT(w1.nid) FROM {webform} w1 INNER JOIN {webform_component} wc ON w1.nid = wc.nid)
1011
    AND nid NOT IN
1012
    (SELECT w2.nid FROM {webform} w2 INNER JOIN {node} n ON w2.nid = n.nid WHERE n.type = 'webform')"
1013
  );
1014
  $empty_nids = array();
1015
  foreach ($result as $row) {
1016
    $empty_nids[] = $row->nid;
1017
  }
1018
  if (!empty($empty_nids)) {
1019
    db_delete('webform')->condition('nid', $empty_nids, 'IN')->execute();
1020
  }
1021
}
1022

    
1023
/**
1024
 * Add an index for nid_uid_sid to webform_submissions.
1025
 */
1026
function webform_update_7311() {
1027
  if (!db_index_exists('webform_submissions', 'nid_uid_sid')) {
1028
    db_add_index('webform_submissions', 'nid_uid_sid', array('nid', 'uid', 'sid'));
1029
  }
1030
}
1031

    
1032
/**
1033
 * Remove unused Webform variables.
1034
 */
1035
function webform_update_7312() {
1036
  variable_del('node_types');
1037
  variable_del('components');
1038
}
1039

    
1040
/**
1041
 * Convert the Date component start and end year options to start and end date.
1042
 */
1043
function webform_update_7313() {
1044
  $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
1045
    ->fields('wc')
1046
    ->condition('type', 'date')
1047
    ->execute();
1048
  foreach ($result as $component) {
1049
    $component['extra'] = unserialize($component['extra']);
1050
    if (!isset($component['extra']['start_date']) && !isset($component['end_date'])) {
1051
      foreach (array('year_start' => 'start_date', 'year_end' => 'end_date') as $key => $replacement) {
1052
        $value = isset($component['extra'][$key]) ? trim($component['extra'][$key]) : '';
1053
        // Relative years.
1054
        if (preg_match('/[-+][ ]*[0-9]+/', $value)) {
1055
          $component['extra'][$replacement] = ($value == 1) ? ($value . ' year') : ($value . ' years');
1056
        }
1057
        // Absolute years.
1058
        elseif (is_numeric($value)) {
1059
          $component['extra'][$replacement] = 'Dec 31 ' . $value;
1060
        }
1061
        unset($component['extra'][$key]);
1062
      }
1063
      $component['extra'] = serialize($component['extra']);
1064
      drupal_write_record('webform_component', $component, array('nid', 'cid'));
1065
    }
1066
  }
1067
}
1068

    
1069
/**
1070
 * Add webform_last_download table to store last downloaded sid per user.
1071
 */
1072
function webform_update_7314() {
1073
  // Safety check to prevent recreating the webform_last_download table.
1074
  if (db_table_exists('webform_last_download')) {
1075
    return;
1076
  }
1077

    
1078
  $schema['webform_last_download'] = array(
1079
    'description' => 'Stores last submission number per user download.',
1080
    'fields' => array(
1081
      'nid' => array(
1082
        'description' => 'The node identifier of a webform.',
1083
        'type' => 'int',
1084
        'unsigned' => TRUE,
1085
        'not null' => TRUE,
1086
        'default' => 0,
1087
      ),
1088
      'uid' => array(
1089
        'description' => 'The user identifier.',
1090
        'type' => 'int',
1091
        'unsigned' => TRUE,
1092
        'not null' => TRUE,
1093
        'default' => 0,
1094
      ),
1095
      'sid' => array(
1096
        'description' => 'The last downloaded submission number.',
1097
        'type' => 'int',
1098
        'unsigned' => TRUE,
1099
        'not null' => TRUE,
1100
        'default' => 0,
1101
      ),
1102
    ),
1103
    'primary key' => array('nid', 'uid'),
1104
  );
1105
  db_create_table('webform_last_download', $schema['webform_last_download']);
1106
}
1107

    
1108
/**
1109
 * Add column for timestamp of last requested CSV download.
1110
 */
1111
function webform_update_7315() {
1112
  if (!db_field_exists('webform_last_download', 'requested')) {
1113
    db_add_field('webform_last_download', 'requested', array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0));
1114
  }
1115
}
1116

    
1117
/**
1118
 * Add additional columns for total submission limit.
1119
 */
1120
function webform_update_7316() {
1121
  if (!db_field_exists('webform', 'total_submit_limit')) {
1122
    db_add_field('webform', 'total_submit_limit', array('type' => 'int', 'not null' => TRUE, 'default' => -1));
1123
  }
1124

    
1125
  if (!db_field_exists('webform', 'total_submit_interval')) {
1126
    db_add_field('webform', 'total_submit_interval', array('type' => 'int', 'not null' => TRUE, 'default' => -1));
1127
  }
1128
}
1129

    
1130
/**
1131
 * Add an index for 'nid_sid' to webform_submissions.
1132
 */
1133
function webform_update_7317() {
1134
  // Even though we already have an index 'nid_uid_sid', adding the index for
1135
  // 'nid_sid' saves us a tablesort on the node/x/webform-results page.
1136
  if (!db_index_exists('webform_submissions', 'nid_sid')) {
1137
    db_add_index('webform_submissions', 'nid_sid', array('nid', 'sid'));
1138
  }
1139
}
1140

    
1141
/**
1142
 * Upgrade file components to support the new AJAX-upload element.
1143
 */
1144
function webform_update_7318() {
1145
  $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
1146
    ->fields('wc')
1147
    ->condition('type', 'file')
1148
    ->execute();
1149
  foreach ($result as $component) {
1150
    $component['extra'] = unserialize($component['extra']);
1151
    if (!isset($component['extra']['directory'])) {
1152
      $component['extra']['directory'] = $component['extra']['savelocation'];
1153
      $component['extra']['scheme'] = file_default_scheme();
1154
      $component['extra']['filtering']['size'] = $component['extra']['filtering']['size'] . ' KB';
1155
      unset($component['extra']['savelocation']);
1156
      $component['extra'] = serialize($component['extra']);
1157
      drupal_write_record('webform_component', $component, array('nid', 'cid'));
1158
    }
1159
  }
1160

    
1161
  return t('File components updated to support AJAX uploading.');
1162
}
1163

    
1164
/**
1165
 * Add file usage entries for all files uploaded through Webform.
1166
 */
1167
function webform_update_7319(&$sandbox) {
1168
  if (!isset($sandbox['progress'])) {
1169
    // Initialize batch update information.
1170
    $sandbox['progress'] = 0;
1171
    $sandbox['last_fid_processed'] = -1;
1172
    $sandbox['max'] = db_select('file_managed')
1173
      ->condition('uri', '%' . db_like('://webform/') . '%', 'LIKE')
1174
      ->countQuery()
1175
      ->execute()
1176
      ->fetchField();
1177
  }
1178

    
1179
  // Process all files attached to a given revision during the same batch.
1180
  $limit = webform_variable_get('webform_update_batch_size');
1181
  $files = db_select('file_managed', 'f')
1182
    ->fields('f')
1183
    ->condition('uri', '%' . db_like('://webform/') . '%', 'LIKE')
1184
    ->condition('fid', $sandbox['last_fid_processed'], '>')
1185
    ->orderBy('fid', 'ASC')
1186
    ->range(0, $limit)
1187
    ->execute()
1188
    ->fetchAllAssoc('fid', PDO::FETCH_ASSOC);
1189

    
1190
  // Determine each submission with which a file is associated.
1191
  if (!empty($files)) {
1192
    foreach ($files as $fid => $file) {
1193
      $file = (object) $file;
1194
      $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);
1195
      foreach ($sids as $sid => $row) {
1196
        // We use a db_merge() instead of file_usage_add() to prevent problems
1197
        // in the event this update was run twice. No file provided by Webform
1198
        // should ever be in use more than once at this point.
1199
        db_merge('file_usage')
1200
          ->key(array(
1201
            'fid' => $file->fid,
1202
            'type' => 'submission',
1203
            'module' => 'webform',
1204
            'id' => $sid,
1205
          ))
1206
          ->fields(array(
1207
            'count' => 1,
1208
          ))
1209
          ->execute();
1210
      }
1211

    
1212
      // Update our progress information for the batch update.
1213
      $sandbox['progress']++;
1214
      $sandbox['last_fid_processed'] = $file->fid;
1215
    }
1216
  }
1217

    
1218
  // If less than limit was processed, the update process is finished.
1219
  if (count($files) < $limit || $sandbox['progress'] == $sandbox['max']) {
1220
    $finished = TRUE;
1221
  }
1222

    
1223
  // If there's no max value then there's nothing to update and we're finished.
1224
  if (empty($sandbox['max']) || isset($finished)) {
1225
    return t('Webform file entries created in the file_usage table.');
1226
  }
1227
  else {
1228
    // Indicate our current progress to the batch update system.
1229
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
1230
  }
1231
}
1232

    
1233
/**
1234
 * Mark files uploaded through Webform that report active usage permanent.
1235
 */
1236
function webform_update_7320() {
1237
  db_query("UPDATE {file_managed} SET status = 1 WHERE fid IN (SELECT fid FROM {file_usage} WHERE module = :module_name)", array(':module_name' => 'webform'));
1238
}
1239

    
1240
/**
1241
 * Rewrite token replacement system to use D7 tokens.
1242
 *
1243
 * If needed, please download and install the Token module from drupal.org.
1244
 * Otherwise some tokens will not be rendered.
1245
 */
1246
function webform_update_7401(&$sandbox) {
1247
  // Define replacements.
1248
  $patterns = array(
1249
    '%username',
1250
    '%useremail',
1251
    '%uid',
1252
    '%date',
1253
    '%ip_address',
1254
    '%site',
1255
    '%nid',
1256
    '%title',
1257
    '%email_values',
1258
    '%submission_url',
1259
    '%sid',
1260
    '%server[REQUEST_URI]',
1261
    // Used to convert nested arrays of %value and %email.
1262
    '][',
1263
  );
1264
  $replacements = array(
1265
    '[current-user:name]',
1266
    '[current-user:mail]',
1267
    '[current-user:uid]',
1268
    '[submission:date:long]',
1269
    '[current-user:ip-address]',
1270
    '[site:name]',
1271
    '[node:nid]',
1272
    '[node:title]',
1273
    '[submission:values]',
1274
    '[submission:url]',
1275
    '[submission:sid]',
1276
    '[current-page:url]',
1277
    // Replace "][" with ":" for %value and %email.
1278
    ':',
1279
  );
1280
  $dpatterns = array(
1281
    '/%get\[([^\]]+)\]/m',
1282
    '/%email\[([^% \n\r\t]+)?\]/m',
1283
    '/%value\[([^% \n\r\t]+)?\]/m',
1284
    '/%profile\[([^\]]+)\]/m',
1285
  );
1286
  $dreplacements = array(
1287
    '[current-page:query:$1]',
1288
    '[submission:values:$1]',
1289
    '[submission:values:$1:nolabel]',
1290
    '[current-user:$1]',
1291
  );
1292

    
1293
  $limit = webform_variable_get('webform_update_batch_size');
1294
  $processed_count = _webform_update_7401_batch($sandbox, $patterns, $replacements, $dpatterns, $dreplacements, $limit);
1295

    
1296
  // If less than limit was processed, the update process is finished.
1297
  if ($processed_count < $limit || $sandbox['progress'] == $sandbox['max']) {
1298
    $finished = TRUE;
1299
  }
1300

    
1301
  // If there's no max value then there's nothing to update and we're finished.
1302
  if (empty($sandbox['max']) || isset($finished)) {
1303
    $message = t('Your existing webforms have been upgraded to use the global Drupal 7 token system.');
1304
    if (!module_exists('token')) {
1305
      $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>';
1306
    }
1307
    return $message;
1308
  }
1309
  else {
1310
    // Indicate our current progress to the batch update system.
1311
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
1312
  }
1313
}
1314

    
1315
/**
1316
 * Utility function to update all the locations that use tokens.
1317
 */
1318
function _webform_update_7401_batch(&$sandbox, $patterns, $replacements, $dpatterns, $dreplacements, $limit) {
1319
  // Set up the initial batch process.
1320
  if (!isset($sandbox['progress'])) {
1321
    $sandbox['progress'] = 0;
1322
    $sandbox['last_nid_processed'] = -1;
1323
    $sandbox['max'] = db_select('webform')
1324
      ->countQuery()
1325
      ->execute()
1326
      ->fetchField();
1327

    
1328
    // Update tokens in variables.
1329
    $variables = array(
1330
      'webform_default_subject',
1331
      'webform_default_from_name',
1332
      'webform_default_from_address',
1333
    );
1334
    foreach ($variables as $variable) {
1335
      $value = variable_get($variable, NULL);
1336
      if ($value !== NULL) {
1337
        $value = str_replace($patterns, $replacements, $value);
1338
        $value = preg_replace($dpatterns, $dreplacements, $value);
1339
        variable_set($variable, $value);
1340
      }
1341
    }
1342
  }
1343

    
1344
  $webforms = db_select('webform', 'w')
1345
    ->fields('w')
1346
    ->condition('nid', $sandbox['last_nid_processed'], '>')
1347
    ->orderBy('nid', 'ASC')
1348
    ->range(0, $limit)
1349
    ->execute()
1350
    ->fetchAllAssoc('nid', PDO::FETCH_ASSOC);
1351

    
1352
  foreach ($webforms as $nid => $webform) {
1353
    // Update the webform record itself.
1354
    $original = $webform;
1355
    $parts = array(
1356
      'confirmation',
1357
      'redirect_url',
1358
    );
1359
    foreach ($parts as $part) {
1360
      $webform[$part] = str_replace($patterns, $replacements, $webform[$part]);
1361
      $webform[$part] = preg_replace($dpatterns, $dreplacements, $webform[$part]);
1362
    }
1363
    if ($webform != $original) {
1364
      drupal_write_record('webform', $webform, array('nid'));
1365
    }
1366

    
1367
    // Update tokens in component configurations.
1368
    $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
1369
      ->fields('wc')
1370
      ->condition('wc.nid', $nid)
1371
      ->execute();
1372
    foreach ($result as $component) {
1373
      $original_extra = $component['extra'];
1374
      $original_value = $component['value'];
1375
      $component['extra'] = unserialize($component['extra']);
1376
      if (isset($component['extra']['description'])) {
1377
        $description = str_replace($patterns, $replacements, $component['extra']['description']);
1378
        $description = preg_replace($dpatterns, $dreplacements, $description);
1379
        $component['extra']['description'] = $description;
1380
      }
1381
      $component['extra'] = serialize($component['extra']);
1382
      $value = str_replace($patterns, $replacements, $component['value']);
1383
      $value = preg_replace($dpatterns, $dreplacements, $value);
1384
      $component['value'] = $value;
1385
      if ($component['extra'] != $original_extra || $component['value'] != $original_value) {
1386
        drupal_write_record('webform_component', $component, array('nid', 'cid'));
1387
      }
1388
    }
1389

    
1390
    // Update tokens in e-mail configurations.
1391
    $result = db_select('webform_emails', 'we', array('fetch' => PDO::FETCH_ASSOC))
1392
      ->fields('we')
1393
      ->condition('we.nid', $nid)
1394
      ->execute();
1395
    foreach ($result as $email) {
1396
      $parts = array(
1397
        'template',
1398
        'subject',
1399
        'from_name',
1400
      );
1401
      $original = $email;
1402
      foreach ($parts as $part) {
1403
        $email[$part] = str_replace($patterns, $replacements, $email[$part]);
1404
        $email[$part] = preg_replace($dpatterns, $dreplacements, $email[$part]);
1405
      }
1406
      if ($email != $original) {
1407
        drupal_write_record('webform_emails', $email, array('nid', 'eid'));
1408
      }
1409
    }
1410

    
1411
    // Update the last processed NID.
1412
    $sandbox['last_nid_processed'] = $nid;
1413
    $sandbox['progress']++;
1414
  }
1415
  return count($webforms);
1416
}
1417

    
1418
/**
1419
 * Add the webform_conditional database table.
1420
 */
1421
function webform_update_7402() {
1422
  // Sanity checks.
1423
  if (db_table_exists('webform_conditional')) {
1424
    // Both tables exist, so these are Webform-core provided tables.
1425
    if (db_table_exists('webform_conditional_rules')) {
1426
      return;
1427
    }
1428
    // Webform Conditional module was installed previously but not uninstalled.
1429
    else {
1430
      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".'));
1431
    }
1432
  }
1433

    
1434
  $schema['webform_conditional'] = array(
1435
    'description' => 'Holds information about conditional logic.',
1436
    'fields' => array(
1437
      'nid' => array(
1438
        'description' => 'The node identifier of a webform.',
1439
        'type' => 'int',
1440
        'unsigned' => TRUE,
1441
        'not null' => TRUE,
1442
        'default' => 0,
1443
      ),
1444
      'rgid' => array(
1445
        'description' => 'The rule group identifier for this group of rules.',
1446
        'type' => 'int',
1447
        'size' => 'small',
1448
        'unsigned' => TRUE,
1449
        'not null' => TRUE,
1450
        'default' => 0,
1451
      ),
1452
      'andor' => array(
1453
        'description' => 'Whether to AND or OR the actions in this group. All actions within the same crid should have the same andor value.',
1454
        'type' => 'varchar',
1455
        'length' => 128,
1456
      ),
1457
      'action' => array(
1458
        '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".',
1459
        'type' => 'varchar',
1460
        'length' => 128,
1461
      ),
1462
      'target_type' => array(
1463
        'description' => 'The type of target to be affected. Either "component" or "email". Indicates what type of ID the "target" column contains.',
1464
        'type' => 'varchar',
1465
        'length' => 128,
1466
      ),
1467
      'target' => array(
1468
        'description' => 'The ID of the target to be affected. Typically a component ID.',
1469
        'type' => 'varchar',
1470
        'length' => 128,
1471
      ),
1472
      'weight' => array(
1473
        'description' => 'Determines the position of this conditional compared to others.',
1474
        'type' => 'int',
1475
        'size' => 'small',
1476
        'not null' => TRUE,
1477
        'default' => 0,
1478
      ),
1479
    ),
1480
    'primary key' => array('nid', 'rgid'),
1481
  );
1482

    
1483
  $schema['webform_conditional_rules'] = array(
1484
    'description' => 'Holds information about conditional logic.',
1485
    'fields' => array(
1486
      'nid' => array(
1487
        'description' => 'The node identifier of a webform.',
1488
        'type' => 'int',
1489
        'unsigned' => TRUE,
1490
        'not null' => TRUE,
1491
        'default' => 0,
1492
      ),
1493
      'rgid' => array(
1494
        'description' => 'The rule group identifier for this group of rules.',
1495
        'type' => 'int',
1496
        'size' => 'small',
1497
        'unsigned' => TRUE,
1498
        'not null' => TRUE,
1499
        'default' => 0,
1500
      ),
1501
      'rid' => array(
1502
        'description' => 'The rule identifier for this conditional rule.',
1503
        'type' => 'int',
1504
        'size' => 'small',
1505
        'unsigned' => TRUE,
1506
        'not null' => TRUE,
1507
        'default' => 0,
1508
      ),
1509
      'source_type' => array(
1510
        'description' => 'The type of source on which the conditional is based. Currently always "component". Indicates what type of ID the "source" column contains.',
1511
        'type' => 'varchar',
1512
        'length' => 128,
1513
      ),
1514
      'source' => array(
1515
        'description' => 'The component ID being used in this condition.',
1516
        'type' => 'int',
1517
        'size' => 'small',
1518
        'unsigned' => TRUE,
1519
        'not null' => TRUE,
1520
        'default' => 0,
1521
      ),
1522
      'operator' => array(
1523
        'description' => 'Which operator (equal, contains, starts with, etc.) should be used for this comparison between the source and value?',
1524
        'type' => 'varchar',
1525
        'length' => 128,
1526
      ),
1527
      'value' => array(
1528
        'description' => 'The value to be compared with source.',
1529
        'type' => 'text',
1530
      ),
1531
    ),
1532
    'primary key' => array('nid', 'rgid', 'rid'),
1533
  );
1534

    
1535
  db_create_table('webform_conditional', $schema['webform_conditional']);
1536
  db_create_table('webform_conditional_rules', $schema['webform_conditional_rules']);
1537
  // Rebuild schema so that webform_update_7403() can use drupal_write_record().
1538
  if (db_table_exists('system') && db_field_exists('system', 'status')) {
1539
    drupal_get_schema(NULL, TRUE);
1540
  }
1541
}
1542

    
1543
/**
1544
 * Convert per-component conditionals to new more flexible conditional system.
1545
 */
1546
function webform_update_7403(&$sandbox) {
1547
  // Set up the initial batch process.
1548
  if (!isset($sandbox['progress'])) {
1549
    $sandbox['progress'] = 0;
1550
    $sandbox['last_nid_processed'] = -1;
1551
    $sandbox['converted_count'] = 0;
1552
    $sandbox['max'] = db_select('webform')
1553
      ->countQuery()
1554
      ->execute()
1555
      ->fetchField();
1556
  }
1557

    
1558
  $limit = webform_variable_get('webform_update_batch_size');
1559
  $webforms = db_select('webform', 'w')
1560
    ->fields('w')
1561
    ->condition('nid', $sandbox['last_nid_processed'], '>')
1562
    ->orderBy('nid', 'ASC')
1563
    ->range(0, $limit)
1564
    ->execute()
1565
    ->fetchAllAssoc('nid', PDO::FETCH_ASSOC);
1566

    
1567
  foreach ($webforms as $nid => $webform) {
1568
    // Update tokens in component configurations.
1569
    $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
1570
      ->fields('wc')
1571
      ->condition('wc.nid', $nid)
1572
      ->execute();
1573
    $rgid = 0;
1574
    foreach ($result as $component) {
1575
      // For each component, check if it has conditional properties that need
1576
      // to be removed and/or migrated. Because these properties may be in any
1577
      // order, copy the original extra array for comparison.
1578
      $component['extra'] = unserialize($component['extra']);
1579
      $original_extra = $component['extra'];
1580

    
1581
      // Remove conditional properties if present.
1582
      if (isset($component['extra']['conditional_component'])) {
1583
        unset($component['extra']['conditional_component']);
1584
      }
1585
      if (isset($component['extra']['conditional_operator'])) {
1586
        unset($component['extra']['conditional_operator']);
1587
      }
1588
      if (isset($component['extra']['conditional_values'])) {
1589
        unset($component['extra']['conditional_values']);
1590

    
1591
        // If the component has conditional values specified, that indicates
1592
        // that this component was conditionally shown. Convert it to a new
1593
        // conditional with multiple rules if needed.
1594
        if (strlen(trim($original_extra['conditional_values'])) && !empty($original_extra['conditional_operator']) && !empty($original_extra['conditional_component'])) {
1595
          $conditional_values = explode("\n", $original_extra['conditional_values']);
1596
          $rules = array();
1597
          $rule = array(
1598
            'nid' => $nid,
1599
            'rgid' => $rgid,
1600
            'rid' => NULL,
1601
            'source_type' => 'component',
1602
            'source' => $original_extra['conditional_component'],
1603
            'operator' => 'equal',
1604
            'value' => NULL,
1605
          );
1606
          foreach ($conditional_values as $value) {
1607
            $value = trim($value);
1608
            if ($value) {
1609
              $new_rule = $rule;
1610
              $new_rule['rid'] = count($rules);
1611
              $new_rule['value'] = $value;
1612
              $rules[] = $new_rule;
1613
            }
1614
          }
1615
          if (count($rules)) {
1616
            $conditional = array(
1617
              'nid' => $nid,
1618
              'rgid' => $rgid,
1619
              'andor' => 'or',
1620
              'action' => ($original_extra['conditional_operator'] === '=') ? 'show' : 'hide',
1621
              'target_type' => 'component',
1622
              'target' => $component['cid'],
1623
              'weight' => 0,
1624
            );
1625

    
1626
            // Cannot use drupal_write_record for webform_conditional because
1627
            // the current schema has fewer fields than the schema in use during
1628
            // this hook_update_N function.
1629
            db_insert('webform_conditional')
1630
              ->fields($conditional)
1631
              ->execute();
1632
            foreach ($rules as $rule) {
1633
              drupal_write_record('webform_conditional_rules', $rule);
1634
            }
1635
            $sandbox['converted_count']++;
1636
            $rgid++;
1637
          }
1638
        }
1639
      }
1640

    
1641
      // Update the component with the conditional properties removed.
1642
      if ($component['extra'] != $original_extra) {
1643
        $component['extra'] = serialize($component['extra']);
1644
        drupal_write_record('webform_component', $component, array('nid', 'cid'));
1645
      }
1646
    }
1647

    
1648
    // Update the last processed NID.
1649
    $sandbox['last_nid_processed'] = $nid;
1650
    $sandbox['progress']++;
1651
  }
1652

    
1653
  // If less than limit was processed, the update process is finished.
1654
  if (count($webforms) < $limit || $sandbox['progress'] == $sandbox['max']) {
1655
    $finished = TRUE;
1656
  }
1657

    
1658
  // If there's no max value then there's nothing to update and we're finished.
1659
  if (empty($sandbox['max']) || isset($finished)) {
1660
    return t('@count webforms using conditionals updated to the new conditional system.', array('@count' => $sandbox['converted_count']));
1661
  }
1662
  else {
1663
    // Indicate our current progress to the batch update system.
1664
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
1665
  }
1666
}
1667

    
1668
/**
1669
 * Remove files left over from deleted submissions.
1670
 *
1671
 * Such files are now deleted automatically.
1672
 */
1673
function webform_update_7404() {
1674
  module_load_include('inc', 'webform', 'components/file');
1675
  $fids = db_query('SELECT fid FROM {file_usage} WHERE module = \'webform\' AND type = \'submission\' AND NOT id IN(SELECT sid FROM {webform_submissions})')->fetchCol();
1676
  foreach ($fids as $fid) {
1677
    _webform_delete_file(NULL, array($fid));
1678
  }
1679
}
1680

    
1681
/**
1682
 * Add an "extra" column to the e-mail table for for non-queryable options.
1683
 */
1684
function webform_update_7405() {
1685
  $schema = array(
1686
    '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.',
1687
    'type' => 'text',
1688
    'not null' => TRUE,
1689
    'initial' => '',
1690
  );
1691
  if (!db_field_exists('webform_emails', 'extra')) {
1692
    db_add_field('webform_emails', 'extra', $schema);
1693
  }
1694
}
1695

    
1696
/**
1697
 * Convert the "webform_use_cookies" setting to "webform_tracking_mode".
1698
 */
1699
function webform_update_7406() {
1700
  // Previously, we only had "strict" and "ip_address" checking. Using cookies
1701
  // meant cookies in addition to IP address.
1702
  $use_cookies = variable_get('webform_use_cookies');
1703
  if (isset($use_cookies)) {
1704
    variable_set('webform_tracking_mode', $use_cookies ? 'strict' : 'ip_address');
1705
    variable_del('webform_use_cookies');
1706
  }
1707
}
1708

    
1709
/**
1710
 * Remove orphaned conditional rules.
1711
 */
1712
function webform_update_7407() {
1713
  // Delete entire conditions for deleted components that were the target.
1714
  // This query is complicated by database compatibility since we're joining two
1715
  // non-matching columns. See http://drupal.org/node/2026891.
1716
  $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");
1717
  foreach ($result as $row) {
1718
    db_delete('webform_conditional')
1719
      ->condition('nid', $row->nid)
1720
      ->condition('rgid', $row->rgid)
1721
      ->execute();
1722
    db_delete('webform_conditional_rules')
1723
      ->condition('nid', $row->nid)
1724
      ->condition('rgid', $row->rgid)
1725
      ->execute();
1726
  }
1727
  // Delete conditional rules for deleted components that were the source.
1728
  $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");
1729
  foreach ($result as $row) {
1730
    db_delete('webform_conditional_rules')
1731
      ->condition('nid', $row->nid)
1732
      ->condition('rgid', $row->rgid)
1733
      ->condition('rid', $row->rid)
1734
      ->execute();
1735
  }
1736
  // Delete any conditions that no longer have any rules left in them.
1737
  $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");
1738
  foreach ($result as $row) {
1739
    db_delete('webform_conditional')
1740
      ->condition('nid', $row->nid)
1741
      ->condition('rgid', $row->rgid)
1742
      ->execute();
1743
  }
1744
}
1745

    
1746
/**
1747
 * Rename the "mandatory" column to "required".
1748
 */
1749
function webform_update_7408() {
1750
  $spec = array(
1751
    'description' => 'Boolean flag for if this component is required.',
1752
    'type' => 'int',
1753
    'size' => 'tiny',
1754
    'not null' => TRUE,
1755
    'default' => 0,
1756
  );
1757
  if (!db_field_exists('webform_component', 'required')) {
1758
    db_change_field('webform_component', 'mandatory', 'required', $spec);
1759
  }
1760
}
1761

    
1762
/**
1763
 * Add progress bar columns to the webform table.
1764
 */
1765
function webform_update_7409() {
1766
  if (!db_field_exists('webform', 'progressbar_bar')) {
1767
    $int_schema = array(
1768
      'type' => 'int',
1769
      'size' => 'tiny',
1770
      'not null' => TRUE,
1771
      'default' => 0,
1772
    );
1773
    $varchar_schema = array(
1774
      'type' => 'varchar',
1775
      'length' => 255,
1776
    );
1777

    
1778
    $int_schema['description'] = 'Boolean value indicating if the bar should be shown as part of the progress bar.';
1779
    db_add_field('webform', 'progressbar_bar', $int_schema);
1780

    
1781
    $int_schema['description'] = 'Boolean value indicating if the page number should be shown as part of the progress bar.';
1782
    db_add_field('webform', 'progressbar_page_number', $int_schema);
1783

    
1784
    $int_schema['description'] = 'Boolean value indicating if the percentage complete should be shown as part of the progress bar.';
1785
    db_add_field('webform', 'progressbar_percent', $int_schema);
1786

    
1787
    $int_schema['description'] = 'Boolean value indicating if the pagebreak labels should be included as part of the progress bar.';
1788
    db_add_field('webform', 'progressbar_pagebreak_labels', $int_schema);
1789

    
1790
    $int_schema['description'] = 'Boolean value indicating if the confirmation page should count as a page in the progress bar.';
1791
    db_add_field('webform', 'progressbar_include_confirmation', $int_schema);
1792

    
1793
    $varchar_schema['description'] = 'Label for the first page of the progress bar.';
1794
    db_add_field('webform', 'progressbar_label_first', $varchar_schema);
1795

    
1796
    $varchar_schema['description'] = 'Label for the last page of the progress bar.';
1797
    db_add_field('webform', 'progressbar_label_confirmation', $varchar_schema);
1798

    
1799
    return t('New webform columns added.');
1800
  }
1801
}
1802

    
1803
/**
1804
 * Remove the "teaser" column from the "webform" table.
1805
 */
1806
function webform_update_7410() {
1807
  if (db_field_exists('webform', 'teaser')) {
1808
    db_drop_field('webform', 'teaser');
1809
    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.');
1810
  }
1811
}
1812

    
1813
/**
1814
 * Remove [submission:values:x] token use of :nolabel and add :withlabel.
1815
 */
1816
function webform_update_7411(&$sandbox) {
1817
  // Define replacements.
1818
  $patterns = array();
1819
  $replacements = array();
1820
  $dpatterns = array(
1821
    '/\[submission:values(:(?!nolabel)[a-z_]+)+\]/m',
1822
    '/\[submission:values(:([a-z_:]+))?:nolabel\]/m',
1823
  );
1824
  $dreplacements = array(
1825
    '[submission:values$1:withlabel]',
1826
    '[submission:values$1]',
1827
  );
1828

    
1829
  $limit = webform_variable_get('webform_update_batch_size');
1830
  $processed_count = _webform_update_7401_batch($sandbox, $patterns, $replacements, $dpatterns, $dreplacements, $limit);
1831

    
1832
  // If less than limit was processed, the update process is finished.
1833
  if ($processed_count < $limit || $sandbox['progress'] == $sandbox['max']) {
1834
    $finished = TRUE;
1835
  }
1836

    
1837
  // If there's no max value then there's nothing to update and we're finished.
1838
  if (empty($sandbox['max']) || isset($finished)) {
1839
    return t('Replaced tokens using [submission:values:x] with [submission:values:x:withlabel].');
1840
  }
1841
  else {
1842
    // Indicate our current progress to the batch update system.
1843
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
1844
  }
1845
}
1846

    
1847
/**
1848
 * Split webform_node_types as content type specific.
1849
 */
1850
function webform_update_7412() {
1851
  $types = variable_get('webform_node_types', array('webform'));
1852
  foreach ($types as $type) {
1853
    variable_set('webform_node_' . $type, TRUE);
1854
  }
1855
  variable_del('webform_node_types');
1856
}
1857

    
1858
/**
1859
 * Add preview page columns to the webform table.
1860
 */
1861
function webform_update_7413() {
1862
  if (!db_field_exists('webform', 'preview')) {
1863
    $int_schema = array(
1864
      'type' => 'int',
1865
      'size' => 'tiny',
1866
      'not null' => TRUE,
1867
      'default' => 0,
1868
    );
1869
    $varchar_schema = array(
1870
      'type' => 'varchar',
1871
      'length' => 255,
1872
    );
1873
    $text_schema = array(
1874
      'type' => 'text',
1875
      'not null' => TRUE,
1876
      'initial' => '',
1877
    );
1878

    
1879
    $int_schema['description'] = 'Boolean value indicating if this form includes a page for previewing the submission.';
1880
    db_add_field('webform', 'preview', $int_schema);
1881

    
1882
    $varchar_schema['description'] = 'The text for the button that will proceed to the preview page.';
1883
    db_add_field('webform', 'preview_next_button_label', $varchar_schema);
1884

    
1885
    $varchar_schema['description'] = 'The text for the button to go backwards from the preview page.';
1886
    db_add_field('webform', 'preview_prev_button_label', $varchar_schema);
1887

    
1888
    $varchar_schema['description'] = 'The title of the preview page, as used by the progress bar.';
1889
    db_add_field('webform', 'preview_title', $varchar_schema);
1890

    
1891
    $text_schema['description'] = 'Text shown on the preview page of the form.';
1892
    db_add_field('webform', 'preview_message', $text_schema);
1893

    
1894
    $varchar_schema['description'] = 'The {filter_format}.format of the preview page message.';
1895
    db_add_field('webform', 'preview_message_format', $varchar_schema);
1896

    
1897
    $text_schema['description'] = 'Comma-separated list of component IDs that should not be included in this form\'s confirmation page.';
1898
    db_add_field('webform', 'preview_excluded_components', $text_schema);
1899

    
1900
    return t('New webform columns added.');
1901
  }
1902
}
1903

    
1904
/**
1905
 * Change email subject, from_name and from_address fields to type 'TEXT'.
1906
 */
1907
function webform_update_7414() {
1908
  $schema = array(
1909
    'subject' => array(
1910
      '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.',
1911
      'type' => 'text',
1912
      'not null' => FALSE,
1913
    ),
1914
    'from_name' => array(
1915
      '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.',
1916
      'type' => 'text',
1917
      'not null' => FALSE,
1918
    ),
1919
    'from_address' => array(
1920
      '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.',
1921
      'type' => 'text',
1922
      'not null' => FALSE,
1923
    ),
1924
  );
1925

    
1926
  db_change_field('webform_emails', 'subject', 'subject', $schema['subject']);
1927
  db_change_field('webform_emails', 'from_name', 'from_name', $schema['from_name']);
1928
  db_change_field('webform_emails', 'from_address', 'from_address', $schema['from_address']);
1929

    
1930
  return t('Custom email columns successfully changed.');
1931
}
1932

    
1933
/**
1934
 * Increase length of webform.redirect_url.
1935
 */
1936
function webform_update_7415() {
1937
  $spec = array(
1938
    'description' => 'The URL a user is redirected to after submitting a form.',
1939
    'type' => 'varchar',
1940
    'length' => 2048,
1941
    'default' => '<confirmation>',
1942
  );
1943
  db_change_field('webform', 'redirect_url', 'redirect_url', $spec);
1944
}
1945

    
1946
/**
1947
 * Add columns for serial numbered submissions. Add serial numbers to existing submissions.
1948
 */
1949
function webform_update_7416() {
1950
  // SQL database implementations vary in how DDL statements are handled within
1951
  // transactions. Since this update routine should be run in maintenance mode
1952
  // without concurrent transactions, no DDL statements are executed within a
1953
  // transaction.
1954
  // Add next_serial column to webform.
1955
  $spec = array(
1956
    'description' => 'The serial number to give to the next submission to this webform.',
1957
    'type' => 'int',
1958
    'unsigned' => TRUE,
1959
    'not null' => TRUE,
1960
    'default' => 1,
1961
  );
1962
  if (!db_field_exists('webform', 'next_serial')) {
1963
    db_add_field('webform', 'next_serial', $spec);
1964
  }
1965

    
1966
  // Add serial column to webform_submissions.
1967
  $spec = array(
1968
    'description' => 'The serial number of this submission.',
1969
    'type' => 'int',
1970
    'unsigned' => TRUE,
1971
  );
1972
  if (!db_field_exists('webform_submissions', 'serial')) {
1973
    db_add_field('webform_submissions', 'serial', $spec);
1974
  }
1975

    
1976
  // Begin a transaction for updating the serial numbers. The transaction will
1977
  // commit when $transaction is unset or goes out-of-scope.
1978
  $transaction = db_transaction();
1979

    
1980
  // Delete stray entries from the Webform tables before adding serial numbers.
1981
  db_query("DELETE FROM {webform_submissions} WHERE nid NOT IN (SELECT nid FROM {webform})");
1982
  db_query("DELETE FROM {webform_submitted_data} WHERE nid NOT IN (SELECT nid FROM {webform})");
1983

    
1984
  // Add serial numbers to all submissions.
1985
  // Repeat for every Webform.
1986
  $nids = db_select('webform', 'w')
1987
    ->fields('w', array('nid'))
1988
    ->execute();
1989
  while ($nid = $nids->fetchColumn()) {
1990
    $serial = 1;
1991
    // Repeat for every submission in this Webform.
1992
    $sids = db_select('webform_submissions', 'ws')
1993
      ->forUpdate()
1994
      ->fields('ws', array('sid'))
1995
      ->condition('nid', $nid)
1996
      ->orderBy('sid')
1997
      ->execute();
1998
    while ($sid = $sids->fetchColumn()) {
1999
      // Set the serial number.
2000
      db_update('webform_submissions')
2001
        ->fields(array('serial' => $serial))
2002
        ->condition('nid', $nid)
2003
        ->condition('sid', $sid)
2004
        ->execute();
2005
      $serial++;
2006
    }
2007
    // Set the next serial number.
2008
    db_update('webform')
2009
      ->fields(array('next_serial' => $serial))
2010
      ->condition('nid', $nid)
2011
      ->execute();
2012
  }
2013
  // Commit the transaction.
2014
  unset($transaction);
2015

    
2016
  // Now that every submission has a serial number, make serial numbers required.
2017
  $spec['not null'] = TRUE;
2018
  $keys = array();
2019
  // Create a unique index if it does not already exist.
2020
  if (!db_index_exists('webform_submissions', 'nid_serial')) {
2021
    $keys = array(
2022
      'unique keys' => array(
2023
        'nid_serial' => array('nid', 'serial'),
2024
      ),
2025
    );
2026
  }
2027
  db_change_field('webform_submissions', 'serial', 'serial', $spec, $keys);
2028

    
2029
  return t('Columns for serial numbered submissions successfully added. Serial numbers added to existing submissions.');
2030
}
2031

    
2032
/**
2033
 * Change webform_component.name to text to allow for longer labels.
2034
 */
2035
function webform_update_7417() {
2036
  // This update function was introduced in 7.x-4.0rc1, which was the first
2037
  // release to require views. While the views requirement was added to the
2038
  // .info file, and while subsequently hook_requirements() added a check for
2039
  // views, it is possible to arrive at this update function via drush updatedb
2040
  // without views enabled.
2041
  $result = NULL;
2042
  if (!module_exists('views')) {
2043
    if (module_enable(array('views'))) {
2044
      $result = t('The Views module was automatically enabled.');
2045
    }
2046
    else {
2047
      throw new DrupalUpdateException(t('UPDATE ERROR: The Views module must be downloaded and enabled before Webform updates can proceed.'));
2048
    }
2049
  }
2050

    
2051
  db_change_field('webform_component', 'name', 'name', array('type' => 'text', 'not null' => TRUE));
2052
  return $result;
2053
}
2054

    
2055
/**
2056
 * Upgrade the "extra" column in the e-mail table.
2057
 *
2058
 * Make the column consistent with a new webform installation.
2059
 *
2060
 * In version 7.x-4.0-rc6 and earlier, the extra field was added with 'not null'
2061
 * FALSE, cause the schema module to report in mismatch. While functionally this
2062
 * causes no problem, this update removes the schema module's warning.
2063
 */
2064
function webform_update_7418() {
2065
  // While there should never be any NULL values in the extra field, change them
2066
  // to '' to be safe.
2067
  db_update('webform_emails')
2068
    ->fields(array('extra' => ''))
2069
    ->isNull('extra')
2070
    ->execute();
2071
  // Pass a complete field specification to db_change_field for cross-database
2072
  // compatiblity.
2073
  $spec = array(
2074
    '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.',
2075
    'type' => 'text',
2076
    'not null' => TRUE,
2077
  );
2078
  db_change_field('webform_emails', 'extra', 'extra', $spec);
2079
}
2080

    
2081
/**
2082
 * Add an index on submitted data.
2083
 */
2084
function webform_update_7419() {
2085
  // Note: MS SQL Server does not support size-limited indexes and the column
2086
  // type (text) is too big to fit inside index size limits.
2087
  if (!db_index_exists('webform_submitted_data', 'data') && db_driver() != 'sqlsrv') {
2088
    db_add_index('webform_submitted_data', 'data', array(array('data', 64)));
2089
  }
2090
}
2091

    
2092
/**
2093
 * Extend access to the WEBFORM tab to anyone who can access the node's EDIT tab.
2094
 */
2095
function webform_update_7420() {
2096
  user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('edit webform components'));
2097
}
2098

    
2099
/**
2100
 * Set the administrator option for an HTML-capable mail system to the current automatically-detected option.
2101
 */
2102
function webform_update_7421() {
2103
  $capable = array_reduce(array('mandrill', 'mimemail', 'htmlmail'),
2104
                          function ($carry, $module) {
2105
                            return $carry || module_exists($module);
2106
                          },
2107
                          FALSE);
2108
  variable_set('webform_email_html_capable', $capable);
2109
  return $capable
2110
            ? t('An HTML-capable module is installed. The option to send HTML e-mail is enabled.')
2111
            : t('No commonly-known HTML capable module is installed. The option to send HTML e-mail is disabled.');
2112
}
2113

    
2114
/**
2115
 * Remove the administrator option "Include webform forms in search index" and rely on the Search Index view mode instead.
2116
 */
2117
function webform_update_7422() {
2118
  variable_del('webform_search_index');
2119
  return module_exists('search')
2120
          ? t('Webform forms will now be included in the search index only if the Webform "field" is displayed in the "Search index" view mode.')
2121
          : NULL;
2122
}
2123

    
2124
/**
2125
 * Convert conditionals to be able to support multiple actions per conditional.
2126
 *
2127
 * Backup your database before proceeding. WARNING: Sites with many, many
2128
 * conditionals should execute this update via drush to avoid a PHP timeout.
2129
 */
2130
function webform_update_7423() {
2131
  // Create webform_condtional_actions table.
2132
  // The table might already exist if this update previously timed-out.
2133
  if (!db_table_exists('webform_conditional_actions')) {
2134
    $schema['webform_conditional_actions'] = array(
2135
      'description' => 'Holds information about conditional actions.',
2136
      'fields' => array(
2137
        'nid' => array(
2138
          'description' => 'The node identifier of a webform.',
2139
          'type' => 'int',
2140
          'unsigned' => TRUE,
2141
          'not null' => TRUE,
2142
          'default' => 0,
2143
        ),
2144
        'rgid' => array(
2145
          'description' => 'The rule group identifier for this group of rules.',
2146
          'type' => 'int',
2147
          'size' => 'small',
2148
          'unsigned' => TRUE,
2149
          'not null' => TRUE,
2150
          'default' => 0,
2151
        ),
2152
        'aid' => array(
2153
          'description' => 'The rule identifier for this conditional action.',
2154
          'type' => 'int',
2155
          'size' => 'small',
2156
          'unsigned' => TRUE,
2157
          'not null' => TRUE,
2158
          'default' => 0,
2159
        ),
2160
        'target_type' => array(
2161
          'description' => 'The type of target to be affected. Currently always "component". Indicates what type of ID the "target" column contains.',
2162
          'type' => 'varchar',
2163
          'length' => 128,
2164
        ),
2165
        'target' => array(
2166
          'description' => 'The ID of the target to be affected. Typically a component ID.',
2167
          'type' => 'varchar',
2168
          'length' => 128,
2169
        ),
2170
        'invert' => array(
2171
          'description' => 'If inverted, execute when rule(s) are false.',
2172
          'type' => 'int',
2173
          'size' => 'small',
2174
          'unsigned' => TRUE,
2175
          'not null' => TRUE,
2176
          'default' => 0,
2177
        ),
2178
        'action' => array(
2179
          'description' => 'The action to be performed on the target.',
2180
          'type' => 'varchar',
2181
          'length' => 128,
2182
        ),
2183
        'argument' => array(
2184
          'description' => 'Optional argument for action.',
2185
          'type' => 'text',
2186
        ),
2187
      ),
2188
      'primary key' => array('nid', 'rgid', 'aid'),
2189
    );
2190
    db_create_table('webform_conditional_actions', $schema['webform_conditional_actions']);
2191
  }
2192

    
2193
  // In a site with many, many conditionals, the db_insert may timeout. Start a
2194
  // transaction to ensure atomic action.
2195
  $transaction = db_transaction();
2196
  // Copy target information from existing webform_conditional table to new
2197
  // webfrom_condtional_actions table.
2198
  $select = db_select('webform_conditional', 'c')
2199
    ->fields('c', array('nid', 'rgid', 'action', 'target_type', 'target'))
2200
    ->orderBy('nid')->orderBy('rgid');
2201
  $select->addExpression("''", 'argument');
2202
  db_insert('webform_conditional_actions')
2203
    ->from($select)
2204
    ->execute();
2205

    
2206
  // Commit the insert.
2207
  unset($transaction);
2208

    
2209
  // Remove unneeded columns from webform_conditional.
2210
  foreach (array('action', 'target_type', 'target') as $fieldname) {
2211
    if (db_field_exists('webform_conditional', $fieldname)) {
2212
      db_drop_field('webform_conditional', $fieldname);
2213
    }
2214
  }
2215

    
2216
  // Rebuild the registry because this point release contains a new class:
2217
  // WebformConditionals.
2218
  registry_rebuild();
2219

    
2220
  return t('Webform database tables were successfully adjusted to allow more than one action for each conditional.');
2221
}
2222

    
2223
/**
2224
 * Convert conditional actions of "hide" to "isn't shown".
2225
 */
2226
function webform_update_7424() {
2227
  $count = db_update('webform_conditional_actions')
2228
    ->fields(array('action' => 'show', 'invert' => 1))
2229
    ->condition('action', 'hide')
2230
    ->execute();
2231
  return format_plural($count,
2232
                       '1 "hide" conditional converted to "isn\'t" shown.',
2233
                       '@count conditionals converted to "isn\'t" shown.');
2234
}
2235

    
2236
/**
2237
 * Add "exclude empty" option to emails.
2238
 */
2239
function webform_update_7425() {
2240
  // Add next_serial column to webform.
2241
  $spec = array(
2242
    'description' => 'Determines if the e-mail will include component with an empty value.',
2243
    'type' => 'int',
2244
    'unsigned' => TRUE,
2245
    'size' => 'tiny',
2246
    'not null' => TRUE,
2247
    'default' => 0,
2248
  );
2249
  if (!db_field_exists('webform_emails', 'exclude_empty')) {
2250
    db_add_field('webform_emails', 'exclude_empty', $spec);
2251
  }
2252

    
2253
  // Clear the views cache since this release use the webform_analysis view.
2254
  cache_clear_all('*', 'cache_views', TRUE);
2255

    
2256
  return t('Webform e-mails were sucessfully updated to add the option to exclude empty components.');
2257
}
2258

    
2259
/**
2260
 * Add configuration to continue sending individual e-mails to multiple recipients.
2261
 */
2262
function webform_update_7426() {
2263
  variable_set('webform_email_address_individual', 1);
2264
}
2265

    
2266
/**
2267
 * Add database columns for submission completed and modified timestamps.
2268
 *
2269
 * Sites with many submissions may wish to use drush to execute this update.
2270
 */
2271
function webform_update_7427() {
2272
  // Create new timestamp columns.
2273
  $specs = array(
2274
    'completed' => array(
2275
      'description' => 'Timestamp when the form was submitted as complete (not draft).',
2276
      'type' => 'int',
2277
      'not null' => TRUE,
2278
      'default' => 0,
2279
    ),
2280
    'modified' => array(
2281
      'description' => 'Timestamp when the form was last saved (complete or draft).',
2282
      'type' => 'int',
2283
      'not null' => TRUE,
2284
      'default' => 0,
2285
    ),
2286
  );
2287
  foreach ($specs as $field_name => $spec) {
2288
    if (!db_field_exists('webform_submissions', $field_name)) {
2289
      db_add_field('webform_submissions', $field_name, $spec);
2290
    }
2291
  }
2292

    
2293
  // In a site with many submissions, the db_update may timeout. Start a
2294
  // transaction to ensure atomic action.
2295
  $transaction = db_transaction();
2296
  // Copy submitted to completed for non-draft submissions.
2297
  db_update('webform_submissions')
2298
    ->expression('completed', 'submitted')
2299
    ->condition('is_draft', 0)
2300
    ->execute();
2301
  // Commit the update.
2302
  unset($transaction);
2303

    
2304
  // Start another transaction.
2305
  $transaction = db_transaction();
2306
  db_update('webform_submissions')
2307
    ->expression('modified', 'submitted')
2308
    ->execute();
2309
  // Commit the update.
2310
  unset($transaction);
2311

    
2312
  // Clear the views cache since to see the completed and modified views fields.
2313
  cache_clear_all('*', 'cache_views', TRUE);
2314

    
2315
  return t('Webform submissions were updated with completed and modified timestamps.');
2316
}
2317

    
2318
/**
2319
 * Add a "confidential" option to webforms.
2320
 */
2321
function webform_update_7428() {
2322
  // Add confidential column to webform.
2323
  if (!db_field_exists('webform', 'confidential')) {
2324
    $spec = array(
2325
      'description' => 'Boolean value for whether to anonymize submissions.',
2326
      'type' => 'int',
2327
      'size' => 'tiny',
2328
      'not null' => TRUE,
2329
      'default' => 0,
2330
    );
2331
    db_add_field('webform', 'confidential', $spec);
2332
  }
2333

    
2334
  return t('Webforms may now be configured to anonymize confidential submissions.');
2335
}
2336

    
2337
/**
2338
 * Add column highest_valid_page to webform_submissions table.
2339
 *
2340
 * Add a column to the submission table to store the page on which to resume a
2341
 * draft. Sites with many, many submissions may wish to execute this update with
2342
 * 'drush updatedb'.
2343
 */
2344
function webform_update_7429() {
2345
  // Add highest_valid_page column to webform_submissions.
2346
  if (!db_field_exists('webform_submissions', 'highest_valid_page')) {
2347
    $spec = array(
2348
      'description' => 'For drafts, the highest validated page number.',
2349
      'type' => 'int',
2350
      'size' => 'small',
2351
      'not null' => TRUE,
2352
      'default' => 0,
2353
    );
2354
    db_add_field('webform_submissions', 'highest_valid_page', $spec);
2355
  }
2356

    
2357
  return t('Webforms will now resume draft submissions on the page where the submitter left off.');
2358
}
2359

    
2360
/**
2361
 * Add a column to the emails table to allow disabling.
2362
 */
2363
function webform_update_7430() {
2364
  // Add status column to webform_emails.
2365
  if (!db_field_exists('webform_emails', 'status')) {
2366
    $spec = array(
2367
      'description' => 'Whether this email is enabled.',
2368
      'type' => 'int',
2369
      'unsigned' => TRUE,
2370
      'size' => 'tiny',
2371
      'not null' => TRUE,
2372
      'default' => 1,
2373
    );
2374
    db_add_field('webform_emails', 'status', $spec);
2375
  }
2376

    
2377
  return t('Webform emails may now be disabled.');
2378
}
2379

    
2380
/**
2381
 * Preserve progress bar as not active for one-page webforms.
2382
 */
2383
function webform_update_7431() {
2384
  // Get a list of all Webforms containing a pagebreak.
2385
  $multipage_webform_nids = db_select('webform_component');
2386
  $multipage_webform_nids->distinct();
2387
  $multipage_webform_nids->addField('webform_component', 'nid');
2388
  $multipage_webform_nids->condition('type', 'pagebreak');
2389

    
2390
  // Remove confirmation page from the progress bar for single-page Webforms.
2391
  $updated_count = db_update('webform')
2392
    ->fields(array('progressbar_include_confirmation' => 0))
2393
    ->condition('preview', 0)
2394
    ->condition('nid', $multipage_webform_nids, 'NOT IN')
2395
    ->execute();
2396

    
2397
  return t("Disabled progress bar for @count single-page webforms.", array('@count' => $updated_count));
2398
}