Source for file bookingentry.php

Documentation is available at bookingentry.php

  1. <?php
  2. /**
  3. * Booking entry object for creating/editing booking
  4. *
  5. @author    Stuart Prescott
  6. @copyright  Copyright Stuart Prescott
  7. @license    http://opensource.org/licenses/gpl-license.php GNU Public License
  8. @version    $Id$
  9. @package    Bumblebee
  10. @subpackage DBObjects
  11. */
  12.  
  13. /** Load ancillary functions */
  14. require_once 'inc/typeinfo.php';
  15.  
  16. require_once 'inc/bb/configreader.php';
  17.  
  18. /** parent object */
  19. require_once 'inc/formslib/dbrow.php';
  20. /** uses fields */
  21. require_once 'inc/formslib/idfield.php';
  22. require_once 'inc/formslib/textfield.php';
  23. require_once 'inc/formslib/datetimefield.php';
  24. require_once 'inc/formslib/timefield.php';
  25. require_once 'inc/formslib/droplist.php';
  26. require_once 'inc/formslib/referencefield.php';
  27. require_once 'inc/formslib/dummyfield.php';
  28. require_once 'inc/formslib/textfield.php';
  29.  
  30. /** uses time slot rules for management */
  31. require_once 'inc/bookings/timeslotrule.php';
  32. /** status codes for success/failure of database actions */
  33. require_once 'inc/statuscodes.php';
  34.  
  35. /**
  36. * Booking entry object for creating/editing booking
  37. *
  38. @package    Bumblebee
  39. @subpackage DBObjects
  40. */
  41. class BookingEntry extends DBRow {
  42.   /** @var TimeSlotRule     rules for when the instrument can be booked    */
  43.   var $slotrules;
  44.   /** @var integer          EUID of booking user @see BumblebeeAuth  */
  45.   var $euid;
  46.   /** @var integer          UID of booking user @see BumblebeeAuth  */
  47.   var $uid;
  48.   /** @var BumblebeeAuth    auth object for checking user permissions */
  49.   var $_auth;
  50.   /** @var integer          minimum notice in hours to be given for unbooking an instrument  */
  51.   var $minunbook;
  52.   /** @var boolean          object not fully constructed (using short constructor for deleting booking only  */
  53.   var $isShort = false;
  54.   /** @var array            list of instrument id numbers */
  55.   var $instrumentid;
  56.  
  57.   /**
  58.   *  Create a new BookingEntry object
  59.   *
  60.   * @param integer       $id           booking id number (existing number or -1 for new)
  61.   * @param BumblebeeAuth $auth         authorisation object
  62.   * @param array         $instrumentid list of instrument id of instruments to be booked
  63.   * @param integer       $minunbook    minimum notice to be given for unbooking (optional)
  64.   * @param string        $ip           IP address of person making booking (for recording) (optional)
  65.   * @param SimpleDate    $start        when the booking should start (optional)
  66.   * @param SimpleTime    $duration     length of the booking (optional)
  67.   * @param string        $granlist     timeslotrule picture (optional)
  68.   */
  69.   function BookingEntry($id$auth$instrumentid$minunbook=''$ip=''$start=''$duration=''$granlist=''{
  70.     //$this->DEBUG = 10;
  71.     $this->DBRow('bookings'$id);
  72.     $this->deleteFromTable = 0;
  73.     $this->_checkAuth($auth$instrumentid);
  74.     $this->minunbook = $minunbook;
  75.     $this->instrumentid = $instrumentid;
  76.     // check if lots of the input data is empty, then the constructor is only being used to delete the booking
  77.     if ($ip=='' && $start=='' && $duration=='' && $granlist==''{
  78.       return $this->_bookingEntryShort($id$instrumentid);
  79.     }
  80.     $this->slotrules = new TimeSlotRule($granlist);
  81.     $this->editable = 1;
  82.     $f new IdField('id'T_('Booking ID'));
  83.     $f->editable 0;
  84.     $f->duplicateName 'bookid';
  85.     $this->addElement($f);
  86.     $f new ReferenceField('instrument'T_('Instrument'));
  87.     $f->extraInfo('instruments''id''name');
  88.     $f->duplicateName 'instrid';
  89.     $f->defaultValue join(','$instrumentid);
  90.     $this->addElement($f);
  91.     $f new TextField('startticks');
  92.     $f->hidden 1;
  93.     $f->required 1;
  94.     $f->editable 0;
  95.     $f->sqlHidden 1;
  96.     $startticks new SimpleDate($start);
  97.     $f->value $startticks->ticks;
  98.     $this->addElement($f);
  99.  
  100.     $startf new DateTimeField('bookwhen'T_('Start'));
  101. //     $this->starttime = &$startf;
  102.     $startf->required 1;
  103.     $startf->defaultValue $start;
  104.     $startf->isValidTest 'is_valid_datetime';
  105.     $attrs array('size' => '24');
  106.     $startf->setAttr($attrs);
  107.     if ($this->_auth->permitted(BBROLE_MAKE_BOOKINGS_FREE$instrumentid)) {
  108.       $startf->setManualRepresentation($this->id == -TF_FREE TF_FREE_ALWAYS);
  109.     else {
  110.       $startf->setManualRepresentation(TF_AUTO);
  111.     }
  112. //     echo $f->manualRepresentation .'-'.$f->time->manualRepresentation."\n";
  113.     $startf->setSlots($this->slotrules);
  114.     $startf->setSlotStart($start);
  115.     $startf->setEditableOutput(falsetrue);
  116.     $this->addElement($startf);
  117.  
  118.     $durationf new TimeField('duration'T_('Duration'));
  119. //     $this->duration = &$durationf;
  120.     $durationf->required 1;
  121.     $durationf->isValidTest 'is_valid_nonzero_time';
  122.     $durationf->defaultValue $duration;
  123.     if ($this->_auth->permitted(BBROLE_MAKE_BOOKINGS_FREE$instrumentid)) {
  124.       $durationf->setManualRepresentation($this->id == -TF_FREE TF_FREE_ALWAYS);
  125.     else {
  126.       $durationf->setManualRepresentation(TF_AUTO);
  127.     }
  128. //     echo $f->manualRepresentation .'-'.$f->time->manualRepresentation."\n";
  129.     $durationf->setSlots($this->slotrules);
  130.     $durationf->setSlotStart($start);
  131.  
  132.     $nextBooking new NextBooking($start$instrumentid);
  133.     $durationf->maxDateDropDown $nextBooking->booking;
  134.  
  135.     // load in instrument settings for how the dropdowns should be configured
  136.     $instrrow quickSQLSelect('instruments''id'$instrumentid);
  137.     $durationf->extendDropDown    issetSet($instrrow,   'bookacrossslots'true);
  138.     $durationf->maxSlotsDropDown  issetSet($instrrow,   'maxslotsbook',    20);
  139.     $durationf->maxPeriodDropDown issetSet($instrrow,   'maxbooklength',   86400);
  140.  
  141.     $this->addElement($durationf);
  142.  
  143.     $f new DropList('projectid'T_('Project'));
  144.     $f->connectDB('projects',
  145.                   array('id''name''longname'),
  146.                   'userid='.qw($this->euid),
  147.                   'name',
  148.                   'id',
  149.                   NULL,
  150.                   array('userprojects'=>'projectid=id'));
  151.     $f->setFormat('id''%s'array('name')' (%35.35s)'array('longname'));
  152.     $f->isValidTest 'is_valid_radiochoice';
  153.     $this->addElement($f);
  154.     $attrs array('size' => '48');
  155.     $f new TextField('comments'T_('Comment to show on calendar'));
  156.     $f->setAttr($attrs);
  157.     $this->addElement($f);
  158.     $f new TextField('log'T_('Logbook Entry'));
  159.     $f->setAttr($attrs);
  160.     $this->addElement($f);
  161.     $f new ReferenceField('userid'T_('User'));
  162.     $f->extraInfo('users''id''name');
  163.     $f->value $this->euid;
  164.     $this->addElement($f);
  165.     $f new ReferenceField('bookedby'T_('Recorded by'));
  166.     $f->extraInfo('users''id''name');
  167.     $f->value $auth->uid;
  168.     $f->editable $this->_auth->permitted(BBROLE_VIEW_BOOKINGS_DETAILS$instrumentid);
  169.     $f->hidden $f->editable;
  170.     $this->addElement($f);
  171.     $f new TextField('discount'T_('Discount (%)'));
  172.     $f->isValidTest 'is_number';
  173.     $f->defaultValue '0';
  174.     $f->editable $this->_auth->permitted(BBROLE_VIEW_BOOKINGS_DETAILS$instrumentid);
  175.     $f->hidden $f->editable;
  176.     $f->setAttr($attrs);
  177.     $this->addElement($f);
  178.     $f new TextField('ip'T_('Computer IP'));
  179.     $f->value $ip;
  180.     $f->editable 0;
  181.     $this->addElement($f);
  182.     $this->fill();
  183.     $this->dumpheader = 'Booking entry object';
  184.     $f new DummyField('edit');
  185.     $f->value '1';
  186.     $this->addElement($f);
  187.   }
  188.  
  189.   /**
  190.   *  secondary constructor that we can use just for deleting
  191.   *
  192.   * @param integer       $id           booking id number (existing number or -1 for new)
  193.   * @param integer       $instrumentid instrument id of instrument to be booked
  194.   */
  195.   function _bookingEntryShort($id$instrumentid{
  196.     $this->isShort = true;
  197.     $f new Field('id');
  198.     $f->value $id;
  199.     $this->addElement($f);
  200.     $f new Field('instrument');   //not necessary, but for peace-of-mind.
  201.     $f->value $instrumentid;
  202.     $this->addElement($f);
  203.     $f new Field('bookwhen');
  204.     $this->addElement($f);
  205.     $f new Field('userid'T_('User'));
  206.     $f->value $this->euid;
  207.     $this->addElement($f);
  208.     $f new Field('log'T_('Log'));
  209.     $this->addElement($f);
  210.     $this->fill();
  211.   }
  212.  
  213.   /**
  214.   *  check our admin status
  215.   *
  216.   * @param BumblebeeAuth $auth         authorisation object
  217.   * @param integer       $instrumentid instrument id of instrument to be booked
  218.   */
  219.   function _checkAuth($auth$instrumentid{
  220.     $this->_auth = $auth;
  221.     $this->uid = $auth->uid;
  222.     if ($this->id > && $this->_auth->permitted(BBROLE_VIEW_BOOKINGS_DETAILS$instrumentid)) {
  223.       $row quickSQLSelect('bookings''id'$this->id);
  224.       $this->euid = $row['userid'];
  225.     else {
  226.       $this->euid = $auth->getEUID();
  227.     }
  228.   }
  229.  
  230.   /**
  231.   * override the default update() method with a custom one that allows us to:
  232.   * - munge the start and finish times to fit in with the permitted granularity
  233.   */
  234.   function update($data{
  235.     $this->_setDefaultDiscount();
  236.     parent::update($data);
  237.     $this->fields['bookwhen']->setSlotStart($this->fields['bookwhen']->getValue());
  238.     $this->fields['duration']->setSlotStart($this->fields['bookwhen']->getValue());
  239.     return $this->changed;
  240.   }
  241.  
  242.   /**
  243.   * override the default fill() method with a custom one that allows us to...
  244.   * - work out what the startticks parameter is for generating links to the current calendar
  245.   * - check permissions on whether we should be allowed to change the dates
  246.   */
  247.   function fill({
  248.     parent::fill();
  249.     if (isset($this->fields['startticks']&& $this->fields['startticks']->value{
  250.       $this->fields['startticks']->value $this->fields['bookwhen']->getValue();
  251.     }
  252.     // check whether we are allowed to modify time fields: this picks up existing objects immediately
  253.     $this->_checkMinNotice();
  254.   }
  255.  
  256.   /**
  257.   * override the default sync() method with a custom one that allows us to...
  258.   * - send a booking confirmation email to the instrument supervisors
  259.   * - update the representation of times
  260.   */
  261.   function sync({
  262.     if (is_array($this->instrumentid&& count($this->instrumentid1{
  263.       $status STATUS_ERR;
  264.       foreach ($this->children as $c{
  265.         $status $c->sync();
  266.         if ($status == STATUS_ERR{
  267.           $this->errorMessage .= $c->errorMessage;
  268.           return $status;
  269.         }
  270.       }
  271.       return $status;
  272.     }
  273.  
  274.     $status parent::sync();
  275.     if ($status STATUS_OK{
  276.       $this->_sendBookingEmail();
  277.       if ($this->_auth->permitted(BBROLE_MAKE_BOOKINGS_FREE$this->instrumentid)) {
  278.         $this->fields['bookwhen']->setManualRepresentation($this->id == -TF_FREE TF_FREE_ALWAYS);
  279.         $this->fields['duration']->setManualRepresentation($this->id == -TF_FREE TF_FREE_ALWAYS);
  280.       }
  281.     }
  282.     return $status;
  283.   }
  284.  
  285.   /**
  286.   * Work out what the default discount for this timeslot is from the timeslotrules
  287.   */
  288.   function _setDefaultDiscount({
  289.     if ($this->isShortreturn;
  290.  
  291.     $starttime new SimpleDate($this->fields['bookwhen']->getValue());
  292.     $slot $this->slotrules->findSlotByStart($starttime);
  293.     if ($this->_auth->permitted(BBROLE_VIEW_BOOKINGS_DETAILS$this->instrumentid)) {
  294.       $this->fields['discount']->value (isset($slot->discount$slot->discount 0);
  295.       $this->log('BookingEntry::_setDefaultDiscount value '.$starttime->dateTimeString().' '.$slot->discount.'%');
  296.       return;
  297.     }
  298.  
  299.     if (isset($this->fields['discount']->value)) {  // handle missing values in the submission
  300.       //preDump($this->slotrules); preDump($slot);
  301.       $this->fields['discount']->defaultValue (isset($slot->discount$slot->discount 0);
  302.       $this->log('BookingEntry::_setDefaultDiscount defaultValue '.$starttime->dateTimeString().' '.$slot->discount.'%');
  303.     }
  304.   }
  305.  
  306.   /**
  307.   * make sure that a non-admin user is not trying to unbook the instrument with less than the minimum notice
  308.   */
  309.   function _checkMinNotice({
  310.     //$this->DEBUG=10;
  311.     // get some cursory checks out of the way to save the expensive checks for later
  312.     if ($this->_auth->permitted(BBROLE_UNBOOK_PAST$this->instrumentid|| $this->id == -1{
  313.       //then we are unrestricted
  314.       $this->log('Booking changes not limited by time restrictions as we are admin or new booking.',9);
  315.       return;
  316.     }
  317.     $booking new SimpleDate($this->fields['bookwhen']->getValue());
  318.     $timeoffset $this->minunbook*60*60;
  319.     $booking->addTime(-1*$timeoffset);
  320.     $now new SimpleDate(time());
  321.     $this->log('Booking times comparison: now='.$now->dateTimeString()
  322.                   .', minunbook='.$booking->dateTimeString());
  323.     if ($booking->ticks $now->ticks{
  324.       // then we can't edit the date and time and we shouldn't delete the booking
  325.       $this->log('Within limitation period, preventing time changes and deletion',9);
  326.       $this->deletable = 0;
  327.       $this->fields['bookwhen']->editable 0;
  328.       $this->fields['duration']->editable 0;
  329.     else {
  330.       $this->log('Booking changes not limited by time restrictions.',9);
  331.     }
  332.   }
  333.  
  334.   /**
  335.   *  if appropriate, send an email to the instrument supervisors to let them know that the
  336.   *  booking has been made
  337.   */
  338.   function _sendBookingEmail({
  339.     $conf ConfigReader::getInstance();
  340.  
  341.     //preDump($this->fields['instrument']);
  342.     $instrument quickSQLSelect('instruments''id'$this->fields['instrument']->getValue());
  343.     if ($instrument['emailonbooking']{
  344.       return;
  345.     }
  346.  
  347.     $emails array();
  348.     foreach(preg_split('/,\s*/'$instrument['supervisors']as $username{
  349.       $user quickSQLSelect('users''username'$username);
  350.       $emails[$user['email'];
  351.     }
  352.     $bookinguser quickSQLSelect('users''id'$this->fields['userid']->value);
  353.     $eol "\r\n";
  354.     $from $instrument['name'].' '.$conf->value('instruments''emailFromName')
  355.             .' <'.$conf->value('main''SystemEmail').'>';
  356.     $replyto $bookinguser['name'].' <'.$bookinguser['email'].'>';
  357.     $to   join($emails',');
  358.     srand(time());
  359.     $id   '<bumblebee-'.time().'-'.rand().'@'.$_SERVER['SERVER_NAME'].'>';
  360.  
  361.     $headers  'From: '.$from .$eol;
  362.     $headers .= 'Reply-To: '.$replyto.$eol;
  363.     $headers .= 'Message-id: ' .$id .$eol;
  364.     $subject $instrument['name']': '($conf->value('instruments''emailSubject')
  365.                     ? $conf->value('instruments''emailSubject''Instrument booking notification');
  366.     $message $this->_getEmailText($instrument$bookinguser);
  367.  
  368.     // Send the message
  369.     #preDump($to);
  370.     #preDump($subject);
  371.     #preDump($headers);
  372.     #preDump($message);
  373.     $ok @mail($to$subject$message$headers);
  374.     return $ok;
  375.  
  376.   }
  377.  
  378.   /**
  379.   *  get the email text from the configured template with standard substitutions
  380.   *
  381.   * @param array  $instrument   instrument data (name => , longname => )
  382.   * @param array  $user         user data (name => , username => )
  383.   *
  384.   * @todo //TODO:  graceful error handling for fopen, fread
  385.   */
  386.   function _getEmailText($instrument$user{
  387.     $conf ConfigReader::getInstance();
  388.  
  389.     $fh fopen($conf->value('instruments''emailTemplate')'r');
  390.     $txt fread($fhfilesize($conf->value('instruments''emailTemplate')));
  391.     fclose($fh);
  392.     $start    new SimpleDate($this->fields['bookwhen']->getValue());
  393.     $duration new SimpleTime($this->fields['duration']->getValue());
  394.     $replace array(
  395.             '/__instrumentname__/'      => $instrument['name'],
  396.             '/__instrumentlongname__/'  => $instrument['longname'],
  397.             '/__start__/'               => $start->dateTimeString(),
  398.             '/__duration__/'            => $duration->timeString(),
  399.             '/__name__/'                => $user['name'],
  400.             '/__username__/'            => $user['username'],
  401.             '/__host__/'                => makeAbsURL()
  402.                     );
  403.     $txt preg_replace(array_keys($replace),
  404.                         array_values($replace),
  405.                         $txt);
  406.     return $txt;
  407.   }
  408.  
  409.   /**
  410.   * override the default checkValid() method with a custom one that also checks that the
  411.   * booking is permissible (i.e. the instrument is indeed free)
  412.   *
  413.   * A temp booking is made by _checkIsFree if all tests are OK. This temporary booking
  414.   * secures the slot (no race conditions) and is then updated by the sync() method.
  415.   */
  416.   function checkValid({
  417.     //$this->DEBUG = 10;
  418.     parent::checkValid();
  419.     if ($this->isValid{
  420.       $this->log('Fields are INVALID; bailing out');
  421.       return $this->isValid;
  422.     }
  423.     $this->log('Individual fields are VALID');
  424.     $this->isValid = $this->_auth->permitted(BBROLE_MAKE_BOOKINGS_FREE$this->instrumentid)
  425.                         || ($this->isValid && $this->_legalSlot(&& $this->_permittedFuture());
  426.     $this->log('After checking for legality of timeslot: '.($this->isValid ? 'VALID' 'INVALID'));
  427.     $this->isValid = $this->isValid && $this->_checkIsFree();
  428.     $this->log('After checking for double bookings: '.($this->isValid ? 'VALID' 'INVALID'));
  429.     return $this->isValid;
  430.   }
  431.  
  432.   function display({
  433.     // check again whether we are allowed to modify time objects -- after sync() we might not
  434.     // be allowed to any more.
  435.     $this->_checkMinNotice();
  436.     return $this->displayAsTable();
  437.   }
  438.  
  439.   /**
  440.   * check that the booking slot is indeed free before booking it
  441.   *
  442.   * Here, we make a temporary booking and make sure that it is unique for that timeslot
  443.   * This is to prevent a race condition for checking and then making the new booking.
  444.   *
  445.   * @global string prefix for table names
  446.   ***/
  447.   function _checkIsFree({
  448.     global $TABLEPREFIX;
  449.  
  450.     if (is_array($this->instrumentid&& count($this->instrumentid1{
  451.       $this->children array();
  452.       foreach ($this->instrumentid as $instr{
  453.         $clone clone($this);
  454.         $clone->instrumentid $instr;
  455.         $clone->fields['instrument']->value $instr;
  456.         $status $clone->_checkIsFree();
  457.         $this->children[$clone;
  458.         if ($status{
  459.           $this->errorMessage .= $clone->errorMessage;
  460.           return $status;
  461.         }
  462.       }
  463.       return $status;
  464.     }
  465.  
  466.     if ($this->changedreturn 1;
  467.     #preDump($this);
  468.     $doubleBook 0;
  469.     $instrument $this->fields['instrument']->getValue();
  470.     $startdate new SimpleDate($this->fields['bookwhen']->getValue());
  471.     $start $startdate->dateTimeString();
  472.     $d new SimpleDate($start);
  473.     $duration new SimpleTime($this->fields['duration']->getValue());
  474.     $d->addTime($duration);
  475.     $stop $d->dateTimeString();
  476.  
  477.     $tmpid $this->_makeTempBooking($instrument$start$duration->getHMSstring());
  478.     $this->log('Created temp row for locking, id='.$tmpid.'(origid='.$this->id.')');
  479.  
  480.     $q 'SELECT bookings.id AS bookid, bookwhen, duration, '
  481.         .'DATE_ADD( bookwhen, INTERVAL duration HOUR_SECOND ) AS stoptime, '
  482.         .'name AS username '
  483.         .'FROM '.$TABLEPREFIX.'bookings AS bookings '
  484.         .'LEFT JOIN '.$TABLEPREFIX.'users AS users ON '
  485.         .'bookings.userid = users.id '
  486.         .'WHERE instrument='.qw($instrument).' '
  487.         .'AND bookings.id<>'.qw($this->id).' '
  488.         .'AND bookings.id<>'.qw($tmpid).' '
  489.         .'AND userid<>0 '
  490.         .'AND bookings.deleted<>1 '        // old version of MySQL cannot handle true, use 1 instead
  491.         .'HAVING (bookwhen <= '.qw($start).' AND stoptime > '.qw($start).') '
  492.         .'OR (bookwhen < '.qw($stop).' AND stoptime >= '.qw($stop).') '
  493.         .'OR (bookwhen >= '.qw($start).' AND stoptime <= '.qw($stop).')';
  494.     $row db_get_single($q$this->fatal_sql);
  495.     if (is_array($row)) {
  496.       // then the booking actually overlaps another!
  497.       $this->log('Overlapping bookings, error');
  498.       $this->_removeTempBooking($tmpid);
  499.       $doubleBook 1;
  500.       $this->errorMessage .= T_('Sorry, the instrument is not free at this time.').'<br /><br />'
  501.                           .sprintf(T_('Instrument booked by %s (%s) from %s until %s.'),
  502.                                   $row['username'],
  503.                                   '<a href="'.
  504.                                     makeURL('book',
  505.                                       array('instrid' => $instrument,
  506.                                             'bookid'  => $row['bookid'],
  507.                                             'isodate' => $startdate->dateString())
  508.                                            ).'">'
  509.                                       .T_('booking #').$row['bookid'].'</a>',
  510.                                   xssqw($row['bookwhen']),
  511.                                   xssqw($row['stoptime']));
  512.     else {
  513.       // then the new booking should take over this one, and we delete the old one.
  514.       $this->log('Booking slot OK, taking over tmp slot');
  515.       $oldid $this->id;
  516.       $this->id = $tmpid;
  517.       $this->fields[$this->idfield]->set($this->id);
  518.       $this->insertRow = 0;
  519.       $this->includeAllFields = 1;
  520.       $this->_removeTempBooking($oldid);
  521.     }
  522.     return $doubleBook;
  523.   }
  524.  
  525.   /**
  526.   * Ensure that the entered data fits the granularity criteria specified for this instrument
  527.   */
  528.   function _legalSlot({
  529.     #$this->DEBUG=10;
  530.     $starttime new SimpleDate($this->fields['bookwhen']->getValue());
  531.     $stoptime $starttime;
  532.     $stoptime->addTime(new SimpleTime($this->fields['duration']->getValue()));
  533.     $this->log('BookingEntry::_legalSlot '.$starttime->dateTimeString()
  534.                   .' '.$stoptime->dateTimeString());
  535.     $validslot $this->slotrules->isValidSlot($starttime$stoptime);
  536.     if ($validslot{
  537.       $this->log('This slot isn\'t legal so far... perhaps it is FreeForm?');
  538.       $startslot $this->slotrules->findSlotByStart($starttime);
  539.       if ($startslot{
  540.         $startslot $this->slotrules->findSlotFromWithin($starttime);
  541.       }
  542.       //echo "now stop";
  543.       $stopslot  $this->slotrules->findSlotByStop($stoptime);
  544.       if ($stopslot{
  545.         $stopslot $this->slotrules->findSlotFromWithin($stoptime);
  546.       }
  547.       #echo $startslot->start->dump();
  548.       #echo $starttime->dump();
  549.       #echo $stopslot->stop->dump();
  550.       #echo $stoptime->dump();
  551.       $validslot $startslot->isFreeForm && $stopslot->isFreeForm;
  552.       $this->log('It '.($validslot 'is' 'is not').'!');
  553.       if ($validslot{
  554.         $this->log('Perhaps it is adjoining another booking with funny times?');
  555.         $startok ($startslot->start->ticks == $starttime->ticks);
  556.         if ($startok{
  557.           $this->log('Checking start time for adjoining stop');
  558.           $startvalid $this->_checkTimesAdjoining('stoptime'$starttime);
  559.         }
  560.         $stopok  ($stopslot->stop->ticks  == $stoptime->ticks);
  561.         if ($stopok{
  562.           $this->log('Checking stop time for adjoining start');
  563.           $stopvalid  $this->_checkTimesAdjoining('bookwhen'$stoptime);
  564.         }
  565.         $validslot ($startok || $startvalid&& ($stopok || $stopvalid);
  566.         $this->log('It '.($validslot 'is' 'is not').'!');
  567.       }
  568.     }
  569.     if ($validslot{
  570.       $this->errorMessage .= T_('Sorry, the timeslot you have selected is not valid, due to restrictions imposed by the instrument administrator.');
  571.     }
  572.     return $validslot;
  573.   }
  574.  
  575.   /**
  576.   * Check that the booking is not too far into the future
  577.   *
  578.   * @returns boolean    the booking is permitted
  579.   */
  580.   function _permittedFuture({
  581.     if ($this->_auth->permitted(BBROLE_MAKE_BOOKINGS_FUTURE$this->instrumentid)) return true;
  582.  
  583.     $now new SimpleDate(time());
  584.     $now->dayRound();
  585.     $now->addDays(1);
  586.  
  587.     $starttime new SimpleDate($this->fields['bookwhen']->getValue());
  588.  
  589.     // permit bookings today or in the past
  590.     if ($now->ticks $starttime->ticksreturn true;
  591.  
  592.     $row quickSQLSelect('instruments''id'$this->instrumentid);
  593.     $now new SimpleDate(time());
  594.     $now->weekRound();
  595.     $now->addDays($row['calfuture'1);
  596.  
  597.     if ($now->ticks $starttime->ticks{
  598.       $this->errorMessage .= T_('Sorry, you cannot book that far into the future, due to restrictions imposed by the instrument administrator.');
  599.     else {
  600.       return true;
  601.     }
  602.   }
  603.  
  604.   /**
  605.   * check if this booking is adjoining existing bookings -- it can explain why the booking
  606.   * is at funny times.
  607.   *
  608.   * @param string   $field        SQL name of the field to be checked (stoptime, bookwhen)
  609.   * @param SimpleDate $checktime  time to check to see if it is adjoining the new booking
  610.   *
  611.   * @return boolean   there is a booking adjoining this time
  612.   * @global string   prefix prepended to all table names in the db
  613.   */
  614.   function _checkTimesAdjoining($field$checktime{
  615.       global $TABLEPREFIX;
  616.       $instrument $this->fields['instrument']->getValue();
  617.       $time $checktime->dateTimeString();
  618.       $q 'SELECT bookings.id AS bookid, bookwhen, duration, '
  619.           .'DATE_ADD( bookwhen, INTERVAL duration HOUR_SECOND ) AS stoptime '
  620.           .'FROM '.$TABLEPREFIX.'bookings AS bookings '
  621.           .'WHERE instrument='.qw($instrument).' '
  622.           .'AND userid<>0 '
  623.           .'AND bookings.deleted<>1 '        // old version of MySQL cannot handle true, use 1 instead
  624.           .'HAVING '.$field.' = '.qw($time);
  625.       $row db_get_single($q$this->fatal_sql);
  626.       $this->log(is_array($row'Found a matching booking' 'No matching booking');
  627.       return (is_array($row));
  628.   }
  629.  
  630.   /**
  631.   * make a temporary booking for this slot to eliminate race conditions for this booking
  632.   *
  633.   * @param integer  $instrument  instrument id
  634.   * @param string   $start       date time string for the start of the booking
  635.   * @param string   $duration    time string for the duration of the booking
  636.   * @return integer  booking id number of the temporary booking
  637.   */
  638.   function _makeTempBooking($instrument$start$duration{
  639.     $row new DBRow('bookings'-1'id');
  640.     $f new Field('id');
  641.     $f->value = -1;
  642.     $row->addElement($f);
  643.     $f new Field('instrument');
  644.     $f->value $instrument;
  645.     $row->addElement($f);
  646.     $f new Field('bookwhen');
  647.     $f->value $start;
  648.     $row->addElement($f);
  649.     $f new Field('duration');
  650.     $f->value $duration;
  651.     $row->addElement($f);
  652.     $row->isValid 1;
  653.     $row->changed 1;
  654.     $row->insertRow 1;
  655.     $row->sync();
  656.     return $row->id;
  657.   }
  658.  
  659.   /**
  660.   * remove the temporary booking for this slot
  661.   *
  662.   * @param integer $tmpid   booking id number of the temporary booking
  663.   */
  664.   function _removeTempBooking($tmpid{
  665.     $this->log('Removing row, id='$tmpid);
  666.     $row new DBRow('bookings'$tmpid'id');
  667.     $row->delete();
  668.   }
  669.  
  670.   /**
  671.   *  delete the entry by marking it as deleted, don't actually delete the
  672.   *
  673.   *  @return integer  from statuscodes
  674.   */
  675.   function delete($unused=null{
  676.     $this->_checkMinNotice();
  677.     if ($this->deletable && $this->_auth->permitted(BBROLE_UNBOOK$this->instrumentid)) {
  678.       // we're not allowed to do so
  679.       $this->errorMessage = T_('Sorry, this booking cannot be deleted due to booking policy.');
  680.       return STATUS_FORBIDDEN;
  681.     }
  682.     $sql_result = -1;
  683.     $today new SimpleDate(time());
  684.     $newlog $this->fields['log']->value
  685.                   .' '
  686.                   .sprintf(T_('Booking deleted by %s (user #%s) on %s.'),
  687.                         $this->_auth->username,
  688.                         $this->uid,
  689.                         $today->dateTimeString());
  690. /*                  .'Booking deleted by '.$this->_auth->username
  691.                   .' (user #'.$this->uid.') on '.$today->dateTimeString().'.';*/
  692.     return parent::delete('log='.qw($newlog));
  693.   }
  694.  
  695.  
  696.  

Documentation generated on Tue, 06 Mar 2007 10:00:53 +0000 by phpDocumentor 1.3.0