Project

General

Profile

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

root / drupal7 / sites / all / modules / webform / webform.install @ 76bdcd04

1
<?php
2

    
3
/**
4
 * @file
5
 * Webform module install/schema hooks.
6
 */
7

    
8
/**
9
 * Implements hook_schema().
10
 */
11
function webform_schema() {
12
  $schema = array();
13

    
14
  $schema['webform'] = array(
15
    'description' => 'Table for storing additional properties for webform nodes.',
16
    'fields' => array(
17
      'nid' => array(
18
        'description' => 'The node identifier of a webform.',
19
        'type' => 'int',
20
        'unsigned' => TRUE,
21
        'not null' => TRUE,
22
      ),
23
      'next_serial' => array(
24
        'description' => 'The serial number to give to the next submission to this webform.',
25
        'type' => 'int',
26
        'unsigned' => TRUE,
27
        'not null' => TRUE,
28
        'default' => 1,
29
      ),
30
      'confirmation' => array(
31
        'description' => 'The confirmation message or URL displayed to the user after submitting a form.',
32
        'type' => 'text',
33
        'not null' => TRUE,
34
      ),
35
      'confirmation_format' => array(
36
        'description' => 'The {filter_format}.format of the confirmation message.',
37
        'type' => 'varchar',
38
        'length' => 255,
39
        'not null' => FALSE,
40
      ),
41
      'redirect_url' => array(
42
        'description' => 'The URL a user is redirected to after submitting a form.',
43
        'type' => 'varchar',
44
        'length' => 2048,
45
        'default' => '<confirmation>',
46
      ),
47
      'status' => array(
48
        'description' => 'Boolean value of a webform for open (1) or closed (0).',
49
        'type' => 'int',
50
        'size' => 'tiny',
51
        'not null' => TRUE,
52
        'default' => 1,
53
      ),
54
      'block' => array(
55
        'description' => 'Boolean value for whether this form be available as a block.',
56
        'type' => 'int',
57
        'size' => 'tiny',
58
        'not null' => TRUE,
59
        'default' => 0,
60
      ),
61
      'allow_draft' => array(
62
        'description' => 'Boolean value for whether submissions to this form be saved as a draft.',
63
        'type' => 'int',
64
        'size' => 'tiny',
65
        'not null' => TRUE,
66
        'default' => 0,
67
      ),
68
      'auto_save' => array(
69
        'description' => 'Boolean value for whether submissions to this form should be auto-saved between pages.',
70
        'type' => 'int',
71
        'size' => 'tiny',
72
        'not null' => TRUE,
73
        'default' => 0,
74
      ),
75
      'submit_notice' => array(
76
        'description' => 'Boolean value for whether to show or hide the previous submissions notification.',
77
        'type' => 'int',
78
        'size' => 'tiny',
79
        'not null' => TRUE,
80
        'default' => 1,
81
      ),
82
      'confidential' => array(
83
        'description' => 'Boolean value for whether to anonymize submissions.',
84
        'type' => 'int',
85
        'size' => 'tiny',
86
        'not null' => TRUE,
87
        'default' => 0,
88
      ),
89
      'submit_text' => array(
90
        'description' => 'The title of the submit button on the form.',
91
        'type' => 'varchar',
92
        'length' => 255,
93
      ),
94
      'submit_limit' => array(
95
        'description' => 'The number of submissions a single user is allowed to submit within an interval. -1 is unlimited.',
96
        'type' => 'int',
97
        'size' => 'tiny',
98
        'not null' => TRUE,
99
        'default' => -1,
100
      ),
101
      'submit_interval' => array(
102
        'description' => 'The amount of time in seconds that must pass before a user can submit another submission within the set limit.',
103
        'type' => 'int',
104
        'not null' => TRUE,
105
        'default' => -1,
106
      ),
107
      'total_submit_limit' => array(
108
        'description' => 'The total number of submissions allowed within an interval. -1 is unlimited.',
109
        'type' => 'int',
110
        'not null' => TRUE,
111
        'default' => -1,
112
      ),
113
      'total_submit_interval' => array(
114
        'description' => 'The amount of time in seconds that must pass before another submission can be submitted within the set limit.',
115
        'type' => 'int',
116
        'not null' => TRUE,
117
        'default' => -1,
118
      ),
119
      'progressbar_bar' => array(
120
        'description' => 'Boolean value indicating if the bar should be shown as part of the progress bar.',
121
        'type' => 'int',
122
        'size' => 'tiny',
123
        'not null' => TRUE,
124
        'default' => 0,
125
      ),
126
      'progressbar_page_number' => array(
127
        'description' => 'Boolean value indicating if the page number should be shown as part of the progress bar.',
128
        'type' => 'int',
129
        'size' => 'tiny',
130
        'not null' => TRUE,
131
        'default' => 0,
132
      ),
133
      'progressbar_percent' => array(
134
        'description' => 'Boolean value indicating if the percentage complete should be shown as part of the progress bar.',
135
        'type' => 'int',
136
        'size' => 'tiny',
137
        'not null' => TRUE,
138
        'default' => 0,
139
      ),
140
      'progressbar_pagebreak_labels' => array(
141
        'description' => 'Boolean value indicating if the pagebreak labels should be included as part of the progress bar.',
142
        'type' => 'int',
143
        'size' => 'tiny',
144
        'not null' => TRUE,
145
        'default' => 0,
146
      ),
147
      'progressbar_include_confirmation' => array(
148
        'description' => 'Boolean value indicating if the confirmation page should count as a page in the progress bar.',
149
        'type' => 'int',
150
        'size' => 'tiny',
151
        'not null' => TRUE,
152
        'default' => 0,
153
      ),
154
      'progressbar_label_first' => array(
155
        'description' => 'Label for the first page of the progress bar.',
156
        'type' => 'varchar',
157
        'length' => 255,
158
      ),
159
      'progressbar_label_confirmation' => array(
160
        'description' => 'Label for the last page of the progress bar.',
161
        'type' => 'varchar',
162
        'length' => 255,
163
      ),
164
      'preview' => array(
165
        'description' => 'Boolean value indicating if this form includes a page for previewing the submission.',
166
        'type' => 'int',
167
        'size' => 'tiny',
168
        'not null' => TRUE,
169
        'default' => 0,
170
      ),
171
      'preview_next_button_label' => array(
172
        'description' => 'The text for the button that will proceed to the preview page.',
173
        'type' => 'varchar',
174
        'length' => 255,
175
      ),
176
      'preview_prev_button_label' => array(
177
        'description' => 'The text for the button to go backwards from the preview page.',
178
        'type' => 'varchar',
179
        'length' => 255,
180
      ),
181
      'preview_title' => array(
182
        'description' => 'The title of the preview page, as used by the progress bar.',
183
        'type' => 'varchar',
184
        'length' => 255,
185
      ),
186
      'preview_message' => array(
187
        'description' => 'Text shown on the preview page of the form.',
188
        'type' => 'text',
189
        'not null' => TRUE,
190
      ),
191
      'preview_message_format' => array(
192
        'description' => 'The {filter_format}.format of the preview page message.',
193
        'type' => 'varchar',
194
        'length' => 255,
195
      ),
196
      'preview_excluded_components' => array(
197
        'description' => 'Comma-separated list of component IDs that should not be included in this form\'s confirmation page.',
198
        'type' => 'text',
199
        'not null' => TRUE,
200
      ),
201
    ),
202
    'primary key' => array('nid'),
203
  );
204

    
205
  $schema['webform_component'] = array(
206
    'description' => 'Stores information about components for webform nodes.',
207
    'fields' => array(
208
      'nid' => array(
209
        'description' => 'The node identifier of a webform.',
210
        'type' => 'int',
211
        'unsigned' => TRUE,
212
        'not null' => TRUE,
213
        'default' => 0,
214
      ),
215
      'cid' => array(
216
        'description' => 'The identifier for this component within this node, starts at 0 for each node.',
217
        'type' => 'int',
218
        'size' => 'small',
219
        'unsigned' => TRUE,
220
        'not null' => TRUE,
221
        'default' => 0,
222
      ),
223
      'pid' => array(
224
        'description' => 'If this component has a parent fieldset, the cid of that component.',
225
        'type' => 'int',
226
        'size' => 'small',
227
        'unsigned' => TRUE,
228
        'not null' => TRUE,
229
        'default' => 0,
230
      ),
231
      'form_key' => array(
232
        'description' => 'When the form is displayed and processed, this key can be used to reference the results.',
233
        'type' => 'varchar',
234
        'length' => 128,
235
      ),
236
      'name' => array(
237
        'description' => 'The label for this component.',
238
        'type' => 'text',
239
        'not null' => TRUE,
240
      ),
241
      'type' => array(
242
        'description' => 'The field type of this component (textfield, select, hidden, etc.).',
243
        'type' => 'varchar',
244
        'length' => 16,
245
      ),
246
      'value' => array(
247
        'description' => 'The default value of the component when displayed to the end-user.',
248
        'type' => 'text',
249
        'not null' => TRUE,
250
      ),
251
      'extra' => array(
252
        'description' => 'Additional information unique to the display or processing of this component.',
253
        'type' => 'text',
254
        'not null' => TRUE,
255
      ),
256
      'required' => array(
257
        'description' => 'Boolean flag for if this component is required.',
258
        'type' => 'int',
259
        'size' => 'tiny',
260
        'not null' => TRUE,
261
        'default' => 0,
262
      ),
263
      'weight' => array(
264
        'description' => 'Determines the position of this component in the form.',
265
        'type' => 'int',
266
        'size' => 'small',
267
        'not null' => TRUE,
268
        'default' => 0,
269
      ),
270
    ),
271
    'primary key' => array('nid', 'cid'),
272
  );
273

    
274
  $schema['webform_conditional'] = array(
275
    'description' => 'Holds information about conditional logic.',
276
    'fields' => array(
277
      'nid' => array(
278
        'description' => 'The node identifier of a webform.',
279
        'type' => 'int',
280
        'unsigned' => TRUE,
281
        'not null' => TRUE,
282
        'default' => 0,
283
      ),
284
      'rgid' => array(
285
        'description' => 'The rule group identifier for this group of rules.',
286
        'type' => 'int',
287
        'size' => 'small',
288
        'unsigned' => TRUE,
289
        'not null' => TRUE,
290
        'default' => 0,
291
      ),
292
      'andor' => array(
293
        'description' => 'Whether to AND or OR the actions in this group. All actions within the same rgid should have the same andor value.',
294
        'type' => 'varchar',
295
        'length' => 128,
296
      ),
297
      'weight' => array(
298
        'description' => 'Determines the position of this conditional compared to others.',
299
        'type' => 'int',
300
        'size' => 'small',
301
        'not null' => TRUE,
302
        'default' => 0,
303
      ),
304
    ),
305
    'primary key' => array('nid', 'rgid'),
306
  );
307

    
308
  $schema['webform_conditional_rules'] = array(
309
    'description' => 'Holds information about conditional logic.',
310
    'fields' => array(
311
      'nid' => array(
312
        'description' => 'The node identifier of a webform.',
313
        'type' => 'int',
314
        'unsigned' => TRUE,
315
        'not null' => TRUE,
316
        'default' => 0,
317
      ),
318
      'rgid' => array(
319
        'description' => 'The rule group identifier for this group of rules.',
320
        'type' => 'int',
321
        'size' => 'small',
322
        'unsigned' => TRUE,
323
        'not null' => TRUE,
324
        'default' => 0,
325
      ),
326
      'rid' => array(
327
        'description' => 'The rule identifier for this conditional rule.',
328
        'type' => 'int',
329
        'size' => 'small',
330
        'unsigned' => TRUE,
331
        'not null' => TRUE,
332
        'default' => 0,
333
      ),
334
      'source_type' => array(
335
        'description' => 'The type of source on which the conditional is based. Currently always "component". Indicates what type of ID the "source" column contains.',
336
        'type' => 'varchar',
337
        'length' => 128,
338
      ),
339
      'source' => array(
340
        'description' => 'The component ID being used in this condition.',
341
        'type' => 'int',
342
        'size' => 'small',
343
        'unsigned' => TRUE,
344
        'not null' => TRUE,
345
        'default' => 0,
346
      ),
347
      'operator' => array(
348
        'description' => 'Which operator (equal, contains, starts with, etc.) should be used for this comparison between the source and value?',
349
        'type' => 'varchar',
350
        'length' => 128,
351
      ),
352
      'value' => array(
353
        'description' => 'The value to be compared with source.',
354
        'type' => 'text',
355
      ),
356
    ),
357
    'primary key' => array('nid', 'rgid', 'rid'),
358
  );
359

    
360
  $schema['webform_conditional_actions'] = array(
361
    'description' => 'Holds information about conditional actions.',
362
    'fields' => array(
363
      'nid' => array(
364
        'description' => 'The node identifier of a webform.',
365
        'type' => 'int',
366
        'unsigned' => TRUE,
367
        'not null' => TRUE,
368
        'default' => 0,
369
      ),
370
      'rgid' => array(
371
        'description' => 'The rule group identifier for this group of rules.',
372
        'type' => 'int',
373
        'size' => 'small',
374
        'unsigned' => TRUE,
375
        'not null' => TRUE,
376
        'default' => 0,
377
      ),
378
      'aid' => array(
379
        'description' => 'The rule identifier for this conditional action.',
380
        'type' => 'int',
381
        'size' => 'small',
382
        'unsigned' => TRUE,
383
        'not null' => TRUE,
384
        'default' => 0,
385
      ),
386
      'target_type' => array(
387
        'description' => 'The type of target to be affected. Currently always "component". Indicates what type of ID the "target" column contains.',
388
        'type' => 'varchar',
389
        'length' => 128,
390
      ),
391
      'target' => array(
392
        'description' => 'The ID of the target to be affected. Typically a component ID.',
393
        'type' => 'varchar',
394
        'length' => 128,
395
      ),
396
      'invert' => array(
397
        'description' => 'If inverted, execute when rule(s) are false.',
398
        'type' => 'int',
399
        'size' => 'small',
400
        'unsigned' => TRUE,
401
        'not null' => TRUE,
402
        'default' => 0,
403
      ),
404
      'action' => array(
405
        'description' => 'The action to be performed on the target.',
406
        'type' => 'varchar',
407
        'length' => 128,
408
      ),
409
      'argument' => array(
410
        'description' => 'Optional argument for action.',
411
        'type' => 'text',
412
      ),
413
    ),
414
    'primary key' => array('nid', 'rgid', 'aid'),
415
  );
416

    
417
  $schema['webform_emails'] = array(
418
    'description' => 'Holds information regarding e-mails that should be sent upon submitting a webform',
419
    'fields' => array(
420
      'nid' => array(
421
        'description' => 'The node identifier of a webform.',
422
        'type' => 'int',
423
        'unsigned' => TRUE,
424
        'not null' => TRUE,
425
        'default' => 0,
426
      ),
427
      'eid' => array(
428
        'description' => 'The e-mail identifier for this row\'s settings.',
429
        'type' => 'int',
430
        'unsigned' => TRUE,
431
        'size' => 'small',
432
        'not null' => TRUE,
433
        'default' => 0,
434
      ),
435
      'email' => array(
436
        'description' => 'The e-mail address that will be sent to upon submission. This may be an e-mail address, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
437
        'type' => 'text',
438
        'not null' => FALSE,
439
      ),
440
      'subject' => array(
441
        'description' => 'The e-mail subject that will be used. This may be a string, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
442
        'type' => 'text',
443
        'not null' => FALSE,
444
      ),
445
      'from_name' => array(
446
        'description' => 'The e-mail "from" name that will be used. This may be a string, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
447
        'type' => 'text',
448
        'not null' => FALSE,
449
      ),
450
      'from_address' => array(
451
        'description' => 'The e-mail "from" e-mail address that will be used. This may be a string, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
452
        'type' => 'text',
453
        'not null' => FALSE,
454
      ),
455
      'template' => array(
456
        'description' => 'A template that will be used for the sent e-mail. This may be a string or the special key "default", which will use the template provided by the theming layer.',
457
        'type' => 'text',
458
        'not null' => FALSE,
459
      ),
460
      'excluded_components' => array(
461
        'description' => 'A list of components that will not be included in the [submission:values] token. A list of CIDs separated by commas.',
462
        'type' => 'text',
463
        'not null' => TRUE,
464
      ),
465
      'html' => array(
466
        'description' => 'Determines if the e-mail will be sent in an HTML format. Requires Mime Mail module.',
467
        'type' => 'int',
468
        'unsigned' => TRUE,
469
        'size' => 'tiny',
470
        'not null' => TRUE,
471
        'default' => 0,
472
      ),
473
      'attachments' => array(
474
        'description' => 'Determines if the e-mail will include file attachments. Requires Mime Mail module.',
475
        'type' => 'int',
476
        'unsigned' => TRUE,
477
        'size' => 'tiny',
478
        'not null' => TRUE,
479
        'default' => 0,
480
      ),
481
      'exclude_empty' => array(
482
        'description' => 'Determines if the e-mail will include component with an empty value.',
483
        'type' => 'int',
484
        'unsigned' => TRUE,
485
        'size' => 'tiny',
486
        'not null' => TRUE,
487
        'default' => 0,
488
      ),
489
      'extra' => array(
490
        'description' => 'A serialized array of additional options for the e-mail configuration, including value mapping for the TO and FROM addresses for select lists.',
491
        'type' => 'text',
492
        'not null' => TRUE,
493
      ),
494
      'status' => array(
495
        'description' => 'Whether this email is enabled.',
496
        'type' => 'int',
497
        'unsigned' => TRUE,
498
        'size' => 'tiny',
499
        'not null' => TRUE,
500
        'default' => 1,
501
      ),
502
    ),
503
    'primary key' => array('nid', 'eid'),
504
  );
505

    
506
  $schema['webform_roles'] = array(
507
    'description' => 'Holds access information regarding which roles are allowed to submit which webform nodes. Does not prevent access to the webform node entirely, use the {node_access} table for that purpose.',
508
    'fields' => array(
509
      'nid' => array(
510
        'description' => 'The node identifier of a webform.',
511
        'type' => 'int',
512
        'unsigned' => TRUE,
513
        'not null' => TRUE,
514
        'default' => 0,
515
      ),
516
      'rid' => array(
517
        'description' => 'The role identifier.',
518
        'type' => 'int',
519
        'unsigned' => TRUE,
520
        'not null' => TRUE,
521
        'default' => 0,
522
      ),
523
    ),
524
    'primary key' => array('nid', 'rid'),
525
  );
526

    
527
  $schema['webform_submissions'] = array(
528
    'description' => 'Holds general information about submissions outside of field values.',
529
    'fields' => array(
530
      'sid' => array(
531
        'description' => 'The unique identifier for this submission.',
532
        'type' => 'serial',
533
        'unsigned' => TRUE,
534
        'not null' => TRUE,
535
      ),
536
      'nid' => array(
537
        'description' => 'The node identifier of a webform.',
538
        'type' => 'int',
539
        'unsigned' => TRUE,
540
        'not null' => TRUE,
541
        'default' => 0,
542
      ),
543
      'serial' => array(
544
        'description' => 'The serial number of this submission.',
545
        'type' => 'int',
546
        'unsigned' => TRUE,
547
        'not null' => TRUE,
548
      ),
549
      'uid' => array(
550
        'description' => 'The id of the user that completed this submission.',
551
        'type' => 'int',
552
        'unsigned' => TRUE,
553
        'not null' => TRUE,
554
        'default' => 0,
555
      ),
556
      'is_draft' => array(
557
        'description' => 'Is this a draft of the submission?',
558
        'type' => 'int',
559
        'size' => 'tiny',
560
        'not null' => TRUE,
561
        'default' => 0,
562
      ),
563
      'highest_valid_page' => array(
564
        'description' => 'For drafts, the highest validated page number.',
565
        'type' => 'int',
566
        'size' => 'small',
567
        'not null' => TRUE,
568
        'default' => 0,
569
      ),
570
      'submitted' => array(
571
        'description' => 'Timestamp when the form was first saved as draft or submitted.',
572
        'type' => 'int',
573
        'not null' => TRUE,
574
        'default' => 0,
575
      ),
576
      'completed' => array(
577
        'description' => 'Timestamp when the form was submitted as complete (not draft).',
578
        'type' => 'int',
579
        'not null' => TRUE,
580
        'default' => 0,
581
      ),
582
      'modified' => array(
583
        'description' => 'Timestamp when the form was last saved (complete or draft).',
584
        'type' => 'int',
585
        'not null' => TRUE,
586
        'default' => 0,
587
      ),
588
      'remote_addr' => array(
589
        'description' => 'The IP address of the user that submitted the form.',
590
        'type' => 'varchar',
591
        'length' => 128,
592
      ),
593
    ),
594
    'primary key' => array('sid'),
595
    'unique keys' => array(
596
      'sid_nid' => array('sid', 'nid'),
597
      'nid_serial' => array('nid', 'serial'),
598
    ),
599
    'indexes' => array(
600
      'nid_uid_sid' => array('nid', 'uid', 'sid'),
601
      'nid_sid' => array('nid', 'sid'),
602
    ),
603
  );
604

    
605
  $schema['webform_submitted_data'] = array(
606
    'description' => 'Stores all submitted field data for webform submissions.',
607
    'fields' => array(
608
      'nid' => array(
609
        'description' => 'The node identifier of a webform.',
610
        'type' => 'int',
611
        'unsigned' => TRUE,
612
        'not null' => TRUE,
613
        'default' => 0,
614
      ),
615
      'sid' => array(
616
        'description' => 'The unique identifier for this submission.',
617
        'type' => 'int',
618
        'unsigned' => TRUE,
619
        'not null' => TRUE,
620
        'default' => 0,
621
      ),
622
      'cid' => array(
623
        'description' => 'The identifier for this component within this node, starts at 0 for each node.',
624
        'type' => 'int',
625
        'size' => 'small',
626
        'unsigned' => TRUE,
627
        'not null' => TRUE,
628
        'default' => 0,
629
      ),
630
      'no' => array(
631
        'description' => 'Usually this value is 0, but if a field has multiple values (such as a time or date), it may require multiple rows in the database.',
632
        'type' => 'varchar',
633
        'length' => 128,
634
        'not null' => TRUE,
635
        'default' => '0',
636
      ),
637
      'data' => array(
638
        'description' => 'The submitted value of this field, may be serialized for some components.',
639
        'type' => 'text',
640
        'size' => 'medium',
641
        'not null' => TRUE,
642
      ),
643
    ),
644
    'primary key' => array('nid', 'sid', 'cid', 'no'),
645
    'indexes' => array(
646
      'nid' => array('nid'),
647
      'sid_nid' => array('sid', 'nid'),
648
      // For all but MS SQL Server databases, 64-character index is created on the data column after the schema is installed.
649
    ),
650
  );
651

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

    
687
  return $schema;
688
}
689

    
690
/**
691
 * Implements hook_requirements().
692
 */
693
function webform_requirements($phase) {
694
  $requirements = array();
695
  $t = get_t();
696

    
697
  // Ensure cURL exists if SimpleTest hasn't checked it already.
698
  if (!class_exists('ZipArchive')) {
699
    $requirements['webform_zip'] = array(
700
      'title' => $t('Zip archive support'),
701
      'value' => $t('Missing'),
702
      'severity' => REQUIREMENT_INFO,
703
      'description' => $t('PHP does not have the zip archive extension available. Webform module requires zip support for exporting submissions to Microsoft Excel.'),
704
    );
705
  }
706
  // Though the .info file specifies PHP version as well, this will prevent
707
  // users from upgrading from 3.x if their PHP version is too old.
708
  if (version_compare(phpversion(), '5.3') < 0) {
709
    $requirements['webform_php'] = array(
710
      'title' => $t('Webform PHP requirement'),
711
      'value' => phpversion(),
712
      'severity' => REQUIREMENT_ERROR,
713
      'description' => $t('Webform requires PHP 5.3 or higher.'),
714
    );
715
  }
716
  // Ensure that views is enabled as it is a new .info requirement starting
717
  // with version 7.x-4.0rc1. On installation, the .info file is sufficient to
718
  // cause the dependencies to be installed. On update, update.php will
719
  // respect this hook_requirements implementation, but as of drush 6.3.0 and
720
  // drush 7.0.0, drush updatedb will not. See:
721
  // https://github.com/drush-ops/drush/issues/1427
722
  if ($phase != 'install' && !module_exists('views')) {
723
    $requirements['webform_views'] = array(
724
      'title' => $t('Webform Views requirement'),
725
      'value' => $t('Missing'),
726
      'severity' => REQUIREMENT_ERROR,
727
      'description' => $t('Webform requires Views, which is not installed and enabled.'),
728
    );
729
  }
730

    
731
  return $requirements;
732
}
733

    
734
/**
735
 * Implements hook_install().
736
 */
737
function webform_install() {
738
  module_load_include('inc', 'node', 'content_types');
739
  db_update('system')
740
    ->condition('name', 'webform')
741
    ->condition('type', 'module')
742
    ->fields(array('weight' => -1))
743
    ->execute();
744

    
745
  // Optionally create the default webform type.
746
  if (variable_get('webform_install_create_content_type', TRUE)) {
747
    $webform_type = array(
748
      'type' => 'webform',
749
      'name' => st('Webform'),
750
      'base' => 'node_content',
751
      'description' => st('Create a new form or questionnaire accessible to users. Submission results and statistics are recorded and accessible to privileged users.'),
752
      'custom' => TRUE,
753
      'modified' => TRUE,
754
      'locked' => FALSE,
755
    );
756
    $webform_type = node_type_set_defaults($webform_type);
757
    node_type_save($webform_type);
758
    // Enable webform components by default on Webform nodes.
759
    variable_set('webform_node_webform', TRUE);
760
    // Now that a webform node type has been created, reset the cache of the
761
    // node types that support webforms. This is needed for tests which will
762
    // create nodes in the same execution.
763
    drupal_static_reset('webform_node_types');
764
    if (variable_get('webform_install_add_body_field', FALSE)) {
765
      node_add_body_field($webform_type);
766
    }
767
    // Disable comments by default on Webform nodes.
768
    variable_set('comment_webform', '0');
769
  }
770
  else {
771
    variable_set('webform_node_types_primary', array());
772
  }
773

    
774
  // Note: MS SQL Server does not support size-limited indexes and the column
775
  // type (text) is too big to fit inside index size limits.
776
  if (!db_index_exists('webform_submitted_data', 'data') && db_driver() != 'sqlsrv') {
777
    db_add_index('webform_submitted_data', 'data', array(array('data', 64)));
778
  }
779
}
780

    
781
/**
782
 * Implements hook_uninstall().
783
 */
784
function webform_uninstall() {
785
  // Unset webform variables.
786
  variable_del('webform_blocks');
787
  variable_del('webform_tracking_mode');
788
  variable_del('webform_allowed_tags');
789
  variable_del('webform_email_address_format');
790
  variable_del('webform_email_address_individual');
791
  variable_del('webform_default_from_name');
792
  variable_del('webform_default_from_address');
793
  variable_del('webform_default_subject');
794
  variable_del('webform_email_replyto');
795
  variable_del('webform_email_html_capable');
796
  variable_del('webform_default_format');
797
  variable_del('webform_format_override');
798
  variable_del('webform_email_select_max');
799
  variable_del('webform_node_types_primary');
800
  variable_del('webform_date_type');
801
  variable_del('webform_export_format');
802
  variable_del('webform_csv_delimiter');
803
  variable_del('webform_csv_line_ending');
804
  variable_del('webform_export_wordwrap');
805
  variable_del('webform_excel_legacy_exporter');
806
  variable_del('webform_progressbar_style');
807
  variable_del('webform_progressbar_label_first');
808
  variable_del('webform_progressbar_label_confirmation');
809
  variable_del('webform_table');
810
  variable_del('webform_submission_access_control');
811
  variable_del('webform_token_access');
812
  variable_del('webform_update_batch_size');
813
  variable_del('webform_disabled_components');
814

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

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

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

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

    
846
}
847

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

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

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

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

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

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

    
920
  $query->execute();
921
}
922

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1797
    return t('New webform columns added.');
1798
  }
1799
}
1800

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1898
    return t('New webform columns added.');
1899
  }
1900
}
1901

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

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

    
1928
  return t('Custom email columns successfully changed.');
1929
}
1930

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

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

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

    
1974
  // Begin a transaction for updating the serial numbers. The transaction will
1975
  // commit when $txn is unset or goesout-of-scope.
1976
  $txn = db_transaction();
1977

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

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

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

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

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

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

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

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

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

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

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

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

    
2186
  // In a site with many, many conditionals, the db_insert may timeout. Start a transaction to ensure atomic action.
2187
  $tx = db_transaction();
2188
  // Copy target information from existing webform_conditional table to new webfrom_condtional_actions table.
2189
  $select = db_select('webform_conditional', 'c')
2190
    ->fields('c', array('nid', 'rgid', 'action', 'target_type', 'target'))
2191
    ->orderBy('nid')->orderBy('rgid');
2192
  $select->addExpression("''", 'argument');
2193
  db_insert('webform_conditional_actions')
2194
    ->from($select)
2195
    ->execute();
2196

    
2197
  // Commit the insert.
2198
  unset($tx);
2199

    
2200
  // Remove unneeded columns from webform_conditional.
2201
  foreach (array('action', 'target_type', 'target') as $fieldname) {
2202
    if (db_field_exists('webform_conditional', $fieldname)) {
2203
      db_drop_field('webform_conditional', $fieldname);
2204
    }
2205
  }
2206

    
2207
  // Rebuild the registry because this point release contains a new class: WebformConditionals.
2208
  registry_rebuild();
2209

    
2210
  return t('Webform database tables were successfully adjusted to allow more than one action for each conditional.');
2211
}
2212

    
2213
/**
2214
 * Convert conditional actions of "hide" to "isn't shown".
2215
 */
2216
function webform_update_7424() {
2217
  $count = db_update('webform_conditional_actions')
2218
    ->fields(array('action' => 'show', 'invert' => 1))
2219
    ->condition('action', 'hide')
2220
    ->execute();
2221
  return format_plural($count,
2222
                       '1 "hide" conditional converted to "isn\'t" shown.',
2223
                       '@count conditionals converted to "isn\'t" shown.');
2224
}
2225

    
2226
/**
2227
 * Add "exclude empty" option to emails.
2228
 */
2229
function webform_update_7425() {
2230
  // Add next_serial column to webform.
2231
  $spec = array(
2232
    'description' => 'Determines if the e-mail will include component with an empty value.',
2233
    'type' => 'int',
2234
    'unsigned' => TRUE,
2235
    'size' => 'tiny',
2236
    'not null' => TRUE,
2237
    'default' => 0,
2238
  );
2239
  if (!db_field_exists('webform_emails', 'exclude_empty')) {
2240
    db_add_field('webform_emails', 'exclude_empty', $spec);
2241
  }
2242

    
2243
  // Clear the views cache since this release use the webform_analysis view.
2244
  cache_clear_all('*', 'cache_views', TRUE);
2245

    
2246
  return t('Webform e-mails were sucessfully updated to add the option to exclude empty components.');
2247
}
2248

    
2249
/**
2250
 * Add configuration to continue sending individual e-mails to multiple recipients.
2251
 */
2252
function webform_update_7426() {
2253
  variable_set('webform_email_address_individual', 1);
2254
}
2255

    
2256
/**
2257
 * Add database columns for submission completed and modified timestamps. Sites with many submissions may wish to use drush to execute this update.
2258
 */
2259
function webform_update_7427() {
2260
  // Create new timestamp columns.
2261
  $specs = array(
2262
    'completed' => array(
2263
      'description' => 'Timestamp when the form was submitted as complete (not draft).',
2264
      'type' => 'int',
2265
      'not null' => TRUE,
2266
      'default' => 0,
2267
    ),
2268
    'modified' => array(
2269
      'description' => 'Timestamp when the form was last saved (complete or draft).',
2270
      'type' => 'int',
2271
      'not null' => TRUE,
2272
      'default' => 0,
2273
    ),
2274
  );
2275
  foreach ($specs as $field_name => $spec) {
2276
    if (!db_field_exists('webform_submissions', $field_name)) {
2277
      db_add_field('webform_submissions', $field_name, $spec);
2278
    }
2279
  }
2280

    
2281
  // In a site with many submissions, the db_update may timeout. Start a transaction to ensure atomic action.
2282
  $tx = db_transaction();
2283
  // Copy submitted to completed for non-draft submissions.
2284
  db_update('webform_submissions')
2285
    ->expression('completed', 'submitted')
2286
    ->condition('is_draft', 0)
2287
    ->execute();
2288
  // Commit the update.
2289
  unset($tx);
2290

    
2291
  // Start another transaction.
2292
  $tx = db_transaction();
2293
  db_update('webform_submissions')
2294
    ->expression('modified', 'submitted')
2295
    ->execute();
2296
  // Commit the update.
2297
  unset($tx);
2298

    
2299
  // Clear the views cache since to see the completed and modified views fields.
2300
  cache_clear_all('*', 'cache_views', TRUE);
2301

    
2302
  return t('Webform submissions were updated with completed and modified timestamps.');
2303
}
2304

    
2305
/**
2306
 * Add a "confidential" option to webforms.
2307
 */
2308
function webform_update_7428() {
2309
  // Add confidential column to webform.
2310
  if (!db_field_exists('webform', 'confidential')) {
2311
    $spec = array(
2312
      'description' => 'Boolean value for whether to anonymize submissions.',
2313
      'type' => 'int',
2314
      'size' => 'tiny',
2315
      'not null' => TRUE,
2316
      'default' => 0,
2317
    );
2318
    db_add_field('webform', 'confidential', $spec);
2319
  }
2320

    
2321
  return t('Webforms may now be configured to anonymize confidential submissions.');
2322
}
2323

    
2324
/**
2325
 * Add a column to the submission table to store the page on which to resume a draft. Sites with many, many submissions may wish to execute this update with 'drush updatedb'.
2326
 */
2327
function webform_update_7429() {
2328
  // Add highest_valid_page column to webform_submissions.
2329
  if (!db_field_exists('webform_submissions', 'highest_valid_page')) {
2330
    $spec = array(
2331
      'description' => 'For drafts, the highest validated page number.',
2332
      'type' => 'int',
2333
      'size' => 'small',
2334
      'not null' => TRUE,
2335
      'default' => 0,
2336
    );
2337
    db_add_field('webform_submissions', 'highest_valid_page', $spec);
2338
  }
2339

    
2340
  return t('Webforms will now resume draft submissions on the page where the submitter left off.');
2341
}
2342

    
2343
/**
2344
 * Add a column to the emails table to allow disabling.
2345
 */
2346
function webform_update_7430() {
2347
  // Add status column to webform_emails.
2348
  if (!db_field_exists('webform_emails', 'status')) {
2349
    $spec = array(
2350
      'description' => 'Whether this email is enabled.',
2351
      'type' => 'int',
2352
      'unsigned' => TRUE,
2353
      'size' => 'tiny',
2354
      'not null' => TRUE,
2355
      'default' => 1,
2356
    );
2357
    db_add_field('webform_emails', 'status', $spec);
2358
  }
2359

    
2360
  return t('Webform emails may now be disabled.');
2361
}