Projet

Général

Profil

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

root / drupal7 / sites / all / modules / calendar / includes / calendar_plugin_style.inc @ 0695d136

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
        $items[$item_start][$time_start][] = $item;
313
      }
314
    }
315

    
316
    ksort($items);
317

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

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

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

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

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

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

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

    
394
      $iehint = 0;
395
      $max_multirow_cnt = 0;
396
      $max_singlerow_cnt = 0;
397

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

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

    
423
        $this->curday = clone($init_day);
424

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

    
429
        for ( $wday = 0; $wday < 7; $wday++) {
430

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
603
      } // End foreach
604

    
605
      $this->curday = $final_day;
606

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

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

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

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

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

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

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

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

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

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

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

    
710
              $is_multi_day = ($start_ydate < $cur_ydate || $end_ydate > $cur_ydate);
711

    
712
              // Does this event span multi-days?
713
              if ($multiday_theme && ($is_multi_day || $all_day)) {
714

    
715
                // Remove multiday items from the total count. We can't hide them or they will break.
716
                $total_cnt--;
717

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

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

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

    
743
                  // Add continuation attributes
744
                  $item->continuation =  ($item->date_start < $this->curday);
745
                  $item->continues = ( $days > $bucket_cnt );
746
                  $item->is_multi_day = TRUE;
747

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

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

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

    
800
            }
801
          }
802
        }
803
      }
804
    }
805

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

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

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

    
881
    $content = array(
882
      'date' => $curday_date,
883
      'datebox' => theme('calendar_datebox', array(
884
         'date' => $curday_date,
885
         'view' => $this->view,
886
         'items' => $this->items,
887
         'selected' => $selected,
888
      )),
889
      'empty' => $empty,
890
      'link' => $link,
891
      'all_day' => $all_day,
892
      'items' => $inner,
893
      );
894
    return $content;
895
  }
896

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

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

    
929
    if (!empty($this->date_info->style_with_weekno)) {
930
      $path = calendar_granularity_path($this->view, 'week');
931
      if (!empty($path)) {
932
        $url = $path . '/' . $this->date_info->year . '-W' . $week;
933
        $weekno = l($week, $url, array('query' => !empty($this->date_info->append) ? $this->date_info->append : ''));
934
      }
935
      else {
936
        // Do not link week numbers, if Week views are disabled.
937
        $weekno = $week;
938
      }
939
      $rows[$week][] = array(
940
        'data' => $weekno,
941
        'class' => 'mini week',
942
        'id' => $this->view->name . '-weekno-' . $curday_date,
943
      );
944
    }
945

    
946
    for ($i = 0; $i < 7; $i++) {
947
      $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
948
      $class = strtolower($weekdays[$i] . ' mini');
949
      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)) {
950
         $class .= ' empty';
951
         $variables = array(
952
          'curday' => $curday_date,
953
          'view' => $this->view,
954
        );
955

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

    
982
}