contentRootId = 'timeTable-treeRootNode'; $this->contentRootTag = 'timetabList'; $this->attributes = array('name' => '', 'intervals' => ''); $this->optionalAttributes = array(); $this->objTagName = 'timetab'; $this->id_prefix = 'tt_'; if (!$sharedObject && !file_exists($this->xmlName)) { $this->createDom(); $this->xp = new domxpath($this->contentDom); } } protected function createDom() { $types = array('timetab' => 'timeTable', 'catalog' => 'catalog'); $rootElement = $this->contentDom->createElement('ws'); foreach ($types as $key => $value) { $contentId = $value.'-treeRootNode'; $contentTag = $key.'List'; $typeElement = $this->contentDom->createElement($contentTag); $typeElement->setAttribute('xml:id', $contentId); $rootElement->appendChild($typeElement); } $this->contentDom->appendChild($rootElement); $this->contentDom->save($this->xmlName); } /* * rename Time Table in id.xml */ protected function renameInResource($name, $id) { if (!file_exists(USERTTDIR.$id.'.xml')) return false; $this->objectDom -> load(USERTTDIR.$id.'.xml'); if (!($objToRename = $this->objectDom->getElementById($id))) return false; $objToRename -> getElementsByTagName('name')->item(0)->nodeValue = $name; $this->objectDom ->save(USERTTDIR.$id.'.xml'); return true; } protected function deleteParameter($id) { if (file_exists(USERTTDIR.$id.'.xml')) unlink(USERTTDIR.$id.'.xml'); } /* * Check if difference is name and info only */ protected function renameOnly($p) { //if (!($p->intervals)) return true; return false; } /* * In case of catalogs */ protected function setParamDescription($param) { } /* * Create Time Table */ protected function createParameter($p, $folder) { if ($this -> objectExistsByName($p->name)) { $p -> id = $this -> getObjectIdByName($p->name); $this -> deleteObject($p); } $this->id = $this->setId(); $this->created = date('Y-m-d\TH:i:s'); if (!$this->id) return array('error' => ID_CREATION_ERROR); $this->resFileName = USERTTDIR.$this->id.'.xml'; //TODO catalog root element = 'timetable' $rootElement = $this->objectDom->createElement('timetable'); $rootElement->setAttribute('xml:id',$this->id); foreach ($p as $key => $value) if ($key != 'id' && $key != 'leaf' && $key != 'nodeType' && $key != 'objName' && $key != 'objFormat' && $key != 'folderId' && $key != 'cacheToken') { if ($key == 'created') { $rootElement->appendChild($this->objectDom->createElement($key, $this->created)); } /*else if ($key == 'intervals') { $n_int = 0; foreach ($value as $item) { $newInterval = $this->objectDom->createElement('intervals'); $newInterval->appendChild($this->objectDom->createElement('start', $item->start)); $newInterval->appendChild($this->objectDom->createElement('stop', $item->stop)); $rootElement->appendChild($newInterval); $n_int++; } }*/ // it is catalog else if ($key == 'parameters') { $paramsElement = $this->setParamDescription($value); if ($paramsElement) $rootElement->appendChild($paramsElement); } else if ($key != 'intervals') $rootElement->appendChild($this->objectDom->createElement($key, htmlspecialchars($value))); } $this->objectDom->appendChild($rootElement); $this->objectDom->save($this->resFileName); $obj = new stdClass(); $obj->name = $p->name; $obj->intervals = $p->nbIntervals; $this -> addToContent($obj, $folder); return array('id' => $this->id,'created' => $this->created,'info' =>$obj->intervals.' intervals' ); } protected function call_intersection($fst, $snd) { $inf = ( $fst[0] > $snd[0] ) ? $fst[0] : $snd[0]; $sup = ( $fst[1] < $snd[1] ) ? $fst[1] : $snd[1]; if ( $inf >= $sup ) { $inter[] = array(0,0); } else {$inter[] = array($inf,$sup); } return $inter; } /* * For TT download : file format in text */ protected function xsl2text($file,$format) { define("format", $format); $xslt = new XSLTProcessor(); // add PHP functions to XSLT functions $xslt -> registerPHPFunctions(); // Load Time table $xml = new domDocument("1.0"); $xml->load($file); // Load XSL file $xsl = new domDocument("1.0"); if ($format == "Y-m-dTH:i:s") $xslName = "xml2iso.xsl"; else $xslName = "xml2all.xsl"; $xsl->load(XMLPATH.$xslName); // Import XSL and write output file in text format $xslt -> importStylesheet($xsl); $filename = $xml->getElementsByTagName('name')->item(0)->nodeValue.".txt"; $output=fopen(USERWORKINGDIR.$filename, "w"); fwrite($output, trim($xslt -> transformToDoc($xml)->firstChild->wholeText)); fclose($output); return USERWORKINGDIR.$filename; } /* * For TT download : file format in vot */ public function xsl2vot($file,$format) { $xslt = new XSLTProcessor(); // Load Time table $xml = new domDocument("1.0"); $xml->load($file); // Load XSL file $xsl = new domDocument("1.0"); $xsl->load(XMLPATH.'xml2vot.xsl'); // Import XSL and write output file in vot format $xslt -> importStylesheet($xsl); $vot = new domDocument("1.0"); $vot -> loadXML($xslt -> transformToXML($xml)); $filename = $xml->getElementsByTagName('name')->item(0)->nodeValue.".xml"; $vot -> save(USERWORKINGDIR.$filename); return USERWORKINGDIR.$filename; } /* * Uploaded text file => convert to array */ protected function text2amda($tmp_file, $onlyDescription = false) { $suffix = explode('.', basename($tmp_file)); $lines = file($tmp_file,FILE_SKIP_EMPTY_LINES); $description="Uploaded Time Table".PHP_EOL; foreach ($lines as $line){ if ($line[0] == '#') $description=$description."\n".substr($line,1,-1); else { $date = explode(' ',$line); if (!strtotime(trim($date[0]))) { $description=$description."\n".$line; continue; } // check if it is ISO format if (!isset($isIso)) $isIso = preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})$/', trim($date[0])); if (!$isIso) { $tempT = strtotime(trim($date[0])); $startDate = date('Y-m-d',$tempT)."T".date('H:i:s',$tempT); $tempT = strtotime(trim($date[1])); $stopDate = date('Y-m-d',$tempT)."T".date('H:i:s',$tempT); //TODO convert time into non standard formats // $startDate = DateTime::createFromFormat($timeFormat, trim($date[0]); // $start = $startDate->format('Y-m-d')."T".$startDate->format('H:i:s'); // $stopDate = DateTime::createFromFormat($timeFormat, trim($date[1]); // $stop = $stopDate->format('Y-m-d')."T".$stopDate->format('H:i:s'); if (!$onlyDescription) $attributesToReturn['intervals'][] = array('start' => $startDate, 'stop' => $stopDate); } else { if (!$onlyDescription) $attributesToReturn['intervals'][] = array('start' => trim($date[0]), 'stop' => trim($date[1])); } } } $attributesToReturn['description'] = $description; $attributesToReturn['name'] = basename($tmp_file, '.'.$suffix[1]); $attributesToReturn['created'] = date('Y-m-d')."T".date('H:i:s'); return $attributesToReturn; } /* * Uploaded vot TT => convert to array */ protected function vot2amda($tmp_file, $onlyDescription = false) { // Load Time table $this->objectDom -> load($tmp_file); $objToGet = $this->objectDom->getElementsByTagName('TABLEDATA')->item(0); $attributesToReturn['name'] = $tmp_file; $attributes = $objToGet -> childNodes; foreach($attributes as $attribute) if ($attribute->tagName == 'TR') { $start = $attribute -> getElementsByTagName('TD')->item(0) -> nodeValue; $stop = $attribute -> getElementsByTagName('TD')->item(1) -> nodeValue; if (!$onlyDescription) $attributesToReturn['intervals'][] = array('start' => $start, 'stop' => $stop); } $suffix = explode('.', basename($tmp_file)); $attributesToReturn['name'] = basename($tmp_file, '.'.$suffix[1]); $attributesToReturn['created'] = date('Y-m-d')."T".date('H:i:s'); $attributesToReturn['description'] = htmlspecialchars($this->objectDom->getElementsByTagName('DESCRIPTION')->item(0) -> nodeValue); return($attributesToReturn); } /***************************************************************** * PUBLIC FUNCTIONS *****************************************************************/ /* * Get Object into Edit */ function getObject($id, $nodeType) { if (substr($nodeType,0,6) == 'shared') { //Shared object $sharedObjMgr = new SharedObjectsMgr(); $path = $sharedObjMgr->getDataFilePath(str_replace('shared', '', $nodeType), $id); } else { $path = USERTTDIR.$id.'.xml'; } if (!file_exists($path)) return array('error' => NO_OBJECT_FILE); $this->objectDom -> load($path); if (!($objToGet = $this->objectDom->getElementById($id))) return array('error' => NO_SUCH_ID); $attributesToReturn['id'] = $objToGet->getAttribute('xml:id'); $attributes = $objToGet -> childNodes; $nbInt = 0; foreach($attributes as $attribute) if($attribute->nodeType == XML_ELEMENT_NODE){ /*if ($attribute->tagName == 'intervals') { $start = $attribute -> getElementsByTagName('start')->item(0) -> nodeValue; $stop = $attribute -> getElementsByTagName('stop')->item(0) -> nodeValue; $attributesToReturn['intervals'][] = array('start' => $start, 'stop' => $stop); } else $attributesToReturn[$attribute->tagName] = $attribute->nodeValue;*/ //BRE - load all except intervals - Intervals will be loaded later with 'loadIntervalsFromTT' function if ($attribute->tagName != 'intervals') $attributesToReturn[$attribute->tagName] = $attribute->nodeValue; else $nbInt++; } $attributesToReturn['nbIntervals'] = $nbInt; return $attributesToReturn; } public function createObject($p, $folder){ if ($p -> leaf) { $result = $this->createParameter($p, $folder); if ($result['error']) return $result; $cacheMgr = new TimeTableCacheMgr(); if (isset($p->cacheToken) && ($p->cacheToken != '')) { $resultSaveInt = $cacheMgr->saveInTT($result['id'], "update", $p->cacheToken); if (!$resultSaveInt['success']) { if ($resultSaveInt['message']) return array('error' => $resultSaveInt['message']); else return array('error' => 'Unknown error during intervals save'); } } return $result; } // else return $this->createFolder($p); //TODO check if this is possible? else return array('error' => 'createFolder should be called from RENAME'); } public function modifyObject($p) { $folder = $this->getObjectFolder($p->id); //Copy TT in a tempory file $ttFilePath = USERTTDIR.$p->id.'.xml'; $tmpFileExist = FALSE; if (file_exists($ttFilePath)) $tmpFileExist = copy($ttFilePath,$ttFilePath.".tmp"); //Delete TT $this->deleteObject($p); //Save modifications try { $result = $this->createObject($p, $folder); if ($result['error']) throw new Exception($result['error']); if ($tmpFileExist) unlink($ttFilePath.".tmp"); return array('id' => $p->id, 'info' => $result['info']); } catch (Exception $e) { //Restore TT file if ($tmpFileExist) { copy($ttFilePath.".tmp", $ttFilePath); unlink($ttFilePath.".tmp"); } return array ('error' => $e->getMessage()); } } public function loadIntervalsFromTT($id,$typeTT,$start = NULL, $limit = NULL) { if ($typeTT == 'sharedtimeTable') { //Shared object $sharedObjMgr = new SharedObjectsMgr(); $path = $sharedObjMgr->getDataFilePath('timeTable', $id); } else { $path = USERTTDIR.$id.'.xml'; } //load intervals from TT id if (!file_exists($path)) return array('success' => false, 'message' => "Cannot find TT file ".$id); $this->objectDom -> load($path); if (!($objToGet = $this->objectDom->getElementById($id))) return array('success' => false, 'message' => NO_SUCH_ID." ".$id); $xpath = new DOMXPath($this->objectDom); $intervals = $xpath->query('//intervals'); $result = array(); if (!isset($start) || !isset($limit)) { foreach ($intervals as $interval) { $startTime = $interval->getElementsByTagName('start')->item(0)->nodeValue; $stopTime = $interval->getElementsByTagName('stop')->item(0)->nodeValue; array_push($result, array('start' => $startTime, 'stop' => $stopTime)); } } else { for ($i = 0; $i < $limit; ++$i) { if ($start+$i >= $intervals->length) break; $startTime = $intervals->item($start+$i)->getElementsByTagName('start')->item(0)->nodeValue; $stopTime = $intervals->item($start+$i)->getElementsByTagName('stop')->item(0)->nodeValue; array_push($result, array('start' => $startTime, 'stop' => $stopTime)); } } return array( 'totalCount' => $intervals->length, 'intervals' => $result, 'start' => isset($start) ? $start : 0, 'limit' => isset($limit) ? $limit : 0, 'success' => true ); } protected function createIntervalElement($interval) { $newInterval = $this->objectDom->createElement('intervals'); $newInterval->appendChild($this->objectDom->createElement('start',$interval->start)); $newInterval->appendChild($this->objectDom->createElement('stop',$interval->stop)); return $newInterval; } public function saveIntervals($id,$intervals,$action) { if (substr($id,0,6) == 'shared') { return array('success' => false, 'message' => "Cannot save shared TimeTable"); } else { $path = USERTTDIR.$id.'.xml'; } if (!file_exists($path)) return array('success' => false, 'message' => "Cannot find TT file ".$id); $this->objectDom -> load($path); if (!($objToGet = $this->objectDom->getElementById($id))) return array('success' => false, 'message' => NO_SUCH_ID." ".$id); //remove old intervals $crtNode = $objToGet->firstChild; while ($crtNode) { if (($crtNode->nodeType != XML_ELEMENT_NODE) || ($crtNode->tagName != 'intervals')) { $crtNode = $crtNode->nextSibling; continue; } $toRemove = $crtNode; $crtNode = $crtNode->nextSibling; $objToGet->removeChild($toRemove); unset($toRemove); } //add new intervals foreach ($intervals as $interval) { $newInterval = $this-> createIntervalElement($interval); $this->objectDom->documentElement->appendChild($newInterval); } //save modifications $this->id = $id; $this->resFileName = USERTTDIR.$this->id.'.xml'; $this->objectDom->save($this->resFileName); unset($this->objectDom); return array('success' => true,'action' => $action, 'nbIntervals' => count($intervals)); } public function getUploadedObject($name, $format, $onlyDescription = false) { if (strpos($name,'.txt') !== false || strpos($name,'.asc') !== false ) { $attributesToReturn = $this->text2amda(USERTEMPDIR.$name, $onlyDescription); $attributesToReturn['objName'] = $name; $attributesToReturn['objFormat'] = $format; return $attributesToReturn; } if ($format == 'VOT') { $attributesToReturn = $this->vot2amda(USERTEMPDIR.$name, $onlyDescription); $attributesToReturn['objName'] = $name; $attributesToReturn['objFormat'] = $format; return $attributesToReturn; } if (strpos($name,'.xml') !== false) { $temp = explode('.xml', $name); $name = $temp[0]; } if (!file_exists(USERTEMPDIR.$name.'.xml')) return array('error' => 'no such name'); $this->objectDom -> load(USERTEMPDIR.$name.'.xml'); if (!($objToGet = $this->objectDom->getElementsByTagName('timetable')->item(0)) && !($objToGet = $this->objectDom->getElementsByTagName('TimeTable')->item(0))) return array('error' => 'no time table'); $attributes = $objToGet -> childNodes; $attributesToReturn['name'] = $name; $attributesToReturn['objName'] = $name; $attributesToReturn['objFormat'] = $format; foreach($attributes as $attribute) if($attribute->nodeType == XML_ELEMENT_NODE){ if ($attribute->tagName == 'intervals') { $start = $attribute -> getElementsByTagName('start')->item(0) -> nodeValue; $stop = $attribute -> getElementsByTagName('stop')->item(0) -> nodeValue; if (!$onlyDescription) $attributesToReturn['intervals'][] = array('start' => $start, 'stop' => $stop); } else if ($attribute->tagName == 'Interval') { $start = $attribute -> getElementsByTagName('Start')->item(0) -> nodeValue; $stop = $attribute -> getElementsByTagName('Stop')->item(0) -> nodeValue; if (!$onlyDescription) $attributesToReturn['intervals'][] = array('start' => $start, 'stop' => $stop); } else { switch (strtolower($attribute->tagName)) { case 'created' : $attributesToReturn['created'] = $attribute->nodeValue; break; case 'chain' : case 'source' : $attributesToReturn['description'] = $attribute->nodeValue; break; default: break; } } } return $attributesToReturn; } //TODO getObject only!!!! => change DD_Search output public function getTmpObject($folderId, $name, $onlyDescription = false) { $filePath = USERWORKINGDIR.$folderId.'/'.$name.'.xml'; if (!file_exists($filePath)) return array('error' => 'Cannot find result file'); $dom = new DomDocument('1.0'); $dom->formatOutput = true; if (!$dom -> load($filePath)) return array('error' => 'Cannot load result file'); $descNodes = $dom->getElementsByTagName('description'); if ($descNodes->length > 0) $attributesToReturn['description'] = $descNodes->item(0)->nodeValue; $creatNodes = $dom->getElementsByTagName('created'); if ($creatNodes->length > 0) $attributesToReturn['created'] = $creatNodes->item(0)->nodeValue; $histNodes = $dom->getElementsByTagName('history'); if ($histNodes->length > 0) $attributesToReturn['history'] = $histNodes->item(0)->nodeValue; $attributesToReturn['objName'] = $name; $attributesToReturn['folderId'] = $folderId; if (!$onlyDescription) { $intNodes = $dom->getElementsByTagName('intervals'); foreach ($intNodes as $intNode) { $startNodes = $intNode->getElementsByTagName('start'); if ($startNodes->length <= 0) return array('error' => 'Error detected in result file'); $stopNodes = $intNode->getElementsByTagName('stop'); if ($stopNodes->length <= 0) return array('error' => 'Error detected in result file'); $attributesToReturn['intervals'][] = array('start' => $startNodes->item(0)->nodeValue, 'stop' => $stopNodes->item(0)->nodeValue); } } return $attributesToReturn; } /* * merge time tables */ public function merge($obj) { /** * Array of intervals, used like : * [{start:'2010-01-01T23:00:00',stop:'2011-01-01T20:00:00'},{start:'2009-01-01T23:00:00',stop:'2010-01-01T20:00:00'}] * $attributesToReturn['intervals'][] = array('start' => $start, 'stop' => $stop); */ $intervals = 0; for ( $i = 0; $i < count($obj->ids); $i++ ) { $table[$i] = $this->loadIntervalsFromTT($obj->ids[$i]); for ($j=0; $j < count($table[$i]['intervals']); $j++) { $interval[$i][$j][0] = $table[$i]['intervals'][$j]['start']; $interval[$i][$j][1] = $table[$i]['intervals'][$j]['stop']; } $intervals += count($interval[$i]); } if ( $intervals > 10000) set_time_limit(1800); $final = array(); for ( $i = 0; $i < count($obj->ids); $i++ ) { $final = array_merge($final, $interval[$i]); } sort($final); // Algorithm of union $line = 0; $i = 0; $a = $final[$i][0]; while ($i < count($final)-1) { if ($final[$i+1][1] <= $final[$i][1]) { array_splice($final,$i+1,1); } else if (($final[$i+1][0] <= $final[$i][1]) && ($final[$i+1][1] >= $final[$i][1])) { $i++; } else { $start[$line] = $a; $stop[$line] = $final[$i][1]; $i++; $line++; $a = $final[$i][0]; } } $start[$line] = $a; $stop[$line] = $final[$i][1]; $line++; $objTT = new stdClass(); $objTT->name = $obj->name; $objTT->nodeType = 'timeTable'; $objTT->leaf = true; $objTT->created = null; $objTT->history = $obj->history; for ($i=0; $i < count($start); $i++) { $inter = new stdClass(); $inter->start = $start[$i]; $inter->stop = $stop[$i]; $objTT->intervals[] = $inter; } $objTT->nbIntervals = count($start); $this->objectDom = new DomDocument('1.0'); $this->objectDom->formatOutput = true; $res = $this->createParameter($objTT, $folder); if ($res['error']) return $res; $this->saveIntervals($res['id'],$objTT->intervals,'merge'); return $res; } /* * intersect time tables */ public function intersect($obj) { for ( $i = 0; $i < count($obj->ids); $i++ ) { $table[$i] = $this->loadIntervalsFromTT($obj->ids[$i]); for ($j=0; $j < count($table[$i]['intervals']); $j++) { $interval[$i][$j][0] = $table[$i]['intervals'][$j]['start']; $interval[$i][$j][1] = $table[$i]['intervals'][$j]['stop']; } $intervals += count($interval[$i]); } if ( $intervals > 10000) set_time_limit(1800); // Sort intervals in time tables sort($interval[0]); sort($interval[1]); $i = 0; $j = 0; $line = 0; while ( ($i < count($interval[0])) && ($j < count($interval[1])) ) { $inter = $this->call_intersection($interval[0][$i], $interval[1][$j]); if ($inter[0][0] != 0 && $inter[0][1] != 0) { $start[$line] = $inter[0][0]; $stop[$line] = $inter[0][1]; $line++; } if ( $interval[0][$i][1] < $interval[1][$j][1] ) { $i++; } else { $j++; } } // Intersection is empty if ( $line == 0 ) { $result ="empty"; } else { $objTT->name = $obj->name; $objTT->nodeType = 'timeTable'; $objTT->leaf = true; $objTT->created = null; $objTT->history = $obj->history; for ($i=0; $i < count($start); $i++) { $inter = new stdClass(); $inter->start = $start[$i]; $inter->stop = $stop[$i]; $objTT->intervals[] = $inter; } $objTT->nbIntervals = count($start); if (count($objTT->intervals) == 0) $result ="empty"; else { $this->objectDom = new DomDocument('1.0'); $this->objectDom->formatOutput = true; $result = $this->createObject($objTT, $folder); if (!isset($result['error'])) $this->saveIntervals($result['id'],$objTT->intervals,'intersect'); } } return $result; } function validNameObject($p){ // overwritten $res = parent::validNameObject($p); if (!$res['valid']) return $res; //no space if (strpos($p->name, ' ') === FALSE) return array('valid' => true); return array('valid' => false, 'error' => 'Space character is not allowed'); } public function copyTT($src_path, $dst_path, $newId, $newName, $newDescription = NULL) { if (!file_exists($src_path)) return FALSE; if (!is_dir($dst_path)) return FALSE; $dom = new DomDocument('1.0'); $dom->formatOutput = true; if (!$dom->load($src_path)) return FALSE; $timeTableNodes = $dom->getElementsByTagName('timetable'); if (count($timeTableNodes) <= 0) return FALSE; $timeTableNode = $timeTableNodes->item(0); $timeTableNode->setAttribute('xml:id', $newId); $nameNodes = $timeTableNode->getElementsByTagName('name'); if (count($nameNodes) <= 0) { //create name node (normally never append) $nameNode = $dom->createElement('name'); $timeTableNode->appendChild($nameNode); } else $nameNode = $nameNodes->item(0); $nameNode->nodeValue = $newName; if (isset($newDescription) && !empty($newDescription)) { $descriptionNodes = $timeTableNode->getElementsByTagName('description'); if (count($descriptionNodes) <= 0) { //create description node (normally never append) $descriptionNode = $dom->createElement('description'); $timeTableNode->appendChild($descriptionNode); } else $descriptionNode = $descriptionNodes->item(0); $descriptionNode->nodeValue = $newDescription; } $dst_file_path = $dst_path."/".$newId.".xml"; if ($dom->save($dst_file_path) === FALSE) return FALSE; chgrp($dst_file_path, APACHE_USER); chmod($dst_file_path, 0775); return TRUE; } } ?>