Projet

Général

Profil

Paste
Télécharger (78,9 ko) Statistiques
| Branche: | Révision:

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

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 requirements'),
712
      'value' => phpversion(),
713
      'severity' => REQUIREMENT_ERROR,
714
      'description' => $t('Webform requires PHP 5.3 or higher.'),
715
    );
716
  }
717

    
718
  return $requirements;
719
}
720

    
721
/**
722
 * Implements hook_install().
723
 */
724
function webform_install() {
725
  module_load_include('inc', 'node', 'content_types');
726
  db_update('system')
727
    ->condition('name', 'webform')
728
    ->condition('type', 'module')
729
    ->fields(array('weight' => -1))
730
    ->execute();
731

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

    
761
  // Note: MS SQL Server does not support size-limited indexes and the column
762
  // type (text) is too big to fit inside index size limits.
763
  if (!db_index_exists('webform_submitted_data', 'data') && db_driver() != 'sqlsrv') {
764
    db_add_index('webform_submitted_data', 'data', array(array('data', 64)));
765
  }
766
}
767

    
768
/**
769
 * Implements hook_uninstall().
770
 */
771
function webform_uninstall() {
772
  // Unset webform variables.
773
  variable_del('webform_blocks');
774
  variable_del('webform_tracking_mode');
775
  variable_del('webform_allowed_tags');
776
  variable_del('webform_email_address_format');
777
  variable_del('webform_email_address_individual');
778
  variable_del('webform_default_from_name');
779
  variable_del('webform_default_from_address');
780
  variable_del('webform_default_subject');
781
  variable_del('webform_email_replyto');
782
  variable_del('webform_email_html_capable');
783
  variable_del('webform_default_format');
784
  variable_del('webform_format_override');
785
  variable_del('webform_email_select_max');
786
  variable_del('webform_node_types_primary');
787
  variable_del('webform_date_type');
788
  variable_del('webform_export_format');
789
  variable_del('webform_csv_delimiter');
790
  variable_del('webform_export_wordwrap');
791
  variable_del('webform_excel_legacy_exporter');
792
  variable_del('webform_progressbar_style');
793
  variable_del('webform_progressbar_label_first');
794
  variable_del('webform_progressbar_label_confirmation');
795
  variable_del('webform_table');
796
  variable_del('webform_submission_access_control');
797
  variable_del('webform_update_batch_size');
798
  variable_del('webform_disabled_components');
799

    
800
  foreach (node_type_get_names() as $type => $name) {
801
    variable_del('webform_node_' . $type);
802
  }
803

    
804
  $component_list = array();
805
  $path = drupal_get_path('module', 'webform') . '/components';
806
  $files = file_scan_directory($path, '/^.*\.inc$/');
807
  foreach ($files as $filename => $file) {
808
    variable_del('webform_enable_' . $file->name, 1);
809
  }
810

    
811
  // Delete uploaded files.
812
  $filepath = file_build_uri('webform');
813
  file_unmanaged_delete_recursive($filepath);
814

    
815
  // Delete the content type "webform" if:
816
  //   a) there are no existing nodes of type webform and
817
  //   b) no additional fields have been defined for node type webform, beyond
818
  //      the default body field.
819
  $query = new EntityFieldQuery();
820
  $results = $query->entityCondition('entity_type', 'node')
821
    ->entityCondition('bundle', 'webform')
822
    ->range(0, 1)
823
    ->execute();
824
  $instances = field_info_instances('node', 'webform');
825
  unset($instances['body']);
826
  if (!$results && !$instances) {
827
    node_type_delete('webform');
828
    drupal_flush_all_caches();
829
  }
830

    
831
}
832

    
833
/**
834
 * Set the minimum upgrade version.
835
 *
836
 * Currently you cannot upgrade from 2.x in Drupal 6 to 3.x in Drupal 7. However
837
 * there are no database changes between the 3.x versions, so no update is
838
 * needed at all to move from 3.x in Drupal 6 to Drupal 7.
839
 */
840
function webform_update_last_removed() {
841
  return 6313;
842
}
843

    
844
/**
845
 * Allow the confirmation format column to have a NULL value.
846
 */
847
function webform_update_7301() {
848
  // These changes are modeled after user_update_7010().
849
  db_change_field('webform', 'confirmation_format', 'confirmation_format', array(
850
    'description' => 'The {filter_format}.format of the confirmation message.',
851
    'type' => 'int',
852
    'unsigned' => TRUE,
853
    'not null' => FALSE,
854
  ));
855
  db_update('webform')
856
    ->fields(array('confirmation_format' => NULL))
857
    ->condition('confirmation', '')
858
    ->condition('confirmation_format', 0)
859
    ->execute();
860
  $existing_formats = db_query("SELECT format FROM {filter_format}")->fetchCol();
861
  $default_format = variable_get('filter_default_format', 1);
862

    
863
  // Since Webform may be updated separately from Drupal core, not all format
864
  // names may be numbers when running this update.
865
  $numeric_formats = array();
866
  foreach ($existing_formats as $format_name) {
867
    if (is_numeric($format_name)) {
868
      $numeric_formats[] = (int) $format_name;
869
    }
870
  }
871

    
872
  $query = db_update('webform')
873
    ->fields(array('confirmation_format' => $default_format))
874
    ->isNotNull('confirmation_format');
875

    
876
  if (!empty($numeric_formats)) {
877
    $query->condition('confirmation_format', $numeric_formats, 'NOT IN');
878
  }
879

    
880
  $query->execute();
881
}
882

    
883
/**
884
 * Add columns for e-mail HTML and attachment settings.
885
 */
886
function webform_update_7302() {
887
  if (!db_field_exists('webform_emails', 'html')) {
888
    db_add_field('webform_emails', 'html', array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'default' => 0, 'not null' => TRUE));
889
    db_add_field('webform_emails', 'attachments', array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'default' => 0, 'not null' => TRUE));
890
  }
891
}
892

    
893
/**
894
 * Set the default for the "submit_notice" column to 1.
895
 */
896
function webform_update_7303() {
897
  db_change_field('webform', 'submit_notice', 'submit_notice', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 1));
898
}
899

    
900
/**
901
 * Add field for block feature and redirection setting.
902
 */
903
function webform_update_7304() {
904
  if (!db_field_exists('webform', 'block')) {
905
    db_add_field('webform', 'block', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0));
906
    db_change_field('webform', 'redirect_url', 'redirect_url', array('type' => 'varchar', 'length' => 255, 'default' => '<confirmation>'));
907
    db_update('webform')
908
      ->fields(array('redirect_url' => 'confirmation'))
909
      ->condition('redirect_url', '')
910
      ->execute();
911
  }
912
}
913

    
914
/**
915
 * Set additional_validate and additional_submit columns to allow NULL.
916
 */
917
function webform_update_7305() {
918
  if (db_field_exists('webform', 'additional_validate')) {
919
    db_change_field('webform', 'additional_validate', 'additional_validate', array('type' => 'text', 'not null' => FALSE));
920
    db_change_field('webform', 'additional_submit', 'additional_submit', array('type' => 'text', 'not null' => FALSE));
921
  }
922
}
923

    
924
/**
925
 * Add column for webform status (open or closed).
926
 */
927
function webform_update_7306() {
928
  if (!db_field_exists('webform', 'status')) {
929
    db_add_field('webform', 'status', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 1));
930
  }
931
}
932

    
933
/**
934
 * Update the confirmation_format column for default text format changes.
935
 */
936
function webform_update_7307() {
937
  // Update removed and moved to webform_update_7301().
938
  // See http://drupal.org/node/976102.
939
}
940

    
941
/**
942
 * Update the confirmation_format column to allow it to store strings.
943
 */
944
function webform_update_7308() {
945
  db_change_field('webform', 'confirmation_format', 'confirmation_format', array(
946
    'description' => 'The {filter_format}.format of the confirmation message.',
947
    'type' => 'varchar',
948
    'length' => 255,
949
    'not null' => FALSE,
950
  ));
951
}
952

    
953
/**
954
 * Add the ability to auto-save as draft between pages.
955
 */
956
function webform_update_7309() {
957
  if (!db_field_exists('webform', 'auto_save')) {
958
    db_add_field('webform', 'auto_save', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0));
959
  }
960
}
961

    
962
/**
963
 * Remove orphaned and unnecessary rows in the webform table.
964
 */
965
function webform_update_7310() {
966
  $result = db_query("SELECT nid FROM {webform} WHERE
967
    nid NOT IN
968
    (SELECT DISTINCT(w1.nid) FROM {webform} w1 INNER JOIN {webform_component} wc ON w1.nid = wc.nid)
969
    AND nid NOT IN
970
    (SELECT w2.nid FROM {webform} w2 INNER JOIN {node} n ON w2.nid = n.nid WHERE n.type = 'webform')"
971
  );
972
  $empty_nids = array();
973
  foreach ($result as $row) {
974
    $empty_nids[] = $row->nid;
975
  }
976
  if (!empty($empty_nids)) {
977
    db_delete('webform')->condition('nid', $empty_nids, 'IN')->execute();
978
  }
979
}
980

    
981
/**
982
 * Add an index for nid_uid_sid to webform_submissions.
983
 */
984
function webform_update_7311() {
985
  if (!db_index_exists('webform_submissions', 'nid_uid_sid')) {
986
    db_add_index('webform_submissions', 'nid_uid_sid', array('nid', 'uid', 'sid'));
987
  }
988
}
989

    
990
/**
991
 * Remove unused Webform variables.
992
 */
993
function webform_update_7312() {
994
  variable_del('node_types');
995
  variable_del('components');
996
}
997

    
998
/**
999
 * Convert the Date component start and end year options to start and end date.
1000
 */
1001
function webform_update_7313() {
1002
  $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
1003
    ->fields('wc')
1004
    ->condition('type', 'date')
1005
    ->execute();
1006
  foreach ($result as $component) {
1007
    $component['extra'] = unserialize($component['extra']);
1008
    if (!isset($component['extra']['start_date']) && !isset($component['end_date'])) {
1009
      foreach (array('year_start' => 'start_date', 'year_end' => 'end_date') as $key => $replacement) {
1010
        $value = isset($component['extra'][$key]) ? trim($component['extra'][$key]) : '';
1011
        // Relative years.
1012
        if (preg_match('/[-+][ ]*[0-9]+/', $value)) {
1013
          $component['extra'][$replacement] = ($value == 1) ? ($value . ' year') : ($value . ' years');
1014
        }
1015
        // Absolute years.
1016
        elseif (is_numeric($value)) {
1017
          $component['extra'][$replacement] = 'Dec 31 ' . $value;
1018
        }
1019
        unset($component['extra'][$key]);
1020
      }
1021
      $component['extra'] = serialize($component['extra']);
1022
      drupal_write_record('webform_component', $component, array('nid', 'cid'));
1023
    }
1024
  }
1025
}
1026

    
1027
/**
1028
 * Add webform_last_download table to store last downloaded sid per user.
1029
 */
1030
function webform_update_7314() {
1031
  // Safety check to prevent recreating the webform_last_download table.
1032
  if (db_table_exists('webform_last_download')) {
1033
    return;
1034
  }
1035

    
1036
  $schema['webform_last_download'] = array(
1037
    'description' => 'Stores last submission number per user download.',
1038
    'fields' => array(
1039
      'nid' => array(
1040
        'description' => 'The node identifier of a webform.',
1041
        'type' => 'int',
1042
        'unsigned' => TRUE,
1043
        'not null' => TRUE,
1044
        'default' => 0,
1045
      ),
1046
      'uid' => array(
1047
        'description' => 'The user identifier.',
1048
        'type' => 'int',
1049
        'unsigned' => TRUE,
1050
        'not null' => TRUE,
1051
        'default' => 0,
1052
      ),
1053
      'sid' => array(
1054
        'description' => 'The last downloaded submission number.',
1055
        'type' => 'int',
1056
        'unsigned' => TRUE,
1057
        'not null' => TRUE,
1058
        'default' => 0,
1059
      ),
1060
    ),
1061
    'primary key' => array('nid', 'uid'),
1062
  );
1063
  db_create_table('webform_last_download', $schema['webform_last_download']);
1064
}
1065

    
1066
/**
1067
 * Add column for timestamp of last requested CSV download.
1068
 */
1069
function webform_update_7315() {
1070
  if (!db_field_exists('webform_last_download', 'requested')) {
1071
    db_add_field('webform_last_download', 'requested', array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0));
1072
  }
1073
}
1074

    
1075
/**
1076
 * Add additional columns for total submission limit.
1077
 */
1078
function webform_update_7316() {
1079
  if (!db_field_exists('webform', 'total_submit_limit')) {
1080
    db_add_field('webform', 'total_submit_limit', array('type' => 'int', 'not null' => TRUE, 'default' => -1));
1081
  }
1082

    
1083
  if (!db_field_exists('webform', 'total_submit_interval')) {
1084
    db_add_field('webform', 'total_submit_interval', array('type' => 'int', 'not null' => TRUE, 'default' => -1));
1085
  }
1086
}
1087

    
1088
/**
1089
 * Add an index for 'nid_sid' to webform_submissions.
1090
 */
1091
function webform_update_7317() {
1092
  // Even though we already have an index 'nid_uid_sid', adding the index for
1093
  // 'nid_sid' saves us a tablesort on the node/x/webform-results page.
1094
  if (!db_index_exists('webform_submissions', 'nid_sid')) {
1095
    db_add_index('webform_submissions', 'nid_sid', array('nid', 'sid'));
1096
  }
1097
}
1098

    
1099
/**
1100
 * Upgrade file components to support the new AJAX-upload element.
1101
 */
1102
function webform_update_7318() {
1103
  $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
1104
    ->fields('wc')
1105
    ->condition('type', 'file')
1106
    ->execute();
1107
  foreach ($result as $component) {
1108
    $component['extra'] = unserialize($component['extra']);
1109
    if (!isset($component['extra']['directory'])) {
1110
      $component['extra']['directory'] = $component['extra']['savelocation'];
1111
      $component['extra']['scheme'] = file_default_scheme();
1112
      $component['extra']['filtering']['size'] = $component['extra']['filtering']['size'] . ' KB';
1113
      unset($component['extra']['savelocation']);
1114
      $component['extra'] = serialize($component['extra']);
1115
      drupal_write_record('webform_component', $component, array('nid', 'cid'));
1116
    }
1117
  }
1118

    
1119
  return t('File components updated to support AJAX uploading.');
1120
}
1121

    
1122
/**
1123
 * Add file usage entries for all files uploaded through Webform.
1124
 */
1125
function webform_update_7319(&$sandbox) {
1126
  if (!isset($sandbox['progress'])) {
1127
    // Initialize batch update information.
1128
    $sandbox['progress'] = 0;
1129
    $sandbox['last_fid_processed'] = -1;
1130
    $sandbox['max'] = db_select('file_managed')
1131
      ->condition('uri', '%' . db_like('://webform/') . '%', 'LIKE')
1132
      ->countQuery()
1133
      ->execute()
1134
      ->fetchField();
1135
  }
1136

    
1137
  // Process all files attached to a given revision during the same batch.
1138
  $limit = webform_variable_get('webform_update_batch_size');
1139
  $files = db_select('file_managed', 'f')
1140
    ->fields('f')
1141
    ->condition('uri', '%' . db_like('://webform/') . '%', 'LIKE')
1142
    ->condition('fid', $sandbox['last_fid_processed'], '>')
1143
    ->orderBy('fid', 'ASC')
1144
    ->range(0, $limit)
1145
    ->execute()
1146
    ->fetchAllAssoc('fid', PDO::FETCH_ASSOC);
1147

    
1148
  // Determine each submission with which a file is associated.
1149
  if (!empty($files)) {
1150
    foreach ($files as $fid => $file) {
1151
      $file = (object) $file;
1152
      $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);
1153
      foreach ($sids as $sid => $row) {
1154
        // We use a db_merge() instead of file_usage_add() to prevent problems
1155
        // in the event this update was run twice. No file provided by Webform
1156
        // should ever be in use more than once at this point.
1157
        db_merge('file_usage')
1158
          ->key(array(
1159
            'fid' => $file->fid,
1160
            'type' => 'submission',
1161
            'module' => 'webform',
1162
            'id' => $sid,
1163
          ))
1164
          ->fields(array(
1165
            'count' => 1,
1166
          ))
1167
          ->execute();
1168
      }
1169

    
1170
      // Update our progress information for the batch update.
1171
      $sandbox['progress']++;
1172
      $sandbox['last_fid_processed'] = $file->fid;
1173
    }
1174
  }
1175

    
1176
  // If less than limit was processed, the update process is finished.
1177
  if (count($files) < $limit || $sandbox['progress'] == $sandbox['max']) {
1178
    $finished = TRUE;
1179
  }
1180

    
1181
  // If there's no max value then there's nothing to update and we're finished.
1182
  if (empty($sandbox['max']) || isset($finished)) {
1183
    return t('Webform file entries created in the file_usage table.');
1184
  }
1185
  else {
1186
    // Indicate our current progress to the batch update system.
1187
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
1188
  }
1189
}
1190

    
1191
/**
1192
 * Mark files uploaded through Webform that report active usage permanent.
1193
 */
1194
function webform_update_7320() {
1195
  db_query("UPDATE {file_managed} SET status = 1 WHERE fid IN (SELECT fid FROM {file_usage} WHERE module = :module_name)", array(':module_name' => 'webform'));
1196
}
1197

    
1198
/**
1199
 * Rewrite token replacement system to use D7 tokens.
1200
 *
1201
 * If needed, please download and install the Token module from drupal.org.
1202
 * Otherwise some tokens will not be rendered.
1203
 */
1204
function webform_update_7401(&$sandbox) {
1205
  // Define replacements.
1206
  $patterns = array(
1207
    '%username',
1208
    '%useremail',
1209
    '%uid',
1210
    '%date',
1211
    '%ip_address',
1212
    '%site',
1213
    '%nid',
1214
    '%title',
1215
    '%email_values',
1216
    '%submission_url',
1217
    '%sid',
1218
    '%server[REQUEST_URI]',
1219
    '][', // Used to convert nested arrays of %value and %email.
1220
  );
1221
  $replacements = array(
1222
    '[current-user:name]',
1223
    '[current-user:mail]',
1224
    '[current-user:uid]',
1225
    '[submission:date:long]',
1226
    '[current-user:ip-address]',
1227
    '[site:name]',
1228
    '[node:nid]',
1229
    '[node:title]',
1230
    '[submission:values]',
1231
    '[submission:url]',
1232
    '[submission:sid]',
1233
    '[current-page:url]',
1234
    ':', // Replace "][" with ":" for %value and %email.
1235
  );
1236
  $dpatterns = array(
1237
    '/%get\[([^\]]+)\]/m',
1238
    '/%email\[([^% \n\r\t]+)?\]/m',
1239
    '/%value\[([^% \n\r\t]+)?\]/m',
1240
    '/%profile\[([^\]]+)\]/m',
1241
  );
1242
  $dreplacements = array(
1243
    '[current-page:query:$1]',
1244
    '[submission:values:$1]',
1245
    '[submission:values:$1:nolabel]',
1246
    '[current-user:$1]',
1247
  );
1248

    
1249
  $limit = webform_variable_get('webform_update_batch_size');
1250
  $processed_count = _webform_update_7401_batch($sandbox, $patterns, $replacements, $dpatterns, $dreplacements, $limit);
1251

    
1252
  // If less than limit was processed, the update process is finished.
1253
  if ($processed_count < $limit || $sandbox['progress'] == $sandbox['max']) {
1254
    $finished = TRUE;
1255
  }
1256

    
1257
  // If there's no max value then there's nothing to update and we're finished.
1258
  if (empty($sandbox['max']) || isset($finished)) {
1259
    $message = t('Your existing webforms have been upgraded to use the global Drupal 7 token system.');
1260
    if (!module_exists('token')) {
1261
      $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>';
1262
    }
1263
    return $message;
1264
  }
1265
  else {
1266
    // Indicate our current progress to the batch update system.
1267
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
1268
  }
1269
}
1270

    
1271
/**
1272
 * Utility function to update all the locations that use tokens.
1273
 */
1274
function _webform_update_7401_batch(&$sandbox, $patterns, $replacements, $dpatterns, $dreplacements, $limit) {
1275
  // Set up the initial batch process.
1276
  if (!isset($sandbox['progress'])) {
1277
    $sandbox['progress'] = 0;
1278
    $sandbox['last_nid_processed'] = -1;
1279
    $sandbox['max'] = db_select('webform')
1280
      ->countQuery()
1281
      ->execute()
1282
      ->fetchField();
1283

    
1284
    // Update tokens in variables.
1285
    $variables = array(
1286
      'webform_default_subject',
1287
      'webform_default_from_name',
1288
      'webform_default_from_address',
1289
    );
1290
    foreach ($variables as $variable) {
1291
      $value = variable_get($variable, NULL);
1292
      if ($value !== NULL) {
1293
        $value = str_replace($patterns, $replacements, $value);
1294
        $value = preg_replace($dpatterns, $dreplacements, $value);
1295
        variable_set($variable, $value);
1296
      }
1297
    }
1298
  }
1299

    
1300
  $webforms = db_select('webform', 'w')
1301
    ->fields('w')
1302
    ->condition('nid', $sandbox['last_nid_processed'], '>')
1303
    ->orderBy('nid', 'ASC')
1304
    ->range(0, $limit)
1305
    ->execute()
1306
    ->fetchAllAssoc('nid', PDO::FETCH_ASSOC);
1307

    
1308
  foreach ($webforms as $nid => $webform) {
1309
    // Update the webform record itself.
1310
    $original = $webform;
1311
    $parts = array(
1312
      'confirmation',
1313
      'redirect_url',
1314
    );
1315
    foreach ($parts as $part) {
1316
      $webform[$part] = str_replace($patterns, $replacements, $webform[$part]);
1317
      $webform[$part] = preg_replace($dpatterns, $dreplacements, $webform[$part]);
1318
    }
1319
    if ($webform != $original) {
1320
      drupal_write_record('webform', $webform, array('nid'));
1321
    }
1322

    
1323
    // Update tokens in component configurations.
1324
    $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
1325
      ->fields('wc')
1326
      ->condition('wc.nid', $nid)
1327
      ->execute();
1328
    foreach ($result as $component) {
1329
      $original_extra = $component['extra'];
1330
      $original_value = $component['value'];
1331
      $component['extra'] = unserialize($component['extra']);
1332
      if (isset($component['extra']['description'])) {
1333
        $description = str_replace($patterns, $replacements, $component['extra']['description']);
1334
        $description = preg_replace($dpatterns, $dreplacements, $description);
1335
        $component['extra']['description'] = $description;
1336
      }
1337
      $component['extra'] = serialize($component['extra']);
1338
      $value = str_replace($patterns, $replacements, $component['value']);
1339
      $value = preg_replace($dpatterns, $dreplacements, $value);
1340
      $component['value'] = $value;
1341
      if ($component['extra'] != $original_extra || $component['value'] != $original_value) {
1342
        drupal_write_record('webform_component', $component, array('nid', 'cid'));
1343
      }
1344
    }
1345

    
1346
    // Update tokens in e-mail configurations.
1347
    $result = db_select('webform_emails', 'we', array('fetch' => PDO::FETCH_ASSOC))
1348
      ->fields('we')
1349
      ->condition('we.nid', $nid)
1350
      ->execute();
1351
    foreach ($result as $email) {
1352
      $parts = array(
1353
        'template',
1354
        'subject',
1355
        'from_name',
1356
      );
1357
      $original = $email;
1358
      foreach ($parts as $part) {
1359
        $email[$part] = str_replace($patterns, $replacements, $email[$part]);
1360
        $email[$part] = preg_replace($dpatterns, $dreplacements, $email[$part]);
1361
      }
1362
      if ($email != $original) {
1363
        drupal_write_record('webform_emails', $email, array('nid', 'eid'));
1364
      }
1365
    }
1366

    
1367
    // Update the last processed NID.
1368
    $sandbox['last_nid_processed'] = $nid;
1369
    $sandbox['progress']++;
1370
  }
1371
  return count($webforms);
1372
}
1373

    
1374
/**
1375
 * Add the webform_conditional database table.
1376
 */
1377
function webform_update_7402() {
1378
  // Sanity checks.
1379
  if (db_table_exists('webform_conditional')) {
1380
    // Both tables exist, so these are Webform-core provided tables.
1381
    if (db_table_exists('webform_conditional_rules')) {
1382
      return;
1383
    }
1384
    // Webform Conditional module was installed previously but not uninstalled.
1385
    else {
1386
      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".'));
1387
    }
1388
  }
1389

    
1390
  $schema['webform_conditional'] = array(
1391
    'description' => 'Holds information about conditional logic.',
1392
    'fields' => array(
1393
      'nid' => array(
1394
        'description' => 'The node identifier of a webform.',
1395
        'type' => 'int',
1396
        'unsigned' => TRUE,
1397
        'not null' => TRUE,
1398
        'default' => 0,
1399
      ),
1400
      'rgid' => array(
1401
        'description' => 'The rule group identifier for this group of rules.',
1402
        'type' => 'int',
1403
        'size' => 'small',
1404
        'unsigned' => TRUE,
1405
        'not null' => TRUE,
1406
        'default' => 0,
1407
      ),
1408
      'andor' => array(
1409
        'description' => 'Whether to AND or OR the actions in this group. All actions within the same crid should have the same andor value.',
1410
        'type' => 'varchar',
1411
        'length' => 128,
1412
      ),
1413
      'action' => array(
1414
        '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".',
1415
        'type' => 'varchar',
1416
        'length' => 128,
1417
      ),
1418
      'target_type' => array(
1419
        'description' => 'The type of target to be affected. Either "component" or "email". Indicates what type of ID the "target" column contains.',
1420
        'type' => 'varchar',
1421
        'length' => 128,
1422
      ),
1423
      'target' => array(
1424
        'description' => 'The ID of the target to be affected. Typically a component ID.',
1425
        'type' => 'varchar',
1426
        'length' => 128,
1427
      ),
1428
      'weight' => array(
1429
        'description' => 'Determines the position of this conditional compared to others.',
1430
        'type' => 'int',
1431
        'size' => 'small',
1432
        'not null' => TRUE,
1433
        'default' => 0,
1434
      ),
1435
    ),
1436
    'primary key' => array('nid', 'rgid'),
1437
  );
1438

    
1439
  $schema['webform_conditional_rules'] = array(
1440
    'description' => 'Holds information about conditional logic.',
1441
    'fields' => array(
1442
      'nid' => array(
1443
        'description' => 'The node identifier of a webform.',
1444
        'type' => 'int',
1445
        'unsigned' => TRUE,
1446
        'not null' => TRUE,
1447
        'default' => 0,
1448
      ),
1449
      'rgid' => array(
1450
        'description' => 'The rule group identifier for this group of rules.',
1451
        'type' => 'int',
1452
        'size' => 'small',
1453
        'unsigned' => TRUE,
1454
        'not null' => TRUE,
1455
        'default' => 0,
1456
      ),
1457
      'rid' => array(
1458
        'description' => 'The rule identifier for this conditional rule.',
1459
        'type' => 'int',
1460
        'size' => 'small',
1461
        'unsigned' => TRUE,
1462
        'not null' => TRUE,
1463
        'default' => 0,
1464
      ),
1465
      'source_type' => array(
1466
        'description' => 'The type of source on which the conditional is based. Currently always "component". Indicates what type of ID the "source" column contains.',
1467
        'type' => 'varchar',
1468
        'length' => 128,
1469
      ),
1470
      'source' => array(
1471
        'description' => 'The component ID being used in this condition.',
1472
        'type' => 'int',
1473
        'size' => 'small',
1474
        'unsigned' => TRUE,
1475
        'not null' => TRUE,
1476
        'default' => 0,
1477
      ),
1478
      'operator' => array(
1479
        'description' => 'Which operator (equal, contains, starts with, etc.) should be used for this comparison between the source and value?',
1480
        'type' => 'varchar',
1481
        'length' => 128,
1482
      ),
1483
      'value' => array(
1484
        'description' => 'The value to be compared with source.',
1485
        'type' => 'text',
1486
      ),
1487
    ),
1488
    'primary key' => array('nid', 'rgid', 'rid'),
1489
  );
1490

    
1491
  db_create_table('webform_conditional', $schema['webform_conditional']);
1492
  db_create_table('webform_conditional_rules', $schema['webform_conditional_rules']);
1493
  // Rebuild schema so that webform_update_7403() can use drupal_write_record().
1494
  if (db_table_exists('system') && db_field_exists('system', 'status')) {
1495
    drupal_get_schema(NULL, TRUE);
1496
  }
1497
}
1498

    
1499
/**
1500
 * Convert per-component conditionals to new more flexible conditional system.
1501
 */
1502
function webform_update_7403(&$sandbox) {
1503
  // Set up the initial batch process.
1504
  if (!isset($sandbox['progress'])) {
1505
    $sandbox['progress'] = 0;
1506
    $sandbox['last_nid_processed'] = -1;
1507
    $sandbox['converted_count'] = 0;
1508
    $sandbox['max'] = db_select('webform')
1509
      ->countQuery()
1510
      ->execute()
1511
      ->fetchField();
1512
  }
1513

    
1514
  $limit = webform_variable_get('webform_update_batch_size');
1515
  $webforms = db_select('webform', 'w')
1516
    ->fields('w')
1517
    ->condition('nid', $sandbox['last_nid_processed'], '>')
1518
    ->orderBy('nid', 'ASC')
1519
    ->range(0, $limit)
1520
    ->execute()
1521
    ->fetchAllAssoc('nid', PDO::FETCH_ASSOC);
1522

    
1523
  foreach ($webforms as $nid => $webform) {
1524
    // Update tokens in component configurations.
1525
    $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
1526
      ->fields('wc')
1527
      ->condition('wc.nid', $nid)
1528
      ->execute();
1529
    $rgid = 0;
1530
    foreach ($result as $component) {
1531
      // For each component, check if it has conditional properties that need
1532
      // to be removed and/or migrated. Because these properties may be in any
1533
      // order, copy the original extra array for comparison.
1534
      $component['extra'] = unserialize($component['extra']);
1535
      $original_extra = $component['extra'];
1536

    
1537
      // Remove conditional properties if present.
1538
      if (isset($component['extra']['conditional_component'])) {
1539
        unset($component['extra']['conditional_component']);
1540
      }
1541
      if (isset($component['extra']['conditional_operator'])) {
1542
        unset($component['extra']['conditional_operator']);
1543
      }
1544
      if (isset($component['extra']['conditional_values'])) {
1545
        unset($component['extra']['conditional_values']);
1546

    
1547
        // If the component has conditional values specified, that indicates
1548
        // that this component was conditionally shown. Convert it to a new
1549
        // conditional with multiple rules if needed.
1550
        if (strlen(trim($original_extra['conditional_values'])) && !empty($original_extra['conditional_operator']) && !empty($original_extra['conditional_component'])) {
1551
          $conditional_values = explode("\n", $original_extra['conditional_values']);
1552
          $rules = array();
1553
          $rule = array(
1554
            'nid' => $nid,
1555
            'rgid' => $rgid,
1556
            'rid' => NULL,
1557
            'source_type' => 'component',
1558
            'source' => $original_extra['conditional_component'],
1559
            'operator' => 'equal',
1560
            'value' => NULL,
1561
          );
1562
          foreach ($conditional_values as $value) {
1563
            $value = trim($value);
1564
            if ($value) {
1565
              $new_rule = $rule;
1566
              $new_rule['rid'] = count($rules);
1567
              $new_rule['value'] = $value;
1568
              $rules[] = $new_rule;
1569
            }
1570
          }
1571
          if (count($rules)) {
1572
            $conditional = array(
1573
              'nid' => $nid,
1574
              'rgid' => $rgid,
1575
              'andor' => 'or',
1576
              'action' => ($original_extra['conditional_operator'] === '=') ? 'show' : 'hide',
1577
              'target_type' => 'component',
1578
              'target' => $component['cid'],
1579
              'weight' => 0,
1580
            );
1581
            drupal_write_record('webform_conditional', $conditional);
1582
            foreach ($rules as $rule) {
1583
              drupal_write_record('webform_conditional_rules', $rule);
1584
            }
1585
            $sandbox['converted_count']++;
1586
            $rgid++;
1587
          }
1588
        }
1589
      }
1590

    
1591
      // Update the component with the conditional properties removed.
1592
      if ($component['extra'] != $original_extra) {
1593
        $component['extra'] = serialize($component['extra']);
1594
        drupal_write_record('webform_component', $component, array('nid', 'cid'));
1595
      }
1596
    }
1597

    
1598
    // Update the last processed NID.
1599
    $sandbox['last_nid_processed'] = $nid;
1600
    $sandbox['progress']++;
1601
  }
1602

    
1603
  // If less than limit was processed, the update process is finished.
1604
  if (count($webforms) < $limit || $sandbox['progress'] == $sandbox['max']) {
1605
    $finished = TRUE;
1606
  }
1607

    
1608
  // If there's no max value then there's nothing to update and we're finished.
1609
  if (empty($sandbox['max']) || isset($finished)) {
1610
    return t('@count webforms using conditionals updated to the new conditional system.', array('@count' => $sandbox['converted_count']));
1611
  }
1612
  else {
1613
    // Indicate our current progress to the batch update system.
1614
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
1615
  }
1616
}
1617

    
1618
/**
1619
 * Remove files left over from deleted submissions. Such files are now deleted
1620
 * automatically.
1621
 */
1622
function webform_update_7404() {
1623
  module_load_include('inc', 'webform', 'components/file');
1624
  $fids = db_query('SELECT fid FROM {file_usage} WHERE module = \'webform\' AND type = \'submission\' AND NOT id IN(SELECT sid FROM {webform_submissions})')->fetchCol();
1625
  foreach ($fids as $fid) {
1626
    _webform_delete_file(NULL, array($fid));
1627
  }
1628
}
1629

    
1630
/**
1631
 * Add an "extra" column to the e-mail table for for non-queryable options.
1632
 */
1633
function webform_update_7405() {
1634
  $schema = array(
1635
    '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.',
1636
    'type' => 'text',
1637
    'not null' => TRUE,
1638
    'initial' => '',
1639
  );
1640
  if (!db_field_exists('webform_emails', 'extra')) {
1641
    db_add_field('webform_emails', 'extra', $schema);
1642
  }
1643
}
1644

    
1645
/**
1646
 * Convert the "webform_use_cookies" setting to "webform_tracking_mode".
1647
 */
1648
function webform_update_7406() {
1649
  // Previously, we only had "strict" and "ip_address" checking. Using cookies
1650
  // meant cookies in addition to IP address.
1651
  $use_cookies = variable_get('webform_use_cookies');
1652
  if (isset($use_cookies)) {
1653
    variable_set('webform_tracking_mode', $use_cookies ? 'strict' : 'ip_address');
1654
    variable_del('webform_use_cookies');
1655
  }
1656
}
1657

    
1658
/**
1659
 * Remove orphaned conditional rules.
1660
 */
1661
function webform_update_7407() {
1662
  // Delete entire conditions for deleted components that were the target.
1663
  // This query is complicated by database compatibility since we're joining two
1664
  // non-matching columns. See http://drupal.org/node/2026891.
1665
  $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");
1666
  foreach ($result as $row) {
1667
    db_delete('webform_conditional')
1668
      ->condition('nid', $row->nid)
1669
      ->condition('rgid', $row->rgid)
1670
      ->execute();
1671
    db_delete('webform_conditional_rules')
1672
      ->condition('nid', $row->nid)
1673
      ->condition('rgid', $row->rgid)
1674
      ->execute();
1675
  }
1676
  // Delete conditional rules for deleted components that were the source.
1677
  $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");
1678
  foreach ($result as $row) {
1679
    db_delete('webform_conditional_rules')
1680
      ->condition('nid', $row->nid)
1681
      ->condition('rgid', $row->rgid)
1682
      ->condition('rid', $row->rid)
1683
      ->execute();
1684
  }
1685
  // Delete any conditions that no longer have any rules left in them.
1686
  $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");
1687
  foreach ($result as $row) {
1688
    db_delete('webform_conditional')
1689
      ->condition('nid', $row->nid)
1690
      ->condition('rgid', $row->rgid)
1691
      ->execute();
1692
  }
1693
}
1694

    
1695
/**
1696
 * Rename the "mandatory" column to "required".
1697
 */
1698
function webform_update_7408() {
1699
  $spec = array(
1700
    'description' => 'Boolean flag for if this component is required.',
1701
    'type' => 'int',
1702
    'size' => 'tiny',
1703
    'not null' => TRUE,
1704
    'default' => 0,
1705
  );
1706
  if (!db_field_exists('webform_component', 'required')) {
1707
    db_change_field('webform_component', 'mandatory', 'required', $spec);
1708
  }
1709
}
1710

    
1711
/**
1712
 * Add progress bar columns to the webform table.
1713
 */
1714
function webform_update_7409() {
1715
  if (!db_field_exists('webform', 'progressbar_bar')) {
1716
    $int_schema = array(
1717
      'type' => 'int',
1718
      'size' => 'tiny',
1719
      'not null' => TRUE,
1720
      'default' => 0,
1721
    );
1722
    $varchar_schema = array(
1723
      'type' => 'varchar',
1724
      'length' => 255,
1725
    );
1726

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

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

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

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

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

    
1742
    $varchar_schema['description'] = 'Label for the first page of the progress bar.';
1743
    db_add_field('webform', 'progressbar_label_first', $varchar_schema);
1744

    
1745
    $varchar_schema['description'] = 'Label for the last page of the progress bar.';
1746
    db_add_field('webform', 'progressbar_label_confirmation', $varchar_schema);
1747

    
1748
    return t('New webform columns added.');
1749
  }
1750
}
1751

    
1752
/**
1753
 * Remove the "teaser" column from the "webform" table.
1754
 */
1755
function webform_update_7410() {
1756
  if (db_field_exists('webform', 'teaser')) {
1757
    db_drop_field('webform', 'teaser');
1758
    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.');
1759
  }
1760
}
1761

    
1762
/**
1763
 * Remove [submission:values:x] token use of :nolabel and add :withlabel.
1764
 */
1765
function webform_update_7411(&$sandbox) {
1766
  // Define replacements.
1767
  $patterns = array();
1768
  $replacements = array();
1769
  $dpatterns = array(
1770
    '/\[submission:values(:(?!nolabel)[a-z_]+)+\]/m',
1771
    '/\[submission:values(:([a-z_:]+))?:nolabel\]/m',
1772
  );
1773
  $dreplacements = array(
1774
    '[submission:values$1:withlabel]',
1775
    '[submission:values$1]',
1776
  );
1777

    
1778
  $limit = webform_variable_get('webform_update_batch_size');
1779
  $processed_count = _webform_update_7401_batch($sandbox, $patterns, $replacements, $dpatterns, $dreplacements, $limit);
1780

    
1781
  // If less than limit was processed, the update process is finished.
1782
  if ($processed_count < $limit || $sandbox['progress'] == $sandbox['max']) {
1783
    $finished = TRUE;
1784
  }
1785

    
1786
  // If there's no max value then there's nothing to update and we're finished.
1787
  if (empty($sandbox['max']) || isset($finished)) {
1788
    return t('Replaced tokens using [submission:values:x] with [submission:values:x:withlabel].');
1789
  }
1790
  else {
1791
    // Indicate our current progress to the batch update system.
1792
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
1793
  }
1794
}
1795

    
1796
/**
1797
 * Split webform_node_types as content type specific.
1798
 */
1799
function webform_update_7412() {
1800
  $types = variable_get('webform_node_types', array('webform'));
1801
  foreach ($types as $type) {
1802
    variable_set('webform_node_' . $type, TRUE);
1803
  }
1804
  variable_del('webform_node_types');
1805
}
1806

    
1807
/**
1808
 * Add preview page columns to the webform table.
1809
 */
1810
function webform_update_7413() {
1811
  if (!db_field_exists('webform', 'preview')) {
1812
    $int_schema = array(
1813
      'type' => 'int',
1814
      'size' => 'tiny',
1815
      'not null' => TRUE,
1816
      'default' => 0,
1817
    );
1818
    $varchar_schema = array(
1819
      'type' => 'varchar',
1820
      'length' => 255,
1821
    );
1822
    $text_schema = array(
1823
      'type' => 'text',
1824
      'not null' => TRUE,
1825
      'initial' => '',
1826
    );
1827

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

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

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

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

    
1840
    $text_schema['description'] = 'Text shown on the preview page of the form.';
1841
    db_add_field('webform', 'preview_message', $text_schema);
1842

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

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

    
1849
    return t('New webform columns added.');
1850
  }
1851
}
1852

    
1853
/**
1854
 * Change email subject, from_name and from_address fields to type 'TEXT'.
1855
 */
1856
function webform_update_7414() {
1857
  $schema = array(
1858
    'subject' => array(
1859
      '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.',
1860
      'type' => 'text',
1861
      'not null' => FALSE,
1862
    ),
1863
    'from_name' => array(
1864
      '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.',
1865
      'type' => 'text',
1866
      'not null' => FALSE,
1867
    ),
1868
    'from_address' => array(
1869
      '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.',
1870
      'type' => 'text',
1871
      'not null' => FALSE,
1872
    ),
1873
  );
1874

    
1875
  db_change_field('webform_emails', 'subject', 'subject', $schema['subject']);
1876
  db_change_field('webform_emails', 'from_name', 'from_name', $schema['from_name']);
1877
  db_change_field('webform_emails', 'from_address', 'from_address', $schema['from_address']);
1878

    
1879
  return t('Custom email columns successfully changed.');
1880
}
1881

    
1882
/**
1883
 * Increase length of webform.redirect_url.
1884
 */
1885
function webform_update_7415() {
1886
  $spec = array(
1887
    'description' => 'The URL a user is redirected to after submitting a form.',
1888
    'type' => 'varchar',
1889
    'length' => 2048,
1890
    'default' => '<confirmation>',
1891
  );
1892
  db_change_field('webform', 'redirect_url', 'redirect_url', $spec);
1893
}
1894

    
1895
/**
1896
 * Add columns for serial numbered submissions. Add serial numbers to existing submissions.
1897
 */
1898
function webform_update_7416() {
1899
  // SQL database implementations vary in how DDL statements are handled within
1900
  // transactions. Since this update routine should be run in maintenance mode
1901
  // without concurrent transactions, no DDL statements are executed within a
1902
  // transaction.
1903

    
1904
  // Add next_serial column to webform.
1905
  $spec = array(
1906
    'description' => 'The serial number to give to the next submission to this webform.',
1907
    'type' => 'int',
1908
    'unsigned' => TRUE,
1909
    'not null' => TRUE,
1910
    'default' => 1,
1911
  );
1912
  if (!db_field_exists('webform', 'next_serial')) {
1913
    db_add_field('webform', 'next_serial', $spec);
1914
  }
1915

    
1916
  // Add serial column to webform_submissions.
1917
  $spec = array(
1918
    'description' => 'The serial number of this submission.',
1919
    'type' => 'int',
1920
    'unsigned' => TRUE,
1921
  );
1922
  if (!db_field_exists('webform_submissions', 'serial')) {
1923
    db_add_field('webform_submissions', 'serial', $spec);
1924
  }
1925

    
1926
  // Begin a transaction for updating the serial numbers. The transaction will
1927
  // commit when $txn is unset or goesout-of-scope.
1928
  $txn = db_transaction();
1929

    
1930
  // Delete stray entries from the Webform tables before adding serial numbers.
1931
  db_query("DELETE FROM {webform_submissions} WHERE nid NOT IN (SELECT nid FROM {webform})");
1932
  db_query("DELETE FROM {webform_submitted_data} WHERE nid NOT IN (SELECT nid FROM {webform})");
1933

    
1934

    
1935
  // Add serial numbers to all submissions.
1936

    
1937
  // Repeat for every Webform.
1938
  $nids = db_select('webform', 'w')
1939
    ->fields('w', array('nid'))
1940
    ->execute();
1941
  while ($nid = $nids->fetchColumn()) {
1942
    $serial = 1;
1943
    // Repeat for every submission in this Webform.
1944
    $sids = db_select('webform_submissions', 'ws')
1945
      ->forUpdate()
1946
      ->fields('ws', array('sid'))
1947
      ->condition('nid', $nid)
1948
      ->orderBy('sid')
1949
      ->execute();
1950
    while ($sid = $sids->fetchColumn()) {
1951
      // Set the serial number.
1952
      db_update('webform_submissions')
1953
        ->fields(array('serial' => $serial))
1954
        ->condition('nid', $nid)
1955
        ->condition('sid', $sid)
1956
        ->execute();
1957
      $serial++;
1958
    }
1959
    // Set the next serial number.
1960
    db_update('webform')
1961
      ->fields(array('next_serial' => $serial))
1962
      ->condition('nid', $nid)
1963
      ->execute();
1964
  }
1965
  // Commit the transaction
1966
  unset($txn);
1967

    
1968
  // Now that every submission has a serial number, make serial numbers required.
1969
  $spec['not null'] = TRUE;
1970
  $keys = array();
1971
  // Create a unique index if it does not already exist.
1972
  if (!db_index_exists('webform_submissions', 'nid_serial')) {
1973
    $keys = array(
1974
      'unique keys' => array(
1975
        'nid_serial' => array('nid', 'serial'),
1976
      ),
1977
    );
1978
  }
1979
  db_change_field('webform_submissions', 'serial', 'serial', $spec, $keys);
1980

    
1981
  return t('Columns for serial numbered submissions successfully added. Serial numbers added to existing submissions.');
1982
}
1983

    
1984
/**
1985
 * Change webform_component.name to text to allow for longer labels.
1986
 */
1987
function webform_update_7417() {
1988
  db_change_field('webform_component', 'name', 'name', array('type' => 'text', 'not null' => TRUE));
1989
}
1990

    
1991
/**
1992
 * Upgrade the "extra" column in the e-mail table to be consistent with a new
1993
 * webform installation.
1994
 *
1995
 * In version 7.x-4.0-rc6 and earlier, the extra field was added with 'not null'
1996
 * FALSE, cause the schema module to report in mismatch. While functionally this
1997
 * causes no problem, this update removes the schema module's warning.
1998
 */
1999
function webform_update_7418() {
2000
  // While there should never be any NULL values in the extra field, change them to '' to be safe.
2001
  db_update('webform_emails')
2002
    ->fields(array('extra' => ''))
2003
    ->isNull('extra')
2004
    ->execute();
2005
  // Pass a complete field specification to db_change_field for cross-database compatiblity.
2006
  $spec = array(
2007
    '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.',
2008
    'type' => 'text',
2009
    'not null' => TRUE,
2010
  );
2011
  db_change_field('webform_emails', 'extra', 'extra', $spec);
2012
}
2013

    
2014
/**
2015
 * Add an index on submitted data.
2016
 */
2017
function webform_update_7419() {
2018
  // Note: MS SQL Server does not support size-limited indexes and the column
2019
  // type (text) is too big to fit inside index size limits.
2020
  if (!db_index_exists('webform_submitted_data', 'data') && db_driver() != 'sqlsrv') {
2021
    db_add_index('webform_submitted_data', 'data', array(array('data', 64)));
2022
  }
2023
}
2024

    
2025
/**
2026
 * Extend access to the WEBFORM tab to anyone who can access the node's EDIT tab.
2027
 */
2028
function webform_update_7420() {
2029
  user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('edit webform components'));
2030
}
2031

    
2032
/**
2033
 * Set the administrator option for an HTML-capable mail system to the current automatically-detected option.
2034
 */
2035
function webform_update_7421() {
2036
  $capable = array_reduce(array('mandrill', 'mimemail', 'htmlmail'),
2037
                          function($carry, $module) { return $carry || module_exists($module); },
2038
                          FALSE);
2039
  variable_set('webform_email_html_capable', $capable);
2040
  return $capable
2041
            ? t('An HTML-capable module is installed. The option to send HTML e-mail is enabled.')
2042
            : t('No commonly-known HTML capable module is installed. The option to send HTML e-mail is disabled.');
2043
}
2044

    
2045
/**
2046
 * Remove the administrator option "Include webform forms in search index" and rely on the Search Index view mode instead.
2047
 */
2048
function webform_update_7422() {
2049
  variable_del('webform_search_index');
2050
  return module_exists('search')
2051
          ? t('Webform forms will now be included in the search index only if the Webform "field" is displayed in the "Search index" view mode.')
2052
          : NULL;
2053
}
2054

    
2055
/**
2056
 * 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.
2057
 */
2058
function webform_update_7423() {
2059
  // Create webform_condtional_actions table.
2060
  // The table might already exist if this update previously timed-out
2061
  if (!db_table_exists('webform_conditional_actions')) {
2062
    $schema['webform_conditional_actions'] = array(
2063
      'description' => 'Holds information about conditional actions.',
2064
      'fields' => array(
2065
        'nid' => array(
2066
          'description' => 'The node identifier of a webform.',
2067
          'type' => 'int',
2068
          'unsigned' => TRUE,
2069
          'not null' => TRUE,
2070
          'default' => 0,
2071
        ),
2072
        'rgid' => array(
2073
          'description' => 'The rule group identifier for this group of rules.',
2074
          'type' => 'int',
2075
          'size' => 'small',
2076
          'unsigned' => TRUE,
2077
          'not null' => TRUE,
2078
          'default' => 0,
2079
        ),
2080
        'aid' => array(
2081
          'description' => 'The rule identifier for this conditional action.',
2082
          'type' => 'int',
2083
          'size' => 'small',
2084
          'unsigned' => TRUE,
2085
          'not null' => TRUE,
2086
          'default' => 0,
2087
        ),
2088
        'target_type' => array(
2089
          'description' => 'The type of target to be affected. Currently always "component". Indicates what type of ID the "target" column contains.',
2090
          'type' => 'varchar',
2091
          'length' => 128,
2092
        ),
2093
        'target' => array(
2094
          'description' => 'The ID of the target to be affected. Typically a component ID.',
2095
          'type' => 'varchar',
2096
          'length' => 128,
2097
        ),
2098
        'invert' => array(
2099
          'description' => 'If inverted, execute when rule(s) are false.',
2100
          'type' => 'int',
2101
          'size' => 'small',
2102
          'unsigned' => TRUE,
2103
          'not null' => TRUE,
2104
          'default' => 0,
2105
        ),
2106
        'action' => array(
2107
          'description' => 'The action to be performed on the target.',
2108
          'type' => 'varchar',
2109
          'length' => 128,
2110
        ),
2111
        'argument' => array(
2112
          'description' => 'Optional argument for action.',
2113
          'type' => 'text',
2114
        ),
2115
      ),
2116
      'primary key' => array('nid', 'rgid', 'aid'),
2117
    );
2118
    db_create_table('webform_conditional_actions', $schema['webform_conditional_actions']);
2119
  }
2120

    
2121
  // In a site with many, many conditionals, the db_insert may timeout. Start a transaction to ensure atomic action.
2122
  $tx = db_transaction();
2123
  // Copy target information from existing webform_conditional table to new webfrom_condtional_actions table.
2124
  $select = db_select('webform_conditional', 'c')
2125
    ->fields('c', array('nid', 'rgid', 'action', 'target_type', 'target'))
2126
    ->orderBy('nid')->orderBy('rgid');
2127
  $select->addExpression("''", 'argument');
2128
  db_insert('webform_conditional_actions')
2129
    ->from($select)
2130
    ->execute();
2131

    
2132
  // Commit the insert
2133
  unset($tx);
2134

    
2135
  // Remove unneeded columns from webform_conditional.
2136
  foreach (array('action', 'target_type', 'target') as $fieldname) {
2137
    if (db_field_exists('webform_conditional', $fieldname)) {
2138
      db_drop_field('webform_conditional', $fieldname);
2139
    }
2140
  }
2141

    
2142
  // Rebuild the registry because this point release contains a new class: WebformConditionals.
2143
  registry_rebuild();
2144

    
2145
  return t('Webform database tables were successfully adjusted to allow more than one action for each conditional.');
2146
}
2147

    
2148
/**
2149
 * Convert conditional actions of "hide" to "isn't shown".
2150
 */
2151
function webform_update_7424() {
2152
  $count = db_update('webform_conditional_actions')
2153
    ->fields(array('action' => 'show', 'invert' => 1))
2154
    ->condition('action', 'hide')
2155
    ->execute();
2156
  return format_plural($count,
2157
                       '1 "hide" conditional converted to "isn\'t" shown.',
2158
                       '@count conditionals converted to "isn\'t" shown.');
2159
}
2160

    
2161
/**
2162
 * Add "exclude empty" option to emails.
2163
 */
2164
function webform_update_7425() {
2165
  // Add next_serial column to webform.
2166
  $spec = array(
2167
    'description' => 'Determines if the e-mail will include component with an empty value.',
2168
    'type' => 'int',
2169
    'unsigned' => TRUE,
2170
    'size' => 'tiny',
2171
    'not null' => TRUE,
2172
    'default' => 0,
2173
  );
2174
  if (!db_field_exists('webform_emails', 'exclude_empty')) {
2175
    db_add_field('webform_emails', 'exclude_empty', $spec);
2176
  }
2177

    
2178
  // Clear the views cache since this release use the webform_analysis view.
2179
  cache_clear_all('*', 'cache_views', TRUE);
2180

    
2181
  return t('Webform e-mails were sucessfully updated to add the option to exclude empty components.');
2182
}
2183

    
2184
/**
2185
 * Add configuration to continue sending individual e-mails to multiple recipients.
2186
 */
2187
function webform_update_7426() {
2188
  variable_set('webform_email_address_individual', 1);
2189
}
2190

    
2191
/**
2192
 * Add database columns for submission completed and modified timestamps. Sites with many submissions may wish to use drush to execute this update.
2193
 */
2194
function webform_update_7427() {
2195
  // Create new timestamp columns.
2196
  $specs = array(
2197
    'completed' => array(
2198
      'description' => 'Timestamp when the form was submitted as complete (not draft).',
2199
      'type' => 'int',
2200
      'not null' => TRUE,
2201
      'default' => 0,
2202
    ),
2203
    'modified' => array(
2204
      'description' => 'Timestamp when the form was last saved (complete or draft).',
2205
      'type' => 'int',
2206
      'not null' => TRUE,
2207
      'default' => 0,
2208
    ),
2209
  );
2210
  foreach ($specs as $field_name => $spec) {
2211
    if (!db_field_exists('webform_submissions', $field_name)) {
2212
      db_add_field('webform_submissions', $field_name, $spec);
2213
    }
2214
  }
2215

    
2216
  // In a site with many submissions, the db_update may timeout. Start a transaction to ensure atomic action.
2217
  $tx = db_transaction();
2218
  // Copy submitted to completed for non-draft submissions.
2219
  db_update('webform_submissions')
2220
    ->expression('completed', 'submitted')
2221
    ->condition('is_draft', 0)
2222
    ->execute();
2223
  // Commit the update.
2224
  unset($tx);
2225

    
2226
  // Start another transaction.
2227
  $tx = db_transaction();
2228
  db_update('webform_submissions')
2229
    ->expression('modified', 'submitted')
2230
    ->execute();
2231
  // Commit the update.
2232
  unset($tx);
2233

    
2234
  // Clear the views cache since to see the completed and modified views fields.
2235
  cache_clear_all('*', 'cache_views', TRUE);
2236

    
2237
  return t('Webform submissions were updated with completed and modified timestamps.');
2238
}
2239

    
2240
/**
2241
 * Add a "confidential" option to webforms.
2242
 */
2243
function webform_update_7428() {
2244
  // Add confidential column to webform.
2245
  if (!db_field_exists('webform', 'confidential')) {
2246
    $spec = array(
2247
      'description' => 'Boolean value for whether to anonymize submissions.',
2248
      'type' => 'int',
2249
      'size' => 'tiny',
2250
      'not null' => TRUE,
2251
      'default' => 0,
2252
    );
2253
    db_add_field('webform', 'confidential', $spec);
2254
  }
2255

    
2256
  return t('Webforms may now be configured to anonymize confidential submissions.');
2257
}
2258

    
2259
/**
2260
 * 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'.
2261
 */
2262
function webform_update_7429() {
2263
  // Add highest_valid_page column to webform_submissions.
2264
  if (!db_field_exists('webform_submissions', 'highest_valid_page')) {
2265
    $spec = array(
2266
      'description' => 'For drafts, the highest validated page number.',
2267
      'type' => 'int',
2268
      'size' => 'small',
2269
      'not null' => TRUE,
2270
      'default' => 0,
2271
    );
2272
    db_add_field('webform_submissions', 'highest_valid_page', $spec);
2273
  }
2274

    
2275
  return t('Webforms will now resume draft submissions on the page where the submitter left off.');
2276
}
2277

    
2278
/**
2279
 * Add a column to the emails table to allow disabling.
2280
 */
2281
function webform_update_7430() {
2282
  // Add status column to webform_emails.
2283
  if (!db_field_exists('webform_emails', 'status')) {
2284
    $spec = array(
2285
      'description' => 'Whether this email is enabled.',
2286
      'type' => 'int',
2287
      'unsigned' => TRUE,
2288
      'size' => 'tiny',
2289
      'not null' => TRUE,
2290
      'default' => 1,
2291
    );
2292
    db_add_field('webform_emails', 'status', $spec);
2293
  }
2294

    
2295
  return t('Webform emails may now be disabled.');
2296
}