Projet

Général

Profil

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

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

1 85ad3d82 Assos Assos
<?php
2
3
/**
4
 * @file
5
 * Views style plugin for the Calendar module.
6
 *
7
 * The style plugin is responsible for setting global values needed by
8
 * the row plugin. It then gathers up an array of view results (as restructured
9
 * by the row plugin) and organizes them into year/month/day/time
10
 * arrays that the theme can use to display the results in a calendar.
11
 */
12
13
/**
14
 * Default style plugin to render an iCal feed.
15
 */
16
class calendar_plugin_style extends views_plugin_style {
17
18
  var $date_info;
19
  var $items;
20
  var $curday;
21
22
  function init(&$view, &$display, $options = NULL) {
23
    parent::init($view, $display, $options);
24
    if (empty($view->date_info)) {
25
      $this->date_info = new stdClass();
26
    }
27
    $this->date_info = &$this->view->date_info;
28
  }
29
30
  /**
31
   * Add custom option definitions.
32
   */
33
  function option_definition() {
34
    $options = parent::option_definition();
35
    $options['calendar_type'] = array('default' => 'month');
36
    $options['name_size'] = array('default' => 3);
37
    $options['mini'] = array('default' => 0);
38
    $options['with_weekno'] = array('default' => 0);
39
    $options['multiday_theme'] = array('default' => 1);
40
    $options['theme_style'] = array('default' => 1);
41
    $options['max_items'] = array('default' => 0);
42
    $options['max_items_behavior'] = array('default' => 'more');
43
    $options['groupby_times'] = array('default' => 'hour');
44
    $options['groupby_times_custom'] = array('default' => '');
45
    $options['groupby_field'] = array('default' => '');
46
    $options['multiday_hidden'] = array('default' => array());
47
    return $options;
48
  }
49
50
  function options_form(&$form, &$form_state) {
51
    parent::options_form($form, $form_state);
52
    $form['calendar_type'] = array(
53
      '#title' => t('Calendar type'),
54
      '#type' => 'select',
55
      '#description' => t('Select the calendar time period for this display.'),
56
      '#default_value' => $this->options['calendar_type'],
57
      '#options' => calendar_display_types(),
58
      );
59
    $form['mini'] = array(
60
      '#title' => t('Display as mini calendar'),
61
      '#default_value' => $this->options['mini'],
62
      '#type' => 'radios',
63
      '#options' => array(0 => t('No'), 1 => t('Yes')),
64
      '#description' => t('Display the mini style calendar, with no item details. Suitable for a calendar displayed in a block.'),
65
      '#dependency' => array('edit-style-options-calendar-type' => array('month')),
66
      );
67
    $form['name_size'] = array(
68
      '#title' => t('Calendar day of week names'),
69
      '#default_value' => $this->options['name_size'],
70
      '#type' => 'radios',
71
      '#options' => array(1 => t('First letter of name'), 2 => t('First two letters of name'), 3 => t('Abbreviated name'), 99 => t('Full name')),
72
      '#description' => t('The way day of week names should be displayed in a calendar.'),
73
      '#dependency' => array('edit-style-options-calendar-type' => array('month', 'week', 'year')),
74
      );
75
    $form['with_weekno'] = array(
76
      '#title' => t('Show week numbers'),
77
      '#default_value' => $this->options['with_weekno'],
78
      '#type' => 'radios',
79
      '#options' => array(0 => t('No'), 1 => t('Yes')),
80
      '#description' => t('Whether or not to show week numbers in the left column of calendar weeks and months.'),
81
      '#dependency' => array('edit-style-options-calendar-type' => array('month')),
82
      );
83
    $form['max_items'] = array(
84
      '#title' => t('Maximum items'),
85
      '#type' => 'select',
86
      '#options' => array(0 => t('Unlimited'), 1 => t('1 item'), 3 => t('3 items'), 5 => t('5 items'), 10 => t('10 items')),
87
      '#default_value' => $this->options['calendar_type'] != 'day' ? $this->options['max_items'] : 0,
88
      '#description' => t('Maximum number of items to show in calendar cells, used to keep the calendar from expanding to a huge size when there are lots of items in one day.'),
89
      '#dependency' => array('edit-style-options-calendar-type' => array('month')),
90
      );
91
    $form['max_items_behavior'] = array(
92
      '#title' => t('Too many items'),
93
      '#type' => 'select',
94
      '#options' => array('more' => t("Show maximum, add 'more' link"), 'hide' => t('Hide all, add link to day')),
95
      '#default_value' => $this->options['calendar_type'] != 'day' ? $this->options['max_items_behavior'] : 'more',
96
      '#description' => t('Behavior when there are more than the above number of items in a single day. When there more items than this limit, a link to the day view will be displayed.'),
97
      '#dependency' => array('edit-style-options-calendar-type' => array('month')),
98
      );
99
    $form['groupby_times'] = array(
100
      '#title' => t('Time grouping'),
101
      '#type' => 'select',
102
      '#default_value' => $this->options['groupby_times'],
103
      '#description' => t("Group items together into time periods based on their start time."),
104
      '#options' => array('' => t('None'), 'hour' => t('Hour'), 'half' => t('Half hour'), 'custom' => t('Custom')),
105
      '#dependency' => array('edit-style-options-calendar-type' => array('day', 'week')),
106
      );
107
    $form['groupby_times_custom'] = array(
108
      '#title' => t('Custom time grouping'),
109
      '#type' => 'textarea',
110
      '#default_value' => $this->options['groupby_times_custom'],
111
      '#description' => t("When choosing the 'custom' Time grouping option above, create custom time period groupings as a comma-separated list of 24-hour times in the format HH:MM:SS, like '00:00:00,08:00:00,18:00:00'. Be sure to start with '00:00:00'. All items after the last time will go in the final group."),
112
      '#dependency' => array('edit-style-options-groupby-times' => array('custom')),
113
      );
114
    $form['theme_style'] = array(
115
      '#title' => t('Overlapping time style'),
116
      '#default_value' => $this->options['theme_style'],
117
      '#type' => 'select',
118
      '#options' => array(0 => t('Do not display overlapping items'), 1 => t('Display overlapping items, with scrolling'), 2 => t('Display overlapping items, no scrolling')),
119
      '#description' => t('Select whether calendar items are displayed as overlapping items. Use scrolling to shrink the window and focus on the selected items, or omit scrolling to display the whole day. This only works if hour or half hour time grouping is chosen!'),
120
      '#dependency' => array('edit-style-options-calendar-type' => array('day', 'week')),
121
      );
122
123
    // Create a list of fields that are available for grouping,
124
    $field_options = array();
125
    $fields = $this->display->handler->get_option('fields');
126
    foreach ($fields as $field_name => $field) {
127
      $field_options[$field_name] = $field['field'];
128
    }
129
    $form['groupby_field'] = array(
130
      '#title' => t('Field grouping'),
131
      '#type' => 'select',
132
      '#default_value' => $this->options['groupby_field'],
133
      '#description' => t("Optionally group items into columns by a field value, for instance select the content type to show items for each content type in their own column, or use a location field to organize items into columns by location. NOTE! This is incompatible with the overlapping style option."),
134
      '#options' => array('' => '') + $field_options,
135
      '#dependency' => array('edit-style-options-calendar-type' => array('day')),
136
      );
137
    $form['multiday_theme'] = array(
138
      '#title' => t('Multi-day style'),
139
      '#default_value' => $this->options['multiday_theme'],
140
      '#type' => 'select',
141
      '#options' => array(0 => t('Display multi-day item as a single column'), 1 => t('Display multi-day item as a multiple column row')),
142
      '#description' => t('If selected, items which span multiple days will displayed as a multi-column row.  If not selected, items will be displayed as an individual column.'),
143
      '#dependency' => array('edit-style-options-calendar-type' => array('month', 'week')),
144
      );
145
    $form['multiday_hidden'] = array(
146
      '#title' => t('Fields to hide in Multi-day rows'),
147
      '#default_value' => $this->options['multiday_hidden'],
148
      '#type' => 'checkboxes',
149
      '#options' => $field_options,
150
      '#description' => t('Choose fields to hide when displayed in multi-day rows. Usually you only want to see the title or Colorbox selector in multi-day rows and would hide all other fields.'),
151
      '#dependency' => array('edit-style-options-calendar-type' => array('month', 'week', 'day')),
152
      );
153
  }
154
155
  function options_validate(&$form, &$form_state) {
156
    $values = $form_state['values']['style_options'];
157
    if ($values['groupby_times'] == 'custom' && empty($values['groupby_times_custom'])) {
158
      form_set_error('style_options][groupby_times_custom', t('Custom groupby times cannot be empty.'));
159
    }
160
    if (!empty($values['theme_style']) && (empty($values['groupby_times']) || !in_array($values['groupby_times'], array('hour', 'half')))) {
161
      form_set_error('style_options][theme_style', t('Overlapping items only work with hour or half hour groupby times.'));
162
    }
163
    if (!empty($values['theme_style']) && !empty($values['groupby_field'])) {
164
      form_set_error('style_options][theme_style', t('You cannot use overlapping items and also try to group by a field value.'));
165
    }
166
    if ($values['groupby_times'] != 'custom') {
167
      form_set_value($form['groupby_times_custom'], NULL, $form_state);
168
    }
169
170
  }
171
172
  function options_submit(&$form, &$form_state, &$options = array()) {
173
    $form_state['values']['style_options']['multiday_hidden'] = array_filter($form_state['values']['style_options']['multiday_hidden']);
174
  }
175
176
  /**
177
   * Helper function to find the date argument handler for this view.
178
   */
179
  function date_argument_handler() {
180
    $i = 0;
181
    foreach ($this->view->argument as $name => $handler) {
182
      if (date_views_handler_is_date($handler, 'argument')) {
183
        $this->date_info->date_arg_pos = $i;
184
        return $handler;
185
      }
186
      $i++;
187
    }
188
    return FALSE;
189
  }
190
191
  /**
192
   * Inspect argument and view information to see which calendar
193
   * period we should show. The argument tells us what to use
194
   * if there is no value, the view args tell us what to use
195
   * if there are values.
196
   */
197
  function granularity() {
198
199
    if (!$handler = $this->date_argument_handler()) {
200
      return 'month';
201
    }
202
    $default_granularity = !empty($handler) && !empty($handler->granularity) ? $handler->granularity : 'month';
203
    $wildcard = !empty($handler) ? $handler->options['exception']['value'] : '';
204
    $argument = $handler->argument;
205
206
    // TODO Anything else we need to do for 'all' arguments?
207
    if ($argument == $wildcard) {
208
      $this->view_granularity = $default_granularity;
209
    }
210
    elseif (!empty($argument)) {
211
      module_load_include('inc', 'date_api', 'date_api_sql');
212
213
      $date_handler = new date_sql_handler();
214
      $this->view_granularity = $date_handler->arg_granularity($argument);
215
    }
216
    else {
217
      $this->view_granularity = $default_granularity;
218
    }
219
    return $this->view_granularity;
220
  }
221
222
  function has_calendar_row_plugin() {
223
    return $this->row_plugin instanceof calendar_plugin_row || $this->row_plugin instanceof calendar_plugin_row_node;
224
  }
225
226
  function render() {
227
    if (empty($this->row_plugin) || !$this->has_calendar_row_plugin()) {
228
      debug('calendar_plugin_style: The calendar row plugin is required when using the calendar style, but it is missing.');
229
      return;
230
    }
231
    if (!$argument = $this->date_argument_handler()) {
232
      debug('calendar_plugin_style: A date argument is required when using the calendar style, but it is missing or is not using the default date.');
233
      return;
234
    }
235
236
    // There are date arguments that have not been added by Date Views.
237
    // They will be missing the information we would need to render the field.
238
    if (empty($argument->min_date)) {
239
      return;
240
    }
241
242
    // Add information from the date argument to the view.
243
    $this->date_info->granularity = $this->granularity();
244
    $this->date_info->calendar_type = $this->options['calendar_type'];
245
    $this->date_info->date_arg = $argument->argument;
246
    $this->date_info->year = date_format($argument->min_date, 'Y');
247
    $this->date_info->month = date_format($argument->min_date, 'n');;
248
    $this->date_info->day = date_format($argument->min_date, 'j');
249
    $this->date_info->week = date_week(date_format($argument->min_date, DATE_FORMAT_DATE));
250
    $this->date_info->date_range = $argument->date_range;
251
    $this->date_info->min_date = $argument->min_date;
252
    $this->date_info->max_date = $argument->max_date;
253
    $this->date_info->limit = $argument->limit;
254
    $this->date_info->url = $this->view->get_url();
255
    $this->date_info->min_date_date = date_format($this->date_info->min_date, DATE_FORMAT_DATE);
256
    $this->date_info->max_date_date = date_format($this->date_info->max_date, DATE_FORMAT_DATE);
257
    $this->date_info->forbid = isset($argument->forbid) ? $argument->forbid : FALSE;
258
259
    // Add calendar style information to the view.
260
    $this->date_info->calendar_popup = $this->display->handler->get_option('calendar_popup');
261
    $this->date_info->style_name_size = $this->options['name_size'];
262
    $this->date_info->mini = $this->options['mini'];
263
    $this->date_info->style_with_weekno = $this->options['with_weekno'];
264
    $this->date_info->style_multiday_theme = $this->options['multiday_theme'];
265
    $this->date_info->style_theme_style = $this->options['theme_style'];
266
    $this->date_info->style_max_items = $this->options['max_items'];
267
    $this->date_info->style_max_items_behavior = $this->options['max_items_behavior'];
268
    if (!empty($this->options['groupby_times_custom'])) {
269
      $this->date_info->style_groupby_times = explode(',', $this->options['groupby_times_custom']);
270
    }
271
    else {
272
      $this->date_info->style_groupby_times = calendar_groupby_times($this->options['groupby_times']);
273
    }
274
    $this->date_info->style_groupby_field = $this->options['groupby_field'];
275
276
    // TODO make this an option setting.
277
    $this->date_info->style_show_empty_times = !empty($this->options['groupby_times_custom']) ? TRUE : FALSE;
278
279
    // Set up parameters for the current view that can be used by the row plugin.
280
    $display_timezone = date_timezone_get($this->date_info->min_date);
281
    $this->date_info->display_timezone = $display_timezone;
282
    $this->date_info->display_timezone_name = timezone_name_get($display_timezone);
283
    $date = clone($this->date_info->min_date);
284
    date_timezone_set($date, $display_timezone);
285
    $this->date_info->min_zone_string = date_format($date, DATE_FORMAT_DATE);
286
    $date = clone($this->date_info->max_date);
287
    date_timezone_set($date, $display_timezone);
288
    $this->date_info->max_zone_string = date_format($date, DATE_FORMAT_DATE);
289
290
    // Let views render fields the way it thinks they should look before we start massaging them.
291
    $this->render_fields($this->view->result);
292
293
    // Invoke the row plugin to massage each result row into calendar items.
294
    // Gather the row items into an array grouped by date and time.
295
    $items = array();
296
    foreach ($this->view->result as $row_index => $row) {
297
      $this->view->row_index = $row_index;
298
      $rows = $this->row_plugin->render($row);
299
      foreach ($rows as $key => $item) {
300
        $item->granularity = $this->date_info->granularity;
301
        $rendered_fields = array();
302
        $item_start = date_format($item->calendar_start_date, DATE_FORMAT_DATE);
303
        $item_end = date_format($item->calendar_end_date, DATE_FORMAT_DATE);
304
        $time_start = date_format($item->calendar_start_date, 'H:i:s');
305
        $item->rendered_fields = $this->rendered_fields[$row_index];
306
        $items[$item_start][$time_start][] = $item;
307
      }
308
    }
309
310
    ksort($items);
311
312
    $rows = array();
313
    $this->curday = clone($this->date_info->min_date);
314
    $this->items = $items;
315
316
    // Retrieve the results array using a the right method for the granularity of the display.
317
    switch ($this->options['calendar_type']) {
318
      case 'year':
319
        $rows = array();
320
        $this->view->date_info->mini = TRUE;
321
        for ($i = 1; $i <= 12; $i++) {
322
          $rows[$i] = $this->calendar_build_mini_month();
323
        }
324
        $this->view->date_info->mini = FALSE;
325
        break;
326
      case 'month':
327
        $rows = !empty($this->date_info->mini) ? $this->calendar_build_mini_month() : $this->calendar_build_month();
328
        break;
329
      case 'day':
330
        $rows = $this->calendar_build_day();
331
        break;
332
      case 'week':
333
        $rows = $this->calendar_build_week();
334
        // Merge the day names in as the first row.
335
        $rows = array_merge(array(calendar_week_header($this->view)), $rows);
336
        break;
337
    }
338
339
    // Send the sorted rows to the right theme for this type of calendar.
340
    $this->definition['theme'] = 'calendar_' . $this->options['calendar_type'];
341
342
    // Adjust the theme to match the currently selected default.
343
    // Only the month view needs the special 'mini' class,
344
    // which is used to retrieve a different, more compact, theme.
345
    if ($this->options['calendar_type'] == 'month' && !empty($this->view->date_info->mini)) {
346
      $this->definition['theme'] = 'calendar_mini';
347
    }
348
    // If the overlap option was selected, choose the overlap version of the theme.
349
    elseif (in_array($this->options['calendar_type'], array('week', 'day')) && !empty($this->options['multiday_theme']) && !empty($this->options['theme_style'])) {
350
      $this->definition['theme'] .= '_overlap';
351
    }
352
353
    $output = theme($this->theme_functions(),
354
      array(
355
        'view' => $this->view,
356
        'options' => $this->options,
357
        'rows' => $rows
358
      ));
359
    unset($this->view->row_index);
360
    return $output;
361
  }
362
363
  /**
364
   * Build one month.
365
   */
366
  function calendar_build_month() {
367
    $translated_days = date_week_days_ordered(date_week_days(TRUE));
368
    $month = date_format($this->curday, 'n');
369
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
370
    $weekdays = calendar_untranslated_days($this->items, $this->view);
371
    date_modify($this->curday, '-' . strval(date_format($this->curday, 'j')-1) . ' days');
372
    $rows = array();
373
    do {
374
      $init_day = clone($this->curday);
375
      $today = date_format(date_now(date_default_timezone()), DATE_FORMAT_DATE);
376
      $month = date_format($this->curday, 'n');
377
      $week = date_week($curday_date);
378
      $first_day = variable_get('date_first_day', 0);
379
      $week_rows = $this->calendar_build_week(TRUE);
380
      $multiday_buckets = $week_rows['multiday_buckets'];
381
      $singleday_buckets = $week_rows['singleday_buckets'];
382
      $total_rows = $week_rows['total_rows'];
383
384
      // Theme each row
385
      $output = "";
386
      $final_day = clone($this->curday);
387
388
      $iehint = 0;
389
      $max_multirow_cnt = 0;
390
      $max_singlerow_cnt = 0;
391
392
      for ($i = 0; $i < intval($total_rows + 1); $i++) {
393
        $inner = "";
394
395
        // If we're displaying the week number, add it as the
396
        // first cell in the week.
397
        if ($i == 0 && !empty($this->date_info->style_with_weekno) && !in_array($this->date_info->granularity, array('day', 'week'))) {
398
          $path = calendar_granularity_path($this->view, 'week');
399
          if (!empty($path)) {
400
            $url = $path . '/' . $this->date_info->year . '-W' . $week;
401
            $weekno = l($week, $url, array('query' => !empty($this->date_info->append) ? $this->date_info->append : ''));
402
          }
403
          else {
404
            // Do not link week numbers, if Week views are disabled.
405
            $weekno = $week;
406
          }
407
          $item = array(
408
            'entry' => $weekno,
409
            'colspan' => 1,
410
            'rowspan' => $total_rows + 1,
411
            'id' => $this->view->name . '-weekno-' . $curday_date,
412
            'class' => 'week',
413
          );
414
          $inner .= theme('calendar_month_col', array('item' => $item));
415
        }
416
417
        $this->curday = clone($init_day);
418
419
        // move backwards to the first day of the week
420
        $day_wday = date_format($this->curday, 'w');
421
        date_modify($this->curday, '-' . strval((7 + $day_wday - $first_day) % 7) . ' days');
422
423
        for ( $wday = 0; $wday < 7; $wday++) {
424
425
          $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
426
          $class = strtolower($weekdays[$wday]);
427
          $item = NULL;
428
          $in_month = !($curday_date < $this->date_info->min_date_date || $curday_date > $this->date_info->max_date_date || date_format($this->curday, 'n') != $month);
429
430
          // Add the datebox
431
          if ($i == 0) {
432
            $variables = array(
433
              'date' => $curday_date,
434
              'view' => $this->view,
435
              'items' => $this->items,
436
              'selected' => $in_month ? count($multiday_buckets[$wday]) + count($singleday_buckets[$wday]) : FALSE,
437
            );
438
            $item = array(
439
              'entry' => theme('calendar_datebox', $variables),
440
              'colspan' => 1,
441
              'rowspan' => 1,
442
              'class' => 'date-box',
443
              'date' => $curday_date,
444
              'id' => $this->view->name . '-' . $curday_date . '-date-box',
445
              'header_id' => $translated_days[$wday],
446
              'day_of_month' => $this->curday->format('j'),
447
            );
448
            $item['class'] .= ($curday_date == $today && $in_month ? ' today' : '') .
449
              ($curday_date < $today ? ' past' : '') .
450
              ($curday_date > $today ? ' future' : '');
451
          }
452
          else {
453
            $index = $i - 1;
454
            $multi_count = count($multiday_buckets[$wday]);
455
456
            // Process multiday buckets first.  If there is a multiday-bucket item in this row...
457
            if ($index < $multi_count) {
458
              // If this item is filled with either a blank or an entry...
459
              if ($multiday_buckets[$wday][$index]['filled']) {
460
461
                // Add item and add class
462
                $item = $multiday_buckets[$wday][$index];
463
                $item['class'] =  'multi-day';
464
                $item['date'] = $curday_date;
465
466
                // Is this an entry?
467
                if (!$multiday_buckets[$wday][$index]['avail']) {
468
469
                  // If the item either starts or ends on today,
470
                  // then add tags so we can style the borders
471
                  if ($curday_date == $today && $in_month) {
472
                    $item['class'] .=  ' starts-today';
473
                  }
474
475
                  // Calculate on which day of this week this item ends on..
476
                  $end_day = clone($this->curday);
477
                  $span = $item['colspan'] - 1;
478
                  date_modify($end_day, '+' . $span . ' day');
479
                  $endday_date = date_format($end_day, DATE_FORMAT_DATE);
480
481
                  // If it ends today, add class
482
                  if ($endday_date == $today && $in_month) {
483
                    $item['class'] .=  ' ends-today';
484
                  }
485
                }
486
              }
487
488
              // If this is an actual entry, add classes regarding the state of the
489
              // item
490
              if ($multiday_buckets[$wday][$index]['avail']) {
491
                $item['class'] .= ' ' . $wday . ' ' . $index . ' no-entry ' . ($curday_date == $today && $in_month ? ' today' : '') .
492
                  ($curday_date < $today ? ' past' : '') .
493
                  ($curday_date > $today ? ' future' : '');
494
              }
495
496
            // Else, process the single day bucket - we only do this once per day
497
            }
498
            elseif ($index == $multi_count) {
499
              $single_day_cnt = 0;
500
              // If it's empty, add class
501
              if (count($singleday_buckets[$wday]) == 0) {
502
                $single_days = "&nbsp;";
503
                if ($max_multirow_cnt == 0 ) {
504
                  $class = ($multi_count > 0 ) ? 'single-day no-entry noentry-multi-day' : 'single-day no-entry';
505
                }
506
                else {
507
                  $class = 'single-day';
508
                }
509
              }
510
              else {
511
                $single_days = "";
512
                foreach ($singleday_buckets[$wday] as $day) {
513
                  foreach ($day as $event) {
514
                    $single_day_cnt++;
515
                    $single_days .= (isset($event['more_link'])) ? '<div class="calendar-more">' . $event['entry'] . '</div>' : $event['entry'];
516
                  }
517
                }
518
                $class = 'single-day';
519
              }
520
521
              $rowspan = $total_rows - $index;
522
              // Add item...
523
              $item = array(
524
                'entry' => $single_days,
525
                'colspan' => 1,
526
                'rowspan' => $rowspan,
527
                'class' => $class,
528
                'date' => $curday_date,
529
                'id' => $this->view->name . '-' . $curday_date . '-' . $index,
530
                'header_id' => $translated_days[$wday],
531
                'day_of_month' => $this->curday->format('j'),
532
              );
533
534
              // Hack for ie to help it properly space single day rows
535
              if ($rowspan > 1 && $in_month && $single_day_cnt > 0) {
536
                $max_multirow_cnt = max($max_multirow_cnt, $single_day_cnt);
537
              }
538
              else {
539
                $max_singlerow_cnt = max($max_singlerow_cnt, $single_day_cnt);
540
              }
541
542
              // If the singlerow is bigger than the multi-row, then null out
543
              // ieheight - I'm estimating that a single row is twice the size of
544
              // multi-row.  This is really the best that can be done with ie
545
              if ($max_singlerow_cnt >= $max_multirow_cnt || $max_multirow_cnt <= $multi_count / 2 ) {
546
                $iehint = 0;
547
              }
548
              elseif ($rowspan > 1 && $in_month && $single_day_cnt > 0) {
549
                $iehint = max($iehint, $rowspan - 1); // How many rows to adjust for?
550
              }
551
552
              // Set the class
553
              $item['class'] .= ($curday_date == $today && $in_month ? ' today' : '') .
554
                ($curday_date < $today ? ' past' : '') .
555
                ($curday_date > $today ? ' future' : '');
556
            }
557
          }
558
559
          // If there isn't an item, then add empty class
560
          if ($item != NULL) {
561
            if (!$in_month) {
562
              $item['class'] .= ' empty';
563
            }
564
            // Style this entry - it will be a <td>.
565
            $inner .= theme('calendar_month_col', array('item' => $item));
566
          }
567
568
          date_modify($this->curday, '+1 day');
569
        }
570
571
        if ($i == 0) {
572
          $output .= theme('calendar_month_row', array(
573
            'inner' => $inner,
574
            'class' => 'date-box',
575
            'iehint' => $iehint,
576
          ));
577
        }
578
        elseif ($i == $total_rows) {
579
          $output .= theme('calendar_month_row', array(
580
            'inner' => $inner,
581
            'class' => 'single-day',
582
            'iehint' => $iehint,
583
          ));
584
          $iehint = 0;
585
          $max_singlerow_cnt = 0;
586
          $max_multirow_cnt = 0;
587
        }
588
        else {
589
          // Style all the columns into a row
590
          $output .= theme('calendar_month_row', array(
591
            'inner' => $inner,
592
            'class' => 'multi-day',
593
            'iehint' => 0,
594
          ));
595
        }
596
597
      } // End foreach
598
599
      $this->curday = $final_day;
600
601
      // Add the row into the row array....
602
      $rows[] = array('data' => $output);
603
604
      $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
605
      $curday_month = date_format($this->curday, 'n');
606
    } while ($curday_month == $month && $curday_date <= $this->date_info->max_date_date);
607
    // Merge the day names in as the first row.
608
    $rows = array_merge(array(calendar_week_header($this->view)), $rows);
609
    return $rows;
610
  }
611
612
  /**
613
   * Build one week row.
614
   */
615
  function calendar_build_week($check_month = FALSE) {
616
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
617
    $weekdays = calendar_untranslated_days($this->items, $this->view);
618
    $month = date_format($this->curday, 'n');
619
    $first_day = variable_get('date_first_day', 0);
620
621
    // Set up buckets
622
    $total_rows = 0;
623
    $multiday_buckets = array( array(), array(), array(), array(), array(), array(), array());
624
    $singleday_buckets = array( array(), array(), array(), array(), array(), array(), array());
625
626
    // move backwards to the first day of the week
627
    $day_wday = date_format($this->curday, 'w');
628
    date_modify($this->curday, '-' . strval((7 + $day_wday - $first_day) % 7) . ' days');
629
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
630
631
    for ($i = 0; $i < 7; $i++) {
632
      if ($check_month && ($curday_date < $this->date_info->min_date_date || $curday_date > $this->date_info->max_date_date || date_format($this->curday, 'n') != $month)) {
633
        $class = strtolower($weekdays[$i]) . ' empty';
634
        $singleday_buckets[$i][][] = array(
635
          'entry' => theme('calendar_empty_day', array(
636
             'curday' => $curday_date,
637
             'view' => $this->view,
638
          )),
639
          'item' => NULL
640
        );
641
      }
642
      else {
643
        $this->calendar_build_week_day($i, $multiday_buckets, $singleday_buckets);
644
      }
645
      $total_rows = max(count($multiday_buckets[$i]) + 1, $total_rows);
646
      date_modify($this->curday, '+1 day');
647
      $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
648
    }
649
650
    $rows = array(
651
      'multiday_buckets' => $multiday_buckets,
652
      'singleday_buckets' => $singleday_buckets,
653
      'total_rows' => $total_rows);
654
    return $rows;
655
  }
656
657
  /**
658
   * Build the contents of a single day for the $rows results.
659
   */
660
  function calendar_build_week_day($wday, &$multiday_buckets, &$singleday_buckets) {
661
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
662
    $max_events = $this->date_info->calendar_type == 'month' && !empty($this->date_info->style_max_items) ? $this->date_info->style_max_items : 0;
663
    $hide = !empty($this->date_info->style_max_items_behavior) ? ($this->date_info->style_max_items_behavior == 'hide') : FALSE;
664
    $multiday_theme = !empty($this->date_info->style_multiday_theme) && $this->date_info->style_multiday_theme == '1';
665
    $first_day = variable_get('date_first_day', 0);
666
    $cur_cnt = 0;
667
    $total_cnt = 0;
668
    $ids = array();
669
670
    // If we are hiding, count before processing further
671
    if ($max_events != CALENDAR_SHOW_ALL) {
672
      foreach ($this->items as $date => $day) {
673
        if ($date == $curday_date) {
674
          foreach ($day as $time => $hour) {
675
            foreach ($hour as $key => $item) {
676
              $total_cnt++;
677
              $ids[] = $item->date_id;
678
            }
679
          }
680
        }
681
      }
682
    }
683
684
    // If we haven't already exceeded the max or we'll showing all, then process the items
685
    if ($max_events == CALENDAR_SHOW_ALL || !$hide || $total_cnt < $max_events) {
686
      // Count currently filled items
687
      foreach ($multiday_buckets[$wday] as $bucket) {
688
        if (!$bucket['avail']) {
689
          $cur_cnt++;
690
        }
691
      }
692
      foreach ($this->items as $date => $day) {
693
        if ($date == $curday_date) {
694
          ksort($day);
695
          foreach ($day as $time => $hour) {
696
            foreach ($hour as $key => $item) {
697
              $all_day = $item->calendar_all_day;
698
699
              // Parse out date part
700
              $start_ydate = date_format($item->date_start, DATE_FORMAT_DATE);
701
              $end_ydate = date_format($item->date_end, DATE_FORMAT_DATE);
702
              $cur_ydate = date_format($this->curday, DATE_FORMAT_DATE);
703
704
              $is_multi_day = ($start_ydate < $cur_ydate || $end_ydate > $cur_ydate);
705
706
              // Does this event span multi-days?
707
              if ($multiday_theme && ($is_multi_day || $all_day)) {
708
709
                // Remove multiday items from the total count. We can't hide them or they will break.
710
                $total_cnt--;
711
712
                // If this the first day of the week, or is the start date of the multi-day event,
713
                // then record this item, otherwise skip over
714
                $day_no = date_format($this->curday, 'd');
715
                if ($wday == 0 || $start_ydate == $cur_ydate || ($this->date_info->granularity == 'month' && $day_no == 1) || ($all_day && !$is_multi_day)) {
716
                  // Calculate the colspan for this event
717
718
                  // If the last day of this event exceeds the end of the current month or week,
719
                  // truncate the remaining days
720
                  $diff =  $this->curday->difference($this->date_info->max_date, 'days');
721
                  $remaining_days = ($this->date_info->granularity == 'month') ? min(6 - $wday, $diff) : $diff - 1;
722
                  // The bucket_cnt defines the colspan.  colspan = bucket_cnt + 1
723
                  $days = $this->curday->difference($item->date_end, 'days');
724
                  $bucket_cnt = max(0, min($days, $remaining_days));
725
726
                  // See if there is an available slot to add an event.  This will allow
727
                  // an event to precede a row filled up by a previous day event
728
                  $avail = FALSE;
729
                  $bucket_index = count($multiday_buckets[$wday]);
730
                  for ($i = 0; $i < $bucket_index; $i++) {
731
                    if ($multiday_buckets[$wday][$i]['avail']) {
732
                      $bucket_index = $i;
733
                      break;
734
                    }
735
                  }
736
737
                  // Add continuation attributes
738
                  $item->continuation =  ($item->date_start < $this->curday);
739
                  $item->continues = ( $days > $bucket_cnt );
740
                  $item->is_multi_day = TRUE;
741
742
                  // Assign the item to the available bucket
743
                  $multiday_buckets[$wday][$bucket_index] = array(
744
                    'colspan' => $bucket_cnt + 1,
745
                    'rowspan' => 1,
746
                    'filled' => TRUE,
747
                    'avail' => FALSE,
748
                    'all_day' => $all_day,
749
                    'item' => $item,
750
                    'wday' => $wday,
751
                    'entry' => theme('calendar_item', array('view' => $this->view, 'rendered_fields' => $item->rendered_fields, 'item' => $item)),
752
                  );
753
754
                  // Block out empty buckets for the next days in this event for this week
755
                  for ($i = 0; $i < $bucket_cnt; $i++) {
756
                    $bucket = &$multiday_buckets[$i + $wday + 1];
757
                    $bucket_row_count = count($bucket);
758
                    $row_diff = $bucket_index - $bucket_row_count;
759
760
                    // Fill up the preceding buckets - these are available for future
761
                    // events
762
                    for ( $j = 0; $j < $row_diff; $j++) {
763
                      $bucket[($bucket_row_count + $j) ] = array(
764
                        'entry' => '&nbsp;',
765
                        'colspan' => 1,
766
                        'rowspan' => 1,
767
                        'filled' => TRUE,
768
                        'avail' => TRUE,
769
                        'wday' => $wday,
770
                        'item' => NULL
771
                      );
772
                    }
773
                    $bucket[$bucket_index] = array(
774
                      'filled' => FALSE,
775
                      'avail' => FALSE
776
                    );
777
                  }
778
                }
779
              }
780
              elseif ($max_events == CALENDAR_SHOW_ALL || $cur_cnt < $max_events) {
781
                $cur_cnt++;
782
                // Assign to single day bucket
783
                $singleday_buckets[$wday][$time][] = array(
784
                  'entry' => theme('calendar_item', array('view' => $this->view, 'rendered_fields' => $item->rendered_fields, 'item' => $item)),
785
                  'item' => $item,
786
                  'colspan' => 1,
787
                  'rowspan' => 1,
788
                  'filled' => TRUE,
789
                  'avail' => FALSE,
790
                  'wday' => $wday,
791
                );
792
              }
793
794
            }
795
          }
796
        }
797
      }
798
    }
799
800
    // Add a more link if necessary
801
    if ($max_events != CALENDAR_SHOW_ALL && $total_cnt > 0 && $cur_cnt < $total_cnt) {
802
      $entry = theme('calendar_' . $this->date_info->calendar_type . '_multiple_entity', array(
803
          'curday' => $curday_date,
804
          'count' => $total_cnt,
805
          'view' => $this->view,
806
          'ids' => $ids,
807
        ));
808
      if (!empty($entry)) {
809
        $singleday_buckets[$wday][][] = array(
810
          'entry' => $entry,
811
          'more_link' => TRUE,
812
          'item' => NULL
813
        );
814
      }
815
    }
816
  }
817
818
  /**
819
   * Build the contents of a single day for the $rows results.
820
   */
821
  function calendar_build_day() {
822
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
823
    $selected = FALSE;
824
    $max_events = !empty($this->date_info->style_max_items) ? $this->date_info->style_max_items : 0;
825
    $ids = array();
826
    $inner = array();
827
    $all_day = array();
828
    $empty = '';
829
    $link = '';
830
    $count = 0;
831
    foreach ($this->items as $date => $day) {
832
      if ($date == $curday_date) {
833
        $count = 0;
834
        $selected = TRUE;
835
        ksort($day);
836
        foreach ($day as $time => $hour) {
837
          foreach ($hour as $key => $item) {
838
            $count++;
839
            if (isset($item->type)) {
840
              $ids[$item->type] = $item;
841
            }
842
            if (empty($this->date_info->mini) && ($max_events == CALENDAR_SHOW_ALL || $count <= $max_events || ($count > 0 && $max_events == CALENDAR_HIDE_ALL))) {
843
              if ($item->calendar_all_day) {
844
                $item->is_multi_day = TRUE;
845
                $all_day[] = $item;
846
              }
847
              else {
848
                $key = date_format($item->calendar_start_date, 'H:i:s');
849
                $inner[$key][] = $item;
850
              }
851
            }
852
          }
853
        }
854
      }
855
    }
856
    ksort($inner);
857
858
    if (empty($inner) && empty($all_day)) {
859
      $empty = theme('calendar_empty_day', array('curday' => $curday_date, 'view' => $this->view));
860
    }
861
    // We have hidden events on this day, use the theme('calendar_multiple_') to show a link.
862
    if ($max_events != CALENDAR_SHOW_ALL && $count > 0 && $count > $max_events && $this->date_info->calendar_type != 'day' && !$this->date_info->mini) {
863
      if ($this->date_info->style_max_items_behavior == 'hide' || $max_events == CALENDAR_HIDE_ALL) {
864
        $all_day = array();
865
        $inner = array();
866
      }
867
      $link = theme('calendar_' . $this->date_info->calendar_type . '_multiple_node', array(
868
        'curday' => $curday_date,
869
        'count' => $count,
870
        'view' => $this->view,
871
        'ids' => $ids,
872
      ));
873
    }
874
875
    $content = array(
876
      'date' => $curday_date,
877
      'datebox' => theme('calendar_datebox', array(
878
         'date' => $curday_date,
879
         'view' => $this->view,
880
         'items' => $this->items,
881
         'selected' => $selected,
882
      )),
883
      'empty' => $empty,
884
      'link' => $link,
885
      'all_day' => $all_day,
886
      'items' => $inner,
887
      );
888
    return $content;
889
  }
890
891
  /**
892
   * Build one mini month.
893
   */
894
  function calendar_build_mini_month() {
895
    $month = date_format($this->curday, 'n');
896
    date_modify($this->curday, '-' . strval(date_format($this->curday, 'j')-1) . ' days');
897
    $rows = array();
898
    do {
899
      $rows = array_merge($rows, $this->calendar_build_mini_week());
900
      $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
901
      $curday_month = date_format($this->curday, 'n');
902
    } while ($curday_month == $month && $curday_date <= $this->date_info->max_date_date);
903
    // Merge the day names in as the first row.
904
    $rows = array_merge(array(calendar_week_header($this->view)), $rows);
905
    return $rows;
906
  }
907
908
  /**
909
   * Build one week row.
910
   */
911
  function calendar_build_mini_week($check_month = TRUE) {
912
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
913
    $weekdays = calendar_untranslated_days($this->items, $this->view);
914
    $today = date_format(date_now(date_default_timezone()), DATE_FORMAT_DATE);
915
    $month = date_format($this->curday, 'n');
916
    $week = date_week($curday_date);
917
    $first_day = variable_get('date_first_day', 0);
918
    // move backwards to the first day of the week
919
    $day_wday = date_format($this->curday, 'w');
920
    date_modify($this->curday, '-' . strval((7 + $day_wday - $first_day) % 7) . ' days');
921
    $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
922
923
    for ($i = 0; $i < 7; $i++) {
924
      $curday_date = date_format($this->curday, DATE_FORMAT_DATE);
925
      $class = strtolower($weekdays[$i] . ' mini');
926
      if ($check_month && ($curday_date < $this->date_info->min_date_date || $curday_date > $this->date_info->max_date_date || date_format($this->curday, 'n') != $month)) {
927
         $class .= ' empty';
928
         $variables = array(
929
          'curday' => $curday_date,
930
          'view' => $this->view,
931
        );
932
933
        $content = array(
934
          'date' => '',
935
          'datebox' => '',
936
          'empty' => theme('calendar_empty_day', $variables),
937
          'link' => '',
938
          'all_day' => array(),
939
          'items' => array(),
940
          );
941
      }
942
      else {
943
        $content = $this->calendar_build_day();
944
        $class .= ($curday_date == $today ? ' today' : '') .
945
          ($curday_date < $today ? ' past' : '') .
946
          ($curday_date > $today ? ' future' : '') .
947
          (empty($this->items[$curday_date]) ? ' has-no-events' : ' has-events');
948
      }
949
      $rows[$week][] = array(
950
        'data' => $content,
951
        'class' => $class,
952
        'id' => $this->view->name . '-' . $curday_date,
953
      );
954
      date_modify($this->curday, '+1 day');
955
    }
956
    return $rows;
957
  }
958
959
}