Projet

Général

Profil

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

root / drupal7 / sites / all / modules / calendar / includes / calendar_plugin_style.inc @ 74f6bef0

1
<?php
2

    
3
/**
4
 * @file
5
 * Views style plugin for the Calendar module.
6
 *
7
 * The style plugin is responsible for setting global values needed by
8
 * the row plugin. It then gathers up an array of view results (as restructured
9
 * by the row plugin) and organizes them into year/month/day/time
10
 * arrays that the theme can use to display the results in a calendar.
11
 */
12

    
13
/**
14
 * Default style plugin to render an iCal feed.
15
 */
16
class calendar_plugin_style extends views_plugin_style {
17

    
18
  var $date_info;
19
  var $items;
20
  var $curday;
21

    
22
  function init(&$view, &$display, $options = NULL) {
23
    parent::init($view, $display, $options);
24
    if (empty($view->date_info)) {
25
      $this->date_info = new stdClass();
26
    }
27
    $this->date_info = &$this->view->date_info;
28
  }
29

    
30
  /**
31
   * Add custom option definitions.
32
   */
33
  function option_definition() {
34
    $options = parent::option_definition();
35
    $options['calendar_type'] = array('default' => 'month');
36
    $options['name_size'] = array('default' => 3);
37
    $options['mini'] = array('default' => 0);
38
    $options['with_weekno'] = array('default' => 0);
39
    $options['multiday_theme'] = array('default' => 1);
40
    $options['theme_style'] = array('default' => 1);
41
    $options['max_items'] = array('default' => 0);
42
    $options['max_items_behavior'] = array('default' => 'more');
43
    $options['groupby_times'] = array('default' => 'hour');
44
    $options['groupby_times_custom'] = array('default' => '');
45
    $options['groupby_field'] = array('default' => '');
46
    $options['multiday_hidden'] = array('default' => array());
47
    return $options;
48
  }
49

    
50
  function options_form(&$form, &$form_state) {
51
    parent::options_form($form, $form_state);
52
    $form['calendar_type'] = array(
53
      '#title' => t('Calendar type'),
54
      '#type' => 'select',
55
      '#description' => t('Select the calendar time period for this display.'),
56
      '#default_value' => $this->options['calendar_type'],
57
      '#options' => calendar_display_types(),
58
      );
59
    $form['mini'] = array(
60
      '#title' => t('Display as mini calendar'),
61
      '#default_value' => $this->options['mini'],
62
      '#type' => 'radios',
63
      '#options' => array(0 => t('No'), 1 => t('Yes')),
64
      '#description' => t('Display the mini style calendar, with no item details. Suitable for a calendar displayed in a block.'),
65
      '#dependency' => array('edit-style-options-calendar-type' => array('month')),
66
      );
67
    $form['name_size'] = array(
68
      '#title' => t('Calendar day of week names'),
69
      '#default_value' => $this->options['name_size'],
70
      '#type' => 'radios',
71
      '#options' => array(1 => t('First letter of name'), 2 => t('First two letters of name'), 3 => t('Abbreviated name'), 99 => t('Full name')),
72
      '#description' => t('The way day of week names should be displayed in a calendar.'),
73
      '#dependency' => array('edit-style-options-calendar-type' => array('month', 'week', 'year')),
74
      );
75
    $form['with_weekno'] = array(
76
      '#title' => t('Show week numbers'),
77
      '#default_value' => $this->options['with_weekno'],
78
      '#type' => 'radios',
79
      '#options' => array(0 => t('No'), 1 => t('Yes')),
80
      '#description' => t('Whether or not to show week numbers in the left column of calendar weeks and months.'),
81
      '#dependency' => array('edit-style-options-calendar-type' => array('month')),
82
      );
83
    $form['max_items'] = array(
84
      '#title' => t('Maximum items'),
85
      '#type' => 'select',
86
      '#options' => array(0 => t('Unlimited'), 1 => t('1 item'), 3 => t('3 items'), 5 => t('5 items'), 10 => t('10 items')),
87
      '#default_value' => $this->options['calendar_type'] != 'day' ? $this->options['max_items'] : 0,
88
      '#description' => t('Maximum number of items to show in calendar cells, used to keep the calendar from expanding to a huge size when there are lots of items in one day.'),
89
      '#dependency' => array('edit-style-options-calendar-type' => array('month')),
90
      );
91
    $form['max_items_behavior'] = array(
92
      '#title' => t('Too many items'),
93
      '#type' => 'select',
94
      '#options' => array('more' => t("Show maximum, add 'more' link"), 'hide' => t('Hide all, add link to day')),
95
      '#default_value' => $this->options['calendar_type'] != 'day' ? $this->options['max_items_behavior'] : 'more',
96
      '#description' => t('Behavior when there are more than the above number of items in a single day. When there more items than this limit, a link to the day view will be displayed.'),
97
      '#dependency' => array('edit-style-options-calendar-type' => array('month')),
98
      );
99
    $form['groupby_times'] = array(
100
      '#title' => t('Time grouping'),
101
      '#type' => 'select',
102
      '#default_value' => $this->options['groupby_times'],
103
      '#description' => t("Group items together into time periods based on their start time."),
104
      '#options' => array('' => t('None'), 'hour' => t('Hour'), 'half' => t('Half hour'), 'custom' => t('Custom')),
105
      '#dependency' => array('edit-style-options-calendar-type' => array('day', 'week')),
106
      );
107
    $form['groupby_times_custom'] = array(
108
      '#title' => t('Custom time grouping'),
109
      '#type' => 'textarea',
110
      '#default_value' => $this->options['groupby_times_custom'],
111
      '#description' => t("When choosing the 'custom' Time grouping option above, create custom time period groupings as a comma-separated list of 24-hour times in the format HH:MM:SS, like '00:00:00,08:00:00,18:00:00'. Be sure to start with '00:00:00'. All items after the last time will go in the final group."),
112
      '#dependency' => array('edit-style-options-groupby-times' => array('custom')),
113
      );
114
    $form['theme_style'] = array(
115
      '#title' => t('Overlapping time style'),
116
      '#default_value' => $this->options['theme_style'],
117
      '#type' => 'select',
118
      '#options' => array(0 => t('Do not display overlapping items'), 1 => t('Display overlapping items, with scrolling'), 2 => t('Display overlapping items, no scrolling')),
119
      '#description' => t('Select whether calendar items are displayed as overlapping items. Use scrolling to shrink the window and focus on the selected items, or omit scrolling to display the whole day. This only works if hour or half hour time grouping is chosen!'),
120
      '#dependency' => array('edit-style-options-calendar-type' => array('day', 'week')),
121
      );
122

    
123
    // Create a list of fields that are available for grouping,
124
    $field_options = array();
125
    $fields = $this->display->handler->get_option('fields');
126
    foreach ($fields as $field_name => $field) {
127
      $field_options[$field_name] = $field['field'];
128
    }
129
    $form['groupby_field'] = array(
130
      '#title' => t('Field grouping'),
131
      '#type' => 'select',
132
      '#default_value' => $this->options['groupby_field'],
133
      '#description' => t("Optionally group items into columns by a field value, for instance select the content type to show items for each content type in their own column, or use a location field to organize items into columns by location. NOTE! This is incompatible with the overlapping style option."),
134
      '#options' => array('' => '') + $field_options,
135
      '#dependency' => array('edit-style-options-calendar-type' => array('day')),
136
      );
137
    $form['multiday_theme'] = array(
138
      '#title' => t('Multi-day style'),
139
      '#default_value' => $this->options['multiday_theme'],
140
      '#type' => 'select',
141
      '#options' => array(0 => t('Display multi-day item as a single column'), 1 => t('Display multi-day item as a multiple column row')),
142
      '#description' => t('If selected, items which span multiple days will displayed as a multi-column row.  If not selected, items will be displayed as an individual column.'),
143
      '#dependency' => array('edit-style-options-calendar-type' => array('month', 'week')),
144
      );
145
    $form['multiday_hidden'] = array(
146
      '#title' => t('Fields to hide in Multi-day rows'),
147
      '#default_value' => $this->options['multiday_hidden'],
148
      '#type' => 'checkboxes',
149
      '#options' => $field_options,
150
      '#description' => t('Choose fields to hide when displayed in multi-day rows. Usually you only want to see the title or Colorbox selector in multi-day rows and would hide all other fields.'),
151
      '#dependency' => array('edit-style-options-calendar-type' => array('month', 'week', 'day')),
152
      );
153
  }
154

    
155
  function options_validate(&$form, &$form_state) {
156
    $values = $form_state['values']['style_options'];
157
    if ($values['groupby_times'] == 'custom' && empty($values['groupby_times_custom'])) {
158
      form_set_error('style_options][groupby_times_custom', t('Custom groupby times cannot be empty.'));
159
    }
160
    if (!empty($values['theme_style']) && (empty($values['groupby_times']) || !in_array($values['groupby_times'], array('hour', 'half')))) {
161
      form_set_error('style_options][theme_style', t('Overlapping items only work with hour or half hour groupby times.'));
162
    }
163
    if (!empty($values['theme_style']) && !empty($values['groupby_field'])) {
164
      form_set_error('style_options][theme_style', t('You cannot use overlapping items and also try to group by a field value.'));
165
    }
166
    if ($values['groupby_times'] != 'custom') {
167
      form_set_value($form['groupby_times_custom'], NULL, $form_state);
168
    }
169

    
170
  }
171

    
172
  function options_submit(&$form, &$form_state, &$options = array()) {
173
    $form_state['values']['style_options']['multiday_hidden'] = array_filter($form_state['values']['style_options']['multiday_hidden']);
174
  }
175

    
176
  /**
177
   * Helper function to find the date argument handler for this view.
178
   */
179
  function date_argument_handler() {
180
    $i = 0;
181
    foreach ($this->view->argument as $name => $handler) {
182
      if (date_views_handler_is_date($handler, 'argument')) {
183
        $this->date_info->date_arg_pos = $i;
184
        return $handler;
185
      }
186
      $i++;
187
    }
188
    return FALSE;
189
  }
190

    
191
  /**
192
   * Inspect argument and view information to see which calendar
193
   * period we should show. The argument tells us what to use
194
   * if there is no value, the view args tell us what to use
195
   * if there are values.
196
   */
197
  function granularity() {
198

    
199
    if (!$handler = $this->date_argument_handler()) {
200
      return 'month';
201
    }
202
    $default_granularity = !empty($handler) && !empty($handler->granularity) ? $handler->granularity : 'month';
203
    $wildcard = !empty($handler) ? $handler->options['exception']['value'] : '';
204
    $argument = $handler->argument;
205

    
206
    // TODO Anything else we need to do for 'all' arguments?
207
    if ($argument == $wildcard) {
208
      $this->view_granularity = $default_granularity;
209
    }
210
    elseif (!empty($argument)) {
211
      module_load_include('inc', 'date_api', 'date_api_sql');
212

    
213
      $date_handler = new date_sql_handler();
214
      $this->view_granularity = $date_handler->arg_granularity($argument);
215
    }
216
    else {
217
      $this->view_granularity = $default_granularity;
218
    }
219
    return $this->view_granularity;
220
  }
221

    
222
  function has_calendar_row_plugin() {
223
    return $this->row_plugin instanceof calendar_plugin_row || $this->row_plugin instanceof calendar_plugin_row_node;
224
  }
225

    
226
  function render() {
227
    if (empty($this->row_plugin) || !$this->has_calendar_row_plugin()) {
228
      debug('calendar_plugin_style: The calendar row plugin is required when using the calendar style, but it is missing.');
229
      return;
230
    }
231
    if (!$argument = $this->date_argument_handler()) {
232
      debug('calendar_plugin_style: A date argument is required when using the calendar style, but it is missing or is not using the default date.');
233
      return;
234
    }
235

    
236
    // There are date arguments that have not been added by Date Views.
237
    // They will be missing the information we would need to render the field.
238
    if (empty($argument->min_date)) {
239
      return;
240
    }
241

    
242
    // Add information from the date argument to the view.
243
    $this->date_info->granularity = $this->granularity();
244
    $this->date_info->calendar_type = $this->options['calendar_type'];
245
    $this->date_info->date_arg = $argument->argument;
246
    $this->date_info->year = date_format($argument->min_date, 'Y');
247
    $this->date_info->month = date_format($argument->min_date, 'n');;
248
    $this->date_info->day = date_format($argument->min_date, 'j');
249
    $this->date_info->week = date_week(date_format($argument->min_date, DATE_FORMAT_DATE));
250
    $this->date_info->date_range = $argument->date_range;
251
    $this->date_info->min_date = $argument->min_date;
252
    $this->date_info->max_date = $argument->max_date;
253
    $this->date_info->limit = $argument->limit;
254
    $this->date_info->url = $this->view->get_url();
255
    $this->date_info->min_date_date = date_format($this->date_info->min_date, DATE_FORMAT_DATE);
256
    $this->date_info->max_date_date = date_format($this->date_info->max_date, DATE_FORMAT_DATE);
257
    $this->date_info->forbid = isset($argument->forbid) ? $argument->forbid : FALSE;
258

    
259
    // Add calendar style information to the view.
260
    $this->date_info->calendar_popup = $this->display->handler->get_option('calendar_popup');
261
    $this->date_info->style_name_size = $this->options['name_size'];
262
    $this->date_info->mini = $this->options['mini'];
263
    $this->date_info->style_with_weekno = $this->options['with_weekno'];
264
    $this->date_info->style_multiday_theme = $this->options['multiday_theme'];
265
    $this->date_info->style_theme_style = $this->options['theme_style'];
266
    $this->date_info->style_max_items = $this->options['max_items'];
267
    $this->date_info->style_max_items_behavior = $this->options['max_items_behavior'];
268
    if (!empty($this->options['groupby_times_custom'])) {
269
      $this->date_info->style_groupby_times = explode(',', $this->options['groupby_times_custom']);
270
    }
271
    else {
272
      $this->date_info->style_groupby_times = calendar_groupby_times($this->options['groupby_times']);
273
    }
274
    $this->date_info->style_groupby_field = $this->options['groupby_field'];
275

    
276
    // TODO make this an option setting.
277
    $this->date_info->style_show_empty_times = !empty($this->options['groupby_times_custom']) ? TRUE : FALSE;
278

    
279
    // Set up parameters for the current view that can be used by the row plugin.
280
    $display_timezone = date_timezone_get($this->date_info->min_date);
281
    $this->date_info->display_timezone = $display_timezone;
282
    $this->date_info->display_timezone_name = timezone_name_get($display_timezone);
283
    $date = clone($this->date_info->min_date);
284
    date_timezone_set($date, $display_timezone);
285
    $this->date_info->min_zone_string = date_format($date, DATE_FORMAT_DATE);
286
    $date = clone($this->date_info->max_date);
287
    date_timezone_set($date, $display_timezone);
288
    $this->date_info->max_zone_string = date_format($date, DATE_FORMAT_DATE);
289

    
290
    // Let views render fields the way it thinks they should look before we start massaging them.
291
    $this->render_fields($this->view->result);
292

    
293
    // Invoke the row plugin to massage each result row into calendar items.
294
    // Gather the row items into an array grouped by date and time.
295
    $items = array();
296
    foreach ($this->view->result as $row_index => $row) {
297
      $this->view->row_index = $row_index;
298
      $rows = $this->row_plugin->render($row);
299
      foreach ($rows as $key => $item) {
300
        $item->granularity = $this->date_info->granularity;
301
        $rendered_fields = array();
302
        $item_start = date_format($item->calendar_start_date, DATE_FORMAT_DATE);
303
        $item_end = date_format($item->calendar_end_date, DATE_FORMAT_DATE);
304
        $time_start = date_format($item->calendar_start_date, 'H:i:s');
305
        $item->rendered_fields = $this->rendered_fields[$row_index];
306
        $items[$item_start][$time_start][] = $item;
307
      }
308
    }
309

    
310
    ksort($items);
311

    
312
    $rows = array();
313
    $this->curday = clone($this->date_info->min_date);
314
    $this->items = $items;
315

    
316
    // Retrieve the results array using a the right method for the granularity of the display.
317
    switch ($this->options['calendar_type']) {
318
      case 'year':
319
        $rows = array();
320
        $this->view->date_info->mini = TRUE;
321
        for ($i = 1; $i <= 12; $i++) {
322
          $rows[$i] = $this->calendar_build_mini_month();
323
        }
324
        $this->view->date_info->mini = FALSE;
325
        break;
326
      case 'month':
327
        $rows = !empty($this->date_info->mini) ? $this->calendar_build_mini_month() : $this->calendar_build_month();
328
        break;
329
      case 'day':
330
        $rows = $this->calendar_build_day();
331
        break;
332
      case 'week':
333
        $rows = $this->calendar_build_week();
334
        // Merge the day names in as the first row.
335
        $rows = array_merge(array(calendar_week_header($this->view)), $rows);
336
        break;
337
    }
338

    
339
    // Send the sorted rows to the right theme for this type of calendar.
340
    $this->definition['theme'] = 'calendar_' . $this->options['calendar_type'];
341

    
342
    // Adjust the theme to match the currently selected default.
343
    // Only the month view needs the special 'mini' class,
344
    // which is used to retrieve a different, more compact, theme.
345
    if ($this->options['calendar_type'] == 'month' && !empty($this->view->date_info->mini)) {
346
      $this->definition['theme'] = 'calendar_mini';
347
    }
348
    // If the overlap option was selected, choose the overlap version of the theme.
349
    elseif (in_array($this->options['calendar_type'], array('week', 'day')) && !empty($this->options['multiday_theme']) && !empty($this->options['theme_style'])) {
350
      $this->definition['theme'] .= '_overlap';
351
    }
352

    
353
    $output = theme($this->theme_functions(),
354
      array(
355
        'view' => $this->view,
356
        'options' => $this->options,
357
        'rows' => $rows
358
      ));
359
    unset($this->view->row_index);
360
    return $output;
361
  }
362

    
363
  /**
364
   * Build one month.
365
   */
366
  function calendar_build_month() {
367
    $translated_days = date_week_days_ordered(date_week_days(TRUE));
368
    $month = date_format($this->curday, 'n');
369
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
370
    $weekdays = calendar_untranslated_days($this->items, $this->view);
371
    date_modify($this->curday, '-' . strval(date_format($this->curday, 'j')-1) . ' days');
372
    $rows = array();
373
    do {
374
      $init_day = clone($this->curday);
375
      $today = date_format(date_now(date_default_timezone()), DATE_FORMAT_DATE);
376
      $month = date_format($this->curday, 'n');
377
      $week = date_week($curday_date);
378
      $first_day = variable_get('date_first_day', 0);
379
      $week_rows = $this->calendar_build_week(TRUE);
380
      $multiday_buckets = $week_rows['multiday_buckets'];
381
      $singleday_buckets = $week_rows['singleday_buckets'];
382
      $total_rows = $week_rows['total_rows'];
383

    
384
      // Theme each row
385
      $output = "";
386
      $final_day = clone($this->curday);
387

    
388
      $iehint = 0;
389
      $max_multirow_cnt = 0;
390
      $max_singlerow_cnt = 0;
391

    
392
      for ($i = 0; $i < intval($total_rows + 1); $i++) {
393
        $inner = "";
394

    
395
        // If we're displaying the week number, add it as the
396
        // first cell in the week.
397
        if ($i == 0 && !empty($this->date_info->style_with_weekno) && !in_array($this->date_info->granularity, array('day', 'week'))) {
398
          $path = calendar_granularity_path($this->view, 'week');
399
          if (!empty($path)) {
400
            $url = $path . '/' . $this->date_info->year . '-W' . $week;
401
            $weekno = l($week, $url, array('query' => !empty($this->date_info->append) ? $this->date_info->append : ''));
402
          }
403
          else {
404
            // Do not link week numbers, if Week views are disabled.
405
            $weekno = $week;
406
          }
407
          $item = array(
408
            'entry' => $weekno,
409
            'colspan' => 1,
410
            'rowspan' => $total_rows + 1,
411
            'id' => $this->view->name . '-weekno-' . $curday_date,
412
            'class' => 'week',
413
          );
414
          $inner .= theme('calendar_month_col', array('item' => $item));
415
        }
416

    
417
        $this->curday = clone($init_day);
418

    
419
        // move backwards to the first day of the week
420
        $day_wday = date_format($this->curday, 'w');
421
        date_modify($this->curday, '-' . strval((7 + $day_wday - $first_day) % 7) . ' days');
422

    
423
        for ( $wday = 0; $wday < 7; $wday++) {
424

    
425
          $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
426
          $class = strtolower($weekdays[$wday]);
427
          $item = NULL;
428
          $in_month = !($curday_date < $this->date_info->min_date_date || $curday_date > $this->date_info->max_date_date || date_format($this->curday, 'n') != $month);
429

    
430
          // Add the datebox
431
          if ($i == 0) {
432
            $variables = array(
433
              'date' => $curday_date,
434
              'view' => $this->view,
435
              'items' => $this->items,
436
              'selected' => $in_month ? count($multiday_buckets[$wday]) + count($singleday_buckets[$wday]) : FALSE,
437
            );
438
            $item = array(
439
              'entry' => theme('calendar_datebox', $variables),
440
              'colspan' => 1,
441
              'rowspan' => 1,
442
              'class' => 'date-box',
443
              'date' => $curday_date,
444
              'id' => $this->view->name . '-' . $curday_date . '-date-box',
445
              'header_id' => $translated_days[$wday],
446
              'day_of_month' => $this->curday->format('j'),
447
            );
448
            $item['class'] .= ($curday_date == $today && $in_month ? ' today' : '') .
449
              ($curday_date < $today ? ' past' : '') .
450
              ($curday_date > $today ? ' future' : '');
451
          }
452
          else {
453
            $index = $i - 1;
454
            $multi_count = count($multiday_buckets[$wday]);
455

    
456
            // Process multiday buckets first.  If there is a multiday-bucket item in this row...
457
            if ($index < $multi_count) {
458
              // If this item is filled with either a blank or an entry...
459
              if ($multiday_buckets[$wday][$index]['filled']) {
460

    
461
                // Add item and add class
462
                $item = $multiday_buckets[$wday][$index];
463
                $item['class'] =  'multi-day';
464
                $item['date'] = $curday_date;
465

    
466
                // Is this an entry?
467
                if (!$multiday_buckets[$wday][$index]['avail']) {
468

    
469
                  // If the item either starts or ends on today,
470
                  // then add tags so we can style the borders
471
                  if ($curday_date == $today && $in_month) {
472
                    $item['class'] .=  ' starts-today';
473
                  }
474

    
475
                  // Calculate on which day of this week this item ends on..
476
                  $end_day = clone($this->curday);
477
                  $span = $item['colspan'] - 1;
478
                  date_modify($end_day, '+' . $span . ' day');
479
                  $endday_date = date_format($end_day, DATE_FORMAT_DATE);
480

    
481
                  // If it ends today, add class
482
                  if ($endday_date == $today && $in_month) {
483
                    $item['class'] .=  ' ends-today';
484
                  }
485
                }
486
              }
487

    
488
              // If this is an actual entry, add classes regarding the state of the
489
              // item
490
              if ($multiday_buckets[$wday][$index]['avail']) {
491
                $item['class'] .= ' ' . $wday . ' ' . $index . ' no-entry ' . ($curday_date == $today && $in_month ? ' today' : '') .
492
                  ($curday_date < $today ? ' past' : '') .
493
                  ($curday_date > $today ? ' future' : '');
494
              }
495

    
496
            // Else, process the single day bucket - we only do this once per day
497
            }
498
            elseif ($index == $multi_count) {
499
              $single_day_cnt = 0;
500
              // If it's empty, add class
501
              if (count($singleday_buckets[$wday]) == 0) {
502
                $single_days = "&nbsp;";
503
                if ($max_multirow_cnt == 0 ) {
504
                  $class = ($multi_count > 0 ) ? 'single-day no-entry noentry-multi-day' : 'single-day no-entry';
505
                }
506
                else {
507
                  $class = 'single-day';
508
                }
509
              }
510
              else {
511
                $single_days = "";
512
                foreach ($singleday_buckets[$wday] as $day) {
513
                  foreach ($day as $event) {
514
                    $single_day_cnt++;
515
                    $single_days .= (isset($event['more_link'])) ? '<div class="calendar-more">' . $event['entry'] . '</div>' : $event['entry'];
516
                  }
517
                }
518
                $class = 'single-day';
519
              }
520

    
521
              $rowspan = $total_rows - $index;
522
              // Add item...
523
              $item = array(
524
                'entry' => $single_days,
525
                'colspan' => 1,
526
                'rowspan' => $rowspan,
527
                'class' => $class,
528
                'date' => $curday_date,
529
                'id' => $this->view->name . '-' . $curday_date . '-' . $index,
530
                'header_id' => $translated_days[$wday],
531
                'day_of_month' => $this->curday->format('j'),
532
              );
533

    
534
              // Hack for ie to help it properly space single day rows
535
              if ($rowspan > 1 && $in_month && $single_day_cnt > 0) {
536
                $max_multirow_cnt = max($max_multirow_cnt, $single_day_cnt);
537
              }
538
              else {
539
                $max_singlerow_cnt = max($max_singlerow_cnt, $single_day_cnt);
540
              }
541

    
542
              // If the singlerow is bigger than the multi-row, then null out
543
              // ieheight - I'm estimating that a single row is twice the size of
544
              // multi-row.  This is really the best that can be done with ie
545
              if ($max_singlerow_cnt >= $max_multirow_cnt || $max_multirow_cnt <= $multi_count / 2 ) {
546
                $iehint = 0;
547
              }
548
              elseif ($rowspan > 1 && $in_month && $single_day_cnt > 0) {
549
                $iehint = max($iehint, $rowspan - 1); // How many rows to adjust for?
550
              }
551

    
552
              // Set the class
553
              $item['class'] .= ($curday_date == $today && $in_month ? ' today' : '') .
554
                ($curday_date < $today ? ' past' : '') .
555
                ($curday_date > $today ? ' future' : '');
556
            }
557
          }
558

    
559
          // If there isn't an item, then add empty class
560
          if ($item != NULL) {
561
            if (!$in_month) {
562
              $item['class'] .= ' empty';
563
            }
564
            // Style this entry - it will be a <td>.
565
            $inner .= theme('calendar_month_col', array('item' => $item));
566
          }
567

    
568
          date_modify($this->curday, '+1 day');
569
        }
570

    
571
        if ($i == 0) {
572
          $output .= theme('calendar_month_row', array(
573
            'inner' => $inner,
574
            'class' => 'date-box',
575
            'iehint' => $iehint,
576
          ));
577
        }
578
        elseif ($i == $total_rows) {
579
          $output .= theme('calendar_month_row', array(
580
            'inner' => $inner,
581
            'class' => 'single-day',
582
            'iehint' => $iehint,
583
          ));
584
          $iehint = 0;
585
          $max_singlerow_cnt = 0;
586
          $max_multirow_cnt = 0;
587
        }
588
        else {
589
          // Style all the columns into a row
590
          $output .= theme('calendar_month_row', array(
591
            'inner' => $inner,
592
            'class' => 'multi-day',
593
            'iehint' => 0,
594
          ));
595
        }
596

    
597
      } // End foreach
598

    
599
      $this->curday = $final_day;
600

    
601
      // Add the row into the row array....
602
      $rows[] = array('data' => $output);
603

    
604
      $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
605
      $curday_month = date_format($this->curday, 'n');
606
    } while ($curday_month == $month && $curday_date <= $this->date_info->max_date_date);
607
    // Merge the day names in as the first row.
608
    $rows = array_merge(array(calendar_week_header($this->view)), $rows);
609
    return $rows;
610
  }
611

    
612
  /**
613
   * Build one week row.
614
   */
615
  function calendar_build_week($check_month = FALSE) {
616
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
617
    $weekdays = calendar_untranslated_days($this->items, $this->view);
618
    $month = date_format($this->curday, 'n');
619
    $first_day = variable_get('date_first_day', 0);
620

    
621
    // Set up buckets
622
    $total_rows = 0;
623
    $multiday_buckets = array( array(), array(), array(), array(), array(), array(), array());
624
    $singleday_buckets = array( array(), array(), array(), array(), array(), array(), array());
625

    
626
    // move backwards to the first day of the week
627
    $day_wday = date_format($this->curday, 'w');
628
    date_modify($this->curday, '-' . strval((7 + $day_wday - $first_day) % 7) . ' days');
629
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
630

    
631
    for ($i = 0; $i < 7; $i++) {
632
      if ($check_month && ($curday_date < $this->date_info->min_date_date || $curday_date > $this->date_info->max_date_date || date_format($this->curday, 'n') != $month)) {
633
        $class = strtolower($weekdays[$i]) . ' empty';
634
        $singleday_buckets[$i][][] = array(
635
          'entry' => theme('calendar_empty_day', array(
636
             'curday' => $curday_date,
637
             'view' => $this->view,
638
          )),
639
          'item' => NULL
640
        );
641
      }
642
      else {
643
        $this->calendar_build_week_day($i, $multiday_buckets, $singleday_buckets);
644
      }
645
      $total_rows = max(count($multiday_buckets[$i]) + 1, $total_rows);
646
      date_modify($this->curday, '+1 day');
647
      $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
648
    }
649

    
650
    $rows = array(
651
      'multiday_buckets' => $multiday_buckets,
652
      'singleday_buckets' => $singleday_buckets,
653
      'total_rows' => $total_rows);
654
    return $rows;
655
  }
656

    
657
  /**
658
   * Build the contents of a single day for the $rows results.
659
   */
660
  function calendar_build_week_day($wday, &$multiday_buckets, &$singleday_buckets) {
661
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
662
    $max_events = $this->date_info->calendar_type == 'month' && !empty($this->date_info->style_max_items) ? $this->date_info->style_max_items : 0;
663
    $hide = !empty($this->date_info->style_max_items_behavior) ? ($this->date_info->style_max_items_behavior == 'hide') : FALSE;
664
    $multiday_theme = !empty($this->date_info->style_multiday_theme) && $this->date_info->style_multiday_theme == '1';
665
    $first_day = variable_get('date_first_day', 0);
666
    $cur_cnt = 0;
667
    $total_cnt = 0;
668
    $ids = array();
669

    
670
    // If we are hiding, count before processing further
671
    if ($max_events != CALENDAR_SHOW_ALL) {
672
      foreach ($this->items as $date => $day) {
673
        if ($date == $curday_date) {
674
          foreach ($day as $time => $hour) {
675
            foreach ($hour as $key => $item) {
676
              $total_cnt++;
677
              $ids[] = $item->date_id;
678
            }
679
          }
680
        }
681
      }
682
    }
683

    
684
    // If we haven't already exceeded the max or we'll showing all, then process the items
685
    if ($max_events == CALENDAR_SHOW_ALL || !$hide || $total_cnt < $max_events) {
686
      // Count currently filled items
687
      foreach ($multiday_buckets[$wday] as $bucket) {
688
        if (!$bucket['avail']) {
689
          $cur_cnt++;
690
        }
691
      }
692
      foreach ($this->items as $date => $day) {
693
        if ($date == $curday_date) {
694
          ksort($day);
695
          foreach ($day as $time => $hour) {
696
            foreach ($hour as $key => $item) {
697
              $all_day = $item->calendar_all_day;
698

    
699
              // Parse out date part
700
              $start_ydate = date_format($item->date_start, DATE_FORMAT_DATE);
701
              $end_ydate = date_format($item->date_end, DATE_FORMAT_DATE);
702
              $cur_ydate = date_format($this->curday, DATE_FORMAT_DATE);
703

    
704
              $is_multi_day = ($start_ydate < $cur_ydate || $end_ydate > $cur_ydate);
705

    
706
              // Does this event span multi-days?
707
              if ($multiday_theme && ($is_multi_day || $all_day)) {
708

    
709
                // Remove multiday items from the total count. We can't hide them or they will break.
710
                $total_cnt--;
711

    
712
                // If this the first day of the week, or is the start date of the multi-day event,
713
                // then record this item, otherwise skip over
714
                $day_no = date_format($this->curday, 'd');
715
                if ($wday == 0 || $start_ydate == $cur_ydate || ($this->date_info->granularity == 'month' && $day_no == 1) || ($all_day && !$is_multi_day)) {
716
                  // Calculate the colspan for this event
717

    
718
                  // If the last day of this event exceeds the end of the current month or week,
719
                  // truncate the remaining days
720
                  $diff =  $this->curday->difference($this->date_info->max_date, 'days');
721
                  $remaining_days = ($this->date_info->granularity == 'month') ? min(6 - $wday, $diff) : $diff - 1;
722
                  // The bucket_cnt defines the colspan.  colspan = bucket_cnt + 1
723
                  $days = $this->curday->difference($item->date_end, 'days');
724
                  $bucket_cnt = max(0, min($days, $remaining_days));
725

    
726
                  // See if there is an available slot to add an event.  This will allow
727
                  // an event to precede a row filled up by a previous day event
728
                  $avail = FALSE;
729
                  $bucket_index = count($multiday_buckets[$wday]);
730
                  for ($i = 0; $i < $bucket_index; $i++) {
731
                    if ($multiday_buckets[$wday][$i]['avail']) {
732
                      $bucket_index = $i;
733
                      break;
734
                    }
735
                  }
736

    
737
                  // Add continuation attributes
738
                  $item->continuation =  ($item->date_start < $this->curday);
739
                  $item->continues = ( $days > $bucket_cnt );
740
                  $item->is_multi_day = TRUE;
741

    
742
                  // Assign the item to the available bucket
743
                  $multiday_buckets[$wday][$bucket_index] = array(
744
                    'colspan' => $bucket_cnt + 1,
745
                    'rowspan' => 1,
746
                    'filled' => TRUE,
747
                    'avail' => FALSE,
748
                    'all_day' => $all_day,
749
                    'item' => $item,
750
                    'wday' => $wday,
751
                    'entry' => theme('calendar_item', array('view' => $this->view, 'rendered_fields' => $item->rendered_fields, 'item' => $item)),
752
                  );
753

    
754
                  // Block out empty buckets for the next days in this event for this week
755
                  for ($i = 0; $i < $bucket_cnt; $i++) {
756
                    $bucket = &$multiday_buckets[$i + $wday + 1];
757
                    $bucket_row_count = count($bucket);
758
                    $row_diff = $bucket_index - $bucket_row_count;
759

    
760
                    // Fill up the preceding buckets - these are available for future
761
                    // events
762
                    for ( $j = 0; $j < $row_diff; $j++) {
763
                      $bucket[($bucket_row_count + $j) ] = array(
764
                        'entry' => '&nbsp;',
765
                        'colspan' => 1,
766
                        'rowspan' => 1,
767
                        'filled' => TRUE,
768
                        'avail' => TRUE,
769
                        'wday' => $wday,
770
                        'item' => NULL
771
                      );
772
                    }
773
                    $bucket[$bucket_index] = array(
774
                      'filled' => FALSE,
775
                      'avail' => FALSE
776
                    );
777
                  }
778
                }
779
              }
780
              elseif ($max_events == CALENDAR_SHOW_ALL || $cur_cnt < $max_events) {
781
                $cur_cnt++;
782
                // Assign to single day bucket
783
                $singleday_buckets[$wday][$time][] = array(
784
                  'entry' => theme('calendar_item', array('view' => $this->view, 'rendered_fields' => $item->rendered_fields, 'item' => $item)),
785
                  'item' => $item,
786
                  'colspan' => 1,
787
                  'rowspan' => 1,
788
                  'filled' => TRUE,
789
                  'avail' => FALSE,
790
                  'wday' => $wday,
791
                );
792
              }
793

    
794
            }
795
          }
796
        }
797
      }
798
    }
799

    
800
    // Add a more link if necessary
801
    if ($max_events != CALENDAR_SHOW_ALL && $total_cnt > 0 && $cur_cnt < $total_cnt) {
802
      $entry = theme('calendar_' . $this->date_info->calendar_type . '_multiple_entity', array(
803
          'curday' => $curday_date,
804
          'count' => $total_cnt,
805
          'view' => $this->view,
806
          'ids' => $ids,
807
        ));
808
      if (!empty($entry)) {
809
        $singleday_buckets[$wday][][] = array(
810
          'entry' => $entry,
811
          'more_link' => TRUE,
812
          'item' => NULL
813
        );
814
      }
815
    }
816
  }
817

    
818
  /**
819
   * Build the contents of a single day for the $rows results.
820
   */
821
  function calendar_build_day() {
822
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
823
    $selected = FALSE;
824
    $max_events = !empty($this->date_info->style_max_items) ? $this->date_info->style_max_items : 0;
825
    $ids = array();
826
    $inner = array();
827
    $all_day = array();
828
    $empty = '';
829
    $link = '';
830
    $count = 0;
831
    foreach ($this->items as $date => $day) {
832
      if ($date == $curday_date) {
833
        $count = 0;
834
        $selected = TRUE;
835
        ksort($day);
836
        foreach ($day as $time => $hour) {
837
          foreach ($hour as $key => $item) {
838
            $count++;
839
            if (isset($item->type)) {
840
              $ids[$item->type] = $item;
841
            }
842
            if (empty($this->date_info->mini) && ($max_events == CALENDAR_SHOW_ALL || $count <= $max_events || ($count > 0 && $max_events == CALENDAR_HIDE_ALL))) {
843
              if ($item->calendar_all_day) {
844
                $item->is_multi_day = TRUE;
845
                $all_day[] = $item;
846
              }
847
              else {
848
                $key = date_format($item->calendar_start_date, 'H:i:s');
849
                $inner[$key][] = $item;
850
              }
851
            }
852
          }
853
        }
854
      }
855
    }
856
    ksort($inner);
857

    
858
    if (empty($inner) && empty($all_day)) {
859
      $empty = theme('calendar_empty_day', array('curday' => $curday_date, 'view' => $this->view));
860
    }
861
    // We have hidden events on this day, use the theme('calendar_multiple_') to show a link.
862
    if ($max_events != CALENDAR_SHOW_ALL && $count > 0 && $count > $max_events && $this->date_info->calendar_type != 'day' && !$this->date_info->mini) {
863
      if ($this->date_info->style_max_items_behavior == 'hide' || $max_events == CALENDAR_HIDE_ALL) {
864
        $all_day = array();
865
        $inner = array();
866
      }
867
      $link = theme('calendar_' . $this->date_info->calendar_type . '_multiple_node', array(
868
        'curday' => $curday_date,
869
        'count' => $count,
870
        'view' => $this->view,
871
        'ids' => $ids,
872
      ));
873
    }
874

    
875
    $content = array(
876
      'date' => $curday_date,
877
      'datebox' => theme('calendar_datebox', array(
878
         'date' => $curday_date,
879
         'view' => $this->view,
880
         'items' => $this->items,
881
         'selected' => $selected,
882
      )),
883
      'empty' => $empty,
884
      'link' => $link,
885
      'all_day' => $all_day,
886
      'items' => $inner,
887
      );
888
    return $content;
889
  }
890

    
891
  /**
892
   * Build one mini month.
893
   */
894
  function calendar_build_mini_month() {
895
    $month = date_format($this->curday, 'n');
896
    date_modify($this->curday, '-' . strval(date_format($this->curday, 'j')-1) . ' days');
897
    $rows = array();
898
    do {
899
      $rows = array_merge($rows, $this->calendar_build_mini_week());
900
      $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
901
      $curday_month = date_format($this->curday, 'n');
902
    } while ($curday_month == $month && $curday_date <= $this->date_info->max_date_date);
903
    // Merge the day names in as the first row.
904
    $rows = array_merge(array(calendar_week_header($this->view)), $rows);
905
    return $rows;
906
  }
907

    
908
  /**
909
   * Build one week row.
910
   */
911
  function calendar_build_mini_week($check_month = TRUE) {
912
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
913
    $weekdays = calendar_untranslated_days($this->items, $this->view);
914
    $today = date_format(date_now(date_default_timezone()), DATE_FORMAT_DATE);
915
    $month = date_format($this->curday, 'n');
916
    $week = date_week($curday_date);
917
    $first_day = variable_get('date_first_day', 0);
918
    // move backwards to the first day of the week
919
    $day_wday = date_format($this->curday, 'w');
920
    date_modify($this->curday, '-' . strval((7 + $day_wday - $first_day) % 7) . ' days');
921
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
922

    
923
    for ($i = 0; $i < 7; $i++) {
924
      $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
925
      $class = strtolower($weekdays[$i] . ' mini');
926
      if ($check_month && ($curday_date < $this->date_info->min_date_date || $curday_date > $this->date_info->max_date_date || date_format($this->curday, 'n') != $month)) {
927
         $class .= ' empty';
928
         $variables = array(
929
          'curday' => $curday_date,
930
          'view' => $this->view,
931
        );
932

    
933
        $content = array(
934
          'date' => '',
935
          'datebox' => '',
936
          'empty' => theme('calendar_empty_day', $variables),
937
          'link' => '',
938
          'all_day' => array(),
939
          'items' => array(),
940
          );
941
      }
942
      else {
943
        $content = $this->calendar_build_day();
944
        $class .= ($curday_date == $today ? ' today' : '') .
945
          ($curday_date < $today ? ' past' : '') .
946
          ($curday_date > $today ? ' future' : '') .
947
          (empty($this->items[$curday_date]) ? ' has-no-events' : ' has-events');
948
      }
949
      $rows[$week][] = array(
950
        'data' => $content,
951
        'class' => $class,
952
        'id' => $this->view->name . '-' . $curday_date,
953
      );
954
      date_modify($this->curday, '+1 day');
955
    }
956
    return $rows;
957
  }
958

    
959
}