Projet

Général

Profil

Paste
Télécharger (21,5 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / webform / components / date.inc @ 8c72e82a

1
<?php
2

    
3
/**
4
 * @file
5
 * Webform module date component.
6
 */
7

    
8
/**
9
 * Implements _webform_defaults_component().
10
 */
11
function _webform_defaults_date() {
12
  return array(
13
    'name' => '',
14
    'form_key' => NULL,
15
    'pid' => 0,
16
    'weight' => 0,
17
    'value' => '',
18
    'required' => 0,
19
    'extra' => array(
20
      'timezone' => 'user',
21
      'exclude' => array(),
22
      'start_date' => '-2 years',
23
      'end_date' => '+2 years',
24
      'year_textfield' => 0,
25
      'datepicker' => 1,
26
      'title_display' => 0,
27
      'description' => '',
28
      'description_above' => FALSE,
29
      'private' => FALSE,
30
      'analysis' => FALSE,
31
    ),
32
  );
33
}
34

    
35
/**
36
 * Implements _webform_theme_component().
37
 */
38
function _webform_theme_date() {
39
  return array(
40
    'webform_date' => array(
41
      'render element' => 'element',
42
      'file' => 'components/date.inc',
43
    ),
44
    'webform_display_date' => array(
45
      'render element' => 'element',
46
      'file' => 'components/date.inc',
47
    ),
48
    'webform_calendar' => array(
49
      'variables' => array('component' => NULL, 'calendar_classes' => NULL),
50
      'template' => 'templates/webform-calendar',
51
    ),
52
  );
53
}
54

    
55
/**
56
 * Implements _webform_edit_component().
57
 */
58
function _webform_edit_date($component) {
59
  $form = array();
60
  $form['value'] = array(
61
    '#type' => 'textfield',
62
    '#title' => t('Default value'),
63
    '#default_value' => $component['value'],
64
    '#description' => t('The default value of the field.') . '<br />' . t('Accepts any date in any <a href="http://www.gnu.org/software/tar/manual/html_chapter/Date-input-formats.html">GNU Date Input Format</a>. Strings such as today, +2 months, and Dec 9 2004 are all valid.'),
65
    '#size' => 60,
66
    '#maxlength' => 127,
67
    '#weight' => 0,
68
  );
69
  $form['extra']['timezone'] = array(
70
    '#type' => 'radios',
71
    '#title' => t('Default value timezone'),
72
    '#default_value' => empty($component['extra']['timezone']) ? 'user' : $component['extra']['timezone'],
73
    '#description' => t('If using relative dates for a default value (for example, "today") base the current day on this timezone.'),
74
    '#options' => array('user' => t('User timezone'), 'site' => t('Website timezone')),
75
    '#weight' => 2,
76
    '#access' => variable_get('configurable_timezones', 1),
77
  );
78

    
79
  $form['extra']['exclude'] = array(
80
    '#type' => 'checkboxes',
81
    '#title' => t('Hide'),
82
    '#default_value' => $component['extra']['exclude'],
83
    '#options' => array(
84
      'day' => t('Day'),
85
      'month' => t('Month'),
86
      'year' => t('Year'),
87
    ),
88
    '#description' => t('A hidden day or month will be set to 1. A hidden year will be set to the year of the default value.'),
89
    '#weight' => 3,
90
  );
91

    
92
  $form['display']['datepicker'] = array(
93
    '#type' => 'checkbox',
94
    '#title' => t('Enable popup calendar'),
95
    '#default_value' => $component['extra']['datepicker'],
96
    '#description' => t('Enable a JavaScript date picker next to the date field.'),
97
    '#weight' => 2,
98
    '#parents' => array('extra', 'datepicker'),
99
  );
100

    
101
  $form['display']['year_textfield'] = array(
102
    '#type' => 'checkbox',
103
    '#title' => t('Use a textfield for year'),
104
    '#default_value' => $component['extra']['year_textfield'],
105
    '#description' => t('If checked, the generated date field will use a textfield for the year. Otherwise it will use a select list.'),
106
    '#weight' => 5,
107
    '#parents' => array('extra', 'year_textfield'),
108
  );
109

    
110
  $form['validation']['start_date'] = array(
111
    '#type' => 'textfield',
112
    '#title' => t('Start date'),
113
    '#default_value' => $component['extra']['start_date'],
114
    '#description' => t('The earliest date that may be entered into the field.') . ' ' . t('Accepts any date in any <a href="http://www.gnu.org/software/tar/manual/html_chapter/Date-input-formats.html">GNU Date Input Format</a>.'),
115
    '#size' => 30,
116
    '#weight' => 3,
117
    '#parents' => array('extra', 'start_date'),
118
  );
119
  $form['validation']['end_date'] = array(
120
    '#type' => 'textfield',
121
    '#title' => t('End date'),
122
    '#default_value' => $component['extra']['end_date'],
123
    '#description' => t('The latest date that may be entered into the field.') . ' ' . t('Accepts any date in any <a href="http://www.gnu.org/software/tar/manual/html_chapter/Date-input-formats.html">GNU Date Input Format</a>.'),
124
    '#size' => 30,
125
    '#weight' => 4,
126
    '#parents' => array('extra', 'end_date'),
127
  );
128

    
129
  $form['#validate'] = array('_webform_edit_date_validate');
130
  return $form;
131
}
132

    
133
/**
134
 * Implements hook_form_id_validate().
135
 *
136
 * Warns user about hiding all the date fields and not using the date picker.
137
 */
138
function _webform_edit_date_validate($form, &$form_state) {
139
  // Reduce checkbox values to simple non-associative array of values.
140
  form_set_value($form['extra']['exclude'], array_filter(array_values($form_state['values']['extra']['exclude'])), $form_state);
141

    
142
  // Note that Drupal 7 doesn't support setting errors no checkboxes due to
143
  // browser issues. See: https://www.drupal.org/node/222380
144
  if (count($form_state['values']['extra']['exclude']) == 3) {
145
    form_set_error('extra][exclude', 'The day, month and year can\'t all be hidden.');
146
  }
147
  if ($form_state['values']['extra']['exclude'] == array('month')) {
148
    form_set_error('extra][exclude', 'You cannot hide just the month.');
149
  }
150

    
151
  // Validate that the start and end dates are valid. Don't validate the default
152
  // date because with token substitution, it might not be valid at component
153
  // definition time. Also note that validation was introduced in 7.x-4.8 and
154
  // previously-defined webform may have invalid start and end dates.
155
  foreach (array('start_date', 'end_date') as $field) {
156
    if (trim($form_state['values']['extra'][$field]) && !($date[$field] = webform_strtodate('c', $form_state['values']['extra'][$field]))) {
157
      form_set_error("extra][$field", t('The @field could not be interpreted in <a href="http://www.gnu.org/software/tar/manual/html_chapter/Date-input-formats.html">GNU Date Input Format</a>.',
158
      array('@field' => $form['validation'][$field]['#title'])));
159
    }
160
  }
161
  if (!empty($date['start_date']) && !empty($date['end_date']) && $date['end_date'] < $date['start_date']) {
162
    form_set_error('extra][end_date', t('The End date must be on or after the Start date.'));
163
  }
164
}
165

    
166
/**
167
 * Implements _webform_render_component().
168
 */
169
function _webform_render_date($component, $value = NULL, $filter = TRUE, $submission = NULL) {
170
  $node = isset($component['nid']) ? node_load($component['nid']) : NULL;
171

    
172
  $element = array(
173
    '#type' => 'date',
174
    '#title' => $filter ? webform_filter_xss($component['name']) : $component['name'],
175
    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
176
    '#weight' => $component['weight'],
177
    '#description' => $filter ? webform_filter_descriptions($component['extra']['description'], $node) : $component['extra']['description'],
178
    '#required' => $component['required'],
179
    '#start_date' => trim($component['extra']['start_date']),
180
    '#end_date' => trim($component['extra']['end_date']),
181
    '#reference_timestamp' => $submission && $submission->completed ? $submission->completed : NULL,
182
    '#year_textfield' => $component['extra']['year_textfield'],
183
    '#default_value' => $filter ? webform_replace_tokens($component['value'], $node) : $component['value'],
184
    '#timezone' => $component['extra']['timezone'],
185
    '#exclude' => $component['extra']['exclude'],
186
    '#process' => array('webform_expand_date'),
187
    '#theme' => 'webform_date',
188
    '#theme_wrappers' => array('webform_element'),
189
    '#element_validate' => array('webform_validate_date'),
190
    '#translatable' => array('title', 'description'),
191
  );
192

    
193
  if ($component['extra']['datepicker']) {
194
    $element['#datepicker'] = TRUE;
195
    $element['#attached'] = array(
196
      'library' => array(
197
        array('system', 'ui.datepicker'),
198
      ),
199
    );
200
  }
201

    
202
  // Set the value from Webform.
203
  if (isset($value[0]) && $value[0] !== '') {
204
    $value = webform_date_array($value[0], 'date');
205
    $element['#default_value'] = $value;
206
  }
207

    
208
  return $element;
209
}
210

    
211
/**
212
 * Form API #process function for Webform date fields.
213
 */
214
function webform_expand_date($element) {
215
  $timezone = $element['#timezone'] != 'user' ? NULL : 'user';
216

    
217
  // Accept a string or array value for #default_value.
218
  if (!empty($element['#default_value']) && is_string($element['#default_value'])) {
219
    $timestring = webform_strtodate('c', $element['#default_value'], $timezone);
220
    $element['#default_value'] = webform_date_array($timestring, 'date');
221
  }
222
  // Prevent an error in PHP 5.4 caused by core's treatment of the #value.
223
  if (isset($element['#value'])) {
224
    unset($element['#value']);
225
  }
226

    
227
  // Set defaults according to existing #default_value (set by Form API)
228
  if (isset($element['#default_value']['month']) || isset($element['#default_value']['day']) || isset($element['#default_value']['year'])) {
229
    $default_values = array(
230
      'month' => $element['#default_value']['month'],
231
      'day' => $element['#default_value']['day'],
232
      'year' => $element['#default_value']['year'],
233
    );
234
  }
235
  else {
236
    $default_values = array(
237
      'day' => NULL,
238
      'month' => NULL,
239
      'year' => NULL,
240
    );
241
  }
242

    
243
  // Let Drupal do it's normal expansion.
244
  $element = form_process_date($element);
245

    
246
  // Convert relative dates to absolute and calculate the year, month and day.
247
  $timezone = $element['#timezone'] != 'user' ? NULL : 'user';
248
  foreach (array('start', 'end') as $start_end) {
249
    $element_field = &$element["#{$start_end}_date"];
250
    $element_field = $element_field ? webform_strtodate('Y-m-d', $element_field, $timezone, $element['#reference_timestamp']) : '';
251
    if ($element_field) {
252
      $parts = explode('-', $element_field);
253
    }
254
    else {
255
      $parts = $start_end == 'start' ? array(webform_strtodate('Y', '-2 years'), 1, 1) : array(webform_strtodate('Y', '+2 years'), 12, 31);
256
      $element_field = '';
257
    }
258
    unset($element_field); // Drop PHP reference.
259
    $parts[3] = $parts[0] . '-' . $parts[1] . '-' . $parts[2];
260
    $range[$start_end] = array_combine(array('year', 'month', 'day', 'date'), $parts);
261
  }
262

    
263
  // The start date is not guaranteeed to be early than the end date for
264
  // historical reasons.
265
  if ($range['start']['date'] > $range['end']['date']) {
266
    $temp = $range['start'];
267
    $range['start'] = $range['end'];
268
    $range['end'] = $temp;
269
  }
270

    
271
  // Restrict the months and days when not all options are valid choices.
272
  if ($element['#start_date'] && $element['#end_date']) {
273
    $delta_months = ($range['end']['year'] * 12 + $range['end']['month']) - ($range['start']['year'] * 12 + $range['start']['month']);
274
    if ($delta_months < 11) {
275
      // There are 10 or fewer months between the start and end date. If there
276
      // were 11, then every month would be possible, and the menu select
277
      // should not be pruned.
278
      $month_options = &$element['month']['#options'];
279
      if ($range['start']['month'] <= $range['end']['month']) {
280
        $month_options = array_intersect_key($month_options, array_flip(range($range['start']['month'], $range['end']['month'])));
281
      }
282
      else {
283
        $month_options = array_intersect_key($month_options, array_flip(range($range['start']['month'], 12))) +
284
          array_intersect_key($month_options, array_flip(range(1, $range['end']['month'])));
285
      }
286
      unset($month_options); // Drop PHP reference.
287
      if ($delta_months <= 1) {
288
        // The start and end date are either on the same month or consequtive
289
        // months. See if the days should be pruned.
290
        $day_options = &$element['day']['#options'];
291
        if ($range['start']['month'] == $range['end']['month']) {
292
          // Range is within the same month. The days are a simple range from
293
          // start day to end day.
294
          $day_options = array_intersect_key($day_options, array_flip(range($range['start']['day'], $range['end']['day'])));
295
        }
296
        elseif ($range['start']['day'] > $range['end']['day'] + 1) {
297
          // Range spans two months and at least one day would be omitted.
298
          $days_in_month = date('t', mktime(0, 0, 0, $range['start']['month'], 1, $range['start']['year']));
299
          $day_options = array_intersect_key($day_options, array_flip(range($range['start']['day'], $days_in_month))) +
300
            array_intersect_key($day_options, array_flip(range(1, $range['end']['day'])));
301
        }
302
        unset($day_options); // Drop PHP reference.
303
      }
304
    }
305
  }
306

    
307
  // Set default values.
308
  foreach ($default_values as $type => $value) {
309
    switch ($type) {
310
      case 'month':
311
        $none = t('Month');
312
        $hidden_default = 1;
313
        break;
314
      case 'day':
315
        $none = t('Day');
316
        $hidden_default = 1;
317
        break;
318
      case 'year':
319
        $none = t('Year');
320
        $hidden_default = !empty($element['#default_value']['year']) ? $element['#default_value']['year'] : webform_strtodate('Y', 'today', $timezone);
321
        break;
322
    }
323
    unset($element[$type]['#value']);
324
    $element[$type]['#title'] = $none;
325
    $element[$type]['#title_display'] = 'invisible';
326
    $element[$type]['#default_value'] = $default_values[$type];
327
    $element[$type]['#options'] = array('' => $none) + $element[$type]['#options'];
328
    if (in_array($type, $element['#exclude'])) {
329
      $element[$type] += array(
330
        '#prefix' => '<div class="webform-date-field-wrapper element-invisible">',
331
        '#suffix' => '</div>',
332
      );
333
      $element[$type]['#default_value'] = $hidden_default;
334
    }
335
  }
336

    
337
  // Tweak the year field.
338
  if ($element['#year_textfield']) {
339
    $element['year']['#type'] = 'textfield';
340
    $element['year']['#size'] = 5;
341
    $element['year']['#maxlength'] = 4;
342
    unset($element['year']['#options']);
343
  }
344
  elseif ($element['#start_date'] || $element['#end_date']) {
345
    $element['year']['#options'] = array('' => t('Year')) + drupal_map_assoc(range($range['start']['year'], $range['end']['year']));
346
  }
347

    
348
  return $element;
349
}
350

    
351
/**
352
 * Theme a webform date element.
353
 */
354
function theme_webform_date($variables) {
355
  $element = $variables['element'];
356

    
357
  $element['year']['#attributes']['class'][] = 'year';
358
  $element['month']['#attributes']['class'][] = 'month';
359
  $element['day']['#attributes']['class'][] = 'day';
360

    
361
  // Add error classes to all items within the element.
362
  if (form_get_error($element)) {
363
    $element['year']['#attributes']['class'][] = 'error';
364
    $element['month']['#attributes']['class'][] = 'error';
365
    $element['day']['#attributes']['class'][] = 'error';
366
  }
367

    
368
  // Add HTML5 required attribute, if needed.
369
  if ($element['#required']) {
370
    $element['year']['#attributes']['required'] = 'required';
371
    $element['month']['#attributes']['required'] = 'required';
372
    $element['day']['#attributes']['required'] = 'required';
373
  }
374

    
375
  $class = array('webform-container-inline');
376

    
377
  // Add the JavaScript calendar if available (provided by Date module package).
378
  if (!empty($element['#datepicker'])) {
379
    $class[] = 'webform-datepicker';
380
    $calendar_class = array('webform-calendar');
381
    if ($element['#start_date']) {
382
      $calendar_class[] = 'webform-calendar-start-' . $element['#start_date'];
383
    }
384
    if ($element['#end_date']) {
385
      $calendar_class[] = 'webform-calendar-end-' . $element['#end_date'];
386
    }
387
    $calendar_class[] ='webform-calendar-day-' . variable_get('date_first_day', 0);
388

    
389
    $calendar = theme('webform_calendar', array('component' => $element['#webform_component'], 'calendar_classes' => $calendar_class));
390
  }
391

    
392
  $output = '';
393
  $output .= '<div class="' . implode(' ', $class) . '">';
394
  $output .= drupal_render_children($element);
395
  $output .= isset($calendar) ? $calendar : '';
396
  $output .= '</div>';
397

    
398
  return $output;
399
}
400

    
401
/**
402
 * Element validation for Webform date fields.
403
 */
404
function webform_validate_date($element, $form_state) {
405
  $field_types = array('day', 'month', 'year');
406

    
407
  // Determine if the user has specified a date. Hidden parts of the date will be submitted automatically.
408
  foreach ($field_types as $field_type) {
409
    if (!in_array($field_type, $element['#exclude']) && $element[$field_type]['#value'] !== '') {
410
      $field_found = TRUE;
411
    }
412
  }
413

    
414
  if (isset($field_found)) {
415
    // Check that each part of the date has been filled in.
416
    foreach ($field_types as $field_type) {
417
      if (empty($element[$field_type]['#value'])) {
418
        form_error($element[$field_type], t('!part in !name is missing.', array('!name' => $element['#title'], '!part' => $element[$field_type]['#title'])));
419
        $missing_fields = TRUE;
420
      }
421
    }
422
    if (isset($missing_fields)) {
423
      return;
424
    }
425

    
426
    // Ensure date is made up of integers.
427
    foreach (array('year', 'month', 'day') as $date_part) {
428
      $element[$date_part]['#value'] = (int) $element[$date_part]['#value'];
429
    }
430

    
431
    // Check for a valid date.
432
    if (!checkdate($element['month']['#value'], $element['day']['#value'], $element['year']['#value'])) {
433
      form_error($element, t('Entered !name is not a valid date.', array('!name' => $element['#title'])));
434
      return;
435
    }
436

    
437
    // Create a timestamp of the entered value for comparison.
438
    $timestamp = strtotime($element['year']['#value'] . '-' . $element['month']['#value'] . '-' . $element['day']['#value']);
439
    $format = webform_date_format('short');
440

    
441
    // Flip start and end if needed. Prior to 7.x-4.8, it was possible to save
442
    // a date component with the end date earlier than the start date.
443
    $date1 = strtotime($element['#start_date']);
444
    $date2 = strtotime($element['#end_date']);
445
    if ($date1 !== FALSE && $date2 !== FALSE) {
446
      $start_date = $date1 < $date2 ? $date1 : $date2;
447
      $end_date = $date1 > $date2 ? $date1 : $date2;
448
    }
449
    else {
450
      $start_date = $date1;
451
      $end_date = $date2;
452
    }
453

    
454
    // Check that the date is after the start date.
455
    if ($start_date !== FALSE && $timestamp < $start_date) {
456
      form_error($element, t('The entered date must be @start_date or later.', array('@start_date' => date($format, $start_date))));
457
    }
458

    
459
    // Check that the date is before the end date.
460
    if ($end_date !== FALSE && $timestamp > $end_date) {
461
      form_error($element, t('The entered date must be @end_date or earlier.', array('@end_date' => date($format, $end_date))));
462
    }
463
  }
464
  elseif ($element['#required']) {
465
    form_error($element, t('!name field is required.', array('!name' => $element['#title'])));
466
  }
467
}
468

    
469
/**
470
 * Implements _webform_submit_component().
471
 */
472
function _webform_submit_date($component, $value) {
473
  // Convert the date to an ISO 8601 format.
474
  return ($value['year'] && $value['month'] && $value['day']) ? webform_date_string($value, 'date') : '';
475
}
476

    
477
/**
478
 * Implements _webform_display_component().
479
 */
480
function _webform_display_date($component, $value, $format = 'html', $submission = array()) {
481
  $value = webform_date_array(isset($value[0]) ? $value[0] : '', 'date');
482

    
483
  return array(
484
    '#title' => $component['name'],
485
    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
486
    '#weight' => $component['weight'],
487
    '#theme' => 'webform_display_date',
488
    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
489
    '#format' => $format,
490
    '#exclude' => $component['extra']['exclude'],
491

    
492
    '#value' => $value,
493
    '#translatable' => array('title'),
494
  );
495
}
496

    
497
/**
498
 * Format the text output for this component.
499
 */
500
function theme_webform_display_date($variables) {
501
  $element = $variables['element'];
502
  $output = ' ';
503
  if ($element['#value']['year'] && $element['#value']['month'] && $element['#value']['day']) {
504
    $timestamp = webform_strtotime($element['#value']['month'] . '/' . $element['#value']['day'] . '/' . $element['#value']['year']);
505
    $format = webform_date_format(NULL, $element['#exclude']);
506
    $output = format_date($timestamp, 'custom', $format, 'UTC');
507
  }
508

    
509
  return $output;
510
}
511

    
512
/**
513
 * Implements _webform_analysis_component().
514
 */
515
function _webform_analysis_date($component, $sids = array(), $single = FALSE, $join = NULL) {
516
  $query = db_select('webform_submitted_data', 'wsd', array('fetch' => PDO::FETCH_ASSOC))
517
    ->fields('wsd', array('no', 'data'))
518
    ->condition('wsd.nid', $component['nid'])
519
    ->condition('wsd.cid', $component['cid'])
520
    ->orderBy('wsd.sid');
521

    
522
  if (count($sids)) {
523
    $query->condition('wsd.sid', $sids, 'IN');
524
  }
525

    
526
  if ($join) {
527
    $query->innerJoin($join, 'ws2_', 'wsd.sid = ws2_.sid');
528
  }
529

    
530
  $result = $query->execute();
531

    
532
  $dates = array();
533
  $submissions = 0;
534
  foreach ($result as $row) {
535
    $submissions++;
536
    if ($row['data']) {
537
      $dates[] = webform_date_array($row['data']);
538
    }
539
  }
540

    
541
  // Display stats.
542
  $nonblanks = count($dates);
543
  $rows[0] = array(t('Left Blank'), ($submissions - $nonblanks));
544
  $rows[1] = array(t('User entered value'), $nonblanks);
545

    
546
  return array(
547
    'table_rows' => $rows,
548
  );
549
}
550

    
551
/**
552
 * Implements _webform_table_component().
553
 */
554
function _webform_table_date($component, $value) {
555
  if ($value[0]) {
556
    $timestamp = webform_strtotime($value[0]);
557
    $format = webform_date_format('short', $component['extra']['exclude']);
558
    return format_date($timestamp, 'custom', $format, 'UTC');
559
  }
560
  else {
561
    return '';
562
  }
563
}
564

    
565
/**
566
 * Implements _webform_csv_headers_component().
567
 */
568
function _webform_csv_headers_date($component, $export_options) {
569
  $header = array();
570
  $header[0] = '';
571
  $header[1] = '';
572
  $header[2] = $export_options['header_keys'] ? $component['form_key'] : $component['name'];
573
  return $header;
574
}
575

    
576
/**
577
 * Implements _webform_csv_data_component().
578
 */
579
function _webform_csv_data_date($component, $export_options, $value) {
580
  if ($value[0]) {
581
    $timestamp = webform_strtotime($value[0]);
582
    if (!empty($export_options['iso8601_date'])) {
583
      $format = 'Y-m-d'; // ISO 8601 date: 2004-02-12
584
    }
585
    else {
586
      $format = webform_date_format('short');
587
    }
588
    return format_date($timestamp, 'custom', $format, 'UTC');
589
  }
590
  else {
591
    return '';
592
  }
593
}