diff options
Diffstat (limited to 'modules-available/locationinfo/inc')
5 files changed, 982 insertions, 0 deletions
diff --git a/modules-available/locationinfo/inc/coursebackend.inc.php b/modules-available/locationinfo/inc/coursebackend.inc.php new file mode 100644 index 00000000..0d84b0fb --- /dev/null +++ b/modules-available/locationinfo/inc/coursebackend.inc.php @@ -0,0 +1,326 @@ +<?php + +/** + * Base class for course query backends + */ +abstract class CourseBackend +{ + + /* + * Static part for handling interfaces + */ + + /** + * @var array list of known backends + */ + private static $backendTypes = false; + /** + * @var boolean|string false = no error, error message otherwise + */ + protected $error; + /** + * @var int as internal serverId + */ + protected $serverId; + /** + * @const int max number of additional locations to fetch (for backends that benefit from request coalesc.) + */ + const MAX_ADDIDIONAL_LOCATIONS = 5; + + /** + * CourseBackend constructor. + */ + public final function __construct() + { + $this->error = false; + } + + /** + * Load all known backend types. This is done + * by including *.inc.php from inc/coursebackend/. + */ + public static function loadDb() + { + if (self::$backendTypes !== false) + return; + self::$backendTypes = array(); + foreach (glob(dirname(__FILE__) . '/coursebackend/coursebackend_*.inc.php', GLOB_NOSORT) as $file) { + require_once $file; + preg_match('#coursebackend_([^/\.]+)\.inc\.php$#i', $file, $out); + if (!class_exists('coursebackend_' . $out[1])) { + trigger_error("Backend type source unit $file doesn't seem to define class CourseBackend_{$out[1]}", E_USER_ERROR); + } + self::$backendTypes[$out[1]] = true; + } + } + + /** + * Get all known config module types. + * + * @return array list of modules + */ + public static function getList() + { + self::loadDb(); + return array_keys(self::$backendTypes); + } + + /** + * Get fresh instance of ConfigModule subclass for given module type. + * + * @param string $moduleType name of module type + * @return \CourseBackend module instance + */ + public static function getInstance($moduleType) + { + self::loadDb(); + if (!isset(self::$backendTypes[$moduleType])) { + error_log('Unknown module type: ' . $moduleType); + return false; + } + if (!is_object(self::$backendTypes[$moduleType])) { + $class = "coursebackend_$moduleType"; + self::$backendTypes[$moduleType] = new $class; + } + return self::$backendTypes[$moduleType]; + } + + /** + * @return string return display name of backend + */ + public abstract function getDisplayName(); + + + /** + * @returns \BackendProperty[] list of properties that need to be set + */ + public abstract function getCredentials(); + + /** + * @return boolean true if the connection works, false otherwise + */ + public abstract function checkConnection(); + + /** + * uses json to setCredentials, the json must follow the form given in + * getCredentials + * + * @param array $data assoc array with data required by backend + * @returns bool if the credentials were in the correct format + */ + public abstract function setCredentialsInternal($data); + + /** + * @return int desired caching time of results, in seconds. 0 = no caching + */ + public abstract function getCacheTime(); + + /** + * @return int age after which timetables are no longer refreshed should be + * greater then CacheTime + */ + public abstract function getRefreshTime(); + + /** + * Internal version of fetch, to be overridden by subclasses. + * + * @param $roomIds array with local ID as key and serverId as value + * @return array a recursive array that uses the roomID as key + * and has the schedule array as value. A shedule array contains an array in this format: + * ["start"=>'JJJJ-MM-DD HH:MM:SS',"end"=>'JJJJ-MM-DD HH:MM:SS',"title"=>string] + */ + protected abstract function fetchSchedulesInternal($roomId); + + /** + * Method for fetching the schedule of the given rooms on a server. + * + * @param array $roomId array of room ID to fetch + * @return array|bool array containing the timetables as value and roomid as key as result, or false on error + */ + public final function fetchSchedule($requestedLocationIds) + { + if (!is_array($requestedLocationIds)) { + $this->error = 'No array of roomids was given to fetchSchedule'; + return false; + } + if (empty($requestedLocationIds)) + return array(); + $NOW = time(); + $dbquery1 = Database::simpleQuery("SELECT locationid, calendar, serverroomid, lastcalendarupdate + FROM location_info WHERE locationid IN (:locations)", + array('locations' => array_values($requestedLocationIds))); + $returnValue = []; + $remoteIds = []; + while ($row = $dbquery1->fetch(PDO::FETCH_ASSOC)) { + //Check if in cache if lastUpdate is null then it is interpreted as 1970 + if ($row['lastcalendarupdate'] + $this->getCacheTime() > $NOW) { + $returnValue[$row['locationid']] = json_decode($row['calendar']); + } else { + $remoteIds[$row['locationid']] = $row['serverroomid']; + } + + } + // No need for additional round trips to backend + if (empty($remoteIds)) { + return $returnValue; + } + // Check if we should refresh other rooms recently requested by front ends + if ($this->getRefreshTime() > $this->getCacheTime()) { + $dbquery4 = Database::simpleQuery("SELECT locationid, serverroomid FROM location_info + WHERE serverid = :serverid AND serverroomid NOT IN (:skiplist) + AND lastcalendarupdate BETWEEN :lowerage AND :upperage + LIMIT " . self::MAX_ADDIDIONAL_LOCATIONS, array( + 'serverid' => $this->serverId, + 'skiplist' => array_values($remoteIds), + 'lowerage' => $NOW - $this->getRefreshTime(), + 'upperage' => $NOW - $this->getCacheTime(), + )); + while ($row = $dbquery4->fetch(PDO::FETCH_ASSOC)) { + $remoteIds[$row['locationid']] = $row['serverroomid']; + } + } + $backendResponse = $this->fetchSchedulesInternal($remoteIds); + if ($backendResponse === false) { + return false; + } + + if ($this->getCacheTime() > 0) { + // Caching requested by backend, write to DB + foreach ($backendResponse as $serverRoomId => $calendar) { + $value = json_encode($calendar); + Database::simpleQuery("UPDATE location_info SET calendar = :ttable, lastcalendarupdate = :now + WHERE serverid = :serverid AND serverroomid = :serverroomid", array( + 'serverid' => $this->serverId, + 'serverroomid' => $serverRoomId, + 'ttable' => $value, + 'now' => $NOW + )); + } + } + // Add rooms that were requested to the final return value + foreach ($remoteIds as $location => $serverRoomId) { + if (isset($backendResponse[$serverRoomId]) && in_array($location, $requestedLocationIds)) { + // Only add if we can map it back to our location id AND it was not an unsolicited coalesced refresh + $returnValue[$location] = $backendResponse[$serverRoomId]; + } + } + + return $returnValue; + } + + public final function setCredentials($serverId, $data) + { + foreach ($this->getCredentials() as $prop) { + if (!isset($data[$prop->property])) { + $data[$prop->property] = $prop->default; + } + if (in_array($prop->type, ['string', 'bool', 'int'])) { + settype($data[$prop->property], $prop->type); + } else { + settype($data[$prop->property], 'string'); + } + } + if ($this->setCredentialsInternal($data)) { + $this->serverId = $serverId; + return true; + } + return false; + } + + /** + * @return false if there was no error string with error message if there was one + */ + public final function getError() + { + return $this->error; + } + + /** + * Query path in array-representation of XML document. + * e.g. 'path/syntax/foo/wanteditem' + * This works for intermediate nodes (that have more children) + * and leaf nodes. The result is always an array on success, or + * false if not found. + */ + protected function getAttributes($array, $path) + { + if (!is_array($path)) { + // Convert 'path/syntax/foo/wanteditem' to array for further processing and recursive calls + $path = explode('/', $path); + } + do { + // Get next element from array, loop to ignore empty elements (so double slashes in the path are allowed) + $element = array_shift($path); + } while (empty($element) && !empty($path)); + if (!isset($array[$element])) { + // Current path element does not exist - error + return false; + } + if (empty($path)) { + // Path is now empty which means we're at 'wanteditem' from out example above + if (!is_array($array[$element]) || !isset($array[$element][0])) { + // If it's a leaf node of the array, wrap it in plain array, so the function will + // always return an array on success + return array($array[$element]); + } + // 'wanteditem' is not a unique leaf node, return as is + // This means it's either a plain array, in case there are multiple 'wanteditem' elements on the same level + // or it's an associative array if 'wanteditem' has any sub-nodes + return $array[$element]; + } + // Recurse + if (!is_array($array[$element])) { + // We're in the middle of the requested path, but the current element is already a leaf node with no + // children - error + return false; + } + if (isset($array[$element][0])) { + // The currently handled element of the path exists multiple times on the current level, so it is + // wrapped in a plain array - recurse into each one of them and merge the results + $return = []; + foreach ($array[$element] as $item) { + $test = $this->getAttributes($item, $path); + If (gettype($test) == "array") { + $return = array_merge($return, $test); + } + + } + return $return; + } + // Unique non-leaf node - simple recursion + return $this->getAttributes($array[$element], $path); + } + + /** + * @param string $response xml document to convert + * @return bool|array array representation of the xml if possible, false otherwise + */ + protected function toArray($response) + { + $cleanresponse = preg_replace('/(<\/?)(\w+):([^>]*>)/', '$1$2$3', $response); + try { + $xml = new SimpleXMLElement($cleanresponse); + } catch (Exception $e) { + $this->error = 'Could not parse reply as XML, got ' . get_class($e) . ': ' . $e->getMessage(); + return false; + } + $array = json_decode(json_encode((array)$xml), true); + return $array; + } + +} + +/** + * Class BackendProperty describes a property a backend requires to define its functionality + */ +class BackendProperty { + public $property; + public $type; + public $default; + public function __construct($property, $type, $default = '') + { + $this->property = $property; + $this->type = $type; + $this->default = $default; + } +} diff --git a/modules-available/locationinfo/inc/coursebackend/coursebackend_davinci.inc.php b/modules-available/locationinfo/inc/coursebackend/coursebackend_davinci.inc.php new file mode 100644 index 00000000..77928b39 --- /dev/null +++ b/modules-available/locationinfo/inc/coursebackend/coursebackend_davinci.inc.php @@ -0,0 +1,128 @@ +<?php + +class CourseBackend_Davinci extends CourseBackend +{ + + private $location; + + public function setCredentialsInternal($data) + { + if (empty($data['baseUrl'])) { + $this->error = "No url is given"; + return false; + } + $location = preg_replace('#/+(davinciis\.dll)?\W*$#i', '', $data['baseUrl']); + $this->location = $location . "/DAVINCIIS.dll?"; + //Davinci doesn't have credentials + return true; + } + + public function checkConnection() + { + if (empty($this->location)) { + $this->error = "Credentials are not set"; + } else { + $data = $this->fetchRoomRaw('someroomid123'); + if (strpos($data, 'DAVINCI SERVER') === false) { + $this->error = "This doesn't seem to be a DAVINCI server"; + } + } + return $this->error === false; + } + + public function getCredentials() + { + return [ + new BackendProperty('baseUrl', 'string') + ]; + } + + public function getDisplayName() + { + return 'Davinci'; + } + + public function getCacheTime() + { + return 30 * 60; + } + + public function getRefreshTime() + { + return 0; + } + + /** + * @param $roomId string name of the room + * @return array|bool if successful the arrayrepresentation of the timetable + */ + private function fetchRoomRaw($roomId) + { + $startDate = new DateTime('today 0:00'); + $endDate = new DateTime('+7 days 0:00'); + $url = $this->location . "content=xml&type=room&name=" . urlencode($roomId) + . "&startdate=" . $startDate->format('d.m.Y') . "&enddate=" . $endDate->format('d.m.Y'); + $ch = curl_init(); + $options = array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_SSL_VERIFYHOST => false, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_URL => $url, + ); + + curl_setopt_array($ch, $options); + $output = curl_exec($ch); + if ($output === false) { + $this->error = 'Curl error: ' . curl_error($ch); + return false; + } else { + $this->error = false; + ///Operation completed successfully + } + curl_close($ch); + return $output; + + } + + public function fetchSchedulesInternal($requestedRoomIds) + { + $schedules = []; + foreach ($requestedRoomIds as $roomId) { + $return = $this->fetchRoomRaw($roomId); + if ($return === false) { + continue; + } + $return = $this->toArray($return); + if ($return === false) { + continue; + } + $lessons = $this->getAttributes($return, '/Lessons/Lesson'); + if ($lessons === false) { + $this->error = "Cannot find /Lessons/Lesson in XML"; + continue; + } + $timetable = []; + foreach ($lessons as $lesson) { + if (!isset($lesson['Date']) || !isset($lesson['Start']) || !isset($lesson['Finish'])) { + $this->error = 'Lesson is missing Date, Start or Finish'; + continue; + } + $date = $lesson['Date']; + $date = substr($date, 0, 4) . '-' . substr($date, 4, 2) . '-' . substr($date, 6, 2); + $start = $lesson['Start']; + $start = substr($start, 0, 2) . ':' . substr($start, 2, 2); + $end = $lesson['Finish']; + $end = substr($end, 0, 2) . ':' . substr($end, 2, 2); + $subject = isset($lesson['Subject']) ? $lesson['Subject'] : '???'; + $timetable[] = array( + 'title' => $subject, + 'start' => $date . " " . $start . ':00', + 'end' => $date . " " . $end . ':00' + ); + } + $schedules[$roomId] = $timetable; + } + return $schedules; + } +} diff --git a/modules-available/locationinfo/inc/coursebackend/coursebackend_dummy.inc.php b/modules-available/locationinfo/inc/coursebackend/coursebackend_dummy.inc.php new file mode 100644 index 00000000..b8329196 --- /dev/null +++ b/modules-available/locationinfo/inc/coursebackend/coursebackend_dummy.inc.php @@ -0,0 +1,113 @@ +<?php + +class CourseBackend_Dummy extends CourseBackend +{ + private $pw; + + /** + * uses json to setCredentials, the json must follow the form given in + * getCredentials + * + * @param array $data with the credentials + * @param string $url address of the server + * @param int $serverId ID of the server + * @returns bool if the credentials were in the correct format + */ + public function setCredentialsInternal($json) + { + $x = $json; + $this->pw = $x['password']; + + if ($this->pw === "mfg") { + $this->error = false; + return true; + } else { + $this->error = "USE mfg as password!"; + return false; + } + } + + /** + * @return boolean true if the connection works, false otherwise + */ + public function checkConnection() + { + if ($this->pw == "mfg") { + $this->error = false; + return true; + } else { + $this->error = "USE mfg as password!"; + return false; + } + } + + /** + * @returns array with parameter name as key and and an array with type, help text and mask as value + */ + public function getCredentials() + { + $options = ["opt1", "opt2", "opt3", "opt4", "opt5", "opt6", "opt7", "opt8"]; + return [ + new BackendProperty('username', 'string', 'default-user'), + new BackendProperty('password', 'password'), + new BackendProperty('integer', 'int', 7), + new BackendProperty('option', $options), + new BackendProperty('CheckTheBox', 'bool'), + new BackendProperty('CB2t', 'bool', true) + ]; + } + + /** + * @return string return display name of backend + */ + public function getDisplayName() + { + return 'Dummy with array'; + } + + /** + * @return int desired caching time of results, in seconds. 0 = no caching + */ + public function getCacheTime() + { + return 0; + } + + /** + * @return int age after which timetables are no longer refreshed should be + * greater then CacheTime + */ + public function getRefreshTime() + { + return 0; + } + + /** + * Internal version of fetch, to be overridden by subclasses. + * + * @param $roomIds array with local ID as key and serverId as value + * @return array a recursive array that uses the roomID as key + * and has the schedule array as value. A shedule array contains an array in this format: + * ["start"=>'JJJJ-MM-DD HH:MM:SS',"end"=>'JJJJ-MM-DD HH:MM:SS',"title"=>string] + */ + public function fetchSchedulesInternal($roomId) + { + $a = array(); + foreach ($roomId as $id) { + $x['id'] = $id; + $calendar['title'] = "test exam"; + $calendar['start'] = "2017-3-08 13:00:00"; + $calendar['end'] = "2017-3-08 16:00:00"; + $calarray = array(); + $calarray[] = $calendar; + $x['calendar'] = $calarray; + $a[$id] = $calarray; + } + + + return $a; + } + +} + +?> diff --git a/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php b/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php new file mode 100644 index 00000000..b01146a8 --- /dev/null +++ b/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php @@ -0,0 +1,352 @@ +<?php + +class CourseBackend_HisInOne extends CourseBackend +{ + private $username = ''; + private $password = ''; + private $open = true; + private $location; + private $verifyHostname = true; + private $verifyCert = true; + + + public function setCredentialsInternal($data) + { + if (!$data['open']) { + // If not using OpenCourseService, require credentials + foreach (['username', 'password'] as $field) { + if (empty($data[$field])) { + $this->error = 'setCredentials: Missing field ' . $field; + return false; + } + } + } + if (empty($data['baseUrl'])) { + $this->error = "No url is given"; + return false; + } + + $this->error = false; + $this->username = $data['username'] . "\t" . $data['role']; + $this->password = $data['password']; + $this->open = $data['open']; + $url = preg_replace('#(/+qisserver(/+services\d+(/+OpenCourseService)?)?)?\W*$#i', '', $data['baseUrl']); + if ($this->open) { + $this->location = $url . "/qisserver/services2/OpenCourseService"; + } else { + $this->location = $url . "/qisserver/services2/CourseService"; + } + $this->verifyHostname = $data['verifyHostname']; + $this->verifyCert = $data['verifyCert']; + + return true; + } + + public function getCredentials() + { + return [ + new BackendProperty('baseUrl', 'string'), + new BackendProperty('username', 'string'), + new BackendProperty('role', 'string'), + new BackendProperty('password', 'password'), + new BackendProperty('open', 'bool', true), + new BackendProperty('verifyCert', 'bool', true), + new BackendProperty('verifyHostname', 'bool', true) + ]; + } + + public function checkConnection() + { + if (empty($this->location)) { + $this->error = "Credentials are not set"; + } else { + $this->findUnit(123456789, true); + } + return $this->error === false; + } + + /** + * @param int $roomId his in one room id to get + * @param bool $connectionCheckOnly true will only check if no soapError is returned, return value will be empty + * @return array|bool if successful an array with the event ids that take place in the room + */ + public function findUnit($roomId, $connectionCheckOnly = false) + { + $termYear = date('Y'); + $termType1 = date('n'); + if ($termType1 > 3 && $termType1 < 10) { + $termType = 2; + } elseif ($termType1 > 10) { + $termType = 1; + $termYear = $termYear + 1; + } else { + $termType = 1; + } + $doc = new DOMDocument('1.0', 'utf-8'); + $doc->formatOutput = true; + $envelope = $doc->createElementNS('http://schemas.xmlsoap.org/soap/envelope/', 'SOAP-ENV:Envelope'); + $doc->appendChild($envelope); + if ($this->open) { + $envelope->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:ns1', 'http://www.his.de/ws/OpenCourseService'); + } else { + $envelope->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:ns1', 'http://www.his.de/ws/CourseService'); + $header = $this->getHeader($doc); + $envelope->appendChild($header); + } + //Body of the request + $body = $doc->createElement('SOAP-ENV:Body'); + $envelope->appendChild($body); + $findUnit = $doc->createElement('ns1:findUnit'); + $body->appendChild($findUnit); + $findUnit->appendChild($doc->createElement('termYear', $termYear)); + if ($termType1 != 3 && $termType1 != 10) { + $findUnit->appendChild($doc->createElement('termTypeValueId', $termType)); + } + $findUnit->appendChild($doc->createElement('ns1:roomId', $roomId)); + + $soap_request = $doc->saveXML(); + $response1 = $this->__doRequest($soap_request, "findUnit"); + if ($this->error !== false) { + return false; + } + $response2 = $this->toArray($response1); + if (!is_array($response2)) { + if ($this->error === false) { + $this->error = 'Cannot convert XML response to array'; + } + return false; + } + if (!isset($response2['soapenvBody'])) { + $this->error = 'findUnit(' . $roomId . '): Backend reply is missing element soapenvBody'; + return false; + } + if (isset($response2['soapenvBody']['soapenvFault'])) { + $this->error = $response2['soapenvBody']['soapenvFault']['faultcode'] . " " . $response2['soapenvBody']['soapenvFault']['faultstring']; + return false; + } + // We only need to check if the connection is working (URL ok, credentials ok, ..) so bail out early + if ($connectionCheckOnly) { + return array(); + } + if ($this->open) { + $path = '/soapenvBody/hisfindUnitResponse/hisunits/hisunit/hisid'; + } else { + $path = '/soapenvBody/hisfindUnitResponse/hisunitIds/hisid'; + } + $id = $this->getAttributes($response2, $path); + if ($id === false) { + $this->error = 'Cannot find ' . $path; + } + return $id; + } + + /** + * @param $doc DOMDocument + * @return DOMElement + */ + private function getHeader($doc) + { + $header = $doc->createElement('SOAP-ENV:Header'); + $security = $doc->createElementNS('http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd', 'ns2:Security'); + $mustunderstand = $doc->createAttribute('SOAP-ENV:mustUnderstand'); + $mustunderstand->value = 1; + $security->appendChild($mustunderstand); + $header->appendChild($security); + $token = $doc->createElement('ns2:UsernameToken'); + $security->appendChild($token); + $user = $doc->createElement('ns2:Username', $this->username); + $token->appendChild($user); + $pass = $doc->createElement('ns2:Password', $this->password); + $type = $doc->createAttribute('Type'); + $type->value = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'; + $pass->appendChild($type); + $token->appendChild($pass); + return $header; + } + + /** + * @param $request string with xml SOAP request + * @param $action string with the name of the SOAP action + * @return bool|string if successful the answer xml from the SOAP server + */ + private function __doRequest($request, $action) + { + $header = array( + "Content-type: text/xml;charset=\"utf-8\"", + "SOAPAction: \"" . $action . "\"", + "Content-length: " . strlen($request), + ); + + $soap_do = curl_init(); + + $options = array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_SSL_VERIFYHOST => $this->verifyHostname, + CURLOPT_SSL_VERIFYPEER => $this->verifyCert, + CURLOPT_URL => $this->location, + CURLOPT_POSTFIELDS => $request, + CURLOPT_HTTPHEADER => $header, + ); + + curl_setopt_array($soap_do, $options); + + $output = curl_exec($soap_do); + + if ($output === false) { + $this->error = 'Curl error: ' . curl_error($soap_do); + } else { + $this->error = false; + ///Operation completed successfully + } + curl_close($soap_do); + return $output; + } + + public function getCacheTime() + { + return 30 * 60; + } + + + public function getRefreshTime() + { + return 60 * 60; + } + + + public function getDisplayName() + { + return "HisInOne"; + } + + public function fetchSchedulesInternal($requestedRoomIds) + { + if (empty($requestedRoomIds)) { + return array(); + } + $tTables = []; + //get all eventIDs in a given room + $eventIds = []; + foreach ($requestedRoomIds as $roomId) { + $roomEventIds = $this->findUnit($roomId); + if ($roomEventIds === false) { + error_log($this->error); + $this->error = false; + // TODO: Error gets swallowed + continue; + } + $tTables[$roomId] = []; + $eventIds = array_merge($eventIds, $roomEventIds); + } + $eventIds = array_unique($eventIds); + if (empty($eventIds)) { + return $tTables; + } + $eventDetails = []; + //get all information on each event + foreach ($eventIds as $eventId) { + $event = $this->readUnit(intval($eventId)); + if ($event === false) { + error_log($this->error); + $this->error = false; + // TODO: Error gets swallowed + continue; + } + $eventDetails = array_merge($eventDetails, $event); + } + $currentWeek = $this->getCurrentWeekDates(); + foreach ($eventDetails as $event) { + foreach (array('/hisdefaulttext', + '/hisshorttext', + '/hisshortcomment', + '/hisplanelements/hisplanelement/hisdefaulttext') as $path) { + $name = $this->getAttributes($event, $path); + if (!empty($name) && !empty($name[0])) + break; + $name = false; + } + if ($name === false) { + $name = ['???']; + } + $unitPlannedDates = $this->getAttributes($event, + '/hisplanelements/hisplanelement/hisplannedDates/hisplannedDate/hisindividualDates/hisindividualDate'); + if ($unitPlannedDates === false) { + $this->error = 'Cannot find ./hisplanelements/hisplanelement/hisplannedDates/hisplannedDate/hisindividualDates/hisindividualDate'; + return false; + } + foreach ($unitPlannedDates as $plannedDate) { + $eventRoomId = $this->getAttributes($plannedDate, '/hisroomId')[0]; + $eventDate = $this->getAttributes($plannedDate, '/hisexecutiondate')[0]; + if (in_array($eventRoomId, $requestedRoomIds) && in_array($eventDate, $currentWeek)) { + $startTime = $this->getAttributes($plannedDate, '/hisstarttime')[0]; + $endTime = $this->getAttributes($plannedDate, '/hisendtime')[0]; + $tTables[$eventRoomId][] = array( + 'title' => $name[0], + 'start' => $eventDate . " " . $startTime, + 'end' => $eventDate . " " . $endTime + ); + } + } + } + return $tTables; + } + + + /** + * @param $unit int ID of the subject in HisInOne database + * @return bool|array false if there was an error otherwise an array with the information about the subject + */ + public function readUnit($unit) + { + $doc = new DOMDocument('1.0', 'utf-8'); + $doc->formatOutput = true; + $envelope = $doc->createElementNS('http://schemas.xmlsoap.org/soap/envelope/', 'SOAP-ENV:Envelope'); + $doc->appendChild($envelope); + if ($this->open) { + $envelope->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:ns1', 'http://www.his.de/ws/OpenCourseService'); + } else { + $envelope->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:ns1', 'http://www.his.de/ws/CourseService'); + $header = $this->getHeader($doc); + $envelope->appendChild($header); + } + //body of the request + $body = $doc->createElement('SOAP-ENV:Body'); + $envelope->appendChild($body); + $readUnit = $doc->createElement('ns1:readUnit'); + $body->appendChild($readUnit); + $readUnit->appendChild($doc->createElement('ns1:unitId', $unit)); + + $soap_request = $doc->saveXML(); + $response1 = $this->__doRequest($soap_request, "readUnit"); + if ($response1 === false) { + return false; + } + $response2 = $this->toArray($response1); + if ($response2 === false) + return false; + if (!isset($response2['soapenvBody'])) { + $this->error = 'findUnit(' . $unit . '): Backend reply is missing element soapenvBody'; + return false; + } + if (isset($response2['soapenvBody']['soapenvFault'])) { + $this->error = 'SOAP-Fault' . $response2['soapenvBody']['soapenvFault']['faultcode'] . " " . $response2['soapenvBody']['soapenvFault']['faultstring']; + return false; + } + return $this->getAttributes($response2, '/soapenvBody/hisreadUnitResponse/hisunit'); + } + + /** + * @return array with days of the current week in datetime format + */ + private function getCurrentWeekDates() + { + $returnValue = array(); + $startDate = time(); + for ($i = 0; $i <= 7; $i++) { + $returnValue[] = date('Y-m-d', strtotime("+{$i} day 12:00", $startDate)); + } + return $returnValue; + } + +} diff --git a/modules-available/locationinfo/inc/locationinfo.inc.php b/modules-available/locationinfo/inc/locationinfo.inc.php new file mode 100644 index 00000000..7617d143 --- /dev/null +++ b/modules-available/locationinfo/inc/locationinfo.inc.php @@ -0,0 +1,63 @@ +<?php + +class LocationInfo +{ + + /** + * Gets the pc data and returns it's state. + * + * @param array $pc The pc data from the db. Array('logintime' =>, 'lastseen' =>, 'lastboot' =>) + * @return int pc state + */ + public static function getPcState($pc) + { + /* pcState: + * [0] = IDLE (NOT IN USE) + * [1] = OCCUPIED (IN USE) + * [2] = OFF + * [3] = 10 days offline (BROKEN?) + */ + // TODO USE STATE NAME instead of numbers + + $logintime = (int)$pc['logintime']; + $lastseen = (int)$pc['lastseen']; + $lastboot = (int)$pc['lastboot']; + $NOW = time(); + + if ($NOW - $lastseen > 14 * 86400) { + return "BROKEN"; + } elseif (($NOW - $lastseen > 610) || $lastboot === 0) { + return "OFF"; + } elseif ($logintime === 0) { + return "IDLE"; + } elseif ($logintime > 0) { + return "OCCUPIED"; + } + return -1; + } + + /** + * Set current error message of given server. Pass null or false to clear. + * + * @param int $serverId id of server + * @param string $message error message to set, null or false clears error. + */ + public static function setServerError($serverId, $message) + { + if ($message === false || $message === null) { + Database::exec("UPDATE `setting_location_info` SET error = NULL + WHERE serverid = :id", array('id' => $serverId)); + } else { + if (empty($message)) { + $message = '<empty error message>'; + } + $error = json_encode(array( + 'timestamp' => time(), + 'error' => (string)$message + )); + Database::exec("UPDATE `setting_location_info` SET error = :error + WHERE serverid = :id", array('id' => $serverId, 'error' => $error)); + } + } + +} |