diff options
Diffstat (limited to 'modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php')
-rw-r--r-- | modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php | 344 |
1 files changed, 62 insertions, 282 deletions
diff --git a/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php b/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php index 4664a011..ee152684 100644 --- a/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php +++ b/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php @@ -2,9 +2,6 @@ class CourseBackend_HisInOne extends CourseBackend { - private $username = ''; - private $password = ''; - private $open = true; private $location; private $verifyHostname = true; private $verifyCert = true; @@ -16,32 +13,12 @@ class CourseBackend_HisInOne extends CourseBackend public function setCredentialsInternal($data) { - if (!$data['open']) { - // If not using OpenCourseService, require credentials - foreach (['username', 'password'] as $field) { - if (empty($data[$field])) { - $this->addError('setCredentials: Missing field ' . $field, true); - return false; - } - } - } if (empty($data['baseUrl'])) { $this->addError("No url is given", true); return false; } - $this->username = $data['username']; - if (!empty($data['role'])) { - $this->username .= "\t" . $data['role']; - } - $this->password = $data['password']; - $this->open = $data['open'] !== 'CourseService'; - $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->location = $this->mangleProperty('baseUrl', $data['baseUrl']); $this->verifyHostname = $data['verifyHostname']; $this->verifyCert = $data['verifyCert']; @@ -52,156 +29,80 @@ class CourseBackend_HisInOne extends CourseBackend { return [ new BackendProperty('baseUrl', 'string'), - new BackendProperty('username', 'string'), - new BackendProperty('role', 'string'), - new BackendProperty('password', 'password'), - new BackendProperty('open', ['OpenCourseService', 'CourseService'], 'OpenCourseService'), new BackendProperty('verifyCert', 'bool', true), new BackendProperty('verifyHostname', 'bool', true) ]; } - public function checkConnection() + public function mangleProperty($prop, $value) { - if (empty($this->location)) { - $this->addError("Credentials are not set", true); - return false; + if ($prop === 'baseUrl') { + // Update form SOAP to iCal url + if (preg_match(',^(http.*?)/qisserver,', $value, $out)) { + $value = $out[1] . '/qisserver/pages/cm/exa/timetable/roomScheduleCalendarExport.faces?roomId='; + } elseif (preg_match(',(.*[/=])\d*$', $value, $out)) { + $value = $out[1]; + } elseif (substr_count($value, '/') <= 3) { + if (substr($value, -1) !== '/') { + $value .= '/'; + } + $value .= 'qisserver/pages/cm/exa/timetable/roomScheduleCalendarExport.faces?roomId='; + } } - return $this->findUnit(123456789, date('Y-m-d'), true) !== false; + return $value; } - /** - * @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, $day, $connectionCheckOnly = false) + public function checkConnection() { - $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('ns1:individualDatesExecutionDate', $day)); - $findUnit->appendChild($doc->createElement('ns1:roomId', $roomId)); - - $soap_request = $doc->saveXML(); - $response1 = $this->postToServer($soap_request, "findUnit"); - if ($response1 === false) { - $this->addError('Could not fetch room ' . $roomId, true); - return false; - } - $response2 = $this->xmlStringToArray($response1, $err); - if (!is_array($response2)) { - $this->addError("Parsing room $roomId: $err", false); - return false; - } - if (!isset($response2['soapenvBody'])) { - $this->addError('Backend reply is missing element soapenvBody', true); - return false; - } - if (isset($response2['soapenvBody']['soapenvFault'])) { - $this->addError('SOAP-Fault (' . $response2['soapenvBody']['soapenvFault']['faultcode'] . ") " . $response2['soapenvBody']['soapenvFault']['faultstring'], true); - 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'; - $subpath = '/hisunit/hisid'; - } else { - $path = '/soapenvBody/hisfindUnitResponse/hisunitIds'; - $subpath = '/hisid'; - } - $idSubDoc = $this->getArrayPath($response2, $path); - if ($idSubDoc === false) { - $this->addError('Cannot find ' . $path, false); - //@file_put_contents('/tmp/findUnit-1.' . $roomId . '.' . microtime(true), print_r($response2, true)); + if (empty($this->location)) { + $this->addError("Credentials are not set", true); return false; } - if (empty($idSubDoc)) - return $idSubDoc; - $idList = $this->getArrayPath($idSubDoc, $subpath); - if ($idList === false) { - $this->addError('Cannot find ' . $subpath . ' after ' . $path, false); - @file_put_contents('/tmp/bwlp-findUnit-2.' . $roomId . '.' . microtime(true), print_r($idSubDoc, true)); + // Unfortunately HisInOne returns an internal server error if you pass an invalid roomId. + // So we just try a bunch and see if anything works. Even if this fails, using + // the backend should work, given the URL is actually correct. + foreach ([60, 100, 5, 10, 50, 110, 200, 210, 250, 300, 333, 500, 1000, 2000] as $roomId) { + if ($this->downloadIcal($roomId) !== null) + return true; } - return $idList; + return false; } /** - * @param $doc DOMDocument - * @return DOMElement + * @param int $roomId room id + * @return ICalEvent[]|null all events for this room in the range -7 days to +7 days, or NULL on error */ - private function getHeader($doc) + private function downloadIcal($roomId) { - $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 postToServer($request, $action) - { - $header = array( - 'Content-type: text/xml;charset="utf-8"', - 'SOAPAction: "' . $action . '"', - ); - if ($this->curlHandle === false) { $this->curlHandle = curl_init(); } + $ical = new ICalParser(['filterDaysBefore' => 7, 'filterDaysAfter' => 7]); $options = array( - CURLOPT_RETURNTRANSFER => true, + CURLOPT_WRITEFUNCTION => function ($ch, $data) use ($ical) { + $ical->feedData($data); + return strlen($data); + }, CURLOPT_FOLLOWLOCATION => true, CURLOPT_SSL_VERIFYHOST => $this->verifyHostname ? 2 : 0, CURLOPT_SSL_VERIFYPEER => $this->verifyCert ? 1 : 0, - CURLOPT_URL => $this->location, - CURLOPT_POSTFIELDS => $request, - CURLOPT_HTTPHEADER => $header, - CURLOPT_TIMEOUT => 15, - CURLOPT_CONNECTTIMEOUT => 3, + CURLOPT_URL => $this->location . $roomId, + CURLOPT_TIMEOUT => 60, + CURLOPT_CONNECTTIMEOUT => 4, ); curl_setopt_array($this->curlHandle, $options); - $output = curl_exec($this->curlHandle); - - if ($output === false) { + if (!curl_exec($this->curlHandle)) { $this->addError('Curl error: ' . curl_error($this->curlHandle), false); } - return $output; + $ical->finish(); + if (!$ical->isValid()) { + error_log('Did not find a VCALENDAR in returned data'); + return null; + } + return $ical->events(); } public function getCacheTime() @@ -209,7 +110,6 @@ class CourseBackend_HisInOne extends CourseBackend return 30 * 60; } - public function getRefreshTime() { return 60 * 60; @@ -226,159 +126,39 @@ class CourseBackend_HisInOne extends CourseBackend if (empty($requestedRoomIds)) { return array(); } - $currentWeek = $this->getCurrentWeekDates(); $tTables = []; - //get all eventIDs in a given room - $eventIds = []; foreach ($requestedRoomIds as $roomId) { - $ok = false; - foreach ($currentWeek as $day) { - $roomEventIds = $this->findUnit($roomId, $day, false); - if ($roomEventIds === false) - continue; - $ok = true; - $eventIds = array_merge($eventIds, $roomEventIds); - } - if ($ok) { - $tTables[$roomId] = []; - } - } - $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) - continue; - $eventDetails = array_merge($eventDetails, $event); - } - $name = false; - $now = time(); - foreach ($eventDetails as $event) { - foreach (array('/hisdefaulttext', - '/hisshorttext', - '/hisshortcomment') as $path) { - $name = $this->getArrayPath($event, $path); - if (!empty($name) && !empty($name[0])) - break; - $name = false; - } - if ($name === false) { - $name = ['???']; - } - $planElements = $this->getArrayPath($event, '/hisplanelements/hisplanelement'); - if ($planElements === false) { - $this->addError('Cannot find ./hisplanelements/hisplanelement', false); - //error_log('Cannot find ./hisplanelements/hisplanelement'); - //error_log(print_r($event, true)); + $data = $this->downloadIcal($roomId); + if ($data === null) { + $this->addError("Downloading ical for $roomId failed", false); continue; } - foreach ($planElements as $planElement) { - if (empty($planElement['hisplannedDates'])) - continue; - // Do not use -- is set improperly for some courses :-( - /* - $checkDate = $this->getArrayPath($planElement, '/hisplannedDates/hisplannedDate/hisenddate'); - if (!empty($checkDate) && strtotime($checkDate[0]) + 86400 < $now) - continue; // Course ended - $checkDate = $this->getArrayPath($planElement, '/hisplannedDates/hisplannedDate/hisstartdate'); - if (!empty($checkDate) && strtotime($checkDate[0]) - 86400 > $now) - continue; // Course didn't start yet - */ - $cancelled = $this->getArrayPath($planElement, '/hiscancelled'); - $cancelled = $cancelled !== false && is_array($cancelled) && ($cancelled[0] > 0 || strtolower($cancelled[0]) === 'true'); - $unitPlannedDates = $this->getArrayPath($planElement, - '/hisplannedDates/hisplannedDate/hisindividualDates/hisindividualDate'); - if ($unitPlannedDates === false) { - $this->addError('Cannot find ./hisplannedDates/hisplannedDate/hisindividualDates/hisindividualDate', false); - //error_log('Cannot find ./hisplannedDates/hisplannedDate/hisindividualDates/hisindividualDate'); - //error_log(print_r($planElement, true)); - continue; - } - $localName = $this->getArrayPath($planElement, '/hisdefaulttext'); - if ($localName === false || empty($localName[0])) { - $localName = $name; - } - foreach ($unitPlannedDates as $plannedDate) { - $eventRoomId = $this->getArrayPath($plannedDate, '/hisroomId')[0]; - $eventDate = $this->getArrayPath($plannedDate, '/hisexecutiondate')[0]; - if (in_array($eventRoomId, $requestedRoomIds) && in_array($eventDate, $currentWeek)) { - $startTime = $this->getArrayPath($plannedDate, '/hisstarttime')[0]; - $endTime = $this->getArrayPath($plannedDate, '/hisendtime')[0]; - $tTables[$eventRoomId][] = array( - 'title' => $localName[0], - 'start' => $eventDate . "T" . $startTime, - 'end' => $eventDate . "T" . $endTime, - 'cancelled' => $cancelled, - ); - } - } + foreach ($data as $event) { + $tTables[$roomId][] = array( + 'title' => $this->toTitle($event), + 'start' => $event->dtstart, + 'end' => $event->dtend, + 'cancelled' => false, // ??? How + ); } } 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 + * Get a usable title from either SUMMARY or DESCRIPTION + * @param ICalEvent $event */ - public function readUnit($unit) + private function toTitle($event) { - $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->postToServer($soap_request, "readUnit"); - if ($response1 === false) { - return false; - } - $response2 = $this->xmlStringToArray($response1, $err); - if ($response2 === false) { - $this->addError("Cannot parse unit $unit as XML: $err", false); - return false; - } - if (!isset($response2['soapenvBody'])) { - $this->addError('Backend reply is missing element soapenvBody', true); - return false; - } - if (isset($response2['soapenvBody']['soapenvFault'])) { - $this->addError('SOAP-Fault (' . $response2['soapenvBody']['soapenvFault']['faultcode'] . ") " . $response2['soapenvBody']['soapenvFault']['faultstring'], true); - return false; + $title = $event->summary; + if (empty($title)) { + $title = $event->description; } - return $this->getArrayPath($response2, '/soapenvBody/hisreadUnitResponse/hisunit'); - } - - /** - * @return array with days of the current week in datetime format - */ - private function getCurrentWeekDates() - { - $returnValue = array(); - $date = date('Y-m-d', strtotime('last Monday')); - for ($i = 0; $i < 14; $i++) { - $returnValue[] = $date; - $date = date('Y-m-d', strtotime($date.' +1 day')); + if (empty($title)) { + $title = 'Unknown'; } - return $returnValue; + return preg_replace([',(\s*<br\s*/?>\s*|\r|\n|\\\r|\\\n)+,', '/\\\\([,;:])/'], ["\n", '$1'], $title); } public function __destruct() |