summaryrefslogblamecommitdiffstats
path: root/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php
blob: 22b1d8fb9eda4f77c21a1934e311db292c00de23 (plain) (tree)
1
2
3
4
5
6
7
8
9
10

     
                                                  
 





                                       



                                    
 
 
                                                     
         






                                                                                                 
                         


                                                         
                                     
                 
 
                                     



                                                                
                                                    
                                                                








                                                                                                                         
                            
         
 
                                                  





                                                                    
                                                                                                                 




                                                                           

                                         


                                                                 
                                                                        
                 
                                              
         
 
           


                                                                                                                        
           
                                                                             
         







                                                                                                                                          
                 

                                                 




                                                                
                                                                                                      
                                                                                   

                                                
                                                                            
                                             
                                     
                 
                                                                 






                                                                                      
                                                                                      

                                     
                                                                       
                                                                                                                                                                                 
                                     





                                                                                                                    

                                                                            
                        

                                                                              
                 

                                                                   
                                                              







                                                                                                                              
                                                                                                                                
                 
                               

         



                                  

















                                                                                                                                                       

         




                                                                                
                                                        






                                                                   


                                                        



                                                       

                                                                                




                                                       
                                                               
 
                                                       

                                        
                                                                                      

                                             

                                                           

                               
 

                                      
                               

         
 




                                        





                                        
                                                                 
         

                                               
                 
                                                            
                              
                                                  

                                                        









                                                                                                               
                                 




                                                                                  
                         
                 

                                                    
                                        
                 
                                   
                                                   

                                                                   
                                               
                                                                                                
                                                     
                                                             
                                         
                         
                                                                           
                 
                              


                                                                        
                                                                                       
                                                                           






                                                                      



                                                                                                       

                                                                 
                         
                                                                 

                                                                           























                                                                                                                                           

                                 
                 
                                

         
 



                                                                                                                   
                                       
         







                                                                                                                                          
                 

                                                 




                                                                
                                                                                 
 
                                                
                                                                            
                                           
                                     
                 
                                                                 


                                                        
                                                                                      


                                                                       
                                                                                                                                                                                 
                                     
                 
                                                                                                   
         
 


                                                                         

                                              

                                       
                                            
                                                                                                 
                 
                                    
         
 






                                                      
 
<?php

class CourseBackend_HisInOne extends CourseBackend
{
	private $username = '';
	private $password = '';
	private $open = true;
	private $location;
	private $verifyHostname = true;
	private $verifyCert = true;
	/**
	 * @var bool|resource
	 */
	private $curlHandle = false;


	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'];
		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->verifyHostname = $data['verifyHostname'];
		$this->verifyCert = $data['verifyCert'];

		return true;
	}

	public function getCredentialDefinitions()
	{
		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()
	{
		if (empty($this->location)) {
			$this->error = "Credentials are not set";
		} else {
			$this->findUnit(123456789, date('Y-m-d'), 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, $day, $connectionCheckOnly = false)
	{
		$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 ($this->error !== false) {
			return false;
		}
		$response2 = $this->xmlStringToArray($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 = '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;
		}
		// 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->error = 'Cannot find ' . $path;
			//@file_put_contents('/tmp/findUnit-1.' . $roomId . '.' . microtime(true), print_r($response2, true));
			return false;
		}
		if (empty($idSubDoc))
			return $idSubDoc;
		$idList = $this->getArrayPath($idSubDoc, $subpath);
		if ($idList === false) {
			$this->error = 'Cannot find ' . $subpath . ' after ' . $path;
			@file_put_contents('/tmp/bwlp-findUnit-2.' . $roomId . '.' . microtime(true), print_r($idSubDoc, true));
		}
		return $idList;
	}

	/**
	 * @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 postToServer($request, $action)
	{
		$header = array(
			"Content-type: text/xml;charset=\"utf-8\"",
			"SOAPAction: \"" . $action . "\"",
			"Content-length: " . strlen($request),
		);

		if ($this->curlHandle === false) {
			$this->curlHandle = curl_init();
		}

		$options = array(
			CURLOPT_RETURNTRANSFER => true,
			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,
		);

		curl_setopt_array($this->curlHandle, $options);

		$output = curl_exec($this->curlHandle);

		if ($output === false) {
			$this->error = 'Curl error: ' . curl_error($this->curlHandle);
		} else {
			$this->error = false;
			///Operation completed successfully
		}
		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();
		}
		$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) {
					if ($this->error !== false) {
						error_log('Cannot findUnit(' . $roomId . '): ' . $this->error);
						$this->error = false;
					}
					// TODO: Error gets swallowed
					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) {
				error_log('Cannot readUnit(' . $eventId . '): ' . $this->error);
				$this->error = false;
				// TODO: Error gets swallowed
				continue;
			}
			$eventDetails = array_merge($eventDetails, $event);
		}
		$name = false;
		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->error = 'Cannot find ./hisplanelements/hisplanelement';
				error_log('Cannot find ./hisplanelements/hisplanelement');
				error_log(print_r($event, true));
				continue;
			}
			foreach ($planElements as $planElement) {
				if (empty($planElement['hisplannedDates']))
					continue;
				$unitPlannedDates = $this->getArrayPath($planElement,
					'/hisplannedDates/hisplannedDate/hisindividualDates/hisindividualDate');
				if ($unitPlannedDates === false) {
					$this->error = 'Cannot find ./hisplannedDates/hisplannedDate/hisindividualDates/hisindividualDate';
					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
						);
					}
				}
			}
		}
		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->postToServer($soap_request, "readUnit");
		if ($response1 === false) {
			return false;
		}
		$response2 = $this->xmlStringToArray($response1);
		if ($response2 === false)
			return false;
		if (!isset($response2['soapenvBody'])) {
			$this->error = '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->getArrayPath($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;
	}

	public function __destruct()
	{
		if ($this->curlHandle !== false) {
			curl_close($this->curlHandle);
		}
	}

}