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 */ 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 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()); } } /* * Check if difference is name and info only */ 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'); } } /* * In case of catalogs */ 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)); } // 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'); } /* * Create Time Table */ protected function setParamDescription($param) { } public function getUploadedObject($name, $format, $onlyDescription = false) { if (strpos($name, '.txt') !== false || strpos($name, '.asc') !== false || strpos($name, '.') == 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; } /* * 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; $recordsNumber = count($lines); $descNumber = 0; foreach ($lines as $line) { if ($line[0] == '#') { $description = $description . PHP_EOL . substr($line, 1, -1); } else { $date = explode(' ', trim(preg_replace('!\s+!', ' ', $line))); if (!strtotime(trim($date[0]))) { $description = $description . PHP_EOL . $line; $descNumber++; 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) { // y-m-d h:m:s for example $dateLength = count($date) / 2; $tempStart = $date[0]; $tempStop = $date[$dateLength]; if ($dateLength > 1) { for ($i = 1; $i < $dateLength; $i++) { $tempStart .= $date[$i]; } for ($i = $dateLength + 1; $i < $dateLength * 2; $i++) { $tempStop .= $date[$i]; } } $startDate = date('Y-m-d\TH:i:s', strtotime($tempStart)); $stopDate = date('Y-m-d\TH:i:s', strtotime($tempStop)); if (!$onlyDescription) { $attributesToReturn['intervals'][] = array('start' => $startDate, 'stop' => $stopDate); } } else { if (!$onlyDescription) { $attributesToReturn['intervals'][] = array('start' => trim($date[0]), 'stop' => trim($date[1])); } } } } if ($recordsNumber == $descNumber) { $description = "Looks like we can not read your time format..." . PHP_EOL . $description; } $attributesToReturn['description'] = $description; $attributesToReturn['name'] = basename($tmp_file, '.' . $suffix[1]); $attributesToReturn['created'] = date('Y-m-d\TH: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 */ 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; } 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; } 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 ); } 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)); } 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 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; } 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; } //TODO getObject only!!!! => change DD_Search output 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'); } /* * merge time tables */ 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 ($timeTableNodes->length <= 0) { return false; } $timeTableNode = $timeTableNodes->item(0); $timeTableNode->setAttribute('xml:id', $newId); $nameNodes = $timeTableNode->getElementsByTagName('name'); if ($nameNodes->length <= 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 ($descriptionNodes->length <= 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; } /* * intersect time tables */ 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'); } } protected function renameOnly($p) { //if (!($p->intervals)) return true; return false; } } ?>