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 @ b720ea3e

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
/**
35
 * Generate the repeat setting form.
36
 */
37
function _date_repeat_rrule_process($element, &$form_state, $form) {
38

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

    
45
  if (date_hidden_element($element)) {
46
    return $element;
47
  }
48

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

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

    
65
  $parts = date_repeat_split_rrule($rrule);
66
  $rrule = $parts[0];
67

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

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

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

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

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

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

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

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

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

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

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

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

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

    
223
      case 3:
224
        $daily_radios_default = 'every_mo_we_fr';
225
        break;
226

    
227
      case 5:
228
        $daily_radios_default = 'every_weekday';
229
        break;
230
    }
231
  }
232

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
879
      default:
880
        break;
881
    }
882
  }
883

    
884
  unset($form_values['daily']);
885
  unset($form_values['weekly']);
886
  unset($form_values['monthly']);
887
  unset($form_values['yearly']);
888

    
889
  if (array_key_exists('range_of_repeat', $form_values)) {
890
    switch ($form_values['range_of_repeat']) {
891
      case 'COUNT':
892
        $form_values['COUNT'] = $form_values['count_child'];
893
        break;
894

    
895
      case 'UNTIL':
896
        $form_values['UNTIL'] = $form_values['until_child'];
897
        break;
898
    }
899
  }
900

    
901
  unset($form_values['count_child']);
902
  unset($form_values['until_child']);
903

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

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

    
912
  if (array_key_exists('BYMONTHDAY', $form_values) && is_array($form_values['BYMONTHDAY'])) {
913
    unset($form_values['BYMONTHDAY']['']);
914
  }
915

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

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

    
966
/**
967
 * Build a RRULE out of the form values.
968
 */
969
function date_repeat_rrule_validate($element, &$form_state) {
970

    
971
  if (date_hidden_element($element)) {
972
    return;
973
  }
974

    
975
  $parents = $element['#parents'];
976
  array_pop($parents);
977
  $field_values = drupal_array_get_nested_value($form_state['values'], $parents);
978
  if ($field_values['show_repeat_settings'] === 0 || $field_values['rrule']['FREQ'] === 'NONE') {
979
    form_set_value($element, NULL, $form_state);
980
    return;
981
  }
982

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

    
992
  module_load_include('inc', 'date_api', 'date_api_ical');
993

    
994
  $item = drupal_array_get_nested_value($form_state['values'], $element['#parents']);
995
  $item = date_repeat_merge($item, $element);
996
  $rrule = date_api_ical_build_rrule($item);
997
  form_set_value($element, $rrule, $form_state);
998
}
999

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

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

    
1033
/**
1034
 * Wrapper fieldset for repeat rule.
1035
 */
1036
function theme_date_repeat_rrule($vars) {
1037
  $element = $vars['element'];
1038
  $id = drupal_html_id('repeat-settings-fieldset');
1039
  $parents = $element['#parents'];
1040

    
1041
  $selector = $parents[0];
1042
  for ($i = 1; $i < count($parents) - 1; $i++) {
1043
    $selector .= '[' . $parents[$i] . ']';
1044
  }
1045
  $selector .= '[show_repeat_settings]';
1046

    
1047
  $fieldset = array(
1048
    '#type' => 'item',
1049
    '#title' => t('Repeat settings'),
1050
    '#title_display' => 'invisible',
1051
    '#markup' => $element['#children'],
1052
    '#states' => array(
1053
      'invisible' => array(
1054
        ":input[name=\"{$selector}\"]" => array('checked' => FALSE),
1055
      ),
1056
    ),
1057
    '#id' => $id,
1058
  );
1059

    
1060
  return drupal_render($fieldset);
1061
}
1062

    
1063
/**
1064
 * Filter non zero values.
1065
 */
1066
function date_repeat_filter_non_zero_value($value) {
1067
  return $value !== 0;
1068
}
1069

    
1070
/**
1071
 * Helper function for transforming the return value of checkbox(es) element.
1072
 *
1073
 * Can be used for transforming the returned value of checkbox(es) element
1074
 * to the format of returned value of multiple select element.
1075
 */
1076
function date_repeat_transform_checkbox_values_to_select_values($values) {
1077
  return array_filter($values, 'date_repeat_filter_non_zero_value');
1078
}