diff options
Diffstat (limited to 'modules-available/locationinfo/inc')
3 files changed, 628 insertions, 22 deletions
diff --git a/modules-available/locationinfo/inc/HisInOneAPI.php b/modules-available/locationinfo/inc/HisInOneAPI.php new file mode 100644 index 00000000..fa7bf32f --- /dev/null +++ b/modules-available/locationinfo/inc/HisInOneAPI.php @@ -0,0 +1,276 @@ +<?php +interface iTimetableRequest +{ + public function getJson($param); + public function getJsons($param); +} + +class HisInOneSoapClient implements iTimetableRequest +{ + private $username; + private $password; + private $location; + + + //Constructs the HisInOneClient all parameters are strings + function __construct($location, $username, $password) { + $this->location = $location; + $this->password = $password; + $this->username = $username; + } + + + //Contstructs the Soap Header $doc is a DOMDocument this returns a 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; + } + + //returns the IDs in an array for a given roomID + public function findUnit($roomID){ + $termyear = date('Y'); + $termtype = date('n'); + if($termtype > 3 && termtype < 10){ + $termtype = 2; + } + elseif ($termtype > 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); + $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); + $unitDefaulttext = $doc->createElement('ns1:unitDefaulttext'); + $findUnit->appendChild($unitDefaulttext); + $unitElementnr = $doc->createElement('ns1:unitElementnr'); + $findUnit->appendChild($unitElementnr); + $editingStatusId = $doc->createElement('ns1:editingStatusId'); + $findUnit->appendChild($editingStatusId); + $courseEventtypeId = $doc->createElement('ns1:courseEventtypeId'); + $findUnit->appendChild($courseEventtypeId); + $courseTeachingLanguageId = $doc->createElement('ns1:courseTeachingLanguageId'); + $findUnit->appendChild($courseTeachingLanguageId); + $planelementDefaulttext = $doc->createElement('ns1:planelementDefaulttext'); + $findUnit->appendChild($planelementDefaulttext); + $parallelgroupId= $doc->createElement('ns1:parallelgroupId'); + $findUnit->appendChild($parallelgroupId); + $termYearN = $doc->createElement('termYear',$termyear); + $findUnit->appendChild($termYearN); + $termTypeValueId = $doc->createElement('termTypeValueId',$termtype); + $findUnit->appendChild($termTypeValueId); + $individualDatesExecutionDate = $doc->createElement('ns1:individualDatesExecutionDate'); + $findUnit->appendChild($individualDatesExecutionDate); + $individualDatesStarttime = $doc->createElement('ns1:individualDatesStarttime'); + $findUnit->appendChild($individualDatesStarttime); + $individualDatesEndtime = $doc->createElement('ns1:individualDatesEndtime'); + $findUnit->appendChild($individualDatesEndtime); + $roomIdN = $doc->createElement('ns1:roomId',$roomID); + $findUnit->appendChild($roomIdN); + + $soap_request = $doc->saveXML(); + $respons1 = $this->__doRequest($soap_request, "findUnit"); + $respons2 = $this->toArray($respons1); + $id = $respons2['soapenvBody']['hisfindUnitResponse']['hisunitIds']['hisid']; + return $id; + } + + //This function sends a Soaprequest with the eventID and returns an array which contains much + // informations, like start and enddates for events and their name. + 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); + $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); + $unitId = $doc->createElement('ns1:unitId',$unit); + $readUnit->appendChild($unitId); + + $soap_request = $doc->saveXML(); + $respons1 = $this->__doRequest($soap_request, "readUnit"); + $respons2 = $this->toArray($respons1); + $respons3 = $respons2['soapenvBody']['hisreadUnitResponse']; + return $respons3; + } + //Makes a SOAP-Request as a normal POST + 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 => false, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_URL => $this->location , + CURLOPT_POSTFIELDS => $request , + CURLOPT_HTTPHEADER => $header , + ); + + curl_setopt_array($soap_do , $options); + + $output = curl_exec($soap_do); + + if( $output === false) + { + $err = 'Curl error: ' . curl_error($soap_do); + echo $err; + } + else + { + ///Operation completed successfully + } + curl_close($soap_do); + return $output; + } + + //Request for a timetable with roomid as int + public function getJson($param){ + //get all eventIDs in a given room + $eventIDs = $this-> findUnit($param); + //get all information on each event + foreach ($eventIDs as $each_event) { + $events[] = $this->readUnit((int) $each_event); + } + $timetable = array(); + $currentWeek = $this->getCurrentWeekDates(); + //Here I go over the soapresponse + foreach ($events as $event){ + $title = $event['hisunit']['hisplanelements']['hisplanelement'][0]['hisdefaulttext']; + foreach($event as $subject){ + $units = $subject['hisplanelements']['hisplanelement']; + foreach($units as $unit){ + $dates = $unit['hisplannedDates']['hisplannedDate']['hisindividualDates']['hisindividualDate']; + foreach($dates as $date){ + $roomID = $date['hisroomId']; + $datum = $date['hisexecutiondate']; + if(intval($roomID) == $param && in_array($datum,$currentWeek)){ + + $startTime = $date['hisstarttime']; + $endTime = $date['hisendtime']; + $json = array( + 'title' => $title, + 'start' => $datum." ".$startTime, + 'end' => $datum." ".$endTime + ); + array_push($timetable,$json); + } + + + + + } + } + } + + } + return json_encode($timetable); + } + + //this function transforms a xml string into an array + private function toArray($response){ + $cleanresponse = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $response); + $xml = new SimpleXMLElement($cleanresponse); + $array = json_decode(json_encode((array)$xml), TRUE); + return $array; + } + + //Request for a timetable with roomids as array + public function getJsons($param){ + //get all eventIDs in a given room + foreach ($param as $ID) { + $eventIDs = $this-> findUnit($ID); + } + $eventIDs = array_unique($eventIDs); + + //get all information on each event + foreach ($eventIDs as $each_event) { + $events[] = $this->readUnit((int) $each_event); + } + $ttables = []; + $currentWeek = $this->getCurrentWeekDates(); + foreach ($param as $room){ + $timetable = array(); + //Here I go over the soapresponse + foreach ($events as $event){ + $title = $event['hisunit']['hisplanelements']['hisplanelement'][0]['hisdefaulttext']; + foreach($event as $subject){ + $units = $subject['hisplanelements']['hisplanelement']; + foreach($units as $unit){ + $dates = $unit['hisplannedDates']['hisplannedDate']['hisindividualDates']['hisindividualDate']; + foreach($dates as $date){ + $roomID = $date['hisroomId']; + $datum = $date['hisexecutiondate']; + if(intval($roomID) == $room && in_array($datum,$currentWeek)){ + + $startTime = $date['hisstarttime']; + $endTime = $date['hisendtime']; + $json = array( + 'title' => $title, + 'start' => $datum." ".$startTime, + 'end' => $datum." ".$endTime + ); + $timetable[]= $json; + } + } + } + } + } + $ttables[$room] =json_encode($timetable); + } + return $ttables; + } + + + function getCurrentWeekDates() + { + $DateArray = array(); + $startdate = strtotime('Now'); + for($i=0 ;$i<=7; $i++) { + $DateArray[] = date('Y-m-d', strtotime("+ {$i} day", $startdate)); + + } + return $DateArray; + } + +} +?> diff --git a/modules-available/locationinfo/inc/coursebackend.inc.php b/modules-available/locationinfo/inc/coursebackend.inc.php index 39ba3b90..184ca048 100644 --- a/modules-available/locationinfo/inc/coursebackend.inc.php +++ b/modules-available/locationinfo/inc/coursebackend.inc.php @@ -75,34 +75,87 @@ abstract class CourseBackend */ public abstract function getDisplayName(); - /** + /** + * initializes the class with url it needs to connect to. + */ + public abstract function __contruct($url); + + + /** + * @returns array with parameter name as key and type as value + */ + public abstract function getCredentials(); + + /** + * uses json to setCredentials, the json must follow the form given in + * getCredentials + * @returns void + */ + public abstract function setCredentials($json); + + /** * @return int desired caching time of results, in seconds. 0 = no caching */ public abstract function getCacheTime(); + + /** + * @return int age after which ttables are no longer refreshed should be + * greater then CacheTime + */ + public abstract function getRefreshTime(); - /** + /** * Internal version of fetch, to be overridden by subclasses. * @param $roomIds - * @return mixed + * @return array a multidemensional array that uses the roomID as key + * and has the sheduls as string in the value */ - protected abstract function fetchSchedulesInternal($roomIds); + protected abstract function fetchSchedulesInternal($roomId); /** - * Method for fetching the schedules of the given rooms. - * @param array $roomIds Array of room IDs to fetch - * @return array|bool some multidimensional array of rooms as result, or false on error + * Method for fetching the schedule of the given room. + * @param int $roomId int of room ID to fetch + * @return string|bool some jsonstring as result, or false on error */ - public final function fetchSchedules($roomIds) + public final function fetchSchedule($roomID) { - // TODO: Check if in cache - // TODO: Check if we should refresh other rooms recently requested by front ends but not included in $roomIds - $aggregatedRoomIds = $roomIds; // + extra rooms - // ... - // If not in cache: - $result = $this->fetchSchedulesInternal($aggregatedRoomIds); - // TODO: Place in cache if necessary - // TODO: Remove entries from result that were not in $roomsIds - return $result; + $dbquery1 = Database::simpleQuery("SELECT servertype, serverid, serverroomid, lastcalenderupdate FROM location_info WHERE locationid = :id", array('id' => $roomID)); + $dbd1=$dbquery1->fetch(PDO::FETCH_ASSOC); + $serverID = $dbd1['serverid']; + $sroomID = $dbd1['serverroomid']; + $lastUpdate = $dbd1['lastcalenderupdate']; + //Check if in cache + if(strtotime($lastUpdate) > strtotime("-".$this->getCachedTime()."seconds") && $this->getCachedTime()>0) { + $dbquery3 = Database::simpleQuery("SELECT calendar FROM location_info WHERE locationid = :id", array('id' => $sroomID)); + $dbd3=$dbquery3->fetch(PDO::FETCH_ASSOC); + return $dbd3['callendar']; + } + //Check if we should refresh other rooms recently requested by front ends + elseif ($this->getCachedTime()>0) { + $dbquery4 = Database::simpleQuery("SELECT serverroomid, lastcalenderupdate FROM location_info WHERE serverid= :id", array('id' => $serverID)); + $roomIDs[] = $sroomID; + foreach($dbquery4->fetchAll(PDO::FETCH_COLUMN) as $row){ + if($row['lastcalenderupdate']<$this->getRefreshTime()){ + $roomIDs[] = $row['serverroomid']; + } + } + $roomIDs = array_unique($roomIDs); + } + else { + $roomIDs[] = $sroomID; + } + $dbquery2 = Database::simpleQuery("SELECT serverurl, credentials FROM `setting_location_info` WHERE serverid = :id", array('id' => $serverID)); + $dbd2=$dbquery2->fetch(PDO::FETCH_ASSOC); + $this->setCredentials($dbd2['credentials']); + $result = $this->getJsons($roomIDs); + + if($this->getCachedTime()>0){ + foreach ($result as $key => $value) { + $now = strtotime('Now'); + $dbquery1 = Database::simpleQuery("UPDATE location_info SET calendar = :ttable, lastcalenderupdate = :now WHERE locationid = :id ", array('id' => $key,'ttable' => $result[$key],'now'=> $now)); + } + } + return $result[$roomID]; } } diff --git a/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php b/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php index 20ddeffb..0e790d27 100644 --- a/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php +++ b/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php @@ -2,12 +2,289 @@ class CourseBackend_HisInOne extends CourseBackend { + private $username; + private $password; + private $location; - public function getDisplayName() - { - return 'HIS in One'; - } - // TODO + //Constructs the HisInOneClient + function __construct($location) { + $this->location = $location."/qisserver/services2/CourseService"; + } + // + public function setCredentials($json) { + $data = json_decode($json, TRUE); + $this->password = $data['password']; + $this->username = $data['username']."/t".$data['role']; + } + + //Cache the timetables for 30 minutes ttables older than 60 are not refreshed + public function getCacheTime(){ + return 30*60; + } + //ttables older than 60 minutes are not refreshed + public function getRefreshTime() { + return 60*60; + } + + public function getDisplayName(){ + return "HisInOne"; + } + + public function getCredentials() { + $credentials = ["username" => "string", "role" =>"string","password"=>"string"]; + return $credentials; + } + + //Contstructs the Soap Header $doc is a DOMDocument this returns a 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; + } + + //returns the IDs in an array for a given roomID + public function findUnit($roomID){ + $termyear = date('Y'); + $termtype = date('n'); + if($termtype > 3 && termtype < 10){ + $termtype = 2; + } + elseif ($termtype > 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); + $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); + $unitDefaulttext = $doc->createElement('ns1:unitDefaulttext'); + $findUnit->appendChild($unitDefaulttext); + $unitElementnr = $doc->createElement('ns1:unitElementnr'); + $findUnit->appendChild($unitElementnr); + $editingStatusId = $doc->createElement('ns1:editingStatusId'); + $findUnit->appendChild($editingStatusId); + $courseEventtypeId = $doc->createElement('ns1:courseEventtypeId'); + $findUnit->appendChild($courseEventtypeId); + $courseTeachingLanguageId = $doc->createElement('ns1:courseTeachingLanguageId'); + $findUnit->appendChild($courseTeachingLanguageId); + $planelementDefaulttext = $doc->createElement('ns1:planelementDefaulttext'); + $findUnit->appendChild($planelementDefaulttext); + $parallelgroupId= $doc->createElement('ns1:parallelgroupId'); + $findUnit->appendChild($parallelgroupId); + $termYearN = $doc->createElement('termYear',$termyear); + $findUnit->appendChild($termYearN); + $termTypeValueId = $doc->createElement('termTypeValueId',$termtype); + $findUnit->appendChild($termTypeValueId); + $individualDatesExecutionDate = $doc->createElement('ns1:individualDatesExecutionDate'); + $findUnit->appendChild($individualDatesExecutionDate); + $individualDatesStarttime = $doc->createElement('ns1:individualDatesStarttime'); + $findUnit->appendChild($individualDatesStarttime); + $individualDatesEndtime = $doc->createElement('ns1:individualDatesEndtime'); + $findUnit->appendChild($individualDatesEndtime); + $roomIdN = $doc->createElement('ns1:roomId',$roomID); + $findUnit->appendChild($roomIdN); + + $soap_request = $doc->saveXML(); + $respons1 = $this->__doRequest($soap_request, "findUnit"); + $respons2 = $this->toArray($respons1); + $id = $respons2['soapenvBody']['hisfindUnitResponse']['hisunitIds']['hisid']; + return $id; + } + + //This function sends a Soaprequest with the eventID and returns an array which contains much + // informations, like start and enddates for events and their name. + 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); + $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); + $unitId = $doc->createElement('ns1:unitId',$unit); + $readUnit->appendChild($unitId); + + $soap_request = $doc->saveXML(); + $respons1 = $this->__doRequest($soap_request, "readUnit"); + $respons2 = $this->toArray($respons1); + $respons3 = $respons2['soapenvBody']['hisreadUnitResponse']; + return $respons3; + } + + //Makes a SOAP-Request as a normal POST + 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 => false, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_URL => $this->location , + CURLOPT_POSTFIELDS => $request , + CURLOPT_HTTPHEADER => $header , + ); + + curl_setopt_array($soap_do , $options); + + $output = curl_exec($soap_do); + + if( $output === false) + { + $err = 'Curl error: ' . curl_error($soap_do); + echo $err; + } + else + { + ///Operation completed successfully + } + curl_close($soap_do); + return $output; + } + + //Request for a timetable with roomid as int + public function getJson($param){ + //get all eventIDs in a given room + $eventIDs = $this-> findUnit($param); + //get all information on each event + foreach ($eventIDs as $each_event) { + $events[] = $this->readUnit((int) $each_event); + } + $timetable = array(); + $currentWeek = $this->getCurrentWeekDates(); + //Here I go over the soapresponse + foreach ($events as $event){ + $title = $event['hisunit']['hisplanelements']['hisplanelement'][0]['hisdefaulttext']; + foreach($event as $subject){ + $units = $subject['hisplanelements']['hisplanelement']; + foreach($units as $unit){ + $dates = $unit['hisplannedDates']['hisplannedDate']['hisindividualDates']['hisindividualDate']; + foreach($dates as $date){ + $roomID = $date['hisroomId']; + $datum = $date['hisexecutiondate']; + if(intval($roomID) == $param && in_array($datum,$currentWeek)){ + + $startTime = $date['hisstarttime']; + $endTime = $date['hisendtime']; + $json = array( + 'title' => $title, + 'start' => $datum." ".$startTime, + 'end' => $datum." ".$endTime + ); + array_push($timetable,$json); + } + + + + + } + } + } + + } + return json_encode($timetable); + } + + //this function transforms a xml string into an array + private function toArray($response){ + $cleanresponse = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $response); + $xml = new SimpleXMLElement($cleanresponse); + $array = json_decode(json_encode((array)$xml), TRUE); + return $array; + } + + //Request for a timetable with roomids as array + public function fetchSchedulesInternal($param){ + //get all eventIDs in a given room + foreach ($param as $ID) { + $eventIDs = $this-> findUnit($ID); + } + $eventIDs = array_unique($eventIDs); + + //get all information on each event + foreach ($eventIDs as $each_event) { + $events[] = $this->readUnit((int) $each_event); + } + $ttables = []; + $currentWeek = $this->getCurrentWeekDates(); + foreach ($param as $room){ + $timetable = array(); + //Here I go over the soapresponse + foreach ($events as $event){ + $title = $event['hisunit']['hisplanelements']['hisplanelement'][0]['hisdefaulttext']; + foreach($event as $subject){ + $units = $subject['hisplanelements']['hisplanelement']; + foreach($units as $unit){ + $dates = $unit['hisplannedDates']['hisplannedDate']['hisindividualDates']['hisindividualDate']; + foreach($dates as $date){ + $roomID = $date['hisroomId']; + $datum = $date['hisexecutiondate']; + if(intval($roomID) == $room && in_array($datum,$currentWeek)){ + + $startTime = $date['hisstarttime']; + $endTime = $date['hisendtime']; + $json = array( + 'title' => $title, + 'start' => $datum." ".$startTime, + 'end' => $datum." ".$endTime + ); + $timetable[]= $json; + } + } + } + } + } + $ttables[$room] =json_encode($timetable); + } + return $ttables; + } + + + private function getCurrentWeekDates(){ + $DateArray = array(); + $startdate = strtotime('Now'); + for($i=0 ;$i<=7; $i++) { + $DateArray[] = date('Y-m-d', strtotime("+ {$i} day", $startdate)); + } + return $DateArray; + } }
\ No newline at end of file |