1: <?php
2: 3: 4: 5: 6: 7: 8:
9: class Kronolith_View_Day extends Kronolith_Day
10: {
11: public $all_day_events = array();
12: public $all_day_rowspan = array();
13: public $all_day_maxrowspan = 0;
14: public $span = array();
15: public $totalspan = 0;
16: public $sidebyside = false;
17: public $events = array();
18: protected $_event_matrix = array();
19: protected $_parsed = false;
20: protected $_currentCalendars = array();
21: protected $_first;
22: protected $_last;
23:
24: 25: 26: 27: 28: 29: 30:
31: public function __construct(Horde_Date $date, array $events = null)
32: {
33: parent::__construct($date->month, $date->mday, $date->year);
34:
35: $this->sidebyside = $GLOBALS['prefs']->getValue('show_shared_side_by_side');
36: if ($this->sidebyside) {
37: $allCalendars = Kronolith::listInternalCalendars();
38: foreach ($GLOBALS['display_calendars'] as $cid) {
39: $this->_currentCalendars[$cid] = $allCalendars[$cid];
40: $this->all_day_events[$cid] = array();
41: }
42: } else {
43: $this->_currentCalendars = array(0);
44: }
45:
46: if ($events === null) {
47: try {
48: $events = Kronolith::listEvents(
49: $this,
50: new Horde_Date(array('year' => $this->year,
51: 'month' => $this->month,
52: 'mday' => $this->mday)));
53: $this->events = array_shift($events);
54: } catch (Exception $e) {
55: $GLOBALS['notification']->push($e, 'horde.error');
56: $this->events = array();
57: }
58: } else {
59: $this->events = $events;
60: }
61:
62: if (!is_array($this->events)) {
63: $this->events = array();
64: }
65: }
66:
67: public function html()
68: {
69: global $prefs;
70:
71: if (!$this->_parsed) {
72: $this->parse();
73: }
74:
75: $started = false;
76: $first_row = true;
77: $addLinks = Kronolith::getDefaultCalendar(Horde_Perms::EDIT) &&
78: ($GLOBALS['injector']->getInstance('Horde_Core_Perms')->hasAppPermission('max_events') === true ||
79: $GLOBALS['injector']->getInstance('Horde_Core_Perms')->hasAppPermission('max_events') > Kronolith::countEvents());
80: $showLocation = Kronolith::viewShowLocation();
81: $showTime = Kronolith::viewShowTime();
82:
83: require KRONOLITH_TEMPLATES . '/day/head.inc';
84: if ($this->sidebyside) {
85: require KRONOLITH_TEMPLATES . '/day/head_side_by_side.inc';
86: }
87: echo '<tbody>';
88:
89: if ($addLinks) {
90: $newEventUrl = Horde::url('new.php')
91: ->add(array('datetime' => sprintf($this->dateString() . '%02d%02d00',
92: $this->slots[0]['hour'], $this->slots[0]['min']),
93: 'allday' => 1,
94: 'url' => $this->link(0, true)))
95: ->link(array('title' => _("Create a New Event"), 'class' =>
96: 'hour'))
97: . _("All day")
98: . Horde::img('new_small.png', '+', array('class' =>
99: 'iconAdd'))
100: . '</a>';
101: } else {
102: $newEventUrl = '<span class="hour">' . _("All day") . '</span>';
103: }
104:
105: 106: 107: 108: 109: 110:
111: $row = '';
112: $rowspan = ($this->all_day_maxrowspan) ? ' rowspan="' . $this->all_day_maxrowspan . '"' : '';
113: for ($k = 0; $k < $this->all_day_maxrowspan; ++$k) {
114: $row = '';
115: foreach (array_keys($this->_currentCalendars) as $cid) {
116: if (count($this->all_day_events[$cid]) === $k) {
117:
118:
119: $row .= '<td class="allday" width="1%" rowspan="' . ($this->all_day_maxrowspan - $k) . '" colspan="'. $this->span[$cid] . '"> </td>';
120: } elseif (count($this->all_day_events[$cid]) > $k) {
121:
122:
123: $event = $this->all_day_events[$cid][$k];
124: $row .= '<td class="day-eventbox"'
125: . $event->getCSSColors()
126: . 'width="' . round(90 / count($this->_currentCalendars)) . '%" '
127: . 'valign="top" colspan="' . $this->span[$cid] . '">'
128: . $event->getLink($this, true, $this->link(0, true));
129: if (!$event->isPrivate() && $showLocation) {
130: $row .= '<div class="event-location">' . htmlspecialchars($event->getLocation()) . '</div>';
131: }
132: $row .= '</td>';
133: }
134: }
135: require KRONOLITH_TEMPLATES . '/day/all_day.inc';
136: $first_row = false;
137: }
138:
139: if ($first_row) {
140: $row .= '<td colspan="' . $this->totalspan . '" width="100%"> </td>';
141: require KRONOLITH_TEMPLATES . '/day/all_day.inc';
142: }
143:
144: $day_hour_force = $prefs->getValue('day_hour_force');
145: $day_hour_start = $prefs->getValue('day_hour_start') / 2 * $this->slotsPerHour;
146: $day_hour_end = $prefs->getValue('day_hour_end') / 2 * $this->slotsPerHour;
147: $rows = array();
148: $covered = array();
149:
150: for ($i = 0; $i < $this->slotsPerDay; ++$i) {
151: if ($i >= $day_hour_end && $i > $this->_last) {
152: break;
153: }
154: if ($i < $this->_first && $i < $day_hour_start) {
155: continue;
156: }
157:
158: $row = '';
159: if (($m = $i % $this->slotsPerHour) != 0) {
160: $time = ':' . $m * $this->slotLength;
161: $hourclass = 'halfhour';
162: } else {
163: $time = Kronolith_View_Day::prefHourFormat($this->slots[$i]['hour']);
164: $hourclass = 'hour';
165: }
166:
167: if (!count($this->_currentCalendars)) {
168: $row .= '<td> </td>';
169: }
170:
171: foreach (array_keys($this->_currentCalendars) as $cid) {
172: $hspan = 0;
173: foreach ($this->_event_matrix[$cid][$i] as $key) {
174: $event = &$this->events[$key];
175:
176:
177:
178:
179:
180: $span = $this->span[$cid] / $event->overlap;
181:
182:
183:
184: if (!isset($event->indent)) {
185: $event->indent = $hspan;
186: }
187:
188:
189:
190:
191:
192:
193:
194: if (!isset($covered[$i][$event->indent])) {
195: $collision = false;
196: $available = 0;
197: for ($y = $event->indent; $y < ($span + $event->indent); ++$y) {
198: if (isset($covered[$i][$y])) {
199: $collision = true;
200: break;
201: }
202: $available++;
203: }
204:
205: if ($collision) {
206: $span = $available;
207: }
208: }
209:
210: $hspan += $span;
211:
212: $start = new Horde_Date(array(
213: 'hour' => floor($i / $this->slotsPerHour),
214: 'min' => ($i % $this->slotsPerHour) * $this->slotLength,
215: 'month' => $this->month,
216: 'mday' => $this->mday,
217: 'year' => $this->year));
218: $end_slot = new Horde_Date($start);
219: $end_slot->min += $this->slotLength;
220: if (((!$day_hour_force || $i >= $day_hour_start) &&
221: $event->start->compareDateTime($start) >= 0 &&
222: $event->start->compareDateTime($end_slot) < 0 ||
223: $start->compareDateTime($this) == 0) ||
224: ($day_hour_force &&
225: $i == $day_hour_start &&
226: $event->start->compareDateTime($start) < 0)) {
227:
228:
229:
230: for ($x = $i; $x < ($i + $event->rowspan); ++$x) {
231: for ($y = $event->indent; $y < $hspan; ++$y) {
232: $covered[$x][$y] = true;
233: }
234: }
235:
236: $row .= '<td class="day-eventbox"'
237: . $event->getCSSColors()
238: . 'width="' . round((90 / count($this->_currentCalendars)) * ($span / $this->span[$cid])) . '%" '
239: . 'valign="top" colspan="' . $span . '" rowspan="' . $event->rowspan . '">'
240: . $event->getLink($this, true, $this->link(0, true));
241: if ($showTime) {
242: $row .= '<div class="event-time">' . htmlspecialchars($event->getTimeRange()) . '</div>';
243: }
244: if (!$event->isPrivate() && $showLocation) {
245: $row .= '<div class="event-location">' . htmlspecialchars($event->getLocation()) . '</div>';
246: }
247: $row .= '</td>';
248: }
249: }
250:
251: $diff = $this->span[$cid] - $hspan;
252: if ($diff > 0) {
253: $row .= str_repeat('<td> </td>', $diff);
254: }
255: }
256:
257: if ($addLinks) {
258: $newEventUrl = Horde::url('new.php')
259: ->add(array('datetime' => sprintf($this->dateString() . '%02d%02d00',
260: $this->slots[$i]['hour'], $this->slots[$i]['min']),
261: 'url' => $this->link(0, true)))
262: ->link(array('title' =>_("Create a New Event"), 'class' => $hourclass))
263: . $time
264: . Horde::img('new_small.png', '+', array('class' => 'iconAdd'))
265: . '</a>';
266: } else {
267: $newEventUrl = '<span class="' . $hourclass . '">' . $time . '</span>';
268: }
269:
270: $rows[] = array('row' => $row, 'slot' => $newEventUrl);
271: }
272:
273: $template = $GLOBALS['injector']->createInstance('Horde_Template');
274: $template->set('row_height', round(20 / $this->slotsPerHour));
275: $template->set('rows', $rows);
276: $template->set('show_slots', true, true);
277: echo $template->fetch(KRONOLITH_TEMPLATES . '/day/rows.html')
278: . '</tbody></table>';
279: }
280:
281: 282: 283: 284: 285:
286: public function parse()
287: {
288: global $prefs;
289:
290: $tmp = array();
291: $this->all_day_maxrowspan = 0;
292: $day_hour_force = $prefs->getValue('day_hour_force');
293: $day_hour_start = $prefs->getValue('day_hour_start') / 2 * $this->slotsPerHour;
294: $day_hour_end = $prefs->getValue('day_hour_end') / 2 * $this->slotsPerHour;
295:
296:
297:
298: foreach (array_keys($this->_currentCalendars) as $cid) {
299: $this->all_day_events[$cid] = array();
300: $this->all_day_rowspan[$cid] = 0;
301: }
302:
303: foreach ($this->events as $key => $event) {
304:
305:
306: if ($this->sidebyside) {
307: $cid = $event->calendar;
308: } else {
309: $cid = 0;
310: }
311:
312:
313: if ($event->isAllDay()) {
314: $this->all_day_events[$cid][] = clone $event;
315: ++$this->all_day_rowspan[$cid];
316: $this->all_day_maxrowspan = max($this->all_day_maxrowspan, $this->all_day_rowspan[$cid]);
317: } else {
318:
319:
320: $event->overlap = 0;
321:
322:
323: $event->rowspan = 0;
324:
325: $tmp[] = clone $event;
326: }
327: }
328: $this->events = $tmp;
329:
330:
331: $spans = array(1 => true);
332:
333:
334:
335:
336: $this->_first = $this->slotsPerDay;
337: $this->_last = 0;
338:
339:
340:
341: for ($i = 0; $i < $this->slotsPerDay; ++$i) {
342:
343: foreach (array_keys($this->_currentCalendars) as $cid) {
344: $this->_event_matrix[$cid][$i] = array();
345: }
346:
347:
348: $start = new Horde_Date(array(
349: 'hour' => floor($i / $this->slotsPerHour),
350: 'min' => ($i % $this->slotsPerHour) * $this->slotLength,
351: 'month' => $this->month,
352: 'mday' => $this->mday,
353: 'year' => $this->year));
354: $end = clone $start;
355: $end->min += $this->slotLength;
356:
357:
358: foreach ($this->events as $key => $event) {
359:
360:
361: if ($this->sidebyside) {
362: $cid = $event->calendar;
363: } else {
364: $cid = 0;
365: }
366:
367:
368:
369:
370: if (($event->end->compareDateTime($start) > 0 &&
371: $event->start->compareDateTime($end) < 0) ||
372: ($event->end->compareDateTime($event->start) == 0 &&
373: $event->start->compareDateTime($start) == 0)) {
374:
375:
376:
377: if ($i > $this->_last &&
378: (!$day_hour_force || $i <= $day_hour_end)) {
379: $this->_last = $i;
380: }
381:
382:
383:
384: if ($i < $this->_first &&
385: (!$day_hour_force || $i >= $day_hour_start)) {
386: $this->_first = $i;
387: }
388:
389: if (!$day_hour_force ||
390: ($i >= $day_hour_start && $i <= $day_hour_end)) {
391:
392: $this->_event_matrix[$cid][$i][] = $key;
393:
394:
395: ++$this->events[$key]->rowspan;
396: }
397: }
398: }
399:
400: foreach (array_keys($this->_currentCalendars) as $cid) {
401:
402:
403: $max = 0;
404: $count = count($this->_event_matrix[$cid][$i]);
405: foreach ($this->_event_matrix[$cid][$i] as $ev) {
406: $this->events[$ev]->overlap = max($this->events[$ev]->overlap, $count);
407: $max = max($max, $this->events[$ev]->overlap);
408: }
409:
410:
411:
412: $spans[$cid][$max] = true;
413: }
414: }
415:
416: foreach (array_keys($this->_currentCalendars) as $cid) {
417:
418:
419: for ($i = $this->_first; $i <= $this->_last; ++$i) {
420: if (count($this->_event_matrix[$cid][$i])) {
421: usort($this->_event_matrix[$cid][$i], array($this, '_sortByStart'));
422: }
423: }
424:
425:
426:
427: $span[$cid] = 1;
428:
429:
430: $spans[$cid] = array_keys($spans[$cid]);
431:
432:
433: rsort($spans[$cid]);
434: foreach ($spans[$cid] as $s) {
435:
436:
437:
438:
439: if ($s != 0 && $span[$cid] % $s != 0) {
440: $span[$cid] *= $s;
441: }
442: }
443: $this->totalspan += $span[$cid];
444: }
445:
446: if (isset($span)) {
447: $this->span = $span;
448: } else {
449: $this->totalspan = 1;
450: }
451:
452:
453: $this->_parsed = true;
454: }
455:
456: public function link($offset = 0, $full = false)
457: {
458: return Horde::url('day.php', $full)
459: ->add('date', $this->getTime('%Y%m%d', $offset));
460: }
461:
462: public function getName()
463: {
464: return 'Day';
465: }
466:
467: public function prefHourFormat($hour)
468: {
469: $hour = $hour % 24;
470: if ($GLOBALS['prefs']->getValue('twentyFour')) {
471: return $hour;
472: }
473: return ($hour % 12 == 0 ? 12 : $hour % 12)
474: . ($hour < 12 ? 'am' : 'pm');
475: }
476:
477: protected function _sortByStart($evA, $evB)
478: {
479: $sA = $this->events[$evA]->start;
480: $sB = $this->events[$evB]->start;
481:
482: return $sB->compareTime($sA);
483: }
484:
485: }
486: