![]() |
Xataface Calendar Module 0.1
Full Calendar for Xataface
|
00001 <?php 00007 class modules_calendar_RepeatEvent { 00008 00012 public static $EX_INVALID_FREQUENCY = 501; 00013 00017 public static $EX_INVALID_REPEAT_ID=502; 00018 00019 00024 private static $REPEAT_TABLE='dataface__calendar_repeats'; 00025 00030 private static $module = null; 00031 00036 private $sourceRecord; 00037 00043 private $startDate; 00044 00050 private $expiryDate; 00051 00052 00064 private $frequency; 00065 private $frequencyChanged=false; 00066 00071 private $_isSaved; 00072 00079 private $startAtt; 00080 00087 private $endAtt; 00088 00094 private $repeatAtt; 00095 00096 00100 public static function createRepeatTable(){ 00101 $tname = self::$REPEAT_TABLE; 00102 $sql = "create table if not exists `$tname` ( 00103 tablename varchar(200) not null, 00104 repeat_id int(11) not null, 00105 repeat_type enum('Daily','Weekly','Biweekly','Weekdays','Monthly','Yearly') default 'Weekly', 00106 repeat_start_date datetime, 00107 repeat_expire_date datetime, 00108 primary key (tablename,repeat_id) 00109 ) 00110 "; 00111 00112 return self::query($sql); 00113 00114 00115 00116 00117 } 00118 00119 00120 public static function dropRepeatTable(){ 00121 $tname = self::$REPEAT_TABLE; 00122 $sql = "drop table if exists `$tname`"; 00123 return self::query($sql); 00124 } 00125 00132 private static function query($sql){ 00133 $res = mysql_query($sql, df_db()); 00134 if ( !$res ){ 00135 throw new Exception(mysql_error(df_db())); 00136 } 00137 return $res; 00138 } 00139 00140 private static function cleanIdent($val){ 00141 return str_replace('`','',$val); 00142 } 00143 00152 private static function queryRepeats($sql){ 00153 00154 try { 00155 return self::query($sql); 00156 } catch (Exception $ex){ 00157 self::createRepeatTable(); 00158 00159 } 00160 return self::query($sql); 00161 } 00162 00167 public static function getModule(){ 00168 if ( !isset(self::$module) ){ 00169 self::$module = Dataface_ModuleTool::getInstance()->loadModule('modules_calendar'); 00170 } 00171 return self::$module; 00172 } 00173 00190 private function getAtt($name){ 00191 $att = self::getModule() 00192 ->loadOntology($this->sourceRecord->table()->tablename) 00193 ->getFieldname($name); 00194 00195 if ( PEAR::isError($att) ){ 00196 throw new Exception("No start attribute found for table."); 00197 } 00198 return $att; 00199 } 00200 00208 public function getStartAtt(){ 00209 return $this->getAtt('start'); 00210 } 00211 00212 00220 public function getEndAtt(){ 00221 return $this->getAtt('end'); 00222 } 00223 00231 public function getRepeatAtt(){ 00232 return $this->getAtt('repeat'); 00233 } 00234 00235 public function getRepeatSeedAtt(){ 00236 return $this->getAtt('repeat_seed'); 00237 } 00238 00244 public function getSourceStart(){ 00245 return $this->sourceRecord->strval($this->getStartAtt()); 00246 } 00247 00253 public function getSourceEnd(){ 00254 return $this->sourceRecord->strval($this->getEndAtt()); 00255 } 00256 00263 public function getRepeatID(){ 00264 return $this->sourceRecord->val($this->getRepeatAtt()); 00265 } 00266 00272 public function __construct(Dataface_Record $source){ 00273 $this->sourceRecord = $source; 00274 $this->loadRepeatInfo(); 00275 } 00276 00277 00278 00285 public function loadRepeatInfo(){ 00286 if ( !$this->getRepeatID() ){ 00287 00288 return; 00289 } 00290 $sql = sprintf("select * from `%s` where `tablename`='%s' and `repeat_id`=%d", 00291 self::cleanIdent(self::$REPEAT_TABLE), 00292 addslashes($this->sourceRecord->table()->tablename), 00293 intval($this->getRepeatID()) 00294 ); 00295 $this->_isSaved = false; 00296 $res = self::queryRepeats($sql); 00297 00298 while ($row = mysql_fetch_assoc($res) ){ 00299 $this->expiryDate = $row['repeat_expire_date']; 00300 $this->frequency = $row['repeat_type']; 00301 $this->startDate = $row['repeat_start_date']; 00302 $this->_isSaved = true; 00303 } 00304 @mysql_free_result($res); 00305 } 00306 00307 00308 public function getDefaultExpiryDifferential(){ 00309 $defaultDiff = '+90 days'; 00310 $settingsField = $this->getModule()->getRepeatSettingsField($this->sourceRecord->table()->tablename); 00311 if ( $settingsField ){ 00312 $settingsFieldDef =& $this->sourceRecord->table()->getField($settingsField); 00313 if ( isset($settingsFieldDef['widget']['default_expiry']) ){ 00314 $defaultDiff = $settingsFieldDef['widget']['default_expiry']; 00315 } 00316 } 00317 return $defaultDiff; 00318 00319 } 00320 00331 public function saveRepeatInfo(){ 00332 00333 if ( !$this->frequency ){ 00334 throw new Exception('Frequency is not set for this repeat.', self::$EX_INVALID_FREQUENCY); 00335 } 00336 00337 if ( !$this->startDate ){ 00338 $this->startDate = $this->getSourceStart(); 00339 } 00340 00341 00342 00343 if ( !$this->expiryDate ){ 00344 $this->expiryDate = date( 00345 'Y-m-d H:i:s' 00346 , strtotime( 00347 $this->getDefaultExpiryDifferential() 00348 , strtotime( 00349 $this->getSourceStart() 00350 ) 00351 ) 00352 ); 00353 00354 } 00355 00356 $repeatID = $this->getRepeatID(); 00357 if ( !$repeatID ){ 00358 // The repeat ID hasn't been set yet 00359 $seedField = $this->getRepeatSeedAtt(); 00360 $this->sourceRecord->setValue( 00361 $this->getRepeatAtt(), 00362 $this->sourceRecord->val($seedField) 00363 ); 00364 $res = $this->sourceRecord->save(null, $secure); 00365 if ( PEAR::isError($res) ){ 00366 throw new Exception($res->getMessage(), $res->getCode()); 00367 } 00368 $repeatID = $this->getRepeatID(); 00369 } 00370 if ( !isset($repeatID) ){ 00371 throw new Exception("Could not commit repeat sequence because it doesn't have a valid repeat id.", self::$EX_INVALID_REPEAT_ID); 00372 00373 } 00374 00375 00376 00377 $isql = sprintf("insert into `%s` (`tablename`,`repeat_id`,`repeat_type`, `repeat_start_date`, `repeat_expire_date`) 00378 values 00379 ('%s',%d,'%s','%s','%s')" 00380 , self::cleanIdent(self::$REPEAT_TABLE) 00381 , addslashes($this->sourceRecord->table()->tablename) 00382 , intval($this->getRepeatID()) 00383 , addslashes($this->frequency) 00384 , addslashes($this->startDate) 00385 , addslashes($this->expiryDate) 00386 ); 00387 $usql = sprintf("update `%s` set 00388 `repeat_type`='%s', 00389 `repeat_start_date`='%s', 00390 `repeat_expire_date`='%s' 00391 where 00392 `tablename`='%s' and `repeat_id`=%d" 00393 , self::cleanIdent(self::$REPEAT_TABLE) 00394 , addslashes($this->frequency) 00395 , addslashes($this->startDate) 00396 , addslashes($this->expiryDate) 00397 , addslashes($this->sourceRecord->table()->tablename) 00398 , intval($this->getRepeatID()) 00399 ); 00400 00401 if ( $this->isSaved() ){ 00402 self::queryRepeats($usql); 00403 } else { 00404 self::queryRepeats($isql); 00405 } 00406 00407 } 00408 00415 public function getRepeatRecords($limit=250, $query=array()){ 00416 00417 $query[$this->getRepeatAtt()] = '='.$this->getRepeatID(); 00418 $query['-limit'] = $limit; 00419 00420 if ( !isset($query[$this->getStartAtt()] ) ){ 00421 $query[$this->getStartAtt()] = '>'.$this->getSourceStart(); 00422 } 00423 return df_get_records_array($this->sourceRecord->table()->tablename, $query); 00424 } 00425 00426 00444 public function updateRepeats(array $changes, $startDiffSeconds, $endDiffSeconds, $secure=false){ 00445 00446 $repeats = $this->getRepeatRecords(999); 00447 $errors = array(); 00448 $del = $this->sourceRecord->table()->getDelegate(); 00449 00450 $beforeUpdateExists = (isset($del) and method_exists($del, 'beforeUpdateRepeat')); 00451 $afterUpdateExists = (isset($del) and method_exists($del, 'afterUpdateRepeat') ); 00452 $beforeSaveRepeatExists = (isset($del) and method_exists($del, 'beforeSafeRepeat')); 00453 $afterSaveRepeatExists = (isset($del) and method_exists($del, 'afterSaveRepeat')); 00454 00455 foreach ($repeats as $event){ 00456 try { 00457 $oldStart = $event->strval($this->getStartAtt()); 00458 $oldEnd = $event->strval($this->getEndAtt()); 00459 00460 $newStartTime = strtotime('+'.$startDiffSeconds.' seconds', strtotime($oldStart)); 00461 $newEndTime = strtotime('+'.$endDiffSeconds.' seconds', strtotime($oldEnd)); 00462 00463 $changes[$this->getStartAtt()] = date('Y-m-d H:i:s', $newStartTime); 00464 $changes[$this->getEndAtt()] = date('Y-m-d H:i:s', $newEndTime); 00465 00466 $event->setValues($changes); 00467 00468 if ( $beforeSaveRepeatExists ){ 00469 $del->beforeSaveRepeat($event, $this, $changes); 00470 } 00471 00472 if ( $beforeUpdateExists ){ 00473 $del->beforeUpdateRepeat($event, $this, $changes); 00474 00475 } 00476 $res = $event->save(null, $secure); 00477 if ( PEAR::isError($res) ){ 00478 throw new Exception($res->getMessage(), $res->getCode()); 00479 00480 } 00481 00482 00483 if ( $afterUpdateExists ){ 00484 $del->afterUpdateRepeat($event, $this, $changes); 00485 00486 } 00487 00488 if ( $afterSaveRepeatExists ){ 00489 $del->afterSaveRepeat($event, $this, $changes); 00490 } 00491 00492 } catch (Exception $ex){ 00493 $errors[] = $ex; 00494 } 00495 00496 } 00497 00498 return $errors; 00499 00500 00501 } 00502 00503 00513 public function clearRepeats($secure=false, $query=array()){ 00514 $del = $this->sourceRecord->table()->getDelegate(); 00515 $repeats = $this->getRepeatRecords(999, $query); 00516 $errors = array(); 00517 $beforeDeleteExists = (isset($del) and method_exists($del, 'beforeDeleteRepeat')); 00518 $afterDeleteExists = (isset($del) and method_exists($del, 'afterDeleteRepeat')); 00519 00520 foreach ($repeats as $event){ 00521 try { 00522 if ( $beforeDeleteExists ){ 00523 00524 $del->beforeDeleteRepeat($event, $this); 00525 00526 00527 } 00528 00529 $res = $event->delete($secure); 00530 if ( PEAR::isError($res) ){ 00531 throw new Exception($res->getMessage(), $res->getCode()); 00532 } 00533 00534 if ( $afterDeleteExists ){ 00535 $res = $del->afterDeleteRepeat($event, $this); 00536 00537 } 00538 } catch (Exception $ex){ 00539 $errors[] = $ex; 00540 } 00541 00542 } 00543 00544 return $errors; 00545 } 00546 00556 public function fillRepeats($secure=false){ 00557 $del = $this->sourceRecord->table()->getDelegate(); 00558 $errors = array(); 00559 $beforeAddRepeatExists = (isset($del) and method_exists($del, 'beforeAddRepeat')); 00560 $afterAddRepeatExists = (isset($del) and method_exists($del, 'afterAddRepeat')); 00561 $beforeSaveRepeatExists = (isset($del) and method_exists($del, 'beforeSafeRepeat')); 00562 $afterSaveRepeatExists = (isset($del) and method_exists($del, 'afterSaveRepeat')); 00563 00564 00565 // Let's get the last existing event in this repeat sequence 00566 $lastEvent = df_get_record($this->sourceRecord->table()->tablename, 00567 array( 00568 $this->getRepeatAtt() => $this->getRepeatID(), 00569 '-sort' => $this->getStartAtt().' desc' 00570 ) 00571 ); 00572 00573 if ( !$lastEvent ){ 00574 throw new Exception("Could not find last event in repeat sequence. Please check that the source record of this event sequence has a valid repeat id."); 00575 00576 } 00577 00578 00579 /* 00580 $currTime = strtotime($this->getSourceStart()); 00581 $maxTime = strtotime($this->getExpiryDate()); 00582 $currEnd = strtotime($this->getSourceEnd()); 00583 */ 00584 $currTime = strtotime( 00585 date('Y-m-d', 00586 strtotime( 00587 $lastEvent->strval($this->getStartAtt()) 00588 ) 00589 ) 00590 . ' ' 00591 . date('H:i:s', 00592 strtotime( 00593 $this->getSourceStart() 00594 ) 00595 ) 00596 ); 00597 00598 $maxTime = strtotime($this->getExpiryDate()); 00599 $currEnd = strtotime( 00600 date('Y-m-d', 00601 strtotime( 00602 $lastEvent->strval($this->getEndAtt()) 00603 ) 00604 ) 00605 . ' ' 00606 . date('H:i:s', 00607 strtotime( 00608 $this->getSourceEnd() 00609 ) 00610 ) 00611 ); 00612 00613 $stepsize = null; 00614 switch ($this->getFrequency()){ 00615 00616 case 'Daily': $stepsize = '+1 day';break; 00617 case 'Weekly': $stepsize = '+1 week';break; 00618 case 'Biweekly': $stepsize = '+2 week'; break; 00619 case 'Monthly': $stepsize = '+1 month'; break; 00620 case 'Yearly': $stepsize = '+1 year'; break; 00621 case 'Weekdays': 00622 if ( date('N', $currtime) == 5 ){ 00623 $stepsize = '+3 day'; 00624 } else { 00625 $stepsize = '+1 day'; 00626 } 00627 break; 00628 } 00629 00630 if ( !$stepsize ){ 00631 throw new Exception("Invalid frequency '".$this->getFrequency()."' in repeat."); 00632 } 00633 00634 $baseVals = $this->sourceRecord->vals(); 00635 $autoField = $this->sourceRecord->table()->getAutoIncrementField(); 00636 if ( !$autoField ){ 00637 throw new Exception("Repeat tables require an auto increment field."); 00638 } 00639 unset($baseVals[$autoField]); 00640 00641 $errors = array(); 00642 while ( $currTime < $maxTime ){ 00643 //echo "[Adding $currTime]"; 00644 try { 00645 $currTime = strtotime($stepsize, $currTime); 00646 $currEnd = strtotime($stepsize, $currEnd); 00647 00648 $event = new Dataface_Record($this->sourceRecord->table()->tablename, array()); 00649 $event->setValues($baseVals); 00650 $event->setValue($this->getStartAtt(), date('Y-m-d H:i:s', $currTime)); 00651 $event->setValue($this->getEndAtt(), date('Y-m-d H:i:s', $currEnd)); 00652 00653 if ( $beforeSaveRepeatExists ){ 00654 $del->beforeSaveRepeat($event, $this); 00655 } 00656 00657 if ( $beforeAddRepeatExists ){ 00658 $del->beforeAddRepeat($event, $this); 00659 } 00660 00661 $res = $event->save(null, $secure); 00662 if ( PEAR::isError($res) ){ 00663 throw new Exception($res->getMessage(), $res->getCode()); 00664 } 00665 00666 if ( $afterAddRepeatExists ){ 00667 $del->afterAddRepeat($event, $this); 00668 } 00669 00670 if ( $afterSaveRepeatExists ){ 00671 $del->afterSaveRepeat($event, $this); 00672 } 00673 00674 } catch (Exception $ex){ 00675 $errors[] = $ex; 00676 } 00677 00678 00679 00680 00681 00682 } 00683 00684 00685 return $errors; 00686 } 00687 00688 00689 00695 public function isSaved(){ 00696 if ( !$this->getRepeatID() ) return false; 00697 if ( !isset($this->_isSaved) ){ 00698 $this->loadRepeatInfo(); 00699 } 00700 return $this->_isSaved; 00701 } 00702 00703 00704 00713 public function setSourceRecord(Dataface_Record $record){ 00714 $this->expiryDate = null; 00715 $this->startDate = null; 00716 $this->frequency = null; 00717 $this->sourceRecord = $record; 00718 $this->_isSaved = null; 00719 } 00720 00728 public function getSourceRecord(){ 00729 return $this->sourceRecord; 00730 } 00731 00742 public function setFrequency($freq){ 00743 if ( $freq != $this->frequency ){ 00744 $this->frequency = $freq; 00745 $this->frequencyChanged = true; 00746 } 00747 } 00748 00760 public function getFrequency(){ return $this->frequency;} 00761 00767 public function setStartDate($date){ $this->startDate = $date;} 00768 00769 public function getStartDate(){ return $this->startDate; } 00770 00775 public function setExpiryDate($d){ $this->expiryDate = $d;} 00776 00781 public function getExpiryDate(){ return $this->expiryDate;} 00782 00783 00784 public function commit(array $changes, $startDiffSeconds, $endDiffSeconds, $secure=false ){ 00785 00786 00787 00788 00789 $this->saveRepeatInfo(); 00790 $errors = array(); 00791 if ( $this->frequencyChanged ){ 00792 //If the frequency has changed, we need to clear all of the existing repeat 00793 // events and build them anew. 00794 $errors = array_merge($errors, $this->clearRepeats($secure)); 00795 $this->frequencyChanged = false; 00796 } else { 00797 // Let's clear all of the repeats that come after the expiry date 00798 $errors = array_merge($errors, $this->clearRepeats($secure, array( 00799 $this->getStartAtt()=>'>'.$this->getExpiryDate() 00800 ))); 00801 } 00802 00803 // Now let's fill all of the repeats 00804 $errors = array_merge($errors,$this->fillRepeats($secure)); 00805 00806 // Now let's update the repeat records. 00807 // Will be redundant the first time the repeats are filled, but that's ok 00808 // we may optimize this later. 00809 if ( $changes or $startDiffSeconds or $endDiffSeconds ){ 00810 $errors = array_merge($errors, 00811 $this->updateRepeats($changes, $startDiffSeconds, $endDiffSeconds, $secure) 00812 ); 00813 } 00814 00815 return $errors; 00816 00817 00818 } 00819 00820 00821 }