Source for file calendar.php
Documentation is available at calendar.php
* @author Stuart Prescott
* @copyright Copyright Stuart Prescott
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* path (bumblebee root)/inc/bb/calendar.php
/** Load ancillary functions */
require_once 'inc/typeinfo.php';
/** date manipulation routines */
require_once 'inc/date.php';
require_once 'inc/bookings/booking.php';
require_once 'inc/bookings/vacancy.php';
require_once 'inc/bookings/cell.php';
/** BookingMatrix object */
require_once 'inc/bookings/matrix.php';
/** BookingData object (list of bookings) */
require_once 'inc/bookings/bookingdata.php';
/** TimeSlot and TimeSlotRule objects */
require_once 'inc/bookings/timeslotrule.php';
* use start and end times from defined slots
* @see Calender::_breakAccordingToList
define('CAL_TIME_SLOTRULE', 1);
* use start and end times from the bookings
* @see Calender::_breakAccordingToList
define('CAL_TIME_BOOKING', 2);
* Retrives, sorts and displays bookings for an instrument over a given period of time
* @todo //TODO: display bugs for off-slot bookings?
* @todo //TODO: week-long / multi-day bookings
/** @var SimpleDate start date/time for the calendar */
/** @var SimpleDate stop date/time for the calendar */
/** @var integer number of days in the calendar */
/** @var integer instrument id number for which the calendar is being displayed */
/** @var array list of instrument ids for which the calendar is being displayed */
/** @var boolean sql errors are fatal */
/** @var BookingData list of bookings */
/** @var string prepended to all zoom hrefs generated by in the calendar */
/** @var string callback function used to make URLs for booking hrefs in the calendar */
/** @var string html/css class to be used for all table cells corresponding to a day */
/** @var string html/css class to be used for all table cells corresponding today */
/** @var mixed array or string. list of html/css class to be rotated through month by month for the time periods in the calendar */
/** @var mixed array or string. list of html/css class to be rotated through month by month for the day header */
/** @var TimeSlotRule time slots that govern this calendar */
/** @var boolean generate the calendar with an admin view (i.e. all times are bookable) */
/** @var boolean only show free/busy information and not the details of the bookings */
/** @var boolean show a popup layer with extra details on it on the page */
/** @var integer time after which the calendar should automatically reload itself (in milliseconds) */
/** @var integer debug level (0=off, 10=verbose) */
* Create a calendar object, can display bookings in calendar format
* @param SimpleDate $start start time to display bookings from
* @param SimpleDate $stop stop time to display bookings until
* @param mixed $instrument what instrument number (or list of instrumets) to display bookings for
function Calendar($start, $stop, $instrument) {
$this->log('Creating calendar from '.
$start->dateString().
' to '.
$stop->dateString(), 5);
//print $this->displayAsTable();
* set the CSS style names by which
* @param string $dayClass class to use in every day header
* @param string $today class to use on today's date
* @param mixed $day string for class on each day, or array to rotate through
* @param string $dayrotate time-part ('m', 'd', 'y' etc) on which day CDD should be rotated
* set the time slot picture (passed straight to a TimeSlotRule object) to apply
* @param string $pic timeslot picture for this instrument and this calendar
//break bookings over the predefined pictures
$this->log('Breaking up bookings according to defined rules');
//print $this->displayAsTable();
* Obtain the booking data for this time period
//preDump($this->bookinglist);
'instrument' =>
$instrument,
$numBookings =
count($list);
if ($numBookings <=
1) return $list;
$cleandata[0] =
$list[0];
$cleandata[0]->children[] =
$list[0];
$now =
clone($list[0]->start);
for ($i=
1; $i <
$numBookings; $i++
) {
#print "<br/>".$booking->generateBookingTitle()."<br />";
// bookings are sorted by:
// start (ascending) and then duration (descending)
if ($now->ticks ==
$booking->start->ticks) {
$cleandata[$idx]->children[] =
$booking;
$this->log("ignored booking because starts match", 8);
if ($cleandata[$idx]->stop->ticks <=
$booking->start->ticks) {
$this->log("copied booking across", 8);
$cleandata[$idx] =
$booking;
$cleandata[$idx]->children[] =
$booking;
$now =
clone($booking->start);
if ($cleandata[$idx]->stop->ticks <
$booking->stop->ticks) {
$this->log("adjusted previous booking because of overlap", 8);
$cleandata[$idx]->children[] =
$booking;
$cleandata[$idx]->stop =
$booking->stop;
$cleandata[$idx]->children[] =
$booking;
$this->log("did nothing with this booking", 8);
* Create pseudo-bookings for all vacancies between the start
* of this calendar and the end.
* For example, if we were constructing a calendar from 00:00 on 2004-01-01 to
* 23:59 on 2004-01-02, but there was only a booking from 10:00 to 11:00
* on 2004-01-01, then we should create vacancy pseudo-bookings from:
* - 2004-01-01-00:00 to 2004-01-01-10:00 and from
* - 2004-01-01-11:00 to 2004-01-01-24:00 and from
* - 2004-01-02-00:00 to 2004-01-02-24:00.
* Bookings are NOT restricted to remaining on one day (i.e. a booking from
* 20:00:00 until 10:00:00 the next day is OK.
$this->log('Creating calendar for '.
$this->numDays.
' days', 5);
//blat over the booking list so we can create the normalised list
// put a vacancy at the end so we don't run off the end of the list.
$v->setTimes(clone($this->stop),clone($this->stop));
//insert a vacancy between each non-consecutive booking
$now =
clone($this->start);
$this->log('Normalising bookings');
if ($now->ticks <
$bookings[$booking]->start->ticks) {
// then we should create a pseudobooking
$stoptime =
new SimpleDate($bookings[$booking]->start->ticks);
$v->setTimes(clone($now), clone($stoptime));
$this->log('Created vacancy: '.
$v->start->dateTimeString()
.
' to '.
$v->stop->dateTimeString(), 9);
// then this is the current timeslot
$bvlist[] =
$bookings[$booking];
$now =
$bookings[$booking]->stop;
$this->log('Included booking: '.
$bookings[$booking]->start->dateTimeString() .
' to '
.
$bookings[$booking]->stop->dateTimeString(), 9);
* Break up bookings that span days (for display purposes only)
* For example, if we had a vacancy pseudo-bookings from
* 2004-01-01-11:00 to 2004-01-02-24:00, then we would
* break it up into two bookings as follows:
* - 2004-01-01-11:00 to 2004-01-01-24:00 and
* - 2004-01-02-00:00 to 2004-01-02-24:00.
$this->log('Breaking up bookings across days');
//break bookings over day boundaries
* Break up bookings that span elements of a defined list (e.g. allowable times or
* days). A TimeSlotRule ($list) is used to define how the times should be broken up
* $keepTimes(Vacant|Book) are set to CAL_TIME_BOOKING, CAL_TIME_SLOTRULE
* start|stop are set according to the timeslotrule and will be used to
* display the timeslot in a graphica display (i.e. calculating height of boxes)
* display(Start|Stop) are set to the values of the original vacancy or booking being examined.
* display(Start|Stop) variables are set to the values of the timeslot rule that breaks up the slots
* with the exception of slots that overlap a booking, where min() or max() is used
* start|stop are set to the same as the start/stop vars
* Note: vacancies that are at the start or end of the booking list are a corner case
* that is handled respectively as: start = slotrule->stop and stop = slotrule->stop
* @param $list TimeSlotRule Object set of rules used to break up booking stream
* @param $keepTimesVacant enum how should the display(Start|Stop) and (start|stop)
* @param $keepTimesBook enum .. variables be set for Vacancy and Booking slots
$this->log('Breaking up bookings according to list');
$this->log($list->dump());
for ($bv=
0; $bv <
count($bl); $bv++
) {
/* $this->log('considering timeslot #'.$bv.': '
.$bl[$bv]->start->dateTimeString().' - '.$bl[$bv]->stop->dateTimeString(), 8);*/
$cbook->original =
clone($cbook);
$slotrule =
$list->findSlotFromWithin($bl[$bv]->start);
#$start = $list->findSlotStart($bl[$bv]->start);
if (!is_object($slotrule) &&
$slotrule ==
0) {
// then the original start time must be outside the proper limits
$slotrule =
$list->findNextSlot($bl[$bv]->start);
do
{ //until the current booking has been broken up across list boundaries
//echo "memusage=".memory_get_usage()."<br />";
$this->slotlog('bookingo', $bl[$bv]);
$this->slotlog('timeslot', $slotrule);
// push the new booking onto the stack; record if it's the start of a booking or not
if ($isStart ==
MIDDLE_BOOKING &&
$slotrule->start->dow() !=
$realStart->dow()) {
$newstart =
clone($this->bookinglist[$booking]->start);
$newstart->max($slotrule->start);
$newstop->min($slotrule->stop);
switch ($keepTimesBook) {
$newstart =
clone($this->bookinglist[$booking]->start);
/* $this->bookinglist[$booking]->displayStart = $newstart;
$this->bookinglist[$booking]->displayStop = $newstop;*/
// $this->bookinglist[$booking]->displayStart = $this->bookinglist[$booking]->original->start;
// $this->bookinglist[$booking]->displayStop = $this->bookinglist[$booking]->original->stop;
switch ($keepTimesVacant) {
$newstart =
clone($slotrule->start);
$newstop =
clone($slotrule->stop);
$this->bookinglist[$booking]->displayStart =
$newstart;
$this->bookinglist[$booking]->isDisabled =
! $slotrule->isAvailable;
// find the next TimeSlotRule to work out how to chop this booking up again (or how
// to chop up the next booking)
$nextslotrule =
$list->findNextSlot($slotrule->start);
$isStart =
$next_isStart;
$slotrule =
$nextslotrule;
//$this->log('oticks='.$this->bookinglist[$booking-1]->original->stop->ticks
// .'nticks='.$slotrule->start->ticks,10);
$this->timelog('nextstart=',$slotrule->start);
//var_dump($this->bookinglist[$booking-1]->original);
//$this->log('ost='.$this->bookinglist[$booking-1]->original->stop->ticks,10);
//$this->log('tst='.$slotrule->start->ticks,10);
} while ($this->bookinglist[$booking-
1]->original->stop->ticks >
$slotrule->start->ticks);
* Generate a booking matrix for all the days we are interested in
// matrix calculation object is shared from day to day which permits caching of data
for ($day =
0; $day <
$this->numDays; $day++
) {
$today =
clone($this->start);
$matrix->setDate($today);
$matrix->prepareMatrix();
$matrixlist[] =
$matrix->getMatrix();
* work out what html/css class this date should be rendered as
* @param SimpleDate $today today's date
* @param SimpleDate $t the date to check
* @return string space-separated css class list for use in class=""
if ($today->dateString()==
$t->dateString()) {
* Display the booking details in a list
* Display the booking details in a list
$t =
'<table class="tabularobject">';
#$t .= '<tr><td>'.$v[0].'</td><td>'.$v[1].'</td></tr>'."\n";
$t .=
$v->displayShort();
* Generate html for the booking details in a table with rowspan based on the duration of the booking
* @param SimpleTime $daystart time from which bookings should be displayed
* @param SimpleTime $daystop time up until which bookings should be displayed
* @param integer $granularity seconds per row in display
* @param integer $reportPeriod seconds between reporting the time in a column down the side
* @return string html representation of the calendar
// echo $this->display();
$numRowsPerDay =
$daystop->subtract($daystart) /
$granularity;
#report the time in a time column on the LHS every nth row:
$time =
clone($daystart);
for ($row=
0; $row<
$numRowsPerDay; $row++
) {
$timecolumn[$row] =
clone($time);
$time->addSecs($granularity);
$t .=
'<table class="tabularobject calendar" summary="'
.
T_('Extended view of instrument bookings').
'">';
$weekstart =
clone($this->start);
$t .=
'<tr><th colspan="2"></th>';
for ($day=
0; $day<
7; $day++
) {
$current =
clone($weekstart);
$t .=
'<th class="caldow">'
.
($conf->value('calendar', 'shortdaynames', true) ?
$current->dowShortStr()
// load up this translation once outside the main loop
$zoomStr =
T_('Zoom in on %s');
for ($row =
0; $row <
$numRows; $row++
) {
$dayRow =
$row %
$numRowsPerDay;
$t .=
'<tr><td colspan="2"></td>';
for ($day=
0; $day<
7; $day++
) {
$current =
clone($weekstart);
$isodate =
$current->dateString();
$zoomwords =
sprintf($zoomStr, $isodate);
$t .=
'<td class="caldatecell '.
$class.
'">';
$t .=
'<div style="float:right;"><a href="'.
$this->zoomhref.
'&isodate='.
$isodate.
'" '
.
'class="but" title="'.
$zoomwords .
'">'
.
'<img src="'.
$conf->BasePath.
'/theme/images/zoom.png" '
.
'alt="'.
$zoomwords .
'" class="calicon" /></a></div>'.
"\n";
$t .=
'<div class="caldate">'
.
$current->getShortDateString()
$t .=
'<tr><td class="dummy"></td>';
//$t .= '<tr><td class="dummy"><img src="/1x1.png" height="5" width="1" alt="" /></td>';
if ($dayRow %
$reportPeriod ==
0) {
//$t .= '<td colspan="2" rowspan="'.$reportPeriod.'">';
$t .=
'<td rowspan="'.
$reportPeriod.
'" class="timemark">';
$t .=
$timecolumn[$dayRow]->getShortString();
for ($day=
0; $day<
7; $day++
) {
$current =
clone($weekstart);
#$currentidx = $current->dsDaysBetween($this->start);
// calculate the day number directly from the cell information rather than
// using date-time functions. (add a small qty to the value so that floor doesn't
// round down to the next integer below due to fp precision)
$currentidx =
floor($row /
$numRowsPerDay +
0.05) *
7 +
$day;
if (isset
($matrix[$currentidx][$dayRow])) {
#preDump($matrix[$currentidx]->rows[$dayRow]);
$b =
& $matrix[$currentidx][$dayRow];
$class .=
($b->booking->isDisabled ?
' disabled' :
'');
//echo "$class <br />\n";
$t .=
"\n\t".
$b->display($class,
* Display the booking details in a table with rowspan based on the duration of the booking
* @param SimpleTime $daystart time from which bookings should be displayed
* @param SimpleTime $daystop time up until which bookings should be displayed
* @param integer $granularity seconds per row in display
* @param integer $reportPeriod seconds between reporting the time in a column down the side
* @return string html representation of the calendar
// echo $this->display();
$numRowsPerDay =
ceil($daystop->subtract($daystart) /
$granularity);
$numRows =
$numRowsPerDay;
#report the time in a time column on the LHS every nth row:
for ($row=
0; $row<
$numRowsPerDay; $row++
) {
$timecolumn[$row] =
clone($time);
$time->addSecs($granularity);
$t .=
'<table class="tabularobject calendar" summary="'.
T_('Day view of instrument bookings').
'">';
$t .=
'<tr><td class="dummy"></td><th></th>';
$t .=
'<td class="caldayzoom">';
$t .=
'<div class="caldate">'
for ($row =
0; $row <
$numRows; $row++
) {
$t .=
'<tr><td class="dummy"></td>';
if ($row %
$reportPeriod ==
0) {
$t .=
'<td rowspan="'.
$reportPeriod.
'">';
$t .=
$timecolumn[$row]->timeString();
if (isset
($matrix[0][$row])) {
#preDump($matrix[$currentidx]->rows[$dayRow]);
$t .=
"\n\t".
$b->display($class,
<script type='text/javascript'>
function ReloadPageCalendar() {
* logging function -- logs debug info to stdout
* The higher $priority, the more verbose (in the debugging sense) the output.
* @param string $string text to be logged
* @param integer $priority (optional, default value 10) debug level of the message
function log ($string, $priority=10) {
if ($priority <= $this->DEBUG) {
echo $string.'<br />'."\n";
* time logging function -- logs the start and stop time of a booking or slot
* @param string $string prefix to text to be logged
* @param TimeSlot $slot the slot whose start/stop is to be logged
* @param boolean $display (optional ) use the displayStart rather than the start data in $slot
* The higher $prio, the more verbose (in the debugging sense) the output.
function slotlog ($string, $slot, $display=false) {
// Efficiency would suggest that &$slot would be better here, but bugs in PHP mean that we can't do that
// see http://bugs.php.net/bug.php?id=24485 and http://bugs.php.net/bug.php?id=30787
// short circuit the evaluation here -- if there's no logging going to be done then
// we don't want to make expensive dateTimeString() calls.
if ($this->DEBUG <
10) return;
$this->log($string.
':start='.
$slot->displayStart->dateTimeString()
.
' '.
$string.
':stop='.
$slot->displayStop->dateTimeString(), 10);
$this->log($string.
':start='.
$slot->start->dateTimeString()
.
' '.
$string.
':stop='.
$slot->stop->dateTimeString(), 10);
* @param string $string prefix to text to be logged
* @param SimpleDate $slot the time to be logged
* The higher $prio, the more verbose (in the debugging sense) the output.
function timelog ($string, $time) {
// Efficiency would suggest that &$time would be better here, but bugs in PHP mean that we can't do that
// see http://bugs.php.net/bug.php?id=24485 and http://bugs.php.net/bug.php?id=30787
// short circuit the evaluation here -- if there's no logging going to be done then
// we don't want to make expensive dateTimeString() calls.
if ($this->DEBUG <
10) return;
$this->log($string.
' '.
$time->dateTimeString(), 10);
Documentation generated on Tue, 06 Mar 2007 10:00:58 +0000 by phpDocumentor 1.3.0