Overview

Packages

  • Kronolith
  • None

Classes

  • Kronolith
  • Kronolith_Ajax_Application
  • Kronolith_Ajax_Imple_ContactAutoCompleter
  • Kronolith_Ajax_Imple_Embed
  • Kronolith_Ajax_Imple_TagActions
  • Kronolith_Ajax_Imple_TagAutoCompleter
  • Kronolith_Api
  • Kronolith_Calendar
  • Kronolith_Calendar_External
  • Kronolith_Calendar_External_Tasks
  • Kronolith_Calendar_Holiday
  • Kronolith_Calendar_Internal
  • Kronolith_Calendar_Remote
  • Kronolith_Calendar_Resource
  • Kronolith_Calendars_Base
  • Kronolith_Calendars_Default
  • Kronolith_Calendars_Kolab
  • Kronolith_Day
  • Kronolith_Driver
  • Kronolith_Driver_Holidays
  • Kronolith_Driver_Horde
  • Kronolith_Driver_Ical
  • Kronolith_Driver_Kolab
  • Kronolith_Driver_Mock
  • Kronolith_Driver_Resource
  • Kronolith_Driver_Sql
  • Kronolith_Event
  • Kronolith_Event_Holidays
  • Kronolith_Event_Horde
  • Kronolith_Event_Ical
  • Kronolith_Event_Kolab
  • Kronolith_Event_Resource
  • Kronolith_Event_Sql
  • Kronolith_Exception
  • Kronolith_Factory_Calendars
  • Kronolith_Factory_Geo
  • Kronolith_Form_CreateCalendar
  • Kronolith_Form_CreateResource
  • Kronolith_Form_CreateResourceGroup
  • Kronolith_Form_DeleteCalendar
  • Kronolith_Form_DeleteResource
  • Kronolith_Form_DeleteResourceGroup
  • Kronolith_Form_EditCalendar
  • Kronolith_Form_EditRemoteCalendar
  • Kronolith_Form_EditResource
  • Kronolith_Form_EditResourceGroup
  • Kronolith_Form_SubscribeRemoteCalendar
  • Kronolith_Form_UnsubscribeRemoteCalendar
  • Kronolith_FreeBusy
  • Kronolith_FreeBusy_View
  • Kronolith_FreeBusy_View_Day
  • Kronolith_FreeBusy_View_Month
  • Kronolith_FreeBusy_View_Week
  • Kronolith_FreeBusy_View_Workweek
  • Kronolith_Geo_Base
  • Kronolith_Geo_Mysql
  • Kronolith_Geo_Sql
  • Kronolith_LoginTasks_SystemTask_Upgrade
  • Kronolith_LoginTasks_Task_PurgeEvents
  • Kronolith_Notification_Listener_AjaxStatus
  • Kronolith_Resource
  • Kronolith_Resource_Base
  • Kronolith_Resource_Group
  • Kronolith_Resource_Single
  • Kronolith_Storage
  • Kronolith_Storage_Kolab
  • Kronolith_Storage_Sql
  • Kronolith_Tagger
  • Kronolith_Test
  • Kronolith_View_Day
  • Kronolith_View_DeleteEvent
  • Kronolith_View_EditEvent
  • Kronolith_View_Event
  • Kronolith_View_ExportEvent
  • Kronolith_View_Month
  • Kronolith_View_Week
  • Kronolith_View_WorkWeek
  • Kronolith_View_Year
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * The Kronolith_View_Week:: class provides an API for viewing weeks.
  4:  *
  5:  * @author  Chuck Hagenbuch <chuck@horde.org>
  6:  * @author  Jan Schneider <jan@horde.org>
  7:  * @package Kronolith
  8:  */
  9: class Kronolith_View_Week
 10: {
 11:     public $parsed = false;
 12:     public $days = array();
 13:     public $week = null;
 14:     public $year = null;
 15:     public $startDay = null;
 16:     public $endDay = null;
 17:     public $startDate = null;
 18:     protected $_controller = 'week.php';
 19:     public $sidebyside = false;
 20:     public $_currentCalendars = array();
 21: 
 22:     /**
 23:      * How many time slots are we dividing each hour into?
 24:      *
 25:      * @var integer
 26:      */
 27:     public $_slotsPerHour = 2;
 28: 
 29:     /**
 30:      * How many slots do we have per day? Calculated from $_slotsPerHour.
 31:      *
 32:      * @see $_slotsPerHour
 33:      * @var integer
 34:      */
 35:     public $_slotsPerDay;
 36: 
 37:     public function __construct(Horde_Date $date)
 38:     {
 39:         $week = $date->weekOfYear();
 40:         $year = $date->year;
 41:         if (!$GLOBALS['prefs']->getValue('week_start_monday') &&
 42:             $date->dayOfWeek() == Horde_Date::DATE_SUNDAY) {
 43:             ++$week;
 44:         }
 45:         if ($week > 51 && $date->month == 1) {
 46:             --$year;
 47:         } elseif ($week == 1 && $date->month == 12) {
 48:             ++$year;
 49:         }
 50: 
 51:         $this->year = $year;
 52:         $this->week = $week;
 53:         $day = Horde_Date_Utils::firstDayOfWeek($week, $year);
 54: 
 55:         if (!isset($this->startDay)) {
 56:             if ($GLOBALS['prefs']->getValue('week_start_monday')) {
 57:                 $this->startDay = Horde_Date::DATE_MONDAY;
 58:                 $this->endDay = Horde_Date::DATE_SUNDAY + 7;
 59:             } else {
 60:                 $day->mday--;
 61:                 $this->startDay = Horde_Date::DATE_SUNDAY;
 62:                 $this->endDay = Horde_Date::DATE_SATURDAY;
 63:             }
 64:         }
 65: 
 66:         $this->startDate = new Horde_Date($day);
 67:         for ($i = $this->startDay; $i <= $this->endDay; ++$i) {
 68:             $this->days[$i] = new Kronolith_View_Day($day, array());
 69:             $day->mday++;
 70:         }
 71:         $endDate = new Horde_Date($day);
 72:         try {
 73:             $allevents = Kronolith::listEvents($this->startDate, $endDate);
 74:         } catch (Exception $e) {
 75:             $GLOBALS['notification']->push($e, 'horde.error');
 76:             $allevents = array();
 77:         }
 78:         for ($i = $this->startDay; $i <= $this->endDay; ++$i) {
 79:             $date_stamp = $this->days[$i]->dateString();
 80:             $this->days[$i]->events = isset($allevents[$date_stamp])
 81:                 ? $allevents[$date_stamp]
 82:                 : array();
 83:         }
 84:         $this->sidebyside = $this->days[$this->startDay]->sidebyside;
 85:         $this->_currentCalendars = $this->days[$this->startDay]->currentCalendars;
 86:         $this->slotsPerHour = $this->days[$this->startDay]->slotsPerHour;
 87:         $this->slotsPerDay = $this->days[$this->startDay]->slotsPerDay;
 88:         $this->slotLength = $this->days[$this->startDay]->slotLength;
 89:     }
 90: 
 91:     public function html()
 92:     {
 93:         global $prefs;
 94: 
 95:         $more_timeslots = $prefs->getValue('time_between_days');
 96:         $include_all_events = !$prefs->getValue('show_shared_side_by_side');
 97:         $showLocation = Kronolith::viewShowLocation();
 98:         $showTime = Kronolith::viewShowTime();
 99: 
100:         if (!$this->parsed) {
101:             $this->parse();
102:         }
103: 
104:         $slots = $this->days[$this->startDay]->slots;
105:         $cid = 0;
106:         require KRONOLITH_TEMPLATES . '/week/head.inc';
107:         if ($this->sidebyside) {
108:             require KRONOLITH_TEMPLATES . '/week/head_side_by_side.inc';
109:         }
110:         echo '</thead><tbody>';
111: 
112:         $event_count = 0;
113:         for ($j = $this->startDay; $j <= $this->endDay; ++$j) {
114:             foreach (array_keys($this->_currentCalendars) as $cid) {
115:                 $event_count = max($event_count, count($this->days[$j]->all_day_events[$cid]));
116:                 reset($this->days[$j]->all_day_events[$cid]);
117:             }
118:         }
119: 
120:         if ($more_timeslots) {
121:             $newEventUrl = null;
122:         } else {
123:             $newEventUrl = _("All day");
124:         }
125: 
126:         $row = '';
127:         for ($j = $this->startDay; $j <= $this->endDay; ++$j) {
128:             $row .= '<td class="hour rightAlign daySpacer">' . ($more_timeslots ? _("All day") : '&nbsp;') . '</td>' .
129:                 '<td colspan="' . $this->days[$j]->totalspan . '" valign="top"><table width="100%" cellspacing="0">';
130:             if ($this->days[$j]->all_day_maxrowspan > 0) {
131:                 for ($k = 0; $k < $this->days[$j]->all_day_maxrowspan; ++$k) {
132:                     $row .= '<tr>';
133:                     foreach (array_keys($this->days[$j]->currentCalendars) as $cid) {
134:                         if (count($this->days[$j]->all_day_events[$cid]) === $k) {
135:                             $row .= '<td rowspan="' . ($this->days[$j]->all_day_maxrowspan - $k) . '" width="'. round(99 / count($this->days[$j]->currentCalendars)) . '%">&nbsp;</td>';
136:                         } elseif (count($this->days[$j]->all_day_events[$cid]) > $k) {
137:                             $event = $this->days[$j]->all_day_events[$cid][$k];
138:                             $row .= '<td class="week-eventbox"'
139:                                 . $event->getCSSColors()
140:                                 . 'width="' . round(99 / count($this->days[$j]->currentCalendars)) . '%" '
141:                                 . 'valign="top">'
142:                                 . $event->getLink($this->days[$j], true, $this->link(0, true));
143:                             if (!$event->isPrivate() && $showLocation) {
144:                                 $row .= '<div class="event-location">' . htmlspecialchars($event->getLocation()) . '</div>';
145:                             }
146:                             $row .= '</td>';
147:                         }
148:                     }
149:                     $row .= '</tr>';
150:                 }
151:             } else {
152:                 $row .= '<tr><td colspan="' . count($this->_currentCalendars) . '">&nbsp;</td></tr>';
153:             }
154:             $row .= '</table></td>';
155:         }
156: 
157:         $rowspan = '';
158:         $first_row = true;
159:         require KRONOLITH_TEMPLATES . '/day/all_day.inc';
160: 
161:         $day_hour_force = $prefs->getValue('day_hour_force');
162:         $day_hour_start = $prefs->getValue('day_hour_start') / 2 * $this->slotsPerHour;
163:         $day_hour_end = $prefs->getValue('day_hour_end') / 2 * $this->slotsPerHour;
164:         $rows = array();
165:         $covered = array();
166: 
167:         for ($i = 0; $i < $this->slotsPerDay; ++$i) {
168:             if ($i >= $day_hour_end && $i > $this->last) {
169:                 break;
170:             }
171:             if ($i < $this->first && $i < $day_hour_start) {
172:                 continue;
173:             }
174: 
175:             if (($m = $i % $this->slotsPerHour) != 0) {
176:                 $time = ':' . $m * $this->slotLength;
177:                 $hourclass = 'halfhour';
178:             } else {
179:                 $time = Kronolith_View_Day::prefHourFormat($slots[$i]['hour']);
180:                 $hourclass = 'hour';
181:             }
182: 
183:             $row = '';
184:             for ($j = $this->startDay; $j <= $this->endDay; ++$j) {
185:                 // Add spacer between days, or timeslots.
186:                 if ($more_timeslots) {
187:                     $row .= '<td align="right" class="' . $hourclass . ' daySpacer">' . $time . '</td>';
188:                 } else {
189:                     $row .= '<td class="daySpacer">&nbsp;</td>';
190:                 }
191: 
192:                 if (!count($this->_currentCalendars)) {
193:                     $row .= '<td>&nbsp;</td>';
194:                 }
195: 
196:                 foreach (array_keys($this->_currentCalendars) as $cid) {
197:                      // Width (sum of colspans) of events for the current time
198:                      // slot.
199:                     $hspan = 0;
200:                      // $hspan + count of empty TDs in the current timeslot.
201:                     $current_indent = 0;
202: 
203:                     // $current_indent is initialized to the position of the
204:                     // first available cell of the day.
205:                     for (; isset($covered[$j][$i][$current_indent]); ++$current_indent);
206: 
207:                     foreach ($this->days[$j]->event_matrix[$cid][$i] as $key) {
208:                         $event = &$this->days[$j]->events[$key];
209:                         if ($include_all_events || $event->calendar == $cid) {
210:                             // Since we've made sure that this event's
211:                             // overlap is a factor of the total span,
212:                             // we get this event's individual span by
213:                             // dividing the total span by this event's
214:                             // overlap.
215:                             $span = $this->days[$j]->span[$cid] / $event->overlap;
216: 
217:                             // Store the indent we're starting this event at
218:                             // for future use.
219:                             if (!isset($event->indent)) {
220:                                 $event->indent = $current_indent;
221:                             }
222: 
223:                             // If $event->span is set this mean than we
224:                             // already calculated the width of the event.
225:                             if (!isset($event->span)) {
226:                                 // If the first node that we would cover is
227:                                 // already covered, we can assume that table
228:                                 // rendering will take care of pushing the
229:                                 // event over. However, if the first node
230:                                 // _isn't_ covered but any others that we
231:                                 // would covered _are_, we only cover the
232:                                 // available nodes.
233:                                 if (!isset($covered[$j][$i][$event->indent])) {
234:                                     $collision = false;
235:                                     $available = 0;
236:                                     for ($y = $event->indent; $y < ($span + $event->indent); ++$y) {
237:                                         if (isset($covered[$j][$i][$y])) {
238:                                             $collision = true;
239:                                             break;
240:                                         }
241:                                         $available++;
242:                                     }
243: 
244:                                     if ($collision) {
245:                                         $span = $available;
246:                                     }
247:                                 }
248: 
249:                                 // We need to store the computed event span
250:                                 // because in some cases it might not be
251:                                 // possible to compute it again (when only the
252:                                 // first half of the event is in colision).
253:                                 // ceil() is needed because of some float
254:                                 // values (bug ?)
255:                                 $event->span = ceil($span);
256:                             }
257: 
258:                             $hspan          += $event->span;
259:                             $current_indent += $event->span;
260: 
261:                             $start = new Horde_Date(array(
262:                                 'hour'  => floor($i / $this->slotsPerHour),
263:                                 'min'   => ($i % $this->slotsPerHour) * $this->slotLength,
264:                                 'month' => $this->days[$j]->month,
265:                                 'mday'  => $this->days[$j]->mday,
266:                                 'year'  => $this->days[$j]->year));
267:                             $slot_end = new Horde_Date($start);
268:                             $slot_end->min += $this->slotLength;
269:                             if (((!$day_hour_force || $i >= $day_hour_start) &&
270:                                  $event->start->compareDateTime($start) >= 0 &&
271:                                  $event->start->compareDateTime($slot_end) < 0 ||
272:                                  $start->compareDateTime($this->days[$j]) == 0) ||
273:                                 ($day_hour_force &&
274:                                  $i == $day_hour_start)) {
275: 
276:                                 // Store the nodes that we're covering for
277:                                 // this event in the coverage graph.
278:                                 for ($x = $i; $x < ($i + $event->rowspan); ++$x) {
279:                                     for ($y = $event->indent; $y < $current_indent; ++$y) {
280:                                         $covered[$j][$x][$y] = true;
281:                                     }
282:                                 }
283: 
284:                                 $row .= '<td class="week-eventbox"'
285:                                     . $event->getCSSColors()
286:                                     . 'valign="top" '
287:                                     . 'width="' . floor(((90 / count($this->days)) / count($this->_currentCalendars)) * ($span / $this->days[$j]->span[$cid])) . '%" '
288:                                     . 'colspan="' . $event->span . '" rowspan="' . $event->rowspan . '">'
289:                                     . $event->getLink($this->days[$j], true, $this->link(0, true));
290:                                 if ($showTime) {
291:                                     $row .= '<div class="event-time">' . htmlspecialchars($event->getTimeRange()) . '</div>';
292:                                 }
293:                                 if (!$event->isPrivate() && $showLocation) {
294:                                     $row .= '<div class="event-location">' . htmlspecialchars($event->getLocation()) . '</div>';
295:                                 }
296:                                 $row .= '</td>';
297:                             }
298:                         }
299:                     }
300: 
301:                     $diff = $this->days[$j]->span[$cid] - $hspan;
302:                     if ($diff > 0) {
303:                         $row .= str_repeat('<td>&nbsp;</td>', $diff);
304:                     }
305:                 }
306:             }
307: 
308:             $rows[] = array('row' => $row, 'slot' => '<span class="' . $hourclass . '">' . $time . '</span>');
309:         }
310: 
311:         $template = $GLOBALS['injector']->createInstance('Horde_Template');
312:         $template->set('row_height', round(20 / $this->slotsPerHour));
313:         $template->set('rows', $rows);
314:         $template->set('show_slots', !$more_timeslots, true);
315:         echo $template->fetch(KRONOLITH_TEMPLATES . '/day/rows.html')
316:             . '</tbody></table>';
317:     }
318: 
319:     /**
320:      * Parse all events for all of the days that we're handling; then
321:      * run through the results to get the total horizontal span for
322:      * the week, and the latest event of the week.
323:      */
324:     public function parse()
325:     {
326:         for ($i = $this->startDay; $i <= $this->endDay; ++$i) {
327:             $this->days[$i]->parse();
328:         }
329: 
330:         $this->totalspan = 0;
331:         $this->span = array();
332:         for ($i = $this->startDay; $i <= $this->endDay; ++$i) {
333:             $this->totalspan += $this->days[$i]->totalspan;
334:             foreach (array_keys($this->_currentCalendars) as $cid) {
335:                 if (isset($this->span[$cid])) {
336:                     $this->span[$cid] += $this->days[$i]->span[$cid];
337:                 } else {
338:                     $this->span[$cid] = $this->days[$i]->span[$cid];
339:                 }
340:             }
341:         }
342: 
343:         $this->last = 0;
344:         $this->first = $this->slotsPerDay;
345:         for ($i = $this->startDay; $i <= $this->endDay; ++$i) {
346:             if ($this->days[$i]->last > $this->last) {
347:                 $this->last = $this->days[$i]->last;
348:             }
349:             if ($this->days[$i]->first < $this->first) {
350:                 $this->first = $this->days[$i]->first;
351:             }
352:         }
353:     }
354: 
355:     public function getWeek($offset = 0)
356:     {
357:         $week = new Horde_Date($this->startDate);
358:         $week->mday += $offset * 7;
359:         return $week;
360:     }
361: 
362:     public function link($offset = 0, $full = false)
363:     {
364:         $week = $this->getWeek($offset);
365:         return Horde::url($this->_controller, $full)
366:             ->add('date', $week->dateString());
367:     }
368: 
369:     public function getName()
370:     {
371:         return 'Week';
372:     }
373: 
374: }
375: 
API documentation generated by ApiGen