Projet

Général

Profil

Paste
Télécharger (81,1 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / webform / webform.install @ b42754b9

1
<?php
2

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

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

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

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

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

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

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

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

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

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

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

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

    
687
  return $schema;
688
}
689

    
690

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

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

    
732
  return $requirements;
733
}
734

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

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

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

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

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

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

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

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

    
847
}
848

    
849
/**
850
 * Implements hook_update_dependencies().
851
 */
852
function webform_update_dependencies() {
853
  // Create the {filter_format} table before trying to use it.
854
  $dependencies['webform'][7301] = array(
855
    'filter' => 7000,
856
  );
857
  // Create the {file_managed} table before trying to use it.
858
  $dependencies['webform'][7319] = array(
859
    'system' => 7034,
860
  );
861
  // Create the {file_usage} table before trying to use it.
862
  $dependencies['webform'][7320] = array(
863
    'system' => 7059,
864
  );
865
  return $dependencies;
866
}
867

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

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

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

    
907
  $query = db_update('webform')
908
    ->fields(array('confirmation_format' => $default_format))
909
    ->isNotNull('confirmation_format');
910

    
911
  if (!empty($numeric_formats)) {
912
    $query->condition('confirmation_format', $numeric_formats, 'NOT IN');
913
  }
914

    
915
  $query->execute();
916
}
917

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

    
928
/**
929
 * Set the default for the "submit_notice" column to 1.
930
 */
931
function webform_update_7303() {
932
  db_change_field('webform', 'submit_notice', 'submit_notice', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 1));
933
}
934

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

    
949
/**
950
 * Set additional_validate and additional_submit columns to allow NULL.
951
 */
952
function webform_update_7305() {
953
  if (db_field_exists('webform', 'additional_validate')) {
954
    db_change_field('webform', 'additional_validate', 'additional_validate', array('type' => 'text', 'not null' => FALSE));
955
    db_change_field('webform', 'additional_submit', 'additional_submit', array('type' => 'text', 'not null' => FALSE));
956
  }
957
}
958

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

    
968
/**
969
 * Update the confirmation_format column for default text format changes.
970
 */
971
function webform_update_7307() {
972
  // Update removed and moved to webform_update_7301().
973
  // See http://drupal.org/node/976102.
974
}
975

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

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

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

    
1016
/**
1017
 * Add an index for nid_uid_sid to webform_submissions.
1018
 */
1019
function webform_update_7311() {
1020
  if (!db_index_exists('webform_submissions', 'nid_uid_sid')) {
1021
    db_add_index('webform_submissions', 'nid_uid_sid', array('nid', 'uid', 'sid'));
1022
  }
1023
}
1024

    
1025
/**
1026
 * Remove unused Webform variables.
1027
 */
1028
function webform_update_7312() {
1029
  variable_del('node_types');
1030
  variable_del('components');
1031
}
1032

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

    
1062
/**
1063
 * Add webform_last_download table to store last downloaded sid per user.
1064
 */
1065
function webform_update_7314() {
1066
  // Safety check to prevent recreating the webform_last_download table.
1067
  if (db_table_exists('webform_last_download')) {
1068
    return;
1069
  }
1070

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

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

    
1110
/**
1111
 * Add additional columns for total submission limit.
1112
 */
1113
function webform_update_7316() {
1114
  if (!db_field_exists('webform', 'total_submit_limit')) {
1115
    db_add_field('webform', 'total_submit_limit', array('type' => 'int', 'not null' => TRUE, 'default' => -1));
1116
  }
1117

    
1118
  if (!db_field_exists('webform', 'total_submit_interval')) {
1119
    db_add_field('webform', 'total_submit_interval', array('type' => 'int', 'not null' => TRUE, 'default' => -1));
1120
  }
1121
}
1122

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

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

    
1154
  return t('File components updated to support AJAX uploading.');
1155
}
1156

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

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

    
1183
  // Determine each submission with which a file is associated.
1184
  if (!empty($files)) {
1185
    foreach ($files as $fid => $file) {
1186
      $file = (object) $file;
1187
      $sids = db_query('SELECT wsd.sid FROM {webform_component} wc INNER JOIN {webform_submitted_data} wsd ON wc.nid = wsd.nid AND wc.type = :file WHERE data = :fid', array(':file' => 'file', ':fid' => $file->fid))->fetchAllAssoc('sid', PDO::FETCH_ASSOC);
1188
      foreach ($sids as $sid => $row) {
1189
        // We use a db_merge() instead of file_usage_add() to prevent problems
1190
        // in the event this update was run twice. No file provided by Webform
1191
        // should ever be in use more than once at this point.
1192
        db_merge('file_usage')
1193
          ->key(array(
1194
            'fid' => $file->fid,
1195
            'type' => 'submission',
1196
            'module' => 'webform',
1197
            'id' => $sid,
1198
          ))
1199
          ->fields(array(
1200
            'count' => 1,
1201
          ))
1202
          ->execute();
1203
      }
1204

    
1205
      // Update our progress information for the batch update.
1206
      $sandbox['progress']++;
1207
      $sandbox['last_fid_processed'] = $file->fid;
1208
    }
1209
  }
1210

    
1211
  // If less than limit was processed, the update process is finished.
1212
  if (count($files) < $limit || $sandbox['progress'] == $sandbox['max']) {
1213
    $finished = TRUE;
1214
  }
1215

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

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

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

    
1284
  $limit = webform_variable_get('webform_update_batch_size');
1285
  $processed_count = _webform_update_7401_batch($sandbox, $patterns, $replacements, $dpatterns, $dreplacements, $limit);
1286

    
1287
  // If less than limit was processed, the update process is finished.
1288
  if ($processed_count < $limit || $sandbox['progress'] == $sandbox['max']) {
1289
    $finished = TRUE;
1290
  }
1291

    
1292
  // If there's no max value then there's nothing to update and we're finished.
1293
  if (empty($sandbox['max']) || isset($finished)) {
1294
    $message = t('Your existing webforms have been upgraded to use the global Drupal 7 token system.');
1295
    if (!module_exists('token')) {
1296
      $message .= ' <strong>' . t('Please download and install the <a href="http://drupal.org/project/token" target="_blank">Token module</a>. Otherwise some tokens will not be rendered.') . '</strong>';
1297
    }
1298
    return $message;
1299
  }
1300
  else {
1301
    // Indicate our current progress to the batch update system.
1302
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
1303
  }
1304
}
1305

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

    
1319
    // Update tokens in variables.
1320
    $variables = array(
1321
      'webform_default_subject',
1322
      'webform_default_from_name',
1323
      'webform_default_from_address',
1324
    );
1325
    foreach ($variables as $variable) {
1326
      $value = variable_get($variable, NULL);
1327
      if ($value !== NULL) {
1328
        $value = str_replace($patterns, $replacements, $value);
1329
        $value = preg_replace($dpatterns, $dreplacements, $value);
1330
        variable_set($variable, $value);
1331
      }
1332
    }
1333
  }
1334

    
1335
  $webforms = db_select('webform', 'w')
1336
    ->fields('w')
1337
    ->condition('nid', $sandbox['last_nid_processed'], '>')
1338
    ->orderBy('nid', 'ASC')
1339
    ->range(0, $limit)
1340
    ->execute()
1341
    ->fetchAllAssoc('nid', PDO::FETCH_ASSOC);
1342

    
1343
  foreach ($webforms as $nid => $webform) {
1344
    // Update the webform record itself.
1345
    $original = $webform;
1346
    $parts = array(
1347
      'confirmation',
1348
      'redirect_url',
1349
    );
1350
    foreach ($parts as $part) {
1351
      $webform[$part] = str_replace($patterns, $replacements, $webform[$part]);
1352
      $webform[$part] = preg_replace($dpatterns, $dreplacements, $webform[$part]);
1353
    }
1354
    if ($webform != $original) {
1355
      drupal_write_record('webform', $webform, array('nid'));
1356
    }
1357

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

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

    
1402
    // Update the last processed NID.
1403
    $sandbox['last_nid_processed'] = $nid;
1404
    $sandbox['progress']++;
1405
  }
1406
  return count($webforms);
1407
}
1408

    
1409
/**
1410
 * Add the webform_conditional database table.
1411
 */
1412
function webform_update_7402() {
1413
  // Sanity checks.
1414
  if (db_table_exists('webform_conditional')) {
1415
    // Both tables exist, so these are Webform-core provided tables.
1416
    if (db_table_exists('webform_conditional_rules')) {
1417
      return;
1418
    }
1419
    // Webform Conditional module was installed previously but not uninstalled.
1420
    else {
1421
      throw new DrupalUpdateException(t('The "Webform Conditional" module has previously been installed on your site. Either uninstall the module or drop the database table "webform_conditional".'));
1422
    }
1423
  }
1424

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

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

    
1526
  db_create_table('webform_conditional', $schema['webform_conditional']);
1527
  db_create_table('webform_conditional_rules', $schema['webform_conditional_rules']);
1528
  // Rebuild schema so that webform_update_7403() can use drupal_write_record().
1529
  if (db_table_exists('system') && db_field_exists('system', 'status')) {
1530
    drupal_get_schema(NULL, TRUE);
1531
  }
1532
}
1533

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

    
1549
  $limit = webform_variable_get('webform_update_batch_size');
1550
  $webforms = db_select('webform', 'w')
1551
    ->fields('w')
1552
    ->condition('nid', $sandbox['last_nid_processed'], '>')
1553
    ->orderBy('nid', 'ASC')
1554
    ->range(0, $limit)
1555
    ->execute()
1556
    ->fetchAllAssoc('nid', PDO::FETCH_ASSOC);
1557

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

    
1572
      // Remove conditional properties if present.
1573
      if (isset($component['extra']['conditional_component'])) {
1574
        unset($component['extra']['conditional_component']);
1575
      }
1576
      if (isset($component['extra']['conditional_operator'])) {
1577
        unset($component['extra']['conditional_operator']);
1578
      }
1579
      if (isset($component['extra']['conditional_values'])) {
1580
        unset($component['extra']['conditional_values']);
1581

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

    
1617
            // Cannot use drupal_write_record for webform_conditional because
1618
            // the current schema has fewer fields than the schema in use during
1619
            // this hook_update_N function.
1620
            db_insert('webform_conditional')
1621
              ->fields($conditional)
1622
              ->execute();
1623
            foreach ($rules as $rule) {
1624
              drupal_write_record('webform_conditional_rules', $rule);
1625
            }
1626
            $sandbox['converted_count']++;
1627
            $rgid++;
1628
          }
1629
        }
1630
      }
1631

    
1632
      // Update the component with the conditional properties removed.
1633
      if ($component['extra'] != $original_extra) {
1634
        $component['extra'] = serialize($component['extra']);
1635
        drupal_write_record('webform_component', $component, array('nid', 'cid'));
1636
      }
1637
    }
1638

    
1639
    // Update the last processed NID.
1640
    $sandbox['last_nid_processed'] = $nid;
1641
    $sandbox['progress']++;
1642
  }
1643

    
1644
  // If less than limit was processed, the update process is finished.
1645
  if (count($webforms) < $limit || $sandbox['progress'] == $sandbox['max']) {
1646
    $finished = TRUE;
1647
  }
1648

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

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

    
1671
/**
1672
 * Add an "extra" column to the e-mail table for for non-queryable options.
1673
 */
1674
function webform_update_7405() {
1675
  $schema = array(
1676
    '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.',
1677
    'type' => 'text',
1678
    'not null' => TRUE,
1679
    'initial' => '',
1680
  );
1681
  if (!db_field_exists('webform_emails', 'extra')) {
1682
    db_add_field('webform_emails', 'extra', $schema);
1683
  }
1684
}
1685

    
1686
/**
1687
 * Convert the "webform_use_cookies" setting to "webform_tracking_mode".
1688
 */
1689
function webform_update_7406() {
1690
  // Previously, we only had "strict" and "ip_address" checking. Using cookies
1691
  // meant cookies in addition to IP address.
1692
  $use_cookies = variable_get('webform_use_cookies');
1693
  if (isset($use_cookies)) {
1694
    variable_set('webform_tracking_mode', $use_cookies ? 'strict' : 'ip_address');
1695
    variable_del('webform_use_cookies');
1696
  }
1697
}
1698

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

    
1736
/**
1737
 * Rename the "mandatory" column to "required".
1738
 */
1739
function webform_update_7408() {
1740
  $spec = array(
1741
    'description' => 'Boolean flag for if this component is required.',
1742
    'type' => 'int',
1743
    'size' => 'tiny',
1744
    'not null' => TRUE,
1745
    'default' => 0,
1746
  );
1747
  if (!db_field_exists('webform_component', 'required')) {
1748
    db_change_field('webform_component', 'mandatory', 'required', $spec);
1749
  }
1750
}
1751

    
1752
/**
1753
 * Add progress bar columns to the webform table.
1754
 */
1755
function webform_update_7409() {
1756
  if (!db_field_exists('webform', 'progressbar_bar')) {
1757
    $int_schema = array(
1758
      'type' => 'int',
1759
      'size' => 'tiny',
1760
      'not null' => TRUE,
1761
      'default' => 0,
1762
    );
1763
    $varchar_schema = array(
1764
      'type' => 'varchar',
1765
      'length' => 255,
1766
    );
1767

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

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

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

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

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

    
1783
    $varchar_schema['description'] = 'Label for the first page of the progress bar.';
1784
    db_add_field('webform', 'progressbar_label_first', $varchar_schema);
1785

    
1786
    $varchar_schema['description'] = 'Label for the last page of the progress bar.';
1787
    db_add_field('webform', 'progressbar_label_confirmation', $varchar_schema);
1788

    
1789
    return t('New webform columns added.');
1790
  }
1791
}
1792

    
1793
/**
1794
 * Remove the "teaser" column from the "webform" table.
1795
 */
1796
function webform_update_7410() {
1797
  if (db_field_exists('webform', 'teaser')) {
1798
    db_drop_field('webform', 'teaser');
1799
    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.');
1800
  }
1801
}
1802

    
1803
/**
1804
 * Remove [submission:values:x] token use of :nolabel and add :withlabel.
1805
 */
1806
function webform_update_7411(&$sandbox) {
1807
  // Define replacements.
1808
  $patterns = array();
1809
  $replacements = array();
1810
  $dpatterns = array(
1811
    '/\[submission:values(:(?!nolabel)[a-z_]+)+\]/m',
1812
    '/\[submission:values(:([a-z_:]+))?:nolabel\]/m',
1813
  );
1814
  $dreplacements = array(
1815
    '[submission:values$1:withlabel]',
1816
    '[submission:values$1]',
1817
  );
1818

    
1819
  $limit = webform_variable_get('webform_update_batch_size');
1820
  $processed_count = _webform_update_7401_batch($sandbox, $patterns, $replacements, $dpatterns, $dreplacements, $limit);
1821

    
1822
  // If less than limit was processed, the update process is finished.
1823
  if ($processed_count < $limit || $sandbox['progress'] == $sandbox['max']) {
1824
    $finished = TRUE;
1825
  }
1826

    
1827
  // If there's no max value then there's nothing to update and we're finished.
1828
  if (empty($sandbox['max']) || isset($finished)) {
1829
    return t('Replaced tokens using [submission:values:x] with [submission:values:x:withlabel].');
1830
  }
1831
  else {
1832
    // Indicate our current progress to the batch update system.
1833
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
1834
  }
1835
}
1836

    
1837
/**
1838
 * Split webform_node_types as content type specific.
1839
 */
1840
function webform_update_7412() {
1841
  $types = variable_get('webform_node_types', array('webform'));
1842
  foreach ($types as $type) {
1843
    variable_set('webform_node_' . $type, TRUE);
1844
  }
1845
  variable_del('webform_node_types');
1846
}
1847

    
1848
/**
1849
 * Add preview page columns to the webform table.
1850
 */
1851
function webform_update_7413() {
1852
  if (!db_field_exists('webform', 'preview')) {
1853
    $int_schema = array(
1854
      'type' => 'int',
1855
      'size' => 'tiny',
1856
      'not null' => TRUE,
1857
      'default' => 0,
1858
    );
1859
    $varchar_schema = array(
1860
      'type' => 'varchar',
1861
      'length' => 255,
1862
    );
1863
    $text_schema = array(
1864
      'type' => 'text',
1865
      'not null' => TRUE,
1866
      'initial' => '',
1867
    );
1868

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

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

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

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

    
1881
    $text_schema['description'] = 'Text shown on the preview page of the form.';
1882
    db_add_field('webform', 'preview_message', $text_schema);
1883

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

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

    
1890
    return t('New webform columns added.');
1891
  }
1892
}
1893

    
1894
/**
1895
 * Change email subject, from_name and from_address fields to type 'TEXT'.
1896
 */
1897
function webform_update_7414() {
1898
  $schema = array(
1899
    'subject' => array(
1900
      '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.',
1901
      'type' => 'text',
1902
      'not null' => FALSE,
1903
    ),
1904
    'from_name' => array(
1905
      '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.',
1906
      'type' => 'text',
1907
      'not null' => FALSE,
1908
    ),
1909
    'from_address' => array(
1910
      '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.',
1911
      'type' => 'text',
1912
      'not null' => FALSE,
1913
    ),
1914
  );
1915

    
1916
  db_change_field('webform_emails', 'subject', 'subject', $schema['subject']);
1917
  db_change_field('webform_emails', 'from_name', 'from_name', $schema['from_name']);
1918
  db_change_field('webform_emails', 'from_address', 'from_address', $schema['from_address']);
1919

    
1920
  return t('Custom email columns successfully changed.');
1921
}
1922

    
1923
/**
1924
 * Increase length of webform.redirect_url.
1925
 */
1926
function webform_update_7415() {
1927
  $spec = array(
1928
    'description' => 'The URL a user is redirected to after submitting a form.',
1929
    'type' => 'varchar',
1930
    'length' => 2048,
1931
    'default' => '<confirmation>',
1932
  );
1933
  db_change_field('webform', 'redirect_url', 'redirect_url', $spec);
1934
}
1935

    
1936
/**
1937
 * Add columns for serial numbered submissions. Add serial numbers to existing submissions.
1938
 */
1939
function webform_update_7416() {
1940
  // SQL database implementations vary in how DDL statements are handled within
1941
  // transactions. Since this update routine should be run in maintenance mode
1942
  // without concurrent transactions, no DDL statements are executed within a
1943
  // transaction.
1944

    
1945
  // Add next_serial column to webform.
1946
  $spec = array(
1947
    'description' => 'The serial number to give to the next submission to this webform.',
1948
    'type' => 'int',
1949
    'unsigned' => TRUE,
1950
    'not null' => TRUE,
1951
    'default' => 1,
1952
  );
1953
  if (!db_field_exists('webform', 'next_serial')) {
1954
    db_add_field('webform', 'next_serial', $spec);
1955
  }
1956

    
1957
  // Add serial column to webform_submissions.
1958
  $spec = array(
1959
    'description' => 'The serial number of this submission.',
1960
    'type' => 'int',
1961
    'unsigned' => TRUE,
1962
  );
1963
  if (!db_field_exists('webform_submissions', 'serial')) {
1964
    db_add_field('webform_submissions', 'serial', $spec);
1965
  }
1966

    
1967
  // Begin a transaction for updating the serial numbers. The transaction will
1968
  // commit when $txn is unset or goesout-of-scope.
1969
  $txn = db_transaction();
1970

    
1971
  // Delete stray entries from the Webform tables before adding serial numbers.
1972
  db_query("DELETE FROM {webform_submissions} WHERE nid NOT IN (SELECT nid FROM {webform})");
1973
  db_query("DELETE FROM {webform_submitted_data} WHERE nid NOT IN (SELECT nid FROM {webform})");
1974

    
1975

    
1976
  // Add serial numbers to all submissions.
1977

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

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

    
2022
  return t('Columns for serial numbered submissions successfully added. Serial numbers added to existing submissions.');
2023
}
2024

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

    
2044
  db_change_field('webform_component', 'name', 'name', array('type' => 'text', 'not null' => TRUE));
2045
  return $result;
2046
}
2047

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

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

    
2082
/**
2083
 * Extend access to the WEBFORM tab to anyone who can access the node's EDIT tab.
2084
 */
2085
function webform_update_7420() {
2086
  user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('edit webform components'));
2087
}
2088

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

    
2102
/**
2103
 * Remove the administrator option "Include webform forms in search index" and rely on the Search Index view mode instead.
2104
 */
2105
function webform_update_7422() {
2106
  variable_del('webform_search_index');
2107
  return module_exists('search')
2108
          ? t('Webform forms will now be included in the search index only if the Webform "field" is displayed in the "Search index" view mode.')
2109
          : NULL;
2110
}
2111

    
2112
/**
2113
 * 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.
2114
 */
2115
function webform_update_7423() {
2116
  // Create webform_condtional_actions table.
2117
  // The table might already exist if this update previously timed-out
2118
  if (!db_table_exists('webform_conditional_actions')) {
2119
    $schema['webform_conditional_actions'] = array(
2120
      'description' => 'Holds information about conditional actions.',
2121
      'fields' => array(
2122
        'nid' => array(
2123
          'description' => 'The node identifier of a webform.',
2124
          'type' => 'int',
2125
          'unsigned' => TRUE,
2126
          'not null' => TRUE,
2127
          'default' => 0,
2128
        ),
2129
        'rgid' => array(
2130
          'description' => 'The rule group identifier for this group of rules.',
2131
          'type' => 'int',
2132
          'size' => 'small',
2133
          'unsigned' => TRUE,
2134
          'not null' => TRUE,
2135
          'default' => 0,
2136
        ),
2137
        'aid' => array(
2138
          'description' => 'The rule identifier for this conditional action.',
2139
          'type' => 'int',
2140
          'size' => 'small',
2141
          'unsigned' => TRUE,
2142
          'not null' => TRUE,
2143
          'default' => 0,
2144
        ),
2145
        'target_type' => array(
2146
          'description' => 'The type of target to be affected. Currently always "component". Indicates what type of ID the "target" column contains.',
2147
          'type' => 'varchar',
2148
          'length' => 128,
2149
        ),
2150
        'target' => array(
2151
          'description' => 'The ID of the target to be affected. Typically a component ID.',
2152
          'type' => 'varchar',
2153
          'length' => 128,
2154
        ),
2155
        'invert' => array(
2156
          'description' => 'If inverted, execute when rule(s) are false.',
2157
          'type' => 'int',
2158
          'size' => 'small',
2159
          'unsigned' => TRUE,
2160
          'not null' => TRUE,
2161
          'default' => 0,
2162
        ),
2163
        'action' => array(
2164
          'description' => 'The action to be performed on the target.',
2165
          'type' => 'varchar',
2166
          'length' => 128,
2167
        ),
2168
        'argument' => array(
2169
          'description' => 'Optional argument for action.',
2170
          'type' => 'text',
2171
        ),
2172
      ),
2173
      'primary key' => array('nid', 'rgid', 'aid'),
2174
    );
2175
    db_create_table('webform_conditional_actions', $schema['webform_conditional_actions']);
2176
  }
2177

    
2178
  // In a site with many, many conditionals, the db_insert may timeout. Start a transaction to ensure atomic action.
2179
  $tx = db_transaction();
2180
  // Copy target information from existing webform_conditional table to new webfrom_condtional_actions table.
2181
  $select = db_select('webform_conditional', 'c')
2182
    ->fields('c', array('nid', 'rgid', 'action', 'target_type', 'target'))
2183
    ->orderBy('nid')->orderBy('rgid');
2184
  $select->addExpression("''", 'argument');
2185
  db_insert('webform_conditional_actions')
2186
    ->from($select)
2187
    ->execute();
2188

    
2189
  // Commit the insert
2190
  unset($tx);
2191

    
2192
  // Remove unneeded columns from webform_conditional.
2193
  foreach (array('action', 'target_type', 'target') as $fieldname) {
2194
    if (db_field_exists('webform_conditional', $fieldname)) {
2195
      db_drop_field('webform_conditional', $fieldname);
2196
    }
2197
  }
2198

    
2199
  // Rebuild the registry because this point release contains a new class: WebformConditionals.
2200
  registry_rebuild();
2201

    
2202
  return t('Webform database tables were successfully adjusted to allow more than one action for each conditional.');
2203
}
2204

    
2205
/**
2206
 * Convert conditional actions of "hide" to "isn't shown".
2207
 */
2208
function webform_update_7424() {
2209
  $count = db_update('webform_conditional_actions')
2210
    ->fields(array('action' => 'show', 'invert' => 1))
2211
    ->condition('action', 'hide')
2212
    ->execute();
2213
  return format_plural($count,
2214
                       '1 "hide" conditional converted to "isn\'t" shown.',
2215
                       '@count conditionals converted to "isn\'t" shown.');
2216
}
2217

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

    
2235
  // Clear the views cache since this release use the webform_analysis view.
2236
  cache_clear_all('*', 'cache_views', TRUE);
2237

    
2238
  return t('Webform e-mails were sucessfully updated to add the option to exclude empty components.');
2239
}
2240

    
2241
/**
2242
 * Add configuration to continue sending individual e-mails to multiple recipients.
2243
 */
2244
function webform_update_7426() {
2245
  variable_set('webform_email_address_individual', 1);
2246
}
2247

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

    
2273
  // In a site with many submissions, the db_update may timeout. Start a transaction to ensure atomic action.
2274
  $tx = db_transaction();
2275
  // Copy submitted to completed for non-draft submissions.
2276
  db_update('webform_submissions')
2277
    ->expression('completed', 'submitted')
2278
    ->condition('is_draft', 0)
2279
    ->execute();
2280
  // Commit the update.
2281
  unset($tx);
2282

    
2283
  // Start another transaction.
2284
  $tx = db_transaction();
2285
  db_update('webform_submissions')
2286
    ->expression('modified', 'submitted')
2287
    ->execute();
2288
  // Commit the update.
2289
  unset($tx);
2290

    
2291
  // Clear the views cache since to see the completed and modified views fields.
2292
  cache_clear_all('*', 'cache_views', TRUE);
2293

    
2294
  return t('Webform submissions were updated with completed and modified timestamps.');
2295
}
2296

    
2297
/**
2298
 * Add a "confidential" option to webforms.
2299
 */
2300
function webform_update_7428() {
2301
  // Add confidential column to webform.
2302
  if (!db_field_exists('webform', 'confidential')) {
2303
    $spec = array(
2304
      'description' => 'Boolean value for whether to anonymize submissions.',
2305
      'type' => 'int',
2306
      'size' => 'tiny',
2307
      'not null' => TRUE,
2308
      'default' => 0,
2309
    );
2310
    db_add_field('webform', 'confidential', $spec);
2311
  }
2312

    
2313
  return t('Webforms may now be configured to anonymize confidential submissions.');
2314
}
2315

    
2316
/**
2317
 * 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'.
2318
 */
2319
function webform_update_7429() {
2320
  // Add highest_valid_page column to webform_submissions.
2321
  if (!db_field_exists('webform_submissions', 'highest_valid_page')) {
2322
    $spec = array(
2323
      'description' => 'For drafts, the highest validated page number.',
2324
      'type' => 'int',
2325
      'size' => 'small',
2326
      'not null' => TRUE,
2327
      'default' => 0,
2328
    );
2329
    db_add_field('webform_submissions', 'highest_valid_page', $spec);
2330
  }
2331

    
2332
  return t('Webforms will now resume draft submissions on the page where the submitter left off.');
2333
}
2334

    
2335
/**
2336
 * Add a column to the emails table to allow disabling.
2337
 */
2338
function webform_update_7430() {
2339
  // Add status column to webform_emails.
2340
  if (!db_field_exists('webform_emails', 'status')) {
2341
    $spec = array(
2342
      'description' => 'Whether this email is enabled.',
2343
      'type' => 'int',
2344
      'unsigned' => TRUE,
2345
      'size' => 'tiny',
2346
      'not null' => TRUE,
2347
      'default' => 1,
2348
    );
2349
    db_add_field('webform_emails', 'status', $spec);
2350
  }
2351

    
2352
  return t('Webform emails may now be disabled.');
2353
}