Projet

Général

Profil

Paste
Télécharger (38,4 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / date / date_repeat / date_repeat_form.inc @ db9ffd17

1
<?php
2
/**
3
 * @file
4
 * Code to add a date repeat selection form to a date field and create
5
 * an iCal RRULE from the chosen selections.
6
 *
7
 * Moved to a separate file since it is not used on most pages
8
 * so the code is not parsed unless needed.
9
 *
10
 * Currently implemented:
11
 * INTERVAL, UNTIL, EXDATE, RDATE, BYDAY, BYMONTHDAY, BYMONTH,
12
 * YEARLY, MONTHLY, WEEKLY, DAILY
13
 *
14
 * Currently not implemented:
15
 *
16
 * BYYEARDAY, MINUTELY, HOURLY, SECONDLY, BYMINUTE, BYHOUR, BYSECOND
17
 *   These could be implemented in the future.
18
 *
19
 * COUNT
20
 *   The goal of this module is to create a way we can parse an iCal
21
 *   RRULE and pull out just dates for a specified date range, for
22
 *   instance with a date that repeats daily for several years, we might
23
 *   want to only be able to pull out the dates for the current year.
24
 *
25
 *   Adding COUNT to the rules we create makes it impossible to do that
26
 *   without parsing and computing the whole range of dates that the rule
27
 *   will create. COUNT is left off of the user form completely for this
28
 *   reason.
29
 *
30
 * BYSETPOS
31
 *   Seldom used anywhere, so no reason to complicated the code.
32
 */
33
/**
34
 * Generate the repeat setting form.
35
 */
36
function _date_repeat_rrule_process($element, &$form_state, $form) {
37

    
38
  // If the RRULE field is not visible to the user, needs no processing or validation.
39
  // The Date field module is not adding this element to forms if the field is hidden,
40
  // this test is just in case some other module attempts to do so.
41

    
42
  if (date_hidden_element($element)) {
43
    return $element;
44
  }
45

    
46
  module_load_include('inc', 'date_api', 'date_api_ical');
47
  if (empty($element['#date_repeat_widget'])) {
48
    $element['#date_repeat_widget'] = module_exists('date_popup') ? 'date_popup' : 'date_select';
49
  }
50
  if (is_array($element['#default_value'])) {
51
    $element['#value'] = date_repeat_merge($element['#value'], $element);
52
    $rrule = date_api_ical_build_rrule($element['#value']);
53
  }
54
  else {
55
    $rrule = $element['#default_value'];
56
  }
57

    
58
  // Empty the original string value of the RRULE so we can create
59
  // an array of values for the form from the RRULE's contents.
60
  $element['#value'] = '';
61

    
62
  $parts = date_repeat_split_rrule($rrule);
63
  $rrule = $parts[0];
64

    
65
  $exceptions = $parts[1];
66
  $additions = $parts[2];
67
  $timezone = !empty($element['#date_timezone']) ? $element['#date_timezone'] : date_default_timezone();
68
  $merged_values = date_repeat_merge($rrule, $element);
69

    
70
  $UNTIL = '';
71
  if (!empty($merged_values['UNTIL']['datetime'])) {
72
    $until_date = new DateObject($merged_values['UNTIL']['datetime'], $merged_values['UNTIL']['tz']);
73
    date_timezone_set($until_date, timezone_open($timezone));
74
    $UNTIL = date_format($until_date, DATE_FORMAT_DATETIME);
75
  }
76

    
77
  $COUNT = '';
78
  if (!empty($merged_values['COUNT'])) {
79
    $COUNT = $merged_values['COUNT'];
80
  }
81

    
82
  $element['FREQ'] = array(
83
    '#type' => 'select',
84
    '#title' => t('Repeats', array(), array('context' => 'Date repeat')),
85
    '#default_value' => !empty($rrule['FREQ']) ? $rrule['FREQ'] : 'WEEKLY',
86
    '#options' => date_repeat_freq_options(),
87
    '#prefix' => '<div class="date-repeat-input">',
88
    '#suffix' => '</div>',
89
  );
90

    
91
  $element['daily'] = array(
92
    '#type' => 'container',
93
    '#tree' => TRUE,
94
    '#prefix' => '<div class="date-clear daily">',
95
    '#suffix' => '</div>',
96
    '#states' => array(
97
      'visible' => array(
98
        ":input[name=\"{$element['#name']}[FREQ]\"]" => array('value' => 'DAILY'),
99
      ),
100
    ),
101
  );
102

    
103
  $element['weekly'] = array(
104
    '#type' => 'container',
105
    '#tree' => TRUE,
106
    '#prefix' => '<div class="date-clear weekly">',
107
    '#suffix' => '</div>',
108
    '#states' => array(
109
      'visible' => array(
110
        ":input[name=\"{$element['#name']}[FREQ]\"]" => array('value' => 'WEEKLY'),
111
      ),
112
    ),
113
  );
114

    
115
  $element['monthly'] = array(
116
    '#type' => 'container',
117
    '#tree' => TRUE,
118
    '#prefix' => '<div class="date-clear monthly">',
119
    '#suffix' => '</div>',
120
    '#states' => array(
121
      'visible' => array(
122
        ":input[name=\"{$element['#name']}[FREQ]\"]" => array('value' => 'MONTHLY'),
123
      ),
124
    ),
125
  );
126

    
127
  $element['yearly'] = array(
128
    '#type' => 'container',
129
    '#tree' => TRUE,
130
    '#prefix' => '<div class="date-clear yearly">',
131
    '#suffix' => '</div>',
132
    '#states' => array(
133
      'visible' => array(
134
        ":input[name=\"{$element['#name']}[FREQ]\"]" => array('value' => 'YEARLY'),
135
      ),
136
    ),
137
  );
138

    
139
  list($prefix, $suffix) = explode('@interval', t('Every @interval days', array(), array('context' => 'Date repeat')));
140
  $DAILY_INTERVAL = array(
141
    '#type' => 'textfield',
142
    '#title' => t('Repeats', array(), array('context' => 'Date repeat')),
143
    '#title_display' => 'invisible',
144
    '#default_value' => (!empty($rrule['INTERVAL']) ? $rrule['INTERVAL'] : 1),
145
    '#element_validate' => array('element_validate_integer_positive'),
146
    '#attributes' => array('placeholder' => array('#')),
147
    '#size' => 3,
148
    '#maxlength' => 3,
149
    '#prefix' => '<div class="date-clear">',
150
    '#suffix' => t('days') . '</div>',
151
    '#field_prefix' => $prefix,
152
    '#field_suffix' => $suffix,
153
  );
154

    
155
  list($prefix, $suffix) = explode('@interval', t('Every @interval weeks', array(), array('context' => 'Date repeat')));
156
  $element['weekly']['INTERVAL'] = array(
157
    '#type' => 'textfield',
158
    '#title' => t('Repeats', array(), array('context' => 'Date repeat')),
159
    '#default_value' => (!empty($rrule['INTERVAL']) ? $rrule['INTERVAL'] : 1),
160
    '#element_validate' => array('element_validate_integer_positive'),
161
    '#attributes' => array('placeholder' => array('#')),
162
    '#size' => 3,
163
    '#maxlength' => 3,
164
    '#prefix' => '<div class="date-clear">',
165
    '#suffix' => '</div>',
166
    '#field_prefix' => $prefix,
167
    '#field_suffix' => $suffix,
168
  );
169

    
170
  list($prefix, $suffix) = explode('@interval', t('Every @interval months', array(), array('context' => 'Date repeat')));
171
  $element['monthly']['INTERVAL'] = array(
172
    '#access' => FALSE,
173
    '#type' => 'textfield',
174
    '#title' => t('Repeats', array(), array('context' => 'Date repeat')),
175
    '#default_value' => (!empty($rrule['INTERVAL']) ? $rrule['INTERVAL'] : 1),
176
    '#element_validate' => array('element_validate_integer_positive'),
177
    '#attributes' => array('placeholder' => array('#')),
178
    '#size' => 3,
179
    '#maxlength' => 3,
180
    '#prefix' => '<div class="date-clear">',
181
    '#suffix' => '</div>',
182
    '#field_prefix' => $prefix,
183
    '#field_suffix' => $suffix,
184
  );
185

    
186
  list($prefix, $suffix) = explode('@interval', t('Every @interval years', array(), array('context' => 'Date repeat')));
187
  $element['yearly']['INTERVAL'] = array(
188
    '#type' => 'textfield',
189
    '#title' => t('Repeats', array(), array('context' => 'Date repeat')),
190
    '#default_value' => (!empty($rrule['INTERVAL']) ? $rrule['INTERVAL'] : 1),
191
    '#element_validate' => array('element_validate_integer_positive'),
192
    '#attributes' => array('placeholder' => array('#')),
193
    '#size' => 3,
194
    '#maxlength' => 3,
195
    '#prefix' => '<div class="date-clear">',
196
    '#suffix' => '</div>',
197
    '#field_prefix' => $prefix,
198
    '#field_suffix' => $suffix,
199
  );
200
  $options = date_repeat_dow_day_options_abbr(TRUE);
201
  $options = date_repeat_dow_day_options_ordered($options);
202
  $element['weekly']['BYDAY'] = array(
203
    '#type' => 'checkboxes',
204
    '#title' => t('Repeat on', array(), array('context' => 'Date repeat')),
205
    '#default_value' => !empty($rrule['BYDAY']) && $rrule['FREQ'] === 'WEEKLY' ? $rrule['BYDAY'] : array(),
206
    '#options' => $options,
207
    '#attributes' => array('class' => array('container-inline byday')),
208
    '#multiple' => TRUE,
209
    '#prefix' => '<div class="date-clear">',
210
    '#suffix' => '</div>',
211
  );
212

    
213
  $DAILY_radios_default = 'INTERVAL';
214
  if (isset($rrule['FREQ']) && $rrule['FREQ'] === 'DAILY' && !empty($rrule['BYDAY'])) {
215
    switch (count($rrule['BYDAY'])) {
216
      case 2:
217
        $DAILY_radios_default = 'every_tu_th';
218
        break;
219
      case 3:
220
        $DAILY_radios_default = 'every_mo_we_fr';
221
        break;
222
      case 5:
223
        $DAILY_radios_default = 'every_weekday';
224
        break;
225
    }
226
  }
227

    
228
  $DAILY_every_weekday = array(
229
    '#type' => 'item',
230
    '#markup' => '<div>' . t('Every weekday', array(), array('context' => 'Date repeat')) . '</div>',
231
  );
232

    
233
  $DAILY_mo_we_fr = array(
234
    '#type' => 'item',
235
    '#markup' => '<div>' . t('Every Mon, Wed, Fri', array(), array('context' => 'Date repeat')) . '</div>',
236
  );
237

    
238
  $DAILY_tu_th = array(
239
    '#type' => 'item',
240
    '#markup' => '<div>' . t('Every Tue, Thu', array(), array('context' => 'Date repeat')) . '</div>',
241
  );
242

    
243
  $element['daily']['byday_radios'] = array(
244
    '#type' => 'date_repeat_form_element_radios',
245
    '#tree' => TRUE,
246
    '#title' => t('Repeats every', array(), array('context' => 'Date repeat')),
247
    '#prefix' => '<div class="date-clear">',
248
    '#suffix' => '</div>',
249
    '#states' => array(
250
      'visible' => array(
251
        ":input[name=\"{$element['#name']}[FREQ]\"]" => array('value' => 'DAILY'),
252
      ),
253
    ),
254
    '#default_value' => $DAILY_radios_default,
255
    '#options' => array(
256
      'INTERVAL' => t('interval'),
257
      'every_weekday' => t('every weekday'),
258
      'every_mo_we_fr' => t('monday wednesday friday'),
259
      'every_tu_th' => t('tuesday thursday'),
260
    ),
261
    'INTERVAL_child' => $DAILY_INTERVAL,
262
    'every_weekday_child' => $DAILY_every_weekday,
263
    'mo_we_fr_child' => $DAILY_mo_we_fr,
264
    'tu_th_child' => $DAILY_tu_th,
265
    '#div_classes' => array(
266
      'container-inline interval',
267
      'container-inline weekday',
268
      'container-inline mo-we-fr',
269
      'container-inline tu-th',
270
    ),
271
  );
272

    
273
  $MONTHLY_day_month_default = 'BYMONTHDAY_BYMONTH';
274
  if (isset($rrule['FREQ']) && $rrule['FREQ'] === 'MONTHLY' && !empty($rrule['BYDAY'])) {
275
    $MONTHLY_day_month_default = 'BYDAY_BYMONTH';
276
  }
277

    
278
  $MONTHLY_on_day_BYMONTHDAY_of_BYMONTH = array(
279
    '#type' => 'container',
280
    '#tree' => TRUE,
281
  );
282

    
283
  list($bymonthday_title, $bymonthday_suffix) = explode('@bymonthday', t('On day @bymonthday of', array(), array('context' => 'Date repeat')));
284
  $MONTHLY_on_day_BYMONTHDAY_of_BYMONTH['BYMONTHDAY'] = array(
285
    '#type' => 'select',
286
    '#title' => $bymonthday_title,
287
    '#default_value' => !empty($rrule['BYMONTHDAY']) && $rrule['FREQ'] === 'MONTHLY' ? $rrule['BYMONTHDAY'] : '',
288
    '#options' => drupal_map_assoc(range(1, 31)) + drupal_map_assoc(range(-1, -31)),
289
    '#multiple' => FALSE,
290
    '#prefix' => '<div class="date-clear bymonthday">',
291
    '#suffix' => '</div>',
292
    '#field_suffix' => $bymonthday_suffix,
293
  );
294

    
295
  $MONTHLY_on_day_BYMONTHDAY_of_BYMONTH['BYMONTH'] = array(
296
    '#type' => 'checkboxes',
297
    '#title' => t('Bymonth', array(), array('context' => 'Date repeat')),
298
    '#title_display' => 'invisible',
299
    '#default_value' => !empty($rrule['BYMONTH']) && $rrule['FREQ'] === 'MONTHLY' && $MONTHLY_day_month_default === 'BYMONTHDAY_BYMONTH' ? $rrule['BYMONTH'] : array(),
300
    '#options' => date_month_names_abbr(TRUE),
301
    '#attributes' => array('class' => array('container-inline')),
302
    '#multiple' => TRUE,
303
    '#prefix' => '<div class="date-clear bymonth">',
304
    '#suffix' => '</div>',
305
  );
306

    
307
  $MONTHLY_on_the_BYDAY_of_BYMONTH = array(
308
    '#type' => 'container',
309
    '#tree' => TRUE,
310
  );
311

    
312
  $MONTHLY_BYDAY_COUNT = '';
313
  $MONTHLY_BYDAY_DAY = '';
314
  if (isset($rrule['BYDAY']) && !empty($rrule['BYDAY']) && $rrule['FREQ'] === 'MONTHLY') {
315
    $MONTHLY_BYDAY_COUNT = substr($rrule['BYDAY'][0], 0, -2);
316
    $MONTHLY_BYDAY_DAY = substr($rrule['BYDAY'][0], -2);;
317
  }
318

    
319
  list($byday_count_title, $byday_day_title) = explode('@byday', t('On the @byday of', array(), array('context' => 'Date repeat')));
320
  $MONTHLY_on_the_BYDAY_of_BYMONTH['BYDAY_COUNT'] = array(
321
    '#type' => 'select',
322
    '#title' => $byday_count_title,
323
    '#default_value' => !empty($MONTHLY_BYDAY_COUNT) ? $MONTHLY_BYDAY_COUNT : '',
324
    '#options' => date_order_translated(),
325
    '#multiple' => FALSE,
326
    '#prefix' => '<div class="date-repeat-input byday-count">',
327
    '#suffix' => '</div>',
328
  );
329

    
330
  $MONTHLY_on_the_BYDAY_of_BYMONTH['BYDAY_DAY'] = array(
331
    '#type' => 'select',
332
    '#title' => $byday_day_title,
333
    '#title_display' => 'after',
334
    '#default_value' => !empty($MONTHLY_BYDAY_DAY) ? $MONTHLY_BYDAY_DAY : '',
335
    '#options' => date_repeat_dow_day_options(TRUE),
336
    '#multiple' => FALSE,
337
    '#prefix' => '<div class="date-repeat-input byday-day">',
338
    '#suffix' => '</div>',
339
  );
340

    
341
  $MONTHLY_on_the_BYDAY_of_BYMONTH['BYMONTH'] = array(
342
    '#type' => 'checkboxes',
343
    '#title' => t('Bymonth', array(), array('context' => 'Date repeat')),
344
    '#title_display' => 'invisible',
345
    '#default_value' => !empty($rrule['BYMONTH']) && $rrule['FREQ'] === 'MONTHLY' && $MONTHLY_day_month_default === 'BYDAY_BYMONTH' ? $rrule['BYMONTH'] : array(),
346
    '#options' => date_month_names_abbr(TRUE),
347
    '#attributes' => array('class' => array('container-inline')),
348
    '#multiple' => TRUE,
349
    '#prefix' => '<div class="date-clear bymonth">',
350
    '#suffix' => '</div>',
351
  );
352

    
353
  $element['monthly']['day_month'] = array(
354
    '#type' => 'date_repeat_form_element_radios',
355
    '#tree' => TRUE,
356
    '#prefix' => '<div class="date-clear">',
357
    '#suffix' => '</div>',
358
    '#states' => array(
359
      'visible' => array(
360
        ":input[name=\"{$element['#name']}[FREQ]\"]" => array('value' => 'MONTHLY'),
361
      ),
362
    ),
363
    '#attributes' => array('class' => array('date-repeat-radios clearfix')),
364
    '#default_value' => $MONTHLY_day_month_default,
365
    '#options' => array(
366
      'BYMONTHDAY_BYMONTH' => t('On day ... of ...'),
367
      'BYDAY_BYMONTH' => t('On the ... of ...'),
368
    ),
369
    'BYMONTHDAY_BYMONTH_child' => $MONTHLY_on_day_BYMONTHDAY_of_BYMONTH,
370
    'BYDAY_BYMONTH_child' => $MONTHLY_on_the_BYDAY_of_BYMONTH,
371
    '#div_classes' => array(
372
      'date-repeat-radios-item date-clear clearfix bymonthday-bymonth',
373
      'date-repeat-radios-item date-clear clearfix byday-bymonth',
374
    ),
375
  );
376

    
377
  $YEARLY_day_month_default = 'BYMONTHDAY_BYMONTH';
378
  if (isset($rrule['FREQ']) && $rrule['FREQ'] === 'YEARLY' && !empty($rrule['BYDAY'])) {
379
    $YEARLY_day_month_default = 'BYDAY_BYMONTH';
380
  }
381

    
382
  $YEARLY_on_day_BYMONTHDAY_of_BYMONTH = array(
383
    '#type' => 'container',
384
    '#tree' => TRUE,
385
  );
386

    
387
  list($bymonthday_title, $bymonthday_suffix) = explode('@bymonthday', t('On day @bymonthday of', array(), array('context' => 'Date repeat')));
388
  $YEARLY_on_day_BYMONTHDAY_of_BYMONTH['BYMONTHDAY'] = array(
389
    '#type' => 'select',
390
    '#title' => $bymonthday_title,
391
    '#default_value' => !empty($rrule['BYMONTHDAY']) && $rrule['FREQ'] === 'YEARLY' ? $rrule['BYMONTHDAY'] : '',
392
    '#options' => drupal_map_assoc(range(1, 31)) + drupal_map_assoc(range(-1, -31)),
393
    '#multiple' => FALSE,
394
    '#prefix' => '<div class="date-clear bymonthday">',
395
    '#suffix' => '</div>',
396
    '#field_suffix' => $bymonthday_suffix,
397
  );
398

    
399
  $YEARLY_on_day_BYMONTHDAY_of_BYMONTH['BYMONTH'] = array(
400
    '#type' => 'checkboxes',
401
    '#title' => t('Bymonth', array(), array('context' => 'Date repeat')),
402
    '#title_display' => 'invisible',
403
    '#default_value' => !empty($rrule['BYMONTH']) && $rrule['FREQ'] === 'YEARLY' && $YEARLY_day_month_default === 'BYMONTHDAY_BYMONTH' ? $rrule['BYMONTH'] : array(),
404
    '#options' => date_month_names_abbr(TRUE),
405
    '#attributes' => array('class' => array('container-inline')),
406
    '#multiple' => TRUE,
407
    '#prefix' => '<div class="date-clear bymonth">',
408
    '#suffix' => '</div>',
409
  );
410

    
411
  $YEARLY_on_the_BYDAY_of_BYMONTH = array(
412
    '#type' => 'container',
413
    '#tree' => TRUE,
414
  );
415

    
416
  $YEARLY_BYDAY_COUNT = '';
417
  $YEARLY_BYDAY_DAY = '';
418
  if (isset($rrule['BYDAY']) && !empty($rrule['BYDAY']) && $rrule['FREQ'] === 'YEARLY') {
419
    $YEARLY_BYDAY_COUNT = substr($rrule['BYDAY'][0], 0, -2);
420
    $YEARLY_BYDAY_DAY = substr($rrule['BYDAY'][0], -2);;
421
  }
422

    
423
  list($byday_count_title, $byday_day_title) = explode('@byday', t('On the @byday of', array(), array('context' => 'Date repeat')));
424
  $YEARLY_on_the_BYDAY_of_BYMONTH['BYDAY_COUNT'] = array(
425
    '#type' => 'select',
426
    '#title' => $byday_count_title,
427
    '#default_value' => !empty($YEARLY_BYDAY_COUNT) ? $YEARLY_BYDAY_COUNT : '',
428
    '#options' => date_order_translated(),
429
    '#multiple' => FALSE,
430
    '#prefix' => '<div class="date-repeat-input byday-count">',
431
    '#suffix' => '</div>',
432
  );
433

    
434
  $YEARLY_on_the_BYDAY_of_BYMONTH['BYDAY_DAY'] = array(
435
    '#type' => 'select',
436
    '#title' => $byday_day_title,
437
    '#title_display' => 'after',
438
    '#default_value' => !empty($YEARLY_BYDAY_DAY) ? $YEARLY_BYDAY_DAY : '',
439
    '#options' => date_repeat_dow_day_options(TRUE),
440
    '#multiple' => FALSE,
441
    '#prefix' => '<div class="date-repeat-input byday-day">',
442
    '#suffix' => '</div>',
443
  );
444

    
445
  $YEARLY_on_the_BYDAY_of_BYMONTH['BYMONTH'] = array(
446
    '#type' => 'checkboxes',
447
    '#title' => t('Bymonth', array(), array('context' => 'Date repeat')),
448
    '#title_display' => 'invisible',
449
    '#default_value' => !empty($rrule['BYMONTH']) && $rrule['FREQ'] === 'YEARLY' && $YEARLY_day_month_default === 'BYDAY_BYMONTH' ? $rrule['BYMONTH'] : array(),
450
    '#options' => date_month_names_abbr(TRUE),
451
    '#attributes' => array('class' => array('container-inline')),
452
    '#multiple' => TRUE,
453
    '#prefix' => '<div class="date-clear bymonth">',
454
    '#suffix' => '</div>',
455
  );
456

    
457
  $element['yearly']['day_month'] = array(
458
    '#type' => 'date_repeat_form_element_radios',
459
    '#tree' => TRUE,
460
    '#prefix' => '<div class="date-clear">',
461
    '#suffix' => '</div>',
462
    '#states' => array(
463
      'visible' => array(
464
        ":input[name=\"{$element['#name']}[FREQ]\"]" => array('value' => 'YEARLY'),
465
      ),
466
    ),
467
    '#attributes' => array('class' => array('date-repeat-radios clearfix')),
468
    '#default_value' => $YEARLY_day_month_default,
469
    '#options' => array(
470
      'BYMONTHDAY_BYMONTH' => t('On day ... of ...'),
471
      'BYDAY_BYMONTH' => t('On the ... of ...'),
472
    ),
473
    'BYMONTHDAY_BYMONTH_child' => $YEARLY_on_day_BYMONTHDAY_of_BYMONTH,
474
    'BYDAY_BYMONTH_child' => $YEARLY_on_the_BYDAY_of_BYMONTH,
475
    '#div_classes' => array(
476
      'date-repeat-radios-item date-clear clearfix bymonthday-bymonth',
477
      'date-repeat-radios-item date-clear clearfix byday-bymonth',
478
    ),
479
  );
480

    
481
  list($prefix, $suffix) = explode('@count', t('After @count occurrences', array(), array('context' => 'Date repeat')));
482
  $count_form_element = array(
483
    '#type' => 'textfield',
484
    '#title' => t('Count', array(), array('context' => 'Date repeat')),
485
    '#default_value' => $COUNT,
486
    '#element_validate' => array('element_validate_integer_positive'),
487
    '#attributes' => array('placeholder' => array('#')),
488
    '#prefix' => $prefix,
489
    '#suffix' => $suffix,
490
    '#size' => 10,
491
    '#maxlength' => 10,
492
  );
493

    
494
  $until_form_element = array(
495
    '#type' => 'container',
496
    '#tree' => TRUE,
497
    '#prefix' => '<div class="date-prefix-inline">' . t('On', array(), array('context' => 'Date repeat')) . '</div>',
498
    'datetime' => array(
499
      '#type' => $element['#date_repeat_widget'],
500
      '#title' => t('Until', array(), array('context' => 'Date repeat')),
501
      '#title_display' => 'invisible',
502
      '#default_value' => $UNTIL,
503
      '#date_format' => !empty($element['#date_format']) ? date_limit_format($element['#date_format'], array('year', 'month', 'day')) : 'Y-m-d',
504
      '#date_timezone' => $timezone,
505
      '#date_text_parts'  => !empty($element['#date_text_parts']) ? $element['#date_text_parts'] : array(),
506
      '#date_year_range'  => !empty($element['#date_year_range']) ? $element['#date_year_range'] : '-3:+3',
507
      '#date_label_position' => !empty($element['#date_label_position']) ? $element['#date_label_position'] : 'within',
508
      '#date_flexible' => 0,
509
    ),
510
    'tz' => array('#type' => 'hidden', '#value' => $element['#date_timezone']),
511
    'all_day' => array('#type' => 'hidden', '#value' => 1),
512
    'granularity' => array('#type' => 'hidden', '#value' => serialize(array('year', 'month', 'day'))),
513
  );
514

    
515
  $range_of_repeat_default = 'COUNT';
516
  if (!empty($UNTIL)) {
517
    $range_of_repeat_default = 'UNTIL';
518
  }
519
  $element['range_of_repeat'] = array(
520
    '#type' => 'date_repeat_form_element_radios',
521
    '#tree' => TRUE,
522
    '#title' => t('Stop repeating', array(), array('context' => 'Date repeat')),
523
    '#title_display' => 'before',
524
    '#prefix' => '<div class="date-clear range-of-repeat">',
525
    '#suffix' => '</div>',
526
    '#states' => array(
527
      'invisible' => array(
528
        ":input[name=\"{$element['#name']}[FREQ]\"]" => array('value' => 'NONE'),
529
      ),
530
    ),
531
    '#default_value' =>  $range_of_repeat_default,
532
    '#options' => array(
533
      'COUNT' => t('Count'),
534
      'UNTIL' => t('Until'),
535
    ),
536
    'count_child' => $count_form_element,
537
    'until_child' => $until_form_element,
538
    '#div_classes' => array(
539
      'container-inline count',
540
      "until widget-{$element['#date_repeat_widget']} label-{$element['#date_label_position']}",
541
    ),
542
  );
543

    
544
  $parents = $element['#array_parents'];
545
  $instance = implode('-', $parents);
546

    
547
  // Make sure this will work right either in the normal form or in an ajax callback from the 'Add more' button.
548
  if (empty($form_state['num_exceptions'][$instance])) {
549
    $form_state['num_exceptions'][$instance] = count($exceptions);
550
  }
551
  if ($form_state['num_exceptions'][$instance] == 0) {
552
    $collapsed = TRUE;
553
  }
554
  else {
555
    $collapsed = FALSE;
556
  }
557

    
558
  $element['show_exceptions'] = array(
559
    '#type' => 'checkbox',
560
    '#title' => t('Exclude dates', array(), array('context' => 'Date repeat')),
561
    '#states' => array(
562
      'invisible' => array(
563
        ":input[name=\"{$element['#name']}[FREQ]\"]" => array('value' => 'NONE'),
564
      ),
565
    ),
566
    '#default_value' => empty($form_state['num_exceptions'][$instance]) ? 0 : 1,
567
  );
568

    
569
  $element['exceptions'] = array(
570
    '#type' => 'container',
571
    '#prefix' => '<div id="date-repeat-exceptions-' . $instance . '" class="date-repeat">',
572
    '#suffix' => '</div>',
573
    '#states' => array(
574
      'visible' => array(
575
        ":input[name=\"{$element['#name']}[show_exceptions]\"]" => array('checked' => TRUE),
576
      ),
577
    ),
578
  );
579
  for ($i = 0; $i < max($form_state['num_exceptions'][$instance], 1) ; $i++) {
580
    $EXCEPT = '';
581
    if (!empty($exceptions[$i]['datetime'])) {
582
      $ex_date = new DateObject($exceptions[$i]['datetime'], $exceptions[$i]['tz']);
583
      date_timezone_set($ex_date, timezone_open($timezone));
584
      $EXCEPT = date_format($ex_date, DATE_FORMAT_DATETIME);
585
    }
586
    $element['exceptions']['EXDATE'][$i] = array(
587
      '#tree' => TRUE,
588
      'datetime' => array(
589
        '#name' => 'exceptions|' . $instance,
590
        '#type' => $element['#date_repeat_widget'],
591
        '#default_value' => $EXCEPT,
592
        '#date_timezone' => !empty($element['#date_timezone']) ? $element['#date_timezone'] : date_default_timezone(),
593
        '#date_format' => !empty($element['#date_format']) ? date_limit_format($element['#date_format'], array('year', 'month', 'day')) : 'Y-m-d',
594
        '#date_text_parts'  => !empty($element['#date_text_parts']) ? $element['#date_text_parts'] : array(),
595
        '#date_year_range'  => !empty($element['#date_year_range']) ? $element['#date_year_range'] : '-3:+3',
596
        '#date_label_position' => !empty($element['#date_label_position']) ? $element['#date_label_position'] : 'within',
597
        '#date_flexible' => 0,
598
        ),
599
      'tz' => array('#type' => 'hidden', '#value' => $element['#date_timezone']),
600
      'all_day' => array('#type' => 'hidden', '#value' => 1),
601
      'granularity' => array('#type' => 'hidden', '#value' => serialize(array('year', 'month', 'day'))),
602
      );
603
  }
604

    
605
  // collect additions in the same way as exceptions - implements RDATE.
606
  if (empty($form_state['num_additions'][$instance])) {
607
    $form_state['num_additions'][$instance] = count($additions);
608
  }
609
  if ($form_state['num_additions'][$instance] == 0) {
610
    $collapsed = TRUE;
611
  }
612
  else {
613
    $collapsed = FALSE;
614
  }
615

    
616
  $element['show_additions'] = array(
617
    '#type' => 'checkbox',
618
    '#title' => t('Include dates', array(), array('context' => 'Date repeat')),
619
    '#states' => array(
620
      'invisible' => array(
621
        ":input[name=\"{$element['#name']}[FREQ]\"]" => array('value' => 'NONE'),
622
      ),
623
    ),
624
    '#default_value' => empty($form_state['num_additions'][$instance]) ? 0 : 1,
625
  );
626

    
627
  $element['additions'] = array(
628
    '#type' => 'container',
629
    '#prefix' => '<div id="date-repeat-additions-' . $instance . '" class="date-repeat">',
630
    '#suffix' => '</div>',
631
    '#states' => array(
632
      'visible' => array(
633
        ":input[name=\"{$element['#name']}[show_additions]\"]" => array('checked' => TRUE),
634
      ),
635
    ),
636
  );
637
  for ($i = 0; $i < max($form_state['num_additions'][$instance], 1) ; $i++) {
638
    $RDATE = '';
639
    if (!empty($additions[$i]['datetime'])) {
640
      $rdate = new DateObject($additions[$i]['datetime'], $additions[$i]['tz']);
641
      date_timezone_set($rdate, timezone_open($timezone));
642
      $RDATE = date_format($rdate, DATE_FORMAT_DATETIME);
643
    }
644
    $element['additions']['RDATE'][$i] = array(
645
      '#tree' => TRUE,
646
      'datetime' => array(
647
        '#type' => $element['#date_repeat_widget'],
648
        '#name' => 'additions|' . $instance,
649
        '#default_value' => $RDATE,
650
        '#date_timezone' => !empty($element['#date_timezone']) ? $element['#date_timezone'] : date_default_timezone(),
651
        '#date_format' => !empty($element['#date_format']) ? date_limit_format($element['#date_format'], array('year', 'month', 'day')) : 'Y-m-d',
652
        '#date_text_parts'  => !empty($element['#date_text_parts']) ? $element['#date_text_parts'] : array(),
653
        '#date_year_range'  => !empty($element['#date_year_range']) ? $element['#date_year_range'] : '-3:+3',
654
        '#date_label_position' => !empty($element['#date_label_position']) ? $element['#date_label_position'] : 'within',
655
        '#date_flexible' => 0,
656
        ),
657
      'tz' => array('#type' => 'hidden', '#value' => $element['#date_timezone']),
658
      'all_day' => array('#type' => 'hidden', '#value' => 1),
659
      'granularity' => array('#type' => 'hidden', '#value' => serialize(array('year', 'month', 'day'))),
660
      );
661
  }
662

    
663
  $element['exceptions']['exceptions_add'] = array(
664
    '#type' => 'submit',
665
    '#name' => 'exceptions_add|' . $instance,
666
    '#value' => t('Add exception'),
667
    '#submit' => array('date_repeat_add_exception'),
668
    '#limit_validation_errors' => array(),
669
    '#ajax' => array(
670
      'callback' => 'date_repeat_add_exception_callback',
671
      'wrapper' => 'date-repeat-exceptions-' . $instance,
672
    ),
673
  );
674
  $element['additions']['additions_add'] = array(
675
    '#type' => 'submit',
676
    '#name' => 'additions_add|' . $instance,
677
    '#value' => t('Add addition'),
678
    '#submit' => array('date_repeat_add_addition'),
679
    '#limit_validation_errors' => array(),
680
    '#ajax' => array(
681
      'callback' => 'date_repeat_add_addition_callback',
682
      'wrapper' => 'date-repeat-additions-' . $instance,
683
    ),
684
  );
685

    
686
  $element['#date_repeat_collapsed'] = !empty($rrule['INTERVAL']) || !empty($rrule['FREQ']) ? 0 : (!empty($element['#date_repeat_collapsed']) ? $element['#date_repeat_collapsed'] : 0);
687
  return $element;
688
}
689

    
690
function date_repeat_add_exception_callback($form, &$form_state) {
691
  $parents = $form_state['triggering_element']['#array_parents'];
692
  $button_key = array_pop($parents);
693
  $element = drupal_array_get_nested_value($form, $parents);
694
  return $element;
695
}
696

    
697
function date_repeat_add_addition_callback($form, &$form_state) {
698
  $parents = $form_state['triggering_element']['#array_parents'];
699
  $button_key = array_pop($parents);
700
  $element = drupal_array_get_nested_value($form, $parents);
701
  return $element;
702
}
703

    
704
function date_repeat_add_exception($form, &$form_state) {
705
  $parents = $form_state['triggering_element']['#array_parents'];
706
  $instance = implode('-', array_slice($parents, 0, count($parents) - 2));
707
  $form_state['num_exceptions'][$instance]++;
708
  $form_state['rebuild'] = TRUE;
709
}
710

    
711
function date_repeat_add_addition($form, &$form_state) {
712
  $parents = $form_state['triggering_element']['#array_parents'];
713
  $instance = implode('-', array_slice($parents, 0, count($parents) - 2));
714
  $form_state['num_additions'][$instance]++;
715
  $form_state['rebuild'] = TRUE;
716
}
717

    
718
/**
719
 * Regroup values back into a consistant array, no matter what state it is in.
720
 */
721
function date_repeat_merge($form_values, $element) {
722
  if (empty($form_values) || !is_array($form_values)) {
723
    return $form_values;
724
  }
725
  if (array_key_exists('exceptions', $form_values) || array_key_exists('additions', $form_values)) {
726
    if (!array_key_exists('exceptions', $form_values)) $form_values['exceptions'] = array();
727
    if (!array_key_exists('additions', $form_values)) $form_values['additions'] = array();
728
    $form_values = array_merge($form_values, (array) $form_values['exceptions'], (array) $form_values['additions']);
729
    unset($form_values['exceptions']);
730
    unset($form_values['additions']);
731
  }
732

    
733
  if (array_key_exists('FREQ', $form_values)) {
734
    switch ($form_values['FREQ']) {
735
      case 'DAILY':
736
        if (array_key_exists('daily', $form_values)) {
737
          switch ($form_values['daily']['byday_radios']) {
738
            case 'INTERVAL':
739
              $form_values['INTERVAL'] = $form_values['daily']['INTERVAL_child'];
740
              break;
741
            case 'every_weekday':
742
              $form_values['BYDAY'] = array('MO', 'TU', 'WE', 'TH', 'FR');
743
              break;
744
            case 'every_mo_we_fr':
745
              $form_values['BYDAY'] = array('MO', 'WE', 'FR');
746
              break;
747
            case 'every_tu_th':
748
              $form_values['BYDAY'] = array('TU', 'TH');
749
              break;
750
          }
751
        }
752
        break;
753
      case 'WEEKLY':
754
        if (array_key_exists('weekly', $form_values)) {
755
          $form_values = array_merge($form_values, (array) $form_values['weekly']);
756
          if (array_key_exists('BYDAY', $form_values)) {
757
            $form_values['BYDAY'] = date_repeat_transform_checkbox_values_to_select_values($form_values['BYDAY']);
758
          }
759
        }
760
        break;
761
      case 'MONTHLY':
762
        if (array_key_exists('monthly', $form_values)) {
763
          switch ($form_values['monthly']['day_month']) {
764
            case 'BYMONTHDAY_BYMONTH':
765
              $form_values['monthly'] = array_merge($form_values['monthly'], (array) $form_values['monthly']['BYMONTHDAY_BYMONTH_child']);
766
              break;
767
            case 'BYDAY_BYMONTH':
768
              $form_values['monthly']['BYDAY_BYMONTH_child']['BYDAY'] = $form_values['monthly']['BYDAY_BYMONTH_child']['BYDAY_COUNT'] . $form_values['monthly']['BYDAY_BYMONTH_child']['BYDAY_DAY'];
769
              $form_values['monthly'] = array_merge($form_values['monthly'], (array) $form_values['monthly']['BYDAY_BYMONTH_child']);
770
              break;
771
          }
772
          unset($form_values['monthly']['BYDAY_BYMONTH_child']);
773
          unset($form_values['monthly']['BYMONTHDAY_BYMONTH_child']);
774
          $form_values = array_merge($form_values, (array) $form_values['monthly']);
775
          if (array_key_exists('BYMONTH', $form_values)) {
776
            $form_values['BYMONTH'] = date_repeat_transform_checkbox_values_to_select_values($form_values['BYMONTH']);
777
          }
778
          if (array_key_exists('BYMONTHDAY', $form_values) && !is_array($form_values['BYMONTHDAY'])) {
779
            $form_values['BYMONTHDAY'] = (array) $form_values['BYMONTHDAY'];
780
          }
781
          if (array_key_exists('BYDAY', $form_values) && !is_array($form_values['BYDAY'])) {
782
            $form_values['BYDAY'] = (array) $form_values['BYDAY'];
783
          }
784
        }
785
        break;
786
      case 'YEARLY':
787
        if (array_key_exists('yearly', $form_values)) {
788
          switch ($form_values['yearly']['day_month']) {
789
            case 'BYMONTHDAY_BYMONTH':
790
              $form_values['yearly'] = array_merge($form_values['yearly'], (array) $form_values['yearly']['BYMONTHDAY_BYMONTH_child']);
791
              break;
792
            case 'BYDAY_BYMONTH':
793
              $form_values['yearly']['BYDAY_BYMONTH_child']['BYDAY'] = $form_values['yearly']['BYDAY_BYMONTH_child']['BYDAY_COUNT'] . $form_values['yearly']['BYDAY_BYMONTH_child']['BYDAY_DAY'];
794
              $form_values['yearly'] = array_merge($form_values['yearly'], (array) $form_values['yearly']['BYDAY_BYMONTH_child']);
795
              break;
796
          }
797
          unset($form_values['yearly']['BYDAY_BYMONTH_child']);
798
          unset($form_values['yearly']['BYMONTHDAY_BYMONTH_child']);
799
          $form_values = array_merge($form_values, (array) $form_values['yearly']);
800
          if (array_key_exists('BYMONTH', $form_values)) {
801
            $form_values['BYMONTH'] = date_repeat_transform_checkbox_values_to_select_values($form_values['BYMONTH']);
802
          }
803
          if (array_key_exists('BYMONTHDAY', $form_values) && !is_array($form_values['BYMONTHDAY'])) {
804
            $form_values['BYMONTHDAY'] = (array) $form_values['BYMONTHDAY'];
805
          }
806
          if (array_key_exists('BYDAY', $form_values) && !is_array($form_values['BYDAY'])) {
807
            $form_values['BYDAY'] = (array) $form_values['BYDAY'];
808
          }
809
        }
810
        break;
811
      default:
812
        break;
813
    }
814
  }
815

    
816
  unset($form_values['daily']);
817
  unset($form_values['weekly']);
818
  unset($form_values['monthly']);
819
  unset($form_values['yearly']);
820

    
821
  if (array_key_exists('range_of_repeat', $form_values)) {
822
    switch ($form_values['range_of_repeat']) {
823
      case 'COUNT':
824
        $form_values['COUNT'] = $form_values['count_child'];
825
        break;
826
      case 'UNTIL':
827
        $form_values['UNTIL'] = $form_values['until_child'];
828
        break;
829
    }
830
  }
831

    
832
  unset($form_values['count_child']);
833
  unset($form_values['until_child']);
834

    
835
  if (array_key_exists('BYDAY', $form_values) && is_array($form_values['BYDAY'])) unset($form_values['BYDAY']['']);
836
  if (array_key_exists('BYMONTH', $form_values) && is_array($form_values['BYMONTH'])) unset($form_values['BYMONTH']['']);
837
  if (array_key_exists('BYMONTHDAY', $form_values) && is_array($form_values['BYMONTHDAY'])) unset($form_values['BYMONTHDAY']['']);
838

    
839
  if (array_key_exists('UNTIL', $form_values) && is_array($form_values['UNTIL']['datetime'])) {
840
    $function = $element['#date_repeat_widget'] . '_input_date';
841
    $until_element = $element;
842
    $until_element['#date_format'] = !empty($element['#date_format']) ? date_limit_format($element['#date_format'], array('year', 'month', 'day')) : 'Y-m-d';
843
    $date = $function($until_element, $form_values['UNTIL']['datetime']);
844
    $form_values['UNTIL']['datetime'] = is_object($date) ? $date->format(DATE_FORMAT_DATETIME) : '';
845
  }
846
  if (array_key_exists('show_exceptions', $form_values) && $form_values['show_exceptions'] === 0) {
847
    unset($form_values['EXDATE']);
848
  }
849
  if (array_key_exists('EXDATE', $form_values) && is_array($form_values['EXDATE'])) {
850
    $function = $element['#date_repeat_widget'] . '_input_date';
851
    $exdate_element = $element;
852
    foreach ($form_values['EXDATE'] as $delta => $value) {
853
      if (is_array($value['datetime'])) {
854
        $exdate_element['#date_format'] = !empty($element['#date_format']) ? date_limit_format($element['#date_format'], array('year', 'month', 'day')) : 'Y-m-d';
855
        $date = $function($exdate_element, $form_values['EXDATE'][$delta]['datetime']);
856
        $form_values['EXDATE'][$delta]['datetime'] = is_object($date) ? $date->format(DATE_FORMAT_DATETIME) : '';
857
      }
858
    }
859
  }
860

    
861
  if (array_key_exists('show_additions', $form_values) && $form_values['show_additions'] === 0) {
862
    unset($form_values['RDATE']);
863
  }
864
  if (array_key_exists('RDATE', $form_values) && is_array($form_values['RDATE'])) {
865
    $function = $element['#date_repeat_widget'] . '_input_date';
866
    $rdate_element = $element;
867
    foreach ($form_values['RDATE'] as $delta => $value) {
868
      if (is_array($value['datetime'])) {
869
        $rdate_element['#date_format'] = !empty($element['#date_format']) ? date_limit_format($element['#date_format'], array('year', 'month', 'day')) : 'Y-m-d';
870
        $date = $function($rdate_element, $form_values['RDATE'][$delta]['datetime']);
871
        $form_values['RDATE'][$delta]['datetime'] = is_object($date) ? $date->format(DATE_FORMAT_DATETIME) : '';
872
      }
873
    }
874
  }
875
  return $form_values;
876
}
877

    
878
/**
879
 * Build a RRULE out of the form values.
880
 */
881
function date_repeat_rrule_validate($element, &$form_state) {
882

    
883
  if (date_hidden_element($element)) {
884
    return;
885
  }
886

    
887
  $parents = $element['#parents'];
888
  array_pop($parents);
889
  $field_values = drupal_array_get_nested_value($form_state['values'], $parents);
890
  if ($field_values['show_repeat_settings'] === 0 || $field_values['rrule']['FREQ'] === 'NONE') {
891
    form_set_value($element, NULL, $form_state);
892
    return;
893
  }
894

    
895
  // Clean the buttons off of the form. Needed to avoid errors when
896
  // the date is used on a user object, which then passes the form
897
  // through form_state_values_clean().
898
  foreach ($form_state['buttons'] as $delta => $item) {
899
    if (!empty($item['#ajax']['callback']) && in_array($item['#ajax']['callback'], array('date_repeat_add_exception_callback', 'date_repeat_add_addition_callback'))) {
900
      unset($form_state['buttons'][$delta]);
901
    }
902
  }
903

    
904
  module_load_include('inc', 'date_api', 'date_api_ical');
905

    
906
  $item = drupal_array_get_nested_value($form_state['values'], $element['#parents']);
907
  $item = date_repeat_merge($item, $element);
908
  $rrule = date_api_ical_build_rrule($item);
909
  form_set_value($element, $rrule, $form_state);
910
}
911

    
912
/**
913
 * Theme the exception list as a table so the buttons line up
914
 */
915
function theme_date_repeat_current_exceptions($vars) {
916
  $rows = $vars['rows'];
917
  $rows_info = array();
918
  foreach ($rows as $key => $value) {
919
    if (substr($key, 0, 1) != '#') {
920
      $rows_info[] = array(drupal_render($value['action']), drupal_render($value['display']));
921
    }
922
  }
923
  return theme('table', array('header' => array(t('Delete'), t('Current exceptions')), 'rows' => $rows_info));
924
}
925

    
926
 /**
927
 * Theme the exception list as a table so the buttons line up
928
 */
929
function theme_date_repeat_current_additions($rows = array()) {
930
  $rows_info = array();
931
  foreach ($rows as $key => $value) {
932
    if (substr($key, 0, 1) != '#') {
933
      $rows_info[] = array(drupal_render($value['action']), drupal_render($value['display']));
934
    }
935
  }
936
  return theme('table', array('header' => array(t('Delete'), t('Current additions')), 'rows' => $rows_info));
937
}
938

    
939
/**
940
 * Wrapper fieldset for repeat rule.
941
 */
942
function theme_date_repeat_rrule($vars) {
943
  $element = $vars['element'];
944
  $id = drupal_html_id('repeat-settings-fieldset');
945
  $parents = $element['#parents'];
946
  $selector = "{$parents[0]}[{$parents[1]}][{$parents[2]}][show_repeat_settings]";
947
  $fieldset = array(
948
    '#type' => 'item',
949
    '#title' => t('Repeat settings'),
950
    '#title_display' => 'invisible',
951
    '#markup' => $element['#children'],
952
    '#states' => array(
953
      'invisible' => array(
954
        ":input[name=\"{$selector}\"]" => array('checked' => FALSE),
955
      ),
956
    ),
957
    '#id' => $id,
958
  );
959

    
960
  return drupal_render($fieldset);
961
}
962

    
963
function date_repeat_filter_non_zero_value($value) {
964
  return $value !== 0;
965
}
966

    
967
/**
968
 * Helper function for transforming the return value of checkbox(es) element.
969
 *
970
 * Can be used for transforming the returned value of checkbox(es) element
971
 * to the format of returned value of multiple select element.
972
 */
973
function date_repeat_transform_checkbox_values_to_select_values($values) {
974
  return array_filter($values, 'date_repeat_filter_non_zero_value');
975
}