Projet

Général

Profil

Paste
Télécharger (41,6 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / calendar / includes / calendar_plugin_style.inc @ 62e0cc08

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(
87
         0 => t('Unlimited'),
88
         1 => format_plural( 1, '1 item', '@count items'),
89
         3 => format_plural( 3, '1 item', '@count items'),
90
         5 => format_plural( 5, '1 item', '@count items'),
91
        10 => format_plural(10, '1 item', '@count items'),
92
      ),
93
      '#default_value' => $this->options['calendar_type'] != 'day' ? $this->options['max_items'] : 0,
94
      '#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.'),
95
      '#dependency' => array('edit-style-options-calendar-type' => array('month')),
96
      );
97
    $form['max_items_behavior'] = array(
98
      '#title' => t('Too many items'),
99
      '#type' => 'select',
100
      '#options' => array('more' => t("Show maximum, add 'more' link"), 'hide' => t('Hide all, add link to day')),
101
      '#default_value' => $this->options['calendar_type'] != 'day' ? $this->options['max_items_behavior'] : 'more',
102
      '#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.'),
103
      '#dependency' => array('edit-style-options-calendar-type' => array('month')),
104
      );
105
    $form['groupby_times'] = array(
106
      '#title' => t('Time grouping'),
107
      '#type' => 'select',
108
      '#default_value' => $this->options['groupby_times'],
109
      '#description' => t("Group items together into time periods based on their start time."),
110
      '#options' => array('' => t('None'), 'hour' => t('Hour'), 'half' => t('Half hour'), 'custom' => t('Custom')),
111
      '#dependency' => array('edit-style-options-calendar-type' => array('day', 'week')),
112
      );
113
    $form['groupby_times_custom'] = array(
114
      '#title' => t('Custom time grouping'),
115
      '#type' => 'textarea',
116
      '#default_value' => $this->options['groupby_times_custom'],
117
      '#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."),
118
      '#dependency' => array('edit-style-options-groupby-times' => array('custom')),
119
      );
120
    $form['theme_style'] = array(
121
      '#title' => t('Overlapping time style'),
122
      '#default_value' => $this->options['theme_style'],
123
      '#type' => 'select',
124
      '#options' => array(0 => t('Do not display overlapping items'), 1 => t('Display overlapping items, with scrolling'), 2 => t('Display overlapping items, no scrolling')),
125
      '#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!'),
126
      '#dependency' => array('edit-style-options-calendar-type' => array('day', 'week')),
127
      );
128

    
129
    // Create a list of fields that are available for grouping,
130
    $field_options = array();
131
    $fields = $this->display->handler->get_option('fields');
132
    foreach ($fields as $field_name => $field) {
133
      $field_options[$field_name] = $field['field'];
134
    }
135
    $form['groupby_field'] = array(
136
      '#title' => t('Field grouping'),
137
      '#type' => 'select',
138
      '#default_value' => $this->options['groupby_field'],
139
      '#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."),
140
      '#options' => array('' => '') + $field_options,
141
      '#dependency' => array('edit-style-options-calendar-type' => array('day')),
142
      );
143
    $form['multiday_theme'] = array(
144
      '#title' => t('Multi-day style'),
145
      '#default_value' => $this->options['multiday_theme'],
146
      '#type' => 'select',
147
      '#options' => array(0 => t('Display multi-day item as a single column'), 1 => t('Display multi-day item as a multiple column row')),
148
      '#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.'),
149
      '#dependency' => array('edit-style-options-calendar-type' => array('month', 'week')),
150
      );
151
    $form['multiday_hidden'] = array(
152
      '#title' => t('Fields to hide in Multi-day rows'),
153
      '#default_value' => $this->options['multiday_hidden'],
154
      '#type' => 'checkboxes',
155
      '#options' => $field_options,
156
      '#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.'),
157
      '#dependency' => array('edit-style-options-calendar-type' => array('month', 'week', 'day')),
158
      );
159
  }
160

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

    
176
  }
177

    
178
  function options_submit(&$form, &$form_state, &$options = array()) {
179
    $form_state['values']['style_options']['multiday_hidden'] = array_filter($form_state['values']['style_options']['multiday_hidden']);
180
  }
181

    
182
  /**
183
   * Helper function to find the date argument handler for this view.
184
   */
185
  function date_argument_handler() {
186
    $i = 0;
187
    foreach ($this->view->argument as $name => $handler) {
188
      if (date_views_handler_is_date($handler, 'argument')) {
189
        $this->date_info->date_arg_pos = $i;
190
        return $handler;
191
      }
192
      $i++;
193
    }
194
    return FALSE;
195
  }
196

    
197
  /**
198
   * Inspect argument and view information to see which calendar
199
   * period we should show. The argument tells us what to use
200
   * if there is no value, the view args tell us what to use
201
   * if there are values.
202
   */
203
  function granularity() {
204

    
205
    if (!$handler = $this->date_argument_handler()) {
206
      return 'month';
207
    }
208
    $default_granularity = !empty($handler) && !empty($handler->granularity) ? $handler->granularity : 'month';
209
    $wildcard = !empty($handler) ? $handler->options['exception']['value'] : '';
210
    $argument = $handler->argument;
211

    
212
    // TODO Anything else we need to do for 'all' arguments?
213
    if ($argument == $wildcard) {
214
      $this->view_granularity = $default_granularity;
215
    }
216
    elseif (!empty($argument)) {
217
      module_load_include('inc', 'date_api', 'date_api_sql');
218

    
219
      $date_handler = new date_sql_handler();
220
      $this->view_granularity = $date_handler->arg_granularity($argument);
221
    }
222
    else {
223
      $this->view_granularity = $default_granularity;
224
    }
225
    return $this->view_granularity;
226
  }
227

    
228
  function has_calendar_row_plugin() {
229
    return $this->row_plugin instanceof calendar_plugin_row || $this->row_plugin instanceof calendar_plugin_row_node;
230
  }
231

    
232
  function render() {
233
    if (empty($this->row_plugin) || !$this->has_calendar_row_plugin()) {
234
      debug('calendar_plugin_style: The calendar row plugin is required when using the calendar style, but it is missing.');
235
      return;
236
    }
237
    if (!$argument = $this->date_argument_handler()) {
238
      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.');
239
      return;
240
    }
241

    
242
    // There are date arguments that have not been added by Date Views.
243
    // They will be missing the information we would need to render the field.
244
    if (empty($argument->min_date)) {
245
      return;
246
    }
247

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

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

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

    
285
    // Set up parameters for the current view that can be used by the row plugin.
286
    $display_timezone = date_timezone_get($this->date_info->min_date);
287
    $this->date_info->display_timezone = $display_timezone;
288
    $this->date_info->display_timezone_name = timezone_name_get($display_timezone);
289
    $date = clone($this->date_info->min_date);
290
    date_timezone_set($date, $display_timezone);
291
    $this->date_info->min_zone_string = date_format($date, DATE_FORMAT_DATE);
292
    $date = clone($this->date_info->max_date);
293
    date_timezone_set($date, $display_timezone);
294
    $this->date_info->max_zone_string = date_format($date, DATE_FORMAT_DATE);
295

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

    
299
    // Invoke the row plugin to massage each result row into calendar items.
300
    // Gather the row items into an array grouped by date and time.
301
    $items = array();
302
    foreach ($this->view->result as $row_index => $row) {
303
      $this->view->row_index = $row_index;
304
      $rows = $this->row_plugin->render($row);
305
      foreach ($rows as $key => $item) {
306
        $item->granularity = $this->date_info->granularity;
307
        $rendered_fields = array();
308
        $item_start = date_format($item->calendar_start_date, DATE_FORMAT_DATE);
309
        $item_end = date_format($item->calendar_end_date, DATE_FORMAT_DATE);
310
        $time_start = date_format($item->calendar_start_date, 'H:i:s');
311
        $item->rendered_fields = $this->rendered_fields[$row_index];
312
        //set custom class to item
313
        $item->classCustom = $this->view->style_plugin->get_row_class($row_index);
314
        $items[$item_start][$time_start][] = $item;
315
      }
316
    }
317

    
318
    ksort($items);
319

    
320
    $rows = array();
321
    $this->curday = clone($this->date_info->min_date);
322
    $this->items = $items;
323

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

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

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

    
361
    $output = theme($this->theme_functions(),
362
      array(
363
        'view' => $this->view,
364
        'options' => $this->options,
365
        'rows' => $rows
366
      ));
367
    unset($this->view->row_index);
368
    return $output;
369
  }
370

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

    
392
      // Theme each row
393
      $output = "";
394
      $final_day = clone($this->curday);
395

    
396
      $iehint = 0;
397
      $max_multirow_cnt = 0;
398
      $max_singlerow_cnt = 0;
399

    
400
      for ($i = 0; $i < intval($total_rows + 1); $i++) {
401
        $inner = "";
402

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

    
425
        $this->curday = clone($init_day);
426

    
427
        // move backwards to the first day of the week
428
        $day_wday = date_format($this->curday, 'w');
429
        date_modify($this->curday, '-' . strval((7 + $day_wday - $first_day) % 7) . ' days');
430

    
431
        for ( $wday = 0; $wday < 7; $wday++) {
432

    
433
          $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
434
          $class = strtolower($weekdays[$wday]);
435
          $item = NULL;
436
          $in_month = !($curday_date < $this->date_info->min_date_date || $curday_date > $this->date_info->max_date_date || date_format($this->curday, 'n') != $month);
437

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

    
464
            // Process multiday buckets first.  If there is a multiday-bucket item in this row...
465
            if ($index < $multi_count) {
466
              // If this item is filled with either a blank or an entry...
467
              if ($multiday_buckets[$wday][$index]['filled']) {
468

    
469
                // Add item and add class
470
                $item = $multiday_buckets[$wday][$index];
471
                $item['class'] =  'multi-day';
472
                $item['date'] = $curday_date;
473

    
474
                // Is this an entry?
475
                if (!$multiday_buckets[$wday][$index]['avail']) {
476

    
477
                  // If the item either starts or ends on today,
478
                  // then add tags so we can style the borders
479
                  if ($curday_date == $today && $in_month) {
480
                    $item['class'] .=  ' starts-today';
481
                  }
482

    
483
                  // Calculate on which day of this week this item ends on..
484
                  $end_day = clone($this->curday);
485
                  $span = $item['colspan'] - 1;
486
                  date_modify($end_day, '+' . $span . ' day');
487
                  $endday_date = date_format($end_day, DATE_FORMAT_DATE);
488

    
489
                  // If it ends today, add class
490
                  if ($endday_date == $today && $in_month) {
491
                    $item['class'] .=  ' ends-today';
492
                  }
493
                }
494
              }
495

    
496
              // If this is an actual entry, add classes regarding the state of the
497
              // item
498
              if ($multiday_buckets[$wday][$index]['avail']) {
499
                $item['class'] .= ' ' . $wday . ' ' . $index . ' no-entry ' . ($curday_date == $today && $in_month ? ' today' : '') .
500
                  ($curday_date < $today ? ' past' : '') .
501
                  ($curday_date > $today ? ' future' : '');
502
              }
503

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

    
529
              $rowspan = $total_rows - $index;
530
              // Add item...
531
              $item = array(
532
                'entry' => $single_days,
533
                'colspan' => 1,
534
                'rowspan' => $rowspan,
535
                'class' => $class,
536
                'date' => $curday_date,
537
                'id' => $this->view->name . '-' . $curday_date . '-' . $index,
538
                'header_id' => $translated_days[$wday],
539
                'day_of_month' => $this->curday->format('j'),
540
              );
541

    
542
              // Hack for ie to help it properly space single day rows
543
              if ($rowspan > 1 && $in_month && $single_day_cnt > 0) {
544
                $max_multirow_cnt = max($max_multirow_cnt, $single_day_cnt);
545
              }
546
              else {
547
                $max_singlerow_cnt = max($max_singlerow_cnt, $single_day_cnt);
548
              }
549

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

    
560
              // Set the class
561
              $item['class'] .= ($curday_date == $today && $in_month ? ' today' : '') .
562
                ($curday_date < $today ? ' past' : '') .
563
                ($curday_date > $today ? ' future' : '');
564
            }
565
          }
566

    
567
          // If there isn't an item, then add empty class
568
          if ($item != NULL) {
569
            if (!$in_month) {
570
              $item['class'] .= ' empty';
571
            }
572
            // Style this entry - it will be a <td>.
573
            $inner .= theme('calendar_month_col', array('item' => $item));
574
          }
575

    
576
          date_modify($this->curday, '+1 day');
577
        }
578

    
579
        if ($i == 0) {
580
          $output .= theme('calendar_month_row', array(
581
            'inner' => $inner,
582
            'class' => 'date-box',
583
            'iehint' => $iehint,
584
          ));
585
        }
586
        elseif ($i == $total_rows) {
587
          $output .= theme('calendar_month_row', array(
588
            'inner' => $inner,
589
            'class' => 'single-day',
590
            'iehint' => $iehint,
591
          ));
592
          $iehint = 0;
593
          $max_singlerow_cnt = 0;
594
          $max_multirow_cnt = 0;
595
        }
596
        else {
597
          // Style all the columns into a row
598
          $output .= theme('calendar_month_row', array(
599
            'inner' => $inner,
600
            'class' => 'multi-day',
601
            'iehint' => 0,
602
          ));
603
        }
604

    
605
      } // End foreach
606

    
607
      $this->curday = $final_day;
608

    
609
      // Add the row into the row array....
610
      $rows[] = array('data' => $output);
611

    
612
      $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
613
      $curday_month = date_format($this->curday, 'n');
614
    } while ($curday_month == $month && $curday_date <= $this->date_info->max_date_date);
615
    // Merge the day names in as the first row.
616
    $rows = array_merge(array(calendar_week_header($this->view)), $rows);
617
    return $rows;
618
  }
619

    
620
  /**
621
   * Build one week row.
622
   */
623
  function calendar_build_week($check_month = FALSE) {
624
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
625
    $weekdays = calendar_untranslated_days($this->items, $this->view);
626
    $month = date_format($this->curday, 'n');
627
    $first_day = variable_get('date_first_day', 0);
628

    
629
    // Set up buckets
630
    $total_rows = 0;
631
    $multiday_buckets = array( array(), array(), array(), array(), array(), array(), array());
632
    $singleday_buckets = array( array(), array(), array(), array(), array(), array(), array());
633

    
634
    // move backwards to the first day of the week
635
    $day_wday = date_format($this->curday, 'w');
636
    date_modify($this->curday, '-' . strval((7 + $day_wday - $first_day) % 7) . ' days');
637
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
638

    
639
    for ($i = 0; $i < 7; $i++) {
640
      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)) {
641
        $class = strtolower($weekdays[$i]) . ' empty';
642
        $singleday_buckets[$i][][] = array(
643
          'entry' => theme('calendar_empty_day', array(
644
             'curday' => $curday_date,
645
             'view' => $this->view,
646
          )),
647
          'item' => NULL
648
        );
649
      }
650
      else {
651
        $this->calendar_build_week_day($i, $multiday_buckets, $singleday_buckets);
652
      }
653
      $total_rows = max(count($multiday_buckets[$i]) + 1, $total_rows);
654
      date_modify($this->curday, '+1 day');
655
      $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
656
    }
657

    
658
    $rows = array(
659
      'multiday_buckets' => $multiday_buckets,
660
      'singleday_buckets' => $singleday_buckets,
661
      'total_rows' => $total_rows);
662
    return $rows;
663
  }
664

    
665
  /**
666
   * Build the contents of a single day for the $rows results.
667
   */
668
  function calendar_build_week_day($wday, &$multiday_buckets, &$singleday_buckets) {
669
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
670
    $max_events = $this->date_info->calendar_type == 'month' && !empty($this->date_info->style_max_items) ? $this->date_info->style_max_items : 0;
671
    $hide = !empty($this->date_info->style_max_items_behavior) ? ($this->date_info->style_max_items_behavior == 'hide') : FALSE;
672
    $multiday_theme = !empty($this->date_info->style_multiday_theme) && $this->date_info->style_multiday_theme == '1';
673
    $first_day = variable_get('date_first_day', 0);
674
    $cur_cnt = 0;
675
    $extra_events = FALSE;
676
    $total_cnt = 0;
677
    $ids = array();
678

    
679
    // If we are hiding, count before processing further
680
    if ($max_events != CALENDAR_SHOW_ALL) {
681
      foreach ($this->items as $date => $day) {
682
        if ($date == $curday_date) {
683
          foreach ($day as $time => $hour) {
684
            foreach ($hour as $key => $item) {
685
              $total_cnt++;
686
              $ids[] = $item->date_id;
687
            }
688
          }
689
        }
690
      }
691
    }
692

    
693
    // If we haven't already exceeded the max or we'll showing all, then process the items
694
    if ($max_events == CALENDAR_SHOW_ALL || !$hide || $total_cnt < $max_events) {
695
      // Count currently filled items
696
      foreach ($multiday_buckets[$wday] as $bucket) {
697
        if (!$bucket['avail']) {
698
          $cur_cnt++;
699
        }
700
      }
701
      foreach ($this->items as $date => $day) {
702
        if ($date == $curday_date) {
703
          ksort($day);
704
          foreach ($day as $time => $hour) {
705
            foreach ($hour as $key => $item) {
706
              $all_day = $item->calendar_all_day;
707

    
708
              // Parse out date part
709
              $start_ydate = date_format($item->date_start, DATE_FORMAT_DATE);
710
              $end_ydate = date_format($item->date_end, DATE_FORMAT_DATE);
711
              $cur_ydate = date_format($this->curday, DATE_FORMAT_DATE);
712

    
713
              $is_multi_day = ($start_ydate < $cur_ydate || $end_ydate > $cur_ydate);
714

    
715
              // Does this event span multi-days?
716
              if ($multiday_theme && ($is_multi_day || $all_day)) {
717

    
718
                // Remove multiday items from the total count. We can't hide them or they will break.
719
                $total_cnt--;
720

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

    
727
                  // If the last day of this event exceeds the end of the current month or week,
728
                  // truncate the remaining days
729
                  $diff =  $this->curday->difference($this->date_info->max_date, 'days');
730
                  $remaining_days = ($this->date_info->granularity == 'month') ? min(6 - $wday, $diff) : $diff;
731
                  // The bucket_cnt defines the colspan.  colspan = bucket_cnt + 1
732
                  $days = $this->curday->difference($item->date_end, 'days');
733
                  $bucket_cnt = max(0, min($days, $remaining_days));
734

    
735
                  // See if there is an available slot to add an event.  This will allow
736
                  // an event to precede a row filled up by a previous day event
737
                  $avail = FALSE;
738
                  $bucket_index = count($multiday_buckets[$wday]);
739
                  for ($i = 0; $i < $bucket_index; $i++) {
740
                    if ($multiday_buckets[$wday][$i]['avail']) {
741
                      $bucket_index = $i;
742
                      break;
743
                    }
744
                  }
745

    
746
                  // Add continuation attributes
747
                  $item->continuation =  ($item->date_start < $this->curday);
748
                  $item->continues = ( $days > $bucket_cnt );
749
                  $item->is_multi_day = TRUE;
750

    
751
                  // Assign the item to the available bucket
752
                  $multiday_buckets[$wday][$bucket_index] = array(
753
                    'colspan' => $bucket_cnt + 1,
754
                    'rowspan' => 1,
755
                    'filled' => TRUE,
756
                    'avail' => FALSE,
757
                    'all_day' => $all_day,
758
                    'item' => $item,
759
                    'wday' => $wday,
760
                    'entry' => theme('calendar_item', array('view' => $this->view, 'rendered_fields' => $item->rendered_fields, 'item' => $item)),
761
                  );
762

    
763
                  // Block out empty buckets for the next days in this event for this week
764
                  for ($i = 0; $i < $bucket_cnt; $i++) {
765
                    $bucket = &$multiday_buckets[$i + $wday + 1];
766
                    $bucket_row_count = count($bucket);
767
                    $row_diff = $bucket_index - $bucket_row_count;
768

    
769
                    // Fill up the preceding buckets - these are available for future
770
                    // events
771
                    for ( $j = 0; $j < $row_diff; $j++) {
772
                      $bucket[($bucket_row_count + $j) ] = array(
773
                        'entry' => '&nbsp;',
774
                        'colspan' => 1,
775
                        'rowspan' => 1,
776
                        'filled' => TRUE,
777
                        'avail' => TRUE,
778
                        'wday' => $wday,
779
                        'item' => NULL
780
                      );
781
                    }
782
                    $bucket[$bucket_index] = array(
783
                      'filled' => FALSE,
784
                      'avail' => FALSE
785
                    );
786
                  }
787
                }
788
              }
789
              elseif ($max_events == CALENDAR_SHOW_ALL || $cur_cnt < $max_events) {
790
                $cur_cnt++;
791
                // Assign to single day bucket
792
                $singleday_buckets[$wday][$time][] = array(
793
                  'entry' => theme('calendar_item', array('view' => $this->view, 'rendered_fields' => $item->rendered_fields, 'item' => $item)),
794
                  'item' => $item,
795
                  'colspan' => 1,
796
                  'rowspan' => 1,
797
                  'filled' => TRUE,
798
                  'avail' => FALSE,
799
                  'wday' => $wday,
800
                );
801
              }
802
              elseif (!$extra_events) {
803
                // The event we're currently on will never be displaed.
804
                // So let's set a flag the make sure more link is set.
805
                $extra_events = TRUE;
806
              }
807

    
808
            }
809
          }
810
        }
811
      }
812
    }
813

    
814
    // Add a more link if necessary
815
    if ($max_events != CALENDAR_SHOW_ALL && $total_cnt > 0 && ($cur_cnt < $total_cnt || $extra_events)) {
816
      $entry = theme('calendar_' . $this->date_info->calendar_type . '_multiple_entity', array(
817
          'curday' => $curday_date,
818
          'count' => $total_cnt,
819
          'view' => $this->view,
820
          'ids' => $ids,
821
        ));
822
      if (!empty($entry)) {
823
        $singleday_buckets[$wday][][] = array(
824
          'entry' => $entry,
825
          'more_link' => TRUE,
826
          'item' => NULL
827
        );
828
      }
829
    }
830
  }
831

    
832
  /**
833
   * Build the contents of a single day for the $rows results.
834
   */
835
  function calendar_build_day() {
836
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
837
    $selected = FALSE;
838
    $max_events = !empty($this->date_info->style_max_items) ? $this->date_info->style_max_items : 0;
839
    $ids = array();
840
    $inner = array();
841
    $all_day = array();
842
    $empty = '';
843
    $link = '';
844
    $count = 0;
845
    foreach ($this->items as $date => $day) {
846
      if ($date == $curday_date) {
847
        $count = 0;
848
        $selected = TRUE;
849
        ksort($day);
850
        foreach ($day as $time => $hour) {
851
          foreach ($hour as $key => $item) {
852
            $count++;
853
            if (isset($item->type)) {
854
              $ids[$item->type] = $item;
855
            }
856
            if (empty($this->date_info->mini) && ($max_events == CALENDAR_SHOW_ALL || $count <= $max_events || ($count > 0 && $max_events == CALENDAR_HIDE_ALL))) {
857
              if ($item->calendar_all_day) {
858
                $item->is_multi_day = TRUE;
859
                $all_day[] = $item;
860
              }
861
              else {
862
                $key = date_format($item->calendar_start_date, 'H:i:s');
863
                $inner[$key][] = $item;
864
              }
865
            }
866
          }
867
        }
868
      }
869
    }
870
    ksort($inner);
871

    
872
    if (empty($inner) && empty($all_day)) {
873
      $empty = theme('calendar_empty_day', array('curday' => $curday_date, 'view' => $this->view));
874
    }
875
    // We have hidden events on this day, use the theme('calendar_multiple_') to show a link.
876
    if ($max_events != CALENDAR_SHOW_ALL && $count > 0 && $count > $max_events && $this->date_info->calendar_type != 'day' && !$this->date_info->mini) {
877
      if ($this->date_info->style_max_items_behavior == 'hide' || $max_events == CALENDAR_HIDE_ALL) {
878
        $all_day = array();
879
        $inner = array();
880
      }
881
      $link = theme('calendar_' . $this->date_info->calendar_type . '_multiple_node', array(
882
        'curday' => $curday_date,
883
        'count' => $count,
884
        'view' => $this->view,
885
        'ids' => $ids,
886
      ));
887
    }
888

    
889
    $content = array(
890
      'date' => $curday_date,
891
      'datebox' => theme('calendar_datebox', array(
892
         'date' => $curday_date,
893
         'view' => $this->view,
894
         'items' => $this->items,
895
         'selected' => $selected,
896
      )),
897
      'empty' => $empty,
898
      'link' => $link,
899
      'all_day' => $all_day,
900
      'items' => $inner,
901
      );
902
    return $content;
903
  }
904

    
905
  /**
906
   * Build one mini month.
907
   */
908
  function calendar_build_mini_month() {
909
    $month = date_format($this->curday, 'n');
910
    date_modify($this->curday, '-' . strval(date_format($this->curday, 'j')-1) . ' days');
911
    $rows = array();
912
    do {
913
      $rows = array_merge($rows, $this->calendar_build_mini_week());
914
      $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
915
      $curday_month = date_format($this->curday, 'n');
916
    } while ($curday_month == $month && $curday_date <= $this->date_info->max_date_date);
917
    // Merge the day names in as the first row.
918
    $rows = array_merge(array(calendar_week_header($this->view)), $rows);
919
    return $rows;
920
  }
921

    
922
  /**
923
   * Build one week row.
924
   */
925
  function calendar_build_mini_week($check_month = TRUE) {
926
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
927
    $weekdays = calendar_untranslated_days($this->items, $this->view);
928
    $today = date_format(date_now(date_default_timezone()), DATE_FORMAT_DATE);
929
    $month = date_format($this->curday, 'n');
930
    $week = date_week($curday_date);
931
    $first_day = variable_get('date_first_day', 0);
932
    // move backwards to the first day of the week
933
    $day_wday = date_format($this->curday, 'w');
934
    date_modify($this->curday, '-' . strval((7 + $day_wday - $first_day) % 7) . ' days');
935
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
936

    
937
    if (!empty($this->date_info->style_with_weekno)) {
938
      $path = calendar_granularity_path($this->view, 'week');
939
      if (!empty($path)) {
940
        $url = $path . '/' . $this->date_info->year . '-W' . $week;
941
        $weekno = l($week, $url, array('query' => !empty($this->date_info->append) ? $this->date_info->append : ''));
942
      }
943
      else {
944
        // Do not link week numbers, if Week views are disabled.
945
        $weekno = $week;
946
      }
947
      $rows[$week][] = array(
948
        'data' => $weekno,
949
        'class' => 'mini week',
950
        'id' => $this->view->name . '-weekno-' . $curday_date,
951
      );
952
    }
953

    
954
    for ($i = 0; $i < 7; $i++) {
955
      $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
956
      $class = strtolower($weekdays[$i] . ' mini');
957
      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)) {
958
         $class .= ' empty';
959
         $variables = array(
960
          'curday' => $curday_date,
961
          'view' => $this->view,
962
        );
963

    
964
        $content = array(
965
          'date' => '',
966
          'datebox' => '',
967
          'empty' => theme('calendar_empty_day', $variables),
968
          'link' => '',
969
          'all_day' => array(),
970
          'items' => array(),
971
          );
972
      }
973
      else {
974
        $content = $this->calendar_build_day();
975
        $class .= ($curday_date == $today ? ' today' : '') .
976
          ($curday_date < $today ? ' past' : '') .
977
          ($curday_date > $today ? ' future' : '') .
978
          (empty($this->items[$curday_date]) ? ' has-no-events' : ' has-events');
979
      }
980
      $rows[$week][] = array(
981
        'data' => $content,
982
        'class' => $class,
983
        'id' => $this->view->name . '-' . $curday_date,
984
      );
985
      date_modify($this->curday, '+1 day');
986
    }
987
    return $rows;
988
  }
989

    
990
}