Projet

Général

Profil

Paste
Télécharger (39,3 ko) Statistiques
| Branche: | Révision:

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

1
<?php
2

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

    
35
/**
36
 * Generate the repeat setting form.
37
 */
38
function _date_repeat_rrule_process($element, &$form_state, $form) {
39
  // If the RRULE field is not visible to the user, needs no processing or
40
  // validation. The Date field module is not adding this element to forms if
41
  // the field is hidden, this test is just in case some other module attempts
42
  // to do so.
43
  if (date_hidden_element($element)) {
44
    return $element;
45
  }
46

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
214
  $daily_radios_default = 'INTERVAL';
215
  if (isset($rrule['FREQ']) && $rrule['FREQ'] === 'DAILY' && !empty($rrule['BYDAY'])) {
216
    switch (count($rrule['BYDAY'])) {
217
      case 2:
218
        $daily_radios_default = 'every_tu_th';
219
        break;
220

    
221
      case 3:
222
        $daily_radios_default = 'every_mo_we_fr';
223
        break;
224

    
225
      case 5:
226
        $daily_radios_default = 'every_weekday';
227
        break;
228
    }
229
  }
230

    
231
  $daily_every_weekday = array(
232
    '#type' => 'item',
233
    '#markup' => '<div>' . t('Every weekday', array(), array('context' => 'Date repeat')) . '</div>',
234
  );
235

    
236
  $daily_mo_we_fr = array(
237
    '#type' => 'item',
238
    '#markup' => '<div>' . t('Every Mon, Wed, Fri', array(), array('context' => 'Date repeat')) . '</div>',
239
  );
240

    
241
  $daily_tu_th = array(
242
    '#type' => 'item',
243
    '#markup' => '<div>' . t('Every Tue, Thu', array(), array('context' => 'Date repeat')) . '</div>',
244
  );
245

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

    
276
  $monthly_day_month_default = 'BYMONTHDAY_BYMONTH';
277
  if (isset($rrule['FREQ']) && $rrule['FREQ'] === 'MONTHLY' && !empty($rrule['BYDAY'])) {
278
    $monthly_day_month_default = 'BYDAY_BYMONTH';
279
  }
280

    
281
  $monthly_on_day_bymonthday_of_bymonth = array(
282
    '#type' => 'container',
283
    '#tree' => TRUE,
284
  );
285

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

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

    
310
  $monthly_on_the_byday_of_bymonth = array(
311
    '#type' => 'container',
312
    '#tree' => TRUE,
313
  );
314

    
315
  $monthly_byday_count = '';
316
  $monthly_byday_day = '';
317
  if (isset($rrule['BYDAY']) && !empty($rrule['BYDAY']) && $rrule['FREQ'] === 'MONTHLY') {
318
    $monthly_byday_count = substr($rrule['BYDAY'][0], 0, -2);
319
    $monthly_byday_day = substr($rrule['BYDAY'][0], -2);;
320
  }
321

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

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

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

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

    
380
  $yearly_day_month_default = 'BYMONTHDAY_BYMONTH';
381
  if (isset($rrule['FREQ']) && $rrule['FREQ'] === 'YEARLY' && !empty($rrule['BYDAY'])) {
382
    $yearly_day_month_default = 'BYDAY_BYMONTH';
383
  }
384

    
385
  $yearly_on_day_bymonthday_of_bymonth = array(
386
    '#type' => 'container',
387
    '#tree' => TRUE,
388
  );
389

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

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

    
414
  $yearly_on_the_byday_of_bymonth = array(
415
    '#type' => 'container',
416
    '#tree' => TRUE,
417
  );
418

    
419
  $yearly_byday_count = '';
420
  $yearly_byday_day = '';
421
  if (isset($rrule['BYDAY']) && !empty($rrule['BYDAY']) && $rrule['FREQ'] === 'YEARLY') {
422
    $yearly_byday_count = substr($rrule['BYDAY'][0], 0, -2);
423
    $yearly_byday_day = substr($rrule['BYDAY'][0], -2);;
424
  }
425

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

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

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

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

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

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

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

    
552
  $parents = $element['#array_parents'];
553
  $instance = implode('-', $parents);
554

    
555
  // Make sure this will work right either in the normal form or in an AJAX
556
  // callback from the 'Add more' button.
557
  if (empty($form_state['num_exceptions'][$instance])) {
558
    $form_state['num_exceptions'][$instance] = count($exceptions);
559
  }
560
  if ($form_state['num_exceptions'][$instance] == 0) {
561
    $collapsed = TRUE;
562
  }
563
  else {
564
    $collapsed = FALSE;
565
  }
566

    
567
  $element['show_exceptions'] = array(
568
    '#type' => 'checkbox',
569
    '#title' => t('Exclude dates', array(), array('context' => 'Date repeat')),
570
    '#states' => array(
571
      'invisible' => array(
572
        ":input[name=\"{$element['#name']}[FREQ]\"]" => array('value' => 'NONE'),
573
      ),
574
    ),
575
    '#default_value' => empty($form_state['num_exceptions'][$instance]) ? 0 : 1,
576
  );
577

    
578
  $element['exceptions'] = array(
579
    '#type' => 'container',
580
    '#prefix' => '<div id="date-repeat-exceptions-' . $instance . '" class="date-repeat">',
581
    '#suffix' => '</div>',
582
    '#states' => array(
583
      'visible' => array(
584
        ":input[name=\"{$element['#name']}[show_exceptions]\"]" => array('checked' => TRUE),
585
      ),
586
    ),
587
  );
588
  for ($i = 0; $i < max($form_state['num_exceptions'][$instance], 1); $i++) {
589
    $except = '';
590
    if (!empty($exceptions[$i]['datetime'])) {
591
      $ex_date = new DateObject($exceptions[$i]['datetime'], $exceptions[$i]['tz']);
592
      date_timezone_set($ex_date, timezone_open($timezone));
593
      $except = date_format($ex_date, DATE_FORMAT_DATETIME);
594
    }
595
    $date_format = 'Y-m-d';
596
    if (!empty($element['#date_format'])) {
597
      $grans = array('year', 'month', 'day');
598
      $date_format = date_limit_format($element['#date_format'], $grans);
599
    }
600
    $element['exceptions']['EXDATE'][$i] = array(
601
      '#tree' => TRUE,
602
      'datetime' => array(
603
        '#name' => 'exceptions|' . $instance,
604
        '#type' => $element['#date_repeat_widget'],
605
        '#default_value' => $except,
606
        '#date_timezone' => !empty($element['#date_timezone']) ?
607
        $element['#date_timezone'] : date_default_timezone(),
608
        '#date_format' => $date_format,
609
        '#date_text_parts'  => !empty($element['#date_text_parts']) ? $element['#date_text_parts'] : array(),
610
        '#date_year_range'  => !empty($element['#date_year_range']) ? $element['#date_year_range'] : '-3:+3',
611
        '#date_label_position' => !empty($element['#date_label_position']) ? $element['#date_label_position'] : 'within',
612
        '#date_flexible' => 0,
613
      ),
614
      'tz' => array(
615
        '#type' => 'hidden',
616
        '#value' => $element['#date_timezone'],
617
      ),
618
      'all_day' => array(
619
        '#type' => 'hidden',
620
        '#value' => 1,
621
      ),
622
      'granularity' => array(
623
        '#type' => 'hidden',
624
        '#value' => serialize(array('year', 'month', 'day')),
625
      ),
626
    );
627
  }
628

    
629
  // Collect additions in the same way as exceptions - implements RDATE.
630
  if (empty($form_state['num_additions'][$instance])) {
631
    $form_state['num_additions'][$instance] = count($additions);
632
  }
633
  if ($form_state['num_additions'][$instance] == 0) {
634
    $collapsed = TRUE;
635
  }
636
  else {
637
    $collapsed = FALSE;
638
  }
639

    
640
  $element['show_additions'] = array(
641
    '#type' => 'checkbox',
642
    '#title' => t('Include dates', array(), array('context' => 'Date repeat')),
643
    '#states' => array(
644
      'invisible' => array(
645
        ":input[name=\"{$element['#name']}[FREQ]\"]" => array('value' => 'NONE'),
646
      ),
647
    ),
648
    '#default_value' => empty($form_state['num_additions'][$instance]) ? 0 : 1,
649
  );
650

    
651
  $element['additions'] = array(
652
    '#type' => 'container',
653
    '#prefix' => '<div id="date-repeat-additions-' . $instance . '" class="date-repeat">',
654
    '#suffix' => '</div>',
655
    '#states' => array(
656
      'visible' => array(
657
        ":input[name=\"{$element['#name']}[show_additions]\"]" => array('checked' => TRUE),
658
      ),
659
    ),
660
  );
661
  for ($i = 0; $i < max($form_state['num_additions'][$instance], 1); $i++) {
662
    $r_date = '';
663
    if (!empty($additions[$i]['datetime'])) {
664
      $rdate = new DateObject($additions[$i]['datetime'], $additions[$i]['tz']);
665
      date_timezone_set($rdate, timezone_open($timezone));
666
      $r_date = date_format($rdate, DATE_FORMAT_DATETIME);
667
    }
668
    $date_format = 'Y-m-d';
669
    if (!empty($element['#date_format'])) {
670
      $grans = array('year', 'month', 'day');
671
      $date_format = date_limit_format($element['#date_format'], $grans);
672
    }
673
    $element['additions']['RDATE'][$i] = array(
674
      '#tree' => TRUE,
675
      'datetime' => array(
676
        '#type' => $element['#date_repeat_widget'],
677
        '#name' => 'additions|' . $instance,
678
        '#default_value' => $r_date,
679
        '#date_timezone' => !empty($element['#date_timezone']) ?
680
        $element['#date_timezone'] : date_default_timezone(),
681
        '#date_format' => $date_format,
682
        '#date_text_parts'  => !empty($element['#date_text_parts']) ? $element['#date_text_parts'] : array(),
683
        '#date_year_range'  => !empty($element['#date_year_range']) ? $element['#date_year_range'] : '-3:+3',
684
        '#date_label_position' => !empty($element['#date_label_position']) ? $element['#date_label_position'] : 'within',
685
        '#date_flexible' => 0,
686
      ),
687
      'tz' => array(
688
        '#type' => 'hidden',
689
        '#value' => $element['#date_timezone'],
690
      ),
691
      'all_day' => array(
692
        '#type' => 'hidden',
693
        '#value' => 1,
694
      ),
695
      'granularity' => array(
696
        '#type' => 'hidden',
697
        '#value' => serialize(array('year', 'month', 'day')),
698
      ),
699
    );
700
  }
701

    
702
  $element['exceptions']['exceptions_add'] = array(
703
    '#type' => 'submit',
704
    '#name' => 'exceptions_add|' . $instance,
705
    '#value' => t('Add exception'),
706
    '#submit' => array('date_repeat_add_exception'),
707
    '#limit_validation_errors' => array(),
708
    '#ajax' => array(
709
      'callback' => 'date_repeat_add_exception_callback',
710
      'wrapper' => 'date-repeat-exceptions-' . $instance,
711
    ),
712
  );
713
  $element['additions']['additions_add'] = array(
714
    '#type' => 'submit',
715
    '#name' => 'additions_add|' . $instance,
716
    '#value' => t('Add addition'),
717
    '#submit' => array('date_repeat_add_addition'),
718
    '#limit_validation_errors' => array(),
719
    '#ajax' => array(
720
      'callback' => 'date_repeat_add_addition_callback',
721
      'wrapper' => 'date-repeat-additions-' . $instance,
722
    ),
723
  );
724

    
725
  $element['#date_repeat_collapsed'] = !empty($rrule['INTERVAL']) || !empty($rrule['FREQ']) ? 0 : (!empty($element['#date_repeat_collapsed']) ? $element['#date_repeat_collapsed'] : 0);
726
  return $element;
727
}
728

    
729
/**
730
 * Add callback to date repeat.
731
 */
732
function date_repeat_add_exception_callback($form, &$form_state) {
733
  $parents = $form_state['triggering_element']['#array_parents'];
734
  $button_key = array_pop($parents);
735
  $element = drupal_array_get_nested_value($form, $parents);
736
  return $element;
737
}
738

    
739
/**
740
 * Add addition callback to date repeat.
741
 */
742
function date_repeat_add_addition_callback($form, &$form_state) {
743
  $parents = $form_state['triggering_element']['#array_parents'];
744
  $button_key = array_pop($parents);
745
  $element = drupal_array_get_nested_value($form, $parents);
746
  return $element;
747
}
748

    
749
/**
750
 * Add exception to date repeat.
751
 */
752
function date_repeat_add_exception($form, &$form_state) {
753
  $parents = $form_state['triggering_element']['#array_parents'];
754
  $instance = implode('-', array_slice($parents, 0, count($parents) - 2));
755
  $form_state['num_exceptions'][$instance]++;
756
  $form_state['rebuild'] = TRUE;
757
}
758

    
759
/**
760
 * Add addition to date repeat.
761
 */
762
function date_repeat_add_addition($form, &$form_state) {
763
  $parents = $form_state['triggering_element']['#array_parents'];
764
  $instance = implode('-', array_slice($parents, 0, count($parents) - 2));
765
  $form_state['num_additions'][$instance]++;
766
  $form_state['rebuild'] = TRUE;
767
}
768

    
769
/**
770
 * Regroup values back into a consistant array, no matter what state it is in.
771
 */
772
function date_repeat_merge($form_values, $element) {
773
  if (empty($form_values) || !is_array($form_values)) {
774
    return $form_values;
775
  }
776
  if (array_key_exists('exceptions', $form_values) || array_key_exists('additions', $form_values)) {
777
    if (!array_key_exists('exceptions', $form_values)) {
778
      $form_values['exceptions'] = array();
779
    }
780

    
781
    if (!array_key_exists('additions', $form_values)) {
782
      $form_values['additions'] = array();
783
    }
784

    
785
    $form_values = array_merge($form_values, (array) $form_values['exceptions'], (array) $form_values['additions']);
786
    unset($form_values['exceptions']);
787
    unset($form_values['additions']);
788
  }
789

    
790
  if (array_key_exists('FREQ', $form_values)) {
791
    switch ($form_values['FREQ']) {
792
      case 'DAILY':
793
        if (array_key_exists('daily', $form_values)) {
794
          switch ($form_values['daily']['byday_radios']) {
795
            case 'INTERVAL':
796
              $form_values['INTERVAL'] = $form_values['daily']['INTERVAL_child'];
797
              break;
798

    
799
            case 'every_weekday':
800
              $form_values['BYDAY'] = array('MO', 'TU', 'WE', 'TH', 'FR');
801
              break;
802

    
803
            case 'every_mo_we_fr':
804
              $form_values['BYDAY'] = array('MO', 'WE', 'FR');
805
              break;
806

    
807
            case 'every_tu_th':
808
              $form_values['BYDAY'] = array('TU', 'TH');
809
              break;
810
          }
811
        }
812
        break;
813

    
814
      case 'WEEKLY':
815
        if (array_key_exists('weekly', $form_values)) {
816
          $form_values = array_merge($form_values, (array) $form_values['weekly']);
817
          if (array_key_exists('BYDAY', $form_values)) {
818
            $form_values['BYDAY'] = date_repeat_transform_checkbox_values_to_select_values($form_values['BYDAY']);
819
          }
820
        }
821
        break;
822

    
823
      case 'MONTHLY':
824
        if (array_key_exists('monthly', $form_values)) {
825
          switch ($form_values['monthly']['day_month']) {
826
            case 'BYMONTHDAY_BYMONTH':
827
              $form_values['monthly'] = array_merge($form_values['monthly'], (array) $form_values['monthly']['BYMONTHDAY_BYMONTH_child']);
828
              break;
829

    
830
            case 'BYDAY_BYMONTH':
831
              $form_values['monthly']['BYDAY_BYMONTH_child']['BYDAY'] = $form_values['monthly']['BYDAY_BYMONTH_child']['BYDAY_COUNT'] . $form_values['monthly']['BYDAY_BYMONTH_child']['BYDAY_DAY'];
832
              $form_values['monthly'] = array_merge($form_values['monthly'], (array) $form_values['monthly']['BYDAY_BYMONTH_child']);
833
              break;
834
          }
835
          unset($form_values['monthly']['BYDAY_BYMONTH_child']);
836
          unset($form_values['monthly']['BYMONTHDAY_BYMONTH_child']);
837
          $form_values = array_merge($form_values, (array) $form_values['monthly']);
838
          if (array_key_exists('BYMONTH', $form_values)) {
839
            $form_values['BYMONTH'] = date_repeat_transform_checkbox_values_to_select_values($form_values['BYMONTH']);
840
          }
841
          if (array_key_exists('BYMONTHDAY', $form_values) && !is_array($form_values['BYMONTHDAY'])) {
842
            $form_values['BYMONTHDAY'] = (array) $form_values['BYMONTHDAY'];
843
          }
844
          if (array_key_exists('BYDAY', $form_values) && !is_array($form_values['BYDAY'])) {
845
            $form_values['BYDAY'] = (array) $form_values['BYDAY'];
846
          }
847
        }
848
        break;
849

    
850
      case 'YEARLY':
851
        if (array_key_exists('yearly', $form_values)) {
852
          switch ($form_values['yearly']['day_month']) {
853
            case 'BYMONTHDAY_BYMONTH':
854
              $form_values['yearly'] = array_merge($form_values['yearly'], (array) $form_values['yearly']['BYMONTHDAY_BYMONTH_child']);
855
              break;
856

    
857
            case 'BYDAY_BYMONTH':
858
              $form_values['yearly']['BYDAY_BYMONTH_child']['BYDAY'] = $form_values['yearly']['BYDAY_BYMONTH_child']['BYDAY_COUNT'] . $form_values['yearly']['BYDAY_BYMONTH_child']['BYDAY_DAY'];
859
              $form_values['yearly'] = array_merge($form_values['yearly'], (array) $form_values['yearly']['BYDAY_BYMONTH_child']);
860
              break;
861
          }
862
          unset($form_values['yearly']['BYDAY_BYMONTH_child']);
863
          unset($form_values['yearly']['BYMONTHDAY_BYMONTH_child']);
864
          $form_values = array_merge($form_values, (array) $form_values['yearly']);
865
          if (array_key_exists('BYMONTH', $form_values)) {
866
            $form_values['BYMONTH'] = date_repeat_transform_checkbox_values_to_select_values($form_values['BYMONTH']);
867
          }
868
          if (array_key_exists('BYMONTHDAY', $form_values) && !is_array($form_values['BYMONTHDAY'])) {
869
            $form_values['BYMONTHDAY'] = (array) $form_values['BYMONTHDAY'];
870
          }
871
          if (array_key_exists('BYDAY', $form_values) && !is_array($form_values['BYDAY'])) {
872
            $form_values['BYDAY'] = (array) $form_values['BYDAY'];
873
          }
874
        }
875
        break;
876
    }
877
  }
878

    
879
  unset($form_values['daily']);
880
  unset($form_values['weekly']);
881
  unset($form_values['monthly']);
882
  unset($form_values['yearly']);
883

    
884
  if (array_key_exists('range_of_repeat', $form_values)) {
885
    switch ($form_values['range_of_repeat']) {
886
      case 'COUNT':
887
        $form_values['COUNT'] = $form_values['count_child'];
888
        break;
889

    
890
      case 'UNTIL':
891
        $form_values['UNTIL'] = $form_values['until_child'];
892
        break;
893
    }
894
  }
895

    
896
  unset($form_values['count_child']);
897
  unset($form_values['until_child']);
898

    
899
  if (array_key_exists('BYDAY', $form_values) && is_array($form_values['BYDAY'])) {
900
    unset($form_values['BYDAY']['']);
901
  }
902

    
903
  if (array_key_exists('BYMONTH', $form_values) && is_array($form_values['BYMONTH'])) {
904
    unset($form_values['BYMONTH']['']);
905
  }
906

    
907
  if (array_key_exists('BYMONTHDAY', $form_values) && is_array($form_values['BYMONTHDAY'])) {
908
    unset($form_values['BYMONTHDAY']['']);
909
  }
910

    
911
  if (array_key_exists('UNTIL', $form_values) && is_array($form_values['UNTIL']['datetime'])) {
912
    $function = $element['#date_repeat_widget'] . '_input_date';
913
    $until_element = $element;
914
    $until_element['#date_format'] = !empty($element['#date_format']) ?
915
    date_limit_format($element['#date_format'], array('year', 'month', 'day')) : 'Y-m-d';
916
    $date = $function($until_element, $form_values['UNTIL']['datetime']);
917
    $form_values['UNTIL']['datetime'] = is_object($date) ? $date->format(DATE_FORMAT_DATETIME) : '';
918
  }
919
  if (array_key_exists('show_exceptions', $form_values) && $form_values['show_exceptions'] === 0) {
920
    unset($form_values['EXDATE']);
921
  }
922
  if (array_key_exists('EXDATE', $form_values) && is_array($form_values['EXDATE'])) {
923
    $function = $element['#date_repeat_widget'] . '_input_date';
924
    $exdate_element = $element;
925
    $date_format = 'Y-m-d';
926
    if (!empty($element['#date_format'])) {
927
      $grans = array('year', 'month', 'day');
928
      $date_format = date_limit_format($element['#date_format'], $grans);
929
    }
930
    foreach ($form_values['EXDATE'] as $delta => $value) {
931
      if (is_array($value['datetime'])) {
932
        $exdate_element['#date_format'] = $date_format;
933
        $date = $function($exdate_element, $form_values['EXDATE'][$delta]['datetime']);
934
        $form_values['EXDATE'][$delta]['datetime'] = is_object($date) ? $date->format(DATE_FORMAT_DATETIME) : '';
935
      }
936
    }
937
  }
938

    
939
  if (array_key_exists('show_additions', $form_values) && $form_values['show_additions'] === 0) {
940
    unset($form_values['RDATE']);
941
  }
942
  if (array_key_exists('RDATE', $form_values) && is_array($form_values['RDATE'])) {
943
    $function = $element['#date_repeat_widget'] . '_input_date';
944
    $rdate_element = $element;
945
    $date_format = 'Y-m-d';
946
    if (!empty($element['#date_format'])) {
947
      $grans = array('year', 'month', 'day');
948
      $date_format = date_limit_format($element['#date_format'], $grans);
949
    }
950
    foreach ($form_values['RDATE'] as $delta => $value) {
951
      if (is_array($value['datetime'])) {
952
        $rdate_element['#date_format'] = $date_format;
953
        $date = $function($rdate_element, $form_values['RDATE'][$delta]['datetime']);
954
        $form_values['RDATE'][$delta]['datetime'] = is_object($date) ? $date->format(DATE_FORMAT_DATETIME) : '';
955
      }
956
    }
957
  }
958
  return $form_values;
959
}
960

    
961
/**
962
 * Build a RRULE out of the form values.
963
 */
964
function date_repeat_rrule_validate($element, &$form_state) {
965
  if (date_hidden_element($element)) {
966
    return;
967
  }
968

    
969
  $parents = $element['#parents'];
970
  array_pop($parents);
971
  $field_values = drupal_array_get_nested_value($form_state['values'], $parents);
972
  if ($field_values['show_repeat_settings'] === 0 || $field_values['rrule']['FREQ'] === 'NONE') {
973
    form_set_value($element, NULL, $form_state);
974
    return;
975
  }
976

    
977
  // Clean the buttons off of the form. Needed to avoid errors when the date is
978
  // used on a user object, which then passes the form through
979
  // form_state_values_clean().
980
  foreach ($form_state['buttons'] as $delta => $item) {
981
    if (!empty($item['#ajax']['callback']) && in_array($item['#ajax']['callback'], array('date_repeat_add_exception_callback', 'date_repeat_add_addition_callback'))) {
982
      unset($form_state['buttons'][$delta]);
983
    }
984
  }
985

    
986
  module_load_include('inc', 'date_api', 'date_api_ical');
987

    
988
  $item = drupal_array_get_nested_value($form_state['values'], $element['#parents']);
989
  $item = date_repeat_merge($item, $element);
990
  $rrule = date_api_ical_build_rrule($item);
991
  form_set_value($element, $rrule, $form_state);
992
}
993

    
994
/**
995
 * Theme the exception list as a table so the buttons line up.
996
 */
997
function theme_date_repeat_current_exceptions($vars) {
998
  $rows = $vars['rows'];
999
  $rows_info = array();
1000
  foreach ($rows as $key => $value) {
1001
    if (substr($key, 0, 1) != '#') {
1002
      $rows_info[] = array(drupal_render($value['action']), drupal_render($value['display']));
1003
    }
1004
  }
1005
  return theme('table', array(
1006
    'header' => array(t('Delete'), t('Current exceptions')),
1007
    'rows' => $rows_info)
1008
  );
1009
}
1010

    
1011
/**
1012
 * Theme the exception list as a table so the buttons line up.
1013
 */
1014
function theme_date_repeat_current_additions($rows = array()) {
1015
  $rows_info = array();
1016
  foreach ($rows as $key => $value) {
1017
    if (substr($key, 0, 1) != '#') {
1018
      $rows_info[] = array(drupal_render($value['action']), drupal_render($value['display']));
1019
    }
1020
  }
1021
  return theme('table', array(
1022
    'header' => array(t('Delete'), t('Current additions')),
1023
    'rows' => $rows_info)
1024
  );
1025
}
1026

    
1027
/**
1028
 * Wrapper fieldset for repeat rule.
1029
 */
1030
function theme_date_repeat_rrule($vars) {
1031
  $element = $vars['element'];
1032
  $id = drupal_html_id('repeat-settings-fieldset');
1033
  $parents = $element['#parents'];
1034

    
1035
  $selector = $parents[0];
1036
  for ($i = 1; $i < count($parents) - 1; $i++) {
1037
    $selector .= '[' . $parents[$i] . ']';
1038
  }
1039
  $selector .= '[show_repeat_settings]';
1040

    
1041
  $fieldset = array(
1042
    '#type' => 'item',
1043
    '#title' => t('Repeat settings'),
1044
    '#title_display' => 'invisible',
1045
    '#markup' => $element['#children'],
1046
    '#states' => array(
1047
      'invisible' => array(
1048
        ":input[name=\"{$selector}\"]" => array('checked' => FALSE),
1049
      ),
1050
    ),
1051
    '#id' => $id,
1052
  );
1053

    
1054
  return drupal_render($fieldset);
1055
}
1056

    
1057
/**
1058
 * Filter non zero values.
1059
 */
1060
function date_repeat_filter_non_zero_value($value) {
1061
  return $value !== 0;
1062
}
1063

    
1064
/**
1065
 * Helper function for transforming the return value of checkbox(es) element.
1066
 *
1067
 * Can be used for transforming the returned value of checkbox(es) element to
1068
 * the format of returned value of multiple select element.
1069
 */
1070
function date_repeat_transform_checkbox_values_to_select_values($values) {
1071
  return array_filter($values, 'date_repeat_filter_non_zero_value');
1072
}