summaryrefslogtreecommitdiffstats
path: root/modules-available/locationinfo/inc
diff options
context:
space:
mode:
Diffstat (limited to 'modules-available/locationinfo/inc')
-rw-r--r--modules-available/locationinfo/inc/coursebackend.inc.php67
-rw-r--r--modules-available/locationinfo/inc/coursebackend/coursebackend_davinci.inc.php20
-rw-r--r--modules-available/locationinfo/inc/coursebackend/coursebackend_dummy.inc.php20
-rwxr-xr-xmodules-available/locationinfo/inc/coursebackend/coursebackend_exchange.inc.php53
-rw-r--r--modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php22
-rw-r--r--modules-available/locationinfo/inc/coursebackend/coursebackend_ical.inc.php12
-rw-r--r--modules-available/locationinfo/inc/icalcoursebackend.inc.php33
-rw-r--r--modules-available/locationinfo/inc/icalevent.inc.php444
-rw-r--r--modules-available/locationinfo/inc/icalparser.inc.php151
-rw-r--r--modules-available/locationinfo/inc/infopanel.inc.php30
-rw-r--r--modules-available/locationinfo/inc/locationinfo.inc.php116
-rw-r--r--modules-available/locationinfo/inc/locationinfohooks.inc.php27
12 files changed, 549 insertions, 446 deletions
diff --git a/modules-available/locationinfo/inc/coursebackend.inc.php b/modules-available/locationinfo/inc/coursebackend.inc.php
index 6e4d77ac..ea1bebac 100644
--- a/modules-available/locationinfo/inc/coursebackend.inc.php
+++ b/modules-available/locationinfo/inc/coursebackend.inc.php
@@ -39,7 +39,7 @@ abstract class CourseBackend
$this->errors = [];
}
- protected final function addError($message, $fatal)
+ protected final function addError(string $message, bool $fatal)
{
$this->errors[] = ['time' => time(), 'message' => $message, 'fatal' => $fatal];
}
@@ -55,7 +55,7 @@ abstract class CourseBackend
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);
+ preg_match('#coursebackend_([^/.]+)\.inc\.php$#i', $file, $out);
$className = 'CourseBackend_' . $out[1];
if (!class_exists($className)) {
trigger_error("Backend type source unit $file doesn't seem to define class $className", E_USER_ERROR);
@@ -71,13 +71,13 @@ abstract class CourseBackend
*
* @return array list of backends
*/
- public static function getList()
+ public static function getList(): array
{
self::loadDb();
return array_keys(self::$backendTypes);
}
- public static function exists($backendType)
+ public static function exists($backendType): bool
{
self::loadDb();
return isset(self::$backendTypes[$backendType]);
@@ -89,7 +89,7 @@ abstract class CourseBackend
* @param string $backendType name of module type
* @return \CourseBackend|false module instance
*/
- public static function getInstance($backendType)
+ public static function getInstance(string $backendType)
{
self::loadDb();
if (!isset(self::$backendTypes[$backendType])) {
@@ -106,18 +106,18 @@ abstract class CourseBackend
/**
* @return string return display name of backend
*/
- public abstract function getDisplayName();
+ public abstract function getDisplayName(): string;
/**
* @returns \BackendProperty[] list of properties that need to be set
*/
- public abstract function getCredentialDefinitions();
+ public abstract function getCredentialDefinitions(): array;
/**
* @return boolean true if the connection works, false otherwise
*/
- public abstract function checkConnection();
+ public abstract function checkConnection(): bool;
/**
* uses json to setCredentials, the json must follow the form given in
@@ -126,42 +126,39 @@ abstract class CourseBackend
* @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);
+ public abstract function setCredentialsInternal(array $data): bool;
/**
* @return int desired caching time of results, in seconds. 0 = no caching
*/
- public abstract function getCacheTime();
+ public abstract function getCacheTime(): int;
/**
* @return int age after which timetables are no longer refreshed should be
* greater then CacheTime
*/
- public abstract function getRefreshTime();
+ public abstract function getRefreshTime(): int;
/**
* Internal version of fetch, to be overridden by subclasses.
*
- * @param $roomIds array with remote IDs for wanted rooms
+ * @param $requestedRoomIds array with remote IDs for wanted rooms
* @return array a recursive array that uses the roomID as key
* and has the schedule array as value. A schedule array contains an array in this format:
* ["start"=>'JJJJ-MM-DD"T"HH:MM:SS',"end"=>'JJJJ-MM-DD"T"HH:MM:SS',"title"=>string]
*/
- protected abstract function fetchSchedulesInternal($roomId);
+ protected abstract function fetchSchedulesInternal(array $requestedRoomIds): array;
/**
* In case you want to sanitize or otherwise mangle a property for your backend,
* override this.
- * @param string $prop
- * @param $value
- * @return mixed
*/
- public function mangleProperty($prop, $value)
+ public function mangleProperty(string $prop, $value)
{
return $value;
}
- private static function fixTime(&$start, &$end)
+ private static function fixTime(string &$start, string &$end): bool
{
if (!preg_match('/^(\d{2}|\d{4})-?\d{2}-?\d{2}-?T\d{1,2}:?\d{2}:?(\d{2})?$/', $start))
return false;
@@ -178,14 +175,10 @@ abstract class CourseBackend
* Method for fetching the schedule of the given rooms on a server.
*
* @param array $requestedLocationIds 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
+ * @return array array containing the timetables as value and roomid as key as result, or false on error
*/
- public final function fetchSchedule($requestedLocationIds)
+ public final function fetchSchedule(array $requestedLocationIds): array
{
- if (!is_array($requestedLocationIds)) {
- $this->addError('No array of roomids was given to fetchSchedule', false);
- return false;
- }
if (empty($requestedLocationIds))
return array();
$requestedLocationIds = array_values($requestedLocationIds);
@@ -195,7 +188,7 @@ abstract class CourseBackend
array('locations' => $requestedLocationIds));
$returnValue = [];
$remoteIds = [];
- while ($row = $dbquery1->fetch(PDO::FETCH_ASSOC)) {
+ foreach ($dbquery1 as $row) {
// Check if in cache - if lastUpdate is null then it is interpreted as 1970
if ($row['lastcalendarupdate'] + $this->getCacheTime() < $NOW) {
$remoteIds[$row['locationid']] = $row['serverlocationid'];
@@ -222,7 +215,7 @@ abstract class CourseBackend
'lastuse' => $NOW - $this->getRefreshTime(),
'minage' => $NOW - $this->getCacheTime(),
));
- while ($row = $dbquery4->fetch(PDO::FETCH_ASSOC)) {
+ foreach ($dbquery4 as $row) {
$remoteIds[$row['locationid']] = $row['serverlocationid'];
}
}
@@ -239,9 +232,6 @@ abstract class CourseBackend
]);
}
$backendResponse = $this->fetchSchedulesInternal(array_unique($remoteIds));
- if ($backendResponse === false) {
- return false;
- }
// Fetching might have taken a while, get current time again
$NOW = time();
@@ -282,7 +272,7 @@ abstract class CourseBackend
return $returnValue;
}
- public final function setCredentials($serverId, $data)
+ public final function setCredentials(int $serverId, array $data): bool
{
foreach ($this->getCredentialDefinitions() as $prop) {
if (!isset($data[$prop->property])) {
@@ -302,18 +292,9 @@ abstract class CourseBackend
}
/**
- * @return false if there was no error string with error message if there was one
- */
- public final function getError()
- {
- trigger_error('getError() is legacy; use getErrors()');
- return $this->error;
- }
-
- /**
* @return array list of errors that occurred during processing.
*/
- public final function getErrors()
+ public final function getErrors(): array
{
return $this->errors;
}
@@ -378,7 +359,7 @@ abstract class CourseBackend
* @param string $response xml document to convert
* @return bool|array array representation of the xml if possible, false otherwise
*/
- protected function xmlStringToArray($response, &$error)
+ protected function xmlStringToArray(string $response, &$error)
{
$cleanresponse = preg_replace('/(<\/?)(\w+):([^>]*>)/', '$1$2$3', $response);
try {
@@ -390,8 +371,7 @@ abstract class CourseBackend
}
return false;
}
- $array = json_decode(json_encode((array)$xml), true);
- return $array;
+ return json_decode(json_encode((array)$xml), true);
}
}
@@ -414,7 +394,6 @@ class BackendProperty {
* Initialize additional fields of this class that are only required
* for rendering the server configuration dialog.
*
- * @param string $backendId target backend id
* @param mixed $current current value of this property.
*/
public function initForRender($current = null) {
diff --git a/modules-available/locationinfo/inc/coursebackend/coursebackend_davinci.inc.php b/modules-available/locationinfo/inc/coursebackend/coursebackend_davinci.inc.php
index 07c8457d..786ab459 100644
--- a/modules-available/locationinfo/inc/coursebackend/coursebackend_davinci.inc.php
+++ b/modules-available/locationinfo/inc/coursebackend/coursebackend_davinci.inc.php
@@ -11,7 +11,7 @@ class CourseBackend_Davinci extends CourseBackend
*/
private $curlHandle = false;
- public function setCredentialsInternal($data)
+ public function setCredentialsInternal(array $data): bool
{
if (empty($data['baseUrl'])) {
$this->addError("No url is given", true);
@@ -24,7 +24,7 @@ class CourseBackend_Davinci extends CourseBackend
return true;
}
- public function checkConnection()
+ public function checkConnection(): bool
{
if (empty($this->location)) {
$this->addError("Credentials are not set", true);
@@ -40,7 +40,7 @@ class CourseBackend_Davinci extends CourseBackend
return true;
}
- public function getCredentialDefinitions()
+ public function getCredentialDefinitions(): array
{
return [
new BackendProperty('baseUrl', 'string'),
@@ -49,17 +49,17 @@ class CourseBackend_Davinci extends CourseBackend
];
}
- public function getDisplayName()
+ public function getDisplayName(): string
{
return 'Davinci';
}
- public function getCacheTime()
+ public function getCacheTime(): int
{
return 30 * 60;
}
- public function getRefreshTime()
+ public function getRefreshTime(): int
{
return 0;
}
@@ -68,9 +68,9 @@ class CourseBackend_Davinci extends CourseBackend
* @param string $roomId unique name of the room, as used by davinci
* @param \DateTime $startDate start date to fetch
* @param \DateTime $endDate end date of range to fetch
- * @return array|bool if successful the arrayrepresentation of the timetable
+ * @return false|string if successful the array representation of the timetable
*/
- private function fetchRoomRaw($roomId, $startDate, $endDate)
+ private function fetchRoomRaw(string $roomId, DateTime $startDate, DateTime $endDate)
{
$url = $this->location . "content=xml&type=room&name=" . urlencode($roomId)
. "&startdate=" . $startDate->format('d.m.Y') . "&enddate=" . $endDate->format('d.m.Y');
@@ -97,7 +97,7 @@ class CourseBackend_Davinci extends CourseBackend
}
- public function fetchSchedulesInternal($requestedRoomIds)
+ public function fetchSchedulesInternal(array $requestedRoomIds): array
{
$startDate = new DateTime('last Monday 0:00');
$endDate = new DateTime('+14 days 0:00');
@@ -134,7 +134,7 @@ class CourseBackend_Davinci extends CourseBackend
$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'] : '???';
+ $subject = $lesson['Subject'] ?? '???';
$timetable[] = array(
'title' => $subject,
'start' => $date . "T" . $start . ':00',
diff --git a/modules-available/locationinfo/inc/coursebackend/coursebackend_dummy.inc.php b/modules-available/locationinfo/inc/coursebackend/coursebackend_dummy.inc.php
index 2cb2be18..4588bf7c 100644
--- a/modules-available/locationinfo/inc/coursebackend/coursebackend_dummy.inc.php
+++ b/modules-available/locationinfo/inc/coursebackend/coursebackend_dummy.inc.php
@@ -15,9 +15,9 @@ class CourseBackend_Dummy extends CourseBackend
* @param int $serverId ID of the server
* @returns bool if the credentials were in the correct format
*/
- public function setCredentialsInternal($json)
+ public function setCredentialsInternal(array $data): bool
{
- $x = $json;
+ $x = $data;
$this->pw = $x['password'];
if ($this->pw === "mfg") {
@@ -30,7 +30,7 @@ class CourseBackend_Dummy extends CourseBackend
/**
* @return boolean true if the connection works, false otherwise
*/
- public function checkConnection()
+ public function checkConnection(): bool
{
if ($this->pw == "mfg") {
return true;
@@ -42,7 +42,7 @@ class CourseBackend_Dummy extends CourseBackend
/**
* @returns array with parameter name as key and and an array with type, help text and mask as value
*/
- public function getCredentialDefinitions()
+ public function getCredentialDefinitions(): array
{
$options = ["opt1", "opt2", "opt3", "opt4", "opt5", "opt6", "opt7", "opt8"];
return [
@@ -58,7 +58,7 @@ class CourseBackend_Dummy extends CourseBackend
/**
* @return string return display name of backend
*/
- public function getDisplayName()
+ public function getDisplayName(): string
{
return 'Dummy with array';
}
@@ -66,7 +66,7 @@ class CourseBackend_Dummy extends CourseBackend
/**
* @return int desired caching time of results, in seconds. 0 = no caching
*/
- public function getCacheTime()
+ public function getCacheTime(): int
{
return 0;
}
@@ -75,7 +75,7 @@ class CourseBackend_Dummy extends CourseBackend
* @return int age after which timetables are no longer refreshed should be
* greater then CacheTime
*/
- public function getRefreshTime()
+ public function getRefreshTime(): int
{
return 0;
}
@@ -83,15 +83,15 @@ class CourseBackend_Dummy extends CourseBackend
/**
* Internal version of fetch, to be overridden by subclasses.
*
- * @param $roomIds array with local ID as key and serverId as value
+ * @param $requestedRoomIds 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 schedule array contains an array in this format:
* ["start"=>'YYYY-MM-DD<T>HH:MM:SS',"end"=>'YYYY-MM-DD<T>HH:MM:SS',"title"=>string]
*/
- public function fetchSchedulesInternal($roomId)
+ public function fetchSchedulesInternal(array $requestedRoomIds): array
{
$a = array();
- foreach ($roomId as $id) {
+ foreach ($requestedRoomIds as $id) {
if ($id == 1) {
$now = time();
return array($id => array(
diff --git a/modules-available/locationinfo/inc/coursebackend/coursebackend_exchange.inc.php b/modules-available/locationinfo/inc/coursebackend/coursebackend_exchange.inc.php
index 44847ce2..df33dadd 100755
--- a/modules-available/locationinfo/inc/coursebackend/coursebackend_exchange.inc.php
+++ b/modules-available/locationinfo/inc/coursebackend/coursebackend_exchange.inc.php
@@ -12,6 +12,7 @@ spl_autoload_register(function ($class) {
require_once $file;
});
+use jamesiarmes\PhpEws\ArrayType\NonEmptyArrayOfBaseFolderIdsType;
use jamesiarmes\PhpEws\Client;
use jamesiarmes\PhpEws\Enumeration\DefaultShapeNamesType;
use jamesiarmes\PhpEws\Enumeration\DistinguishedFolderIdNameType;
@@ -38,7 +39,7 @@ class CourseBackend_Exchange extends CourseBackend
/**
* @return string return display name of backend
*/
- public function getDisplayName()
+ public function getDisplayName(): string
{
return "Microsoft Exchange";
}
@@ -46,7 +47,7 @@ class CourseBackend_Exchange extends CourseBackend
/**
* @returns \BackendProperty[] list of properties that need to be set
*/
- public function getCredentialDefinitions()
+ public function getCredentialDefinitions(): array
{
$options = [
Client::VERSION_2007,
@@ -72,7 +73,7 @@ class CourseBackend_Exchange extends CourseBackend
/**
* @return boolean true if the connection works, false otherwise
*/
- public function checkConnection()
+ public function checkConnection(): bool
{
$client = $this->getClient();
$request = new ResolveNamesType();
@@ -104,7 +105,7 @@ class CourseBackend_Exchange extends CourseBackend
* @param array $data assoc array with data required by backend
* @returns bool if the credentials were in the correct format
*/
- public function setCredentialsInternal($data)
+ public function setCredentialsInternal(array $data): bool
{
foreach (['username', 'password'] as $field) {
if (empty($data[$field])) {
@@ -133,7 +134,7 @@ class CourseBackend_Exchange extends CourseBackend
/**
* @return int desired caching time of results, in seconds. 0 = no caching
*/
- public function getCacheTime()
+ public function getCacheTime(): int
{
return 15 * 60;
}
@@ -142,7 +143,7 @@ class CourseBackend_Exchange extends CourseBackend
* @return int age after which timetables are no longer refreshed. should be
* greater than CacheTime.
*/
- public function getRefreshTime()
+ public function getRefreshTime(): int
{
return 30 * 60;
}
@@ -150,12 +151,12 @@ class CourseBackend_Exchange extends CourseBackend
/**
* Internal version of fetch, to be overridden by subclasses.
*
- * @param $roomIds array with local ID as key and serverId as value
+ * @param $requestedRoomIds 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 schedule array contains an array in this format:
* ["start"=>'JJJJ-MM-DD HH:MM:SS',"end"=>'JJJJ-MM-DD HH:MM:SS',"title"=>string]
*/
- protected function fetchSchedulesInternal($requestedRoomIds)
+ protected function fetchSchedulesInternal(array $requestedRoomIds): array
{
$startDate = new DateTime('last Monday 0:00');
$endDate = new DateTime('+14 days 0:00');
@@ -172,8 +173,13 @@ class CourseBackend_Exchange extends CourseBackend
// Iterate over the events that were found, printing some data for each.
foreach ($items as $item) {
- $start = new DateTime($item->Start);
- $end = new DateTime($item->End);
+ try {
+ $start = new DateTime($item->Start);
+ $end = new DateTime($item->End);
+ } catch (Exception $e) {
+ $this->addError("Invalid date range: '{$item->Start}' -> '{$item->End}'", false);
+ continue;
+ }
$schedules[$roomId][] = array(
'title' => $item->Subject,
@@ -186,13 +192,9 @@ class CourseBackend_Exchange extends CourseBackend
}
/**
- * @param \jamesiarmes\PhpEws\Client $client
- * @param \DateTime $startDate
- * @param \DateTime $endDate
- * @param string $roomAddress
* @return \jamesiarmes\PhpEws\Type\CalendarItemType[]
*/
- public function findEventsForRoom($client, $startDate, $endDate, $roomAddress)
+ public function findEventsForRoom(Client $client, DateTime $startDate, DateTime $endDate, string $roomAddress): array
{
$request = new FindItemType();
$request->Traversal = ItemQueryTraversalType::SHALLOW;
@@ -206,12 +208,20 @@ class CourseBackend_Exchange extends CourseBackend
$folderId->Id = DistinguishedFolderIdNameType::CALENDAR;
$folderId->Mailbox = new EmailAddressType();
$folderId->Mailbox->EmailAddress = $roomAddress;
+ $request->ParentFolderIds = new NonEmptyArrayOfBaseFolderIdsType();
$request->ParentFolderIds->DistinguishedFolderId[] = $folderId;
- $response = $client->FindItem($request);
- $response_messages = $response->ResponseMessages->FindItemResponseMessage;
-
+ try {
+ $response = $client->FindItem($request);
+ } catch (Exception $e) {
+ $this->addError('Exception calling FindItem: ' . $e->getMessage(), true);
+ return [];
+ }
+ if (!is_object($response->ResponseMessages)) {
+ $this->addError('FindItem returned response without ResponseMessages', true);
+ return [];
+ }
$items = [];
- foreach ($response_messages as $response_message) {
+ foreach ($response->ResponseMessages->FindItemResponseMessage as $response_message) {
// Make sure the request succeeded.
if ($response_message->ResponseClass !== ResponseClassType::SUCCESS) {
$code = $response_message->ResponseCode;
@@ -224,10 +234,7 @@ class CourseBackend_Exchange extends CourseBackend
return $items;
}
- /**
- * @return \jamesiarmes\PhpEws\Client
- */
- public function getClient()
+ public function getClient(): Client
{
$client = new Client($this->serverAddress, $this->username, $this->password, $this->clientVersion);
$client->setTimezone($this->timezone);
diff --git a/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php b/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php
index 8bd18169..55d5ed4b 100644
--- a/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php
+++ b/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php
@@ -3,7 +3,7 @@
class CourseBackend_HisInOne extends ICalCourseBackend
{
- public function setCredentialsInternal($data)
+ public function setCredentialsInternal(array $data): bool
{
if (empty($data['baseUrl'])) {
$this->addError("No url is given", true);
@@ -16,7 +16,7 @@ class CourseBackend_HisInOne extends ICalCourseBackend
return true;
}
- public function getCredentialDefinitions()
+ public function getCredentialDefinitions(): array
{
return [
new BackendProperty('baseUrl', 'string'),
@@ -25,7 +25,7 @@ class CourseBackend_HisInOne extends ICalCourseBackend
];
}
- public function mangleProperty($prop, $value)
+ public function mangleProperty(string $prop, $value)
{
if ($prop === 'baseUrl') {
// Update form SOAP to iCal url
@@ -43,7 +43,15 @@ class CourseBackend_HisInOne extends ICalCourseBackend
return $value;
}
- public function checkConnection()
+ protected function toTitle(ICalEvent $event): string
+ {
+ $title = parent::toTitle($event);
+ // His in one seems to prefix *some* (but *not* all) of the lectures by their ID/("Nummer")
+ // No clue what that format is supposed to be, this regex is some guesswork after observing this for a while
+ return preg_replace('#^[0-9][0-9A-ZÄÖÜ]{3,9}-[A-Za-z0-9/_ÄÖÜäöüß.-]{4,30}\s+#u', '', $title);
+ }
+
+ public function checkConnection(): bool
{
if (!$this->isOK())
return false;
@@ -57,18 +65,18 @@ class CourseBackend_HisInOne extends ICalCourseBackend
return false;
}
- public function getCacheTime()
+ public function getCacheTime(): int
{
return 30 * 60;
}
- public function getRefreshTime()
+ public function getRefreshTime(): int
{
return 60 * 60;
}
- public function getDisplayName()
+ public function getDisplayName(): string
{
return "HisInOne";
}
diff --git a/modules-available/locationinfo/inc/coursebackend/coursebackend_ical.inc.php b/modules-available/locationinfo/inc/coursebackend/coursebackend_ical.inc.php
index 98dca1cb..f1791c4e 100644
--- a/modules-available/locationinfo/inc/coursebackend/coursebackend_ical.inc.php
+++ b/modules-available/locationinfo/inc/coursebackend/coursebackend_ical.inc.php
@@ -6,7 +6,7 @@ class CourseBackend_ICal extends ICalCourseBackend
/** @var string room ID for testing connection */
private $testId;
- public function setCredentialsInternal($data)
+ public function setCredentialsInternal(array $data): bool
{
if (empty($data['baseUrl'])) {
$this->addError("No url is given", true);
@@ -20,7 +20,7 @@ class CourseBackend_ICal extends ICalCourseBackend
return true;
}
- public function getCredentialDefinitions()
+ public function getCredentialDefinitions(): array
{
return [
new BackendProperty('baseUrl', 'string'),
@@ -33,7 +33,7 @@ class CourseBackend_ICal extends ICalCourseBackend
];
}
- public function checkConnection()
+ public function checkConnection(): bool
{
if (!$this->isOK())
return false;
@@ -42,18 +42,18 @@ class CourseBackend_ICal extends ICalCourseBackend
return ($this->downloadIcal($this->testId) !== null);
}
- public function getCacheTime()
+ public function getCacheTime(): int
{
return 30 * 60;
}
- public function getRefreshTime()
+ public function getRefreshTime(): int
{
return 60 * 60;
}
- public function getDisplayName()
+ public function getDisplayName(): string
{
return "iCal";
}
diff --git a/modules-available/locationinfo/inc/icalcoursebackend.inc.php b/modules-available/locationinfo/inc/icalcoursebackend.inc.php
index fba0866c..838d18b7 100644
--- a/modules-available/locationinfo/inc/icalcoursebackend.inc.php
+++ b/modules-available/locationinfo/inc/icalcoursebackend.inc.php
@@ -18,18 +18,9 @@ abstract class ICalCourseBackend extends CourseBackend
/** @var bool|resource */
private $curlHandle = false;
- /**
- * Initialize values
- *
- * @param string $location
- * @param bool $verifyCert
- * @param bool $verifyHostname
- * @param string $authMethod
- * @param string $user
- * @param string $pass
- */
- protected function init($location, $verifyCert, $verifyHostname,
- $authMethod = 'NONE', $user = '', $pass = '')
+ protected function init(
+ string $location, bool $verifyCert, bool $verifyHostname,
+ string $authMethod = 'NONE', string $user = '', string $pass = '')
{
$this->verifyCert = $verifyCert;
$this->verifyHostname = $verifyHostname;
@@ -43,19 +34,26 @@ abstract class ICalCourseBackend extends CourseBackend
}
/**
- * @param int $roomId room id
+ * @param string $roomId room id
* @param callable $errorFunc
* @return ICalEvent[]|null all events for this room in the range -7 days to +7 days, or NULL on error
*/
- protected function downloadIcal($roomId)
+ protected function downloadIcal(string $roomId): ?array
{
if (!$this->isOK())
return null;
+
+ try {
+ $ical = new ICalParser(['filterDaysBefore' => 7, 'filterDaysAfter' => 7]);
+ } catch (Exception $e) {
+ $this->addError('Error instantiating ICalParser: ' . $e->getMessage(), true);
+ return null;
+ }
+
if ($this->curlHandle === false) {
$this->curlHandle = curl_init();
}
- $ical = new ICalParser(['filterDaysBefore' => 7, 'filterDaysAfter' => 7]);
$options = [
CURLOPT_WRITEFUNCTION => function ($ch, $data) use ($ical) {
$ical->feedData($data);
@@ -92,7 +90,7 @@ abstract class ICalCourseBackend extends CourseBackend
return $ical->events();
}
- public function fetchSchedulesInternal($requestedRoomIds): array
+ public function fetchSchedulesInternal(array $requestedRoomIds): array
{
if (empty($requestedRoomIds) || !$this->isOK()) {
return array();
@@ -118,9 +116,8 @@ abstract class ICalCourseBackend extends CourseBackend
/**
* Get a usable title from either SUMMARY or DESCRIPTION
- * @param ICalEvent $event
*/
- private function toTitle($event): string
+ protected function toTitle(ICalEvent $event): string
{
$title = $event->summary;
if (empty($title)) {
diff --git a/modules-available/locationinfo/inc/icalevent.inc.php b/modules-available/locationinfo/inc/icalevent.inc.php
index 1fb586ee..c5aea349 100644
--- a/modules-available/locationinfo/inc/icalevent.inc.php
+++ b/modules-available/locationinfo/inc/icalevent.inc.php
@@ -2,199 +2,253 @@
class ICalEvent
{
- // phpcs:disable Generic.Arrays.DisallowLongArraySyntax
-
- const HTML_TEMPLATE = '<p>%s: %s</p>';
-
- /**
- * https://www.kanzaki.com/docs/ical/summary.html
- *
- * @var $summary
- */
- public $summary;
-
- /**
- * https://www.kanzaki.com/docs/ical/dtstart.html
- *
- * @var $dtstart
- */
- public $dtstart;
-
- /**
- * https://www.kanzaki.com/docs/ical/dtend.html
- *
- * @var $dtend
- */
- public $dtend;
-
- /**
- * https://www.kanzaki.com/docs/ical/duration.html
- *
- * @var $duration
- */
- public $duration;
-
- /**
- * https://www.kanzaki.com/docs/ical/dtstamp.html
- *
- * @var $dtstamp
- */
- public $dtstamp;
-
- /**
- * https://www.kanzaki.com/docs/ical/uid.html
- *
- * @var $uid
- */
- public $uid;
-
- /**
- * https://www.kanzaki.com/docs/ical/created.html
- *
- * @var $created
- */
- public $created;
-
- /**
- * https://www.kanzaki.com/docs/ical/lastModified.html
- *
- * @var $lastmodified
- */
- public $lastmodified;
-
- /**
- * https://www.kanzaki.com/docs/ical/description.html
- *
- * @var $description
- */
- public $description;
-
- /**
- * https://www.kanzaki.com/docs/ical/location.html
- *
- * @var $location
- */
- public $location;
-
- /**
- * https://www.kanzaki.com/docs/ical/sequence.html
- *
- * @var $sequence
- */
- public $sequence;
-
- /**
- * https://www.kanzaki.com/docs/ical/status.html
- *
- * @var $status
- */
- public $status;
-
- /**
- * https://www.kanzaki.com/docs/ical/transp.html
- *
- * @var $transp
- */
- public $transp;
-
- /**
- * https://www.kanzaki.com/docs/ical/organizer.html
- *
- * @var $organizer
- */
- public $organizer;
-
- /**
- * https://www.kanzaki.com/docs/ical/attendee.html
- *
- * @var $attendee
- */
- public $attendee;
-
- /**
- * Creates the Event object
- *
- * @param array $data
- * @return void
- */
- public function __construct(array $data = array())
- {
- foreach ($data as $key => $value) {
- $variable = self::snakeCase($key);
- $this->{$variable} = self::prepareData($value);
- }
- }
-
- /**
- * Prepares the data for output
- *
- * @param mixed $value
- * @return mixed
- */
- protected function prepareData($value)
- {
- if (is_string($value)) {
- return stripslashes(trim(str_replace('\n', "\n", $value)));
- } elseif (is_array($value)) {
- return array_map('self::prepareData', $value);
- }
-
- return $value;
- }
-
- /**
- * Returns Event data excluding anything blank
- * within an HTML template
- *
- * @param string $html HTML template to use
- * @return string
- */
- public function printData($html = self::HTML_TEMPLATE)
- {
- $data = array(
- 'SUMMARY' => $this->summary,
- 'DTSTART' => $this->dtstart,
- 'DTEND' => $this->dtend,
- 'DURATION' => $this->duration,
- 'DTSTAMP' => $this->dtstamp,
- 'UID' => $this->uid,
- 'CREATED' => $this->created,
- 'LAST-MODIFIED' => $this->lastmodified,
- 'DESCRIPTION' => $this->description,
- 'LOCATION' => $this->location,
- 'SEQUENCE' => $this->sequence,
- 'STATUS' => $this->status,
- 'TRANSP' => $this->transp,
- 'ORGANISER' => $this->organizer,
- 'ATTENDEE(S)' => $this->attendee,
- );
-
- // Remove any blank values
- $data = array_filter($data);
-
- $output = '';
-
- foreach ($data as $key => $value) {
- $output .= sprintf($html, $key, $value);
- }
-
- return $output;
- }
-
- /**
- * Converts the given input to snake_case
- *
- * @param string $input
- * @param string $glue
- * @param string $separator
- * @return string
- */
- protected static function snakeCase($input, $glue = '_', $separator = '-')
- {
- $input = preg_split('/(?<=[a-z])(?=[A-Z])/x', $input);
- $input = implode($glue, $input);
- $input = str_replace($separator, $glue, $input);
-
- return strtolower($input);
- }
+ // phpcs:disable Generic.Arrays.DisallowLongArraySyntax
+
+ const HTML_TEMPLATE = '<p>%s: %s</p>';
+
+ /**
+ * https://www.kanzaki.com/docs/ical/summary.html
+ *
+ * @var string
+ */
+ public $summary;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/dtstart.html
+ *
+ * @var string
+ */
+ public $dtstart;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/dtend.html
+ *
+ * @var string
+ */
+ public $dtend;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/duration.html
+ *
+ * @var string
+ */
+ public $duration;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/dtstamp.html
+ *
+ * @var string
+ */
+ public $dtstamp;
+
+ /**
+ * When the event starts, represented as a timezone-adjusted string
+ *
+ * @var string
+ */
+ public $dtstart_tz;
+
+ /**
+ * When the event ends, represented as a timezone-adjusted string
+ *
+ * @var string
+ */
+ public $dtend_tz;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/uid.html
+ *
+ * @var string
+ */
+ public $uid;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/created.html
+ *
+ * @var string
+ */
+ public $created;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/lastModified.html
+ *
+ * @var string
+ */
+ public $last_modified;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/description.html
+ *
+ * @var string
+ */
+ public $description;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/location.html
+ *
+ * @var string
+ */
+ public $location;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/sequence.html
+ *
+ * @var string
+ */
+ public $sequence;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/status.html
+ *
+ * @var string
+ */
+ public $status;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/transp.html
+ *
+ * @var string
+ */
+ public $transp;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/organizer.html
+ *
+ * @var string
+ */
+ public $organizer;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/attendee.html
+ *
+ * @var string
+ */
+ public $attendee;
+
+ /**
+ * Manage additional properties
+ *
+ * @var array<string, mixed>
+ */
+ private $additionalProperties = array();
+
+ /**
+ * Creates the Event object
+ *
+ * @param array $data
+ * @return void
+ */
+ public function __construct(array $data = array())
+ {
+ foreach ($data as $key => $value) {
+ $variable = self::snakeCase($key);
+ if (property_exists($this, $variable)) {
+ $this->{$variable} = $this->prepareData($value);
+ } else {
+ $this->additionalProperties[$variable] = $this->prepareData($value);
+ }
+ }
+ }
+
+ /**
+ * Magic getter method
+ *
+ * @param string $additionalPropertyName
+ * @return mixed
+ */
+ public function __get(string $additionalPropertyName)
+ {
+ if (array_key_exists($additionalPropertyName, $this->additionalProperties)) {
+ return $this->additionalProperties[$additionalPropertyName];
+ }
+
+ return null;
+ }
+
+ /**
+ * Magic isset method
+ */
+ public function __isset(string $name): bool
+ {
+ return is_null($this->$name) === false;
+ }
+
+ /**
+ * Prepares the data for output
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ protected function prepareData($value)
+ {
+ if (is_string($value)) {
+ return stripslashes(trim(str_replace('\n', "\n", $value)));
+ }
+
+ if (is_array($value)) {
+ return array_map(function ($value) {
+ return $this->prepareData($value);
+ }, $value);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Returns Event data excluding anything blank
+ * within an HTML template
+ *
+ * @param string $html HTML template to use
+ * @return string
+ */
+ public function printData($html = self::HTML_TEMPLATE)
+ {
+ $data = array(
+ 'SUMMARY' => $this->summary,
+ 'DTSTART' => $this->dtstart,
+ 'DTEND' => $this->dtend,
+ 'DTSTART_TZ' => $this->dtstart_tz,
+ 'DTEND_TZ' => $this->dtend_tz,
+ 'DURATION' => $this->duration,
+ 'DTSTAMP' => $this->dtstamp,
+ 'UID' => $this->uid,
+ 'CREATED' => $this->created,
+ 'LAST-MODIFIED' => $this->last_modified,
+ 'DESCRIPTION' => $this->description,
+ 'LOCATION' => $this->location,
+ 'SEQUENCE' => $this->sequence,
+ 'STATUS' => $this->status,
+ 'TRANSP' => $this->transp,
+ 'ORGANISER' => $this->organizer,
+ 'ATTENDEE(S)' => $this->attendee,
+ );
+
+ // Remove any blank values
+ $data = array_filter($data);
+
+ $output = '';
+
+ foreach ($data as $key => $value) {
+ $output .= sprintf($html, $key, $value);
+ }
+
+ return $output;
+ }
+
+ /**
+ * Converts the given input to snake_case
+ *
+ * @param string $input
+ * @param string $glue
+ * @param string $separator
+ * @return string
+ */
+ protected static function snakeCase($input, $glue = '_', $separator = '-')
+ {
+ $input = preg_split('/(?<=[a-z])(?=[A-Z])/x', $input);
+ $input = implode($glue, $input);
+ $input = str_replace($separator, $glue, $input);
+
+ return strtolower($input);
+ }
}
diff --git a/modules-available/locationinfo/inc/icalparser.inc.php b/modules-available/locationinfo/inc/icalparser.inc.php
index 0be8777b..eacb67b1 100644
--- a/modules-available/locationinfo/inc/icalparser.inc.php
+++ b/modules-available/locationinfo/inc/icalparser.inc.php
@@ -23,7 +23,6 @@ class ICalParser
const ICAL_DATE_TIME_TEMPLATE = 'TZID=%s:';
const ISO_8601_WEEK_START = 'MO';
const RECURRENCE_EVENT = 'Generated recurrence event';
- const TIME_FORMAT = 'His';
const TIME_ZONE_UTC = 'UTC';
const UNIX_FORMAT = 'U';
@@ -481,15 +480,12 @@ class ICalParser
/**
* Creates the ICal object
*
- * @param mixed $files
* @param array $options
* @return void
* @throws Exception
*/
public function __construct(array $options = array())
{
- ini_set('auto_detect_line_endings', '1');
-
foreach ($options as $option => $value) {
if (in_array($option, self::$configurableOptions)) {
$this->{$option} = $value;
@@ -501,10 +497,7 @@ class ICalParser
$this->defaultTimeZone = date_default_timezone_get();
}
- // Ideally you would use `PHP_INT_MIN` from PHP 7
- $php_int_min = -2147483648;
-
- $this->windowMinTimestamp = is_null($this->filterDaysBefore) ? $php_int_min : (new DateTime('now'))->sub(new DateInterval('P' . $this->filterDaysBefore . 'D'))->getTimestamp();
+ $this->windowMinTimestamp = is_null($this->filterDaysBefore) ? PHP_INT_MIN : (new DateTime('now'))->sub(new DateInterval('P' . $this->filterDaysBefore . 'D'))->getTimestamp();
$this->windowMaxTimestamp = is_null($this->filterDaysAfter) ? PHP_INT_MAX : (new DateTime('now'))->add(new DateInterval('P' . $this->filterDaysAfter . 'D'))->getTimestamp();
$this->shouldFilterByWindow = !is_null($this->filterDaysBefore) || !is_null($this->filterDaysAfter);
@@ -516,7 +509,7 @@ class ICalParser
*
* @param string $data
*/
- public function feedData($data)
+ public function feedData(string $data)
{
$this->feedBuffer .= $data;
$start = 0;
@@ -581,7 +574,7 @@ class ICalParser
*
* @return bool
*/
- public function isValid()
+ public function isValid(): bool
{
return $this->hasSeenStart;
}
@@ -591,7 +584,7 @@ class ICalParser
*
* @param string $line
*/
- protected function handleLine($line)
+ protected function handleLine(string $line)
{
$line = rtrim($line); // Trim trailing whitespace
$line = $this->removeUnprintableChars($line);
@@ -602,18 +595,18 @@ class ICalParser
$add = $this->keyValueFromString($line);
- if ($add === false) {
+ if ($add === null) {
return;
}
- $keyword = $add[0];
+ $keyword = $add[0]; // string
$values = $add[1]; // May be an array containing multiple values
if (!is_array($values)) {
if (!empty($values)) {
$values = array($values); // Make an array as not already
$blankArray = array(); // Empty placeholder array
- array_push($values, $blankArray);
+ $values[] = $blankArray;
} else {
$values = array(); // Use blank array to ignore this line
}
@@ -752,16 +745,9 @@ class ICalParser
foreach ($events as $key => $anEvent) {
if ($anEvent === null) {
unset($events[$key]);
-
- continue;
- }
-
- if ($this->doesEventStartOutsideWindow($anEvent)) {
+ } elseif ($this->doesEventStartOutsideWindow($anEvent)) {
$this->eventCount--;
-
unset($events[$key]);
-
- continue;
}
}
@@ -776,7 +762,7 @@ class ICalParser
* @param array $event
* @return boolean
*/
- protected function doesEventStartOutsideWindow(array $event)
+ protected function doesEventStartOutsideWindow(array $event): bool
{
return !isset($event['DTSTART']) || !$this->isValidDate($event['DTSTART'])
|| $this->isOutOfRange($event['DTSTART'], $this->windowMinTimestamp, $this->windowMaxTimestamp);
@@ -790,7 +776,7 @@ class ICalParser
* @param integer $maxTimestamp
* @return boolean
*/
- protected function isOutOfRange($calendarDate, $minTimestamp, $maxTimestamp)
+ protected function isOutOfRange(string $calendarDate, int $minTimestamp, int $maxTimestamp): bool
{
$timestamp = strtotime(explode('T', $calendarDate)[0]);
@@ -798,36 +784,15 @@ class ICalParser
}
/**
- * Unfolds an iCal file in preparation for parsing
- * (https://icalendar.org/iCalendar-RFC-5545/3-1-content-lines.html)
- *
- * @param array $lines
- * @return array
- */
- protected function unfold(array $lines)
- {
- $string = implode(PHP_EOL, $lines);
- $string = preg_replace('/' . PHP_EOL . '[ \t]/', '', $string);
-
- $lines = explode(PHP_EOL, $string);
-
- return $lines;
- }
-
- /**
* Add one key and value pair to the `$this->cal` array
*
* @param string $component
- * @param string|boolean $keyword
- * @param string $value
+ * @param string $keyword
+ * @param string|string[] $value
* @return void
*/
- protected function addCalendarComponentWithKeyAndValue($component, $keyword, $value)
+ protected function addCalendarComponentWithKeyAndValue(string $component, string $keyword, $value)
{
- if ($keyword == false) {
- $keyword = $this->lastKeyword;
- }
-
switch ($component) {
case 'VALARM':
$key1 = 'VEVENT';
@@ -840,7 +805,7 @@ class ICalParser
if (is_array($value)) {
// Add array of properties to the end
- array_push($this->cal[$key1][$key2][$key3]["{$keyword}_array"], $value);
+ $this->cal[$key1][$key2][$key3]["{$keyword}_array"][] = $value;
} else {
if (!isset($this->cal[$key1][$key2][$key3][$keyword])) {
$this->cal[$key1][$key2][$key3][$keyword] = $value;
@@ -862,7 +827,7 @@ class ICalParser
if (is_array($value)) {
// Add array of properties to the end
- array_push($this->cal[$key1][$key2]["{$keyword}_array"], $value);
+ $this->cal[$key1][$key2]["{$keyword}_array"][] = $value;
} else {
if (!isset($this->cal[$key1][$key2][$keyword])) {
$this->cal[$key1][$key2][$keyword] = $value;
@@ -882,7 +847,7 @@ class ICalParser
if ($keyword === 'DURATION') {
try {
$duration = new DateInterval($value);
- array_push($this->cal[$key1][$key2]["{$keyword}_array"], $duration);
+ $this->cal[$key1][$key2]["{$keyword}_array"][] = $duration;
} catch (Exception $e) {
error_log('Ignoring invalid duration ' . $value);
}
@@ -928,6 +893,7 @@ class ICalParser
break;
}
+ // Remove?
$this->lastKeyword = $keyword;
}
@@ -935,9 +901,9 @@ class ICalParser
* Gets the key value pair from an iCal string
*
* @param string $text
- * @return array|boolean
+ * @return ?array
*/
- protected function keyValueFromString($text)
+ protected function keyValueFromString(string $text): ?array
{
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
@@ -974,14 +940,14 @@ class ICalParser
}
if (count($matches) === 0) {
- return false;
+ return null;
}
- if (preg_match('/^([A-Z-]+)([;][\w\W]*)?$/', $matches[1])) {
+ if (preg_match('/^([A-Z-]+)(;[\w\W]*)?$/', $matches[1])) {
$matches = array_splice($matches, 1, 2); // Remove first match and re-align ordering
// Process properties
- if (preg_match('/([A-Z-]+)[;]([\w\W]*)/', $matches[0], $properties)) {
+ if (preg_match('/([A-Z-]+);([\w\W]*)/', $matches[0], $properties)) {
// Remove first match
array_shift($properties);
// Fix to ignore everything in keyword after a ; (e.g. Language, TZID, etc.)
@@ -1023,9 +989,8 @@ class ICalParser
}
return $matches;
- } else {
- return false; // Ignore this match
}
+ return null; // Ignore this match
}
/**
@@ -1034,7 +999,7 @@ class ICalParser
* @param string $icalDate
* @return DateTime
*/
- public function iCalDateToDateTime($icalDate)
+ public function iCalDateToDateTime(string $icalDate): DateTime
{
/**
* iCal times may be in 3 formats, (https://www.kanzaki.com/docs/ical/dateTime.html)
@@ -1091,7 +1056,7 @@ class ICalParser
* @param string $icalDate
* @return integer
*/
- public function iCalDateToUnixTimestamp($icalDate)
+ public function iCalDateToUnixTimestamp(string $icalDate): int
{
return $this->iCalDateToDateTime($icalDate)->getTimestamp();
}
@@ -1104,7 +1069,7 @@ class ICalParser
* @param string $format
* @return string|boolean
*/
- public function iCalDateWithTimeZone(array $event, $key, $format = self::DATE_TIME_FORMAT)
+ public function iCalDateWithTimeZone(array $event, string $key, string $format = self::DATE_TIME_FORMAT)
{
if (!isset($event["{$key}_array"]) || !isset($event[$key])) {
return false;
@@ -1141,7 +1106,6 @@ class ICalParser
if (empty($this->cal['VEVENT']))
return;
$events =& $this->cal['VEVENT'];
- $checks = null;
foreach ($events as $key => $anEvent) {
foreach (array('DTSTART', 'DTEND', 'RECURRENCE-ID') as $type) {
@@ -1175,23 +1139,20 @@ class ICalParser
$eventKeysToRemove = array();
foreach ($events as $key => $event) {
- $checks[] = !isset($event['RECURRENCE-ID']);
- $checks[] = isset($event['UID']);
- $checks[] = isset($event['UID']) && isset($this->alteredRecurrenceInstances[$event['UID']]);
+ $checks = !isset($event['RECURRENCE-ID'])
+ && isset($event['UID']) && isset($this->alteredRecurrenceInstances[$event['UID']]);
- if ((bool)array_product($checks)) {
+ if ($checks) {
$eventDtstartUnix = $this->iCalDateToUnixTimestamp($event['DTSTART_array'][3]);
// phpcs:ignore CustomPHPCS.ControlStructures.AssignmentInCondition
if (($alteredEventKey = array_search($eventDtstartUnix, $this->alteredRecurrenceInstances[$event['UID']])) !== false) {
$eventKeysToRemove[] = $alteredEventKey;
- $alteredEvent = array_replace_recursive($events[$key], $events[$alteredEventKey]);
+ $alteredEvent = array_replace_recursive($event, $events[$alteredEventKey]);
$this->alteredRecurrenceInstances[$event['UID']]['altered-event'] = array($key => $alteredEvent);
}
}
-
- unset($checks);
}
foreach ($eventKeysToRemove as $eventKeyToRemove) {
@@ -1568,7 +1529,7 @@ class ICalParser
* @param DateTime $initialDateTime
* @return array
*/
- protected function getDaysOfMonthMatchingByDayRRule(array $byDays, $initialDateTime)
+ protected function getDaysOfMonthMatchingByDayRRule(array $byDays, DateTime $initialDateTime): array
{
$matchingDays = array();
@@ -1628,7 +1589,7 @@ class ICalParser
* @param array $valuesList
* @return array
*/
- protected function filterValuesUsingBySetPosRRule(array $bySetPos, array $valuesList)
+ protected function filterValuesUsingBySetPosRRule(array $bySetPos, array $valuesList): array
{
$filteredMatches = array();
@@ -1688,7 +1649,7 @@ class ICalParser
*
* @return ICalEvent[]
*/
- public function events()
+ public function events(): array
{
if (empty($this->cal) || empty($this->cal['VEVENT']))
return [];
@@ -1706,9 +1667,9 @@ class ICalParser
*
* @return string
*/
- public function calendarName()
+ public function calendarName(): string
{
- return isset($this->cal['VCALENDAR']['X-WR-CALNAME']) ? $this->cal['VCALENDAR']['X-WR-CALNAME'] : '';
+ return $this->cal['VCALENDAR']['X-WR-CALNAME'] ?? '';
}
/**
@@ -1716,9 +1677,9 @@ class ICalParser
*
* @return string
*/
- public function calendarDescription()
+ public function calendarDescription(): string
{
- return isset($this->cal['VCALENDAR']['X-WR-CALDESC']) ? $this->cal['VCALENDAR']['X-WR-CALDESC'] : '';
+ return $this->cal['VCALENDAR']['X-WR-CALDESC'] ?? '';
}
/**
@@ -1727,7 +1688,7 @@ class ICalParser
* @param boolean $ignoreUtc
* @return string
*/
- public function calendarTimeZone($ignoreUtc = false)
+ public function calendarTimeZone(bool $ignoreUtc = false): ?string
{
if (isset($this->cal['VCALENDAR']['X-WR-TIMEZONE'])) {
$timeZone = $this->cal['VCALENDAR']['X-WR-TIMEZONE'];
@@ -1754,11 +1715,11 @@ class ICalParser
*
* @return array
*/
- public function freeBusyEvents()
+ public function freeBusyEvents(): array
{
$array = $this->cal;
- return isset($array['VFREEBUSY']) ? $array['VFREEBUSY'] : array();
+ return $array['VFREEBUSY'] ?? array();
}
/**
@@ -1784,7 +1745,7 @@ class ICalParser
* @return array
* @throws Exception
*/
- public function eventsFromRange($rangeStart = null, $rangeEnd = null)
+ public function eventsFromRange(string $rangeStart = null, string $rangeEnd = null): array
{
// Sort events before processing range
$events = $this->sortEventsWithOrder($this->events());
@@ -1857,7 +1818,7 @@ class ICalParser
* @param integer $sortOrder Either SORT_ASC, SORT_DESC, SORT_REGULAR, SORT_NUMERIC, SORT_STRING
* @return array
*/
- public function sortEventsWithOrder(array $events, $sortOrder = SORT_ASC)
+ public function sortEventsWithOrder(array $events, int $sortOrder = SORT_ASC): array
{
$extendedEvents = array();
$timestamp = array();
@@ -1878,7 +1839,7 @@ class ICalParser
* @param string $timeZone
* @return boolean
*/
- protected function isValidTimeZoneId($timeZone)
+ protected function isValidTimeZoneId(string $timeZone): bool
{
return $this->isValidIanaTimeZoneId($timeZone) !== false
|| $this->isValidCldrTimeZoneId($timeZone) !== false
@@ -1891,7 +1852,7 @@ class ICalParser
* @param string $timeZone
* @return boolean
*/
- protected function isValidIanaTimeZoneId($timeZone)
+ protected function isValidIanaTimeZoneId(string $timeZone): bool
{
if (in_array($timeZone, $this->validIanaTimeZones)) {
return true;
@@ -1923,7 +1884,7 @@ class ICalParser
* @param string $timeZone
* @return boolean
*/
- public function isValidCldrTimeZoneId($timeZone)
+ public function isValidCldrTimeZoneId(string $timeZone): bool
{
return array_key_exists(html_entity_decode($timeZone), self::$cldrTimeZonesMap);
}
@@ -1934,7 +1895,7 @@ class ICalParser
* @param string $timeZone
* @return boolean
*/
- public function isValidWindowsTimeZoneId($timeZone)
+ public function isValidWindowsTimeZoneId(string $timeZone): bool
{
return array_key_exists(html_entity_decode($timeZone), self::$windowsTimeZonesMap);
}
@@ -1942,12 +1903,9 @@ class ICalParser
/**
* Parses a duration and applies it to a date
*
- * @param string $date
- * @param DateInterval $duration
- * @param string $format
* @return integer|DateTime
*/
- protected function parseDuration($date, $duration, $format = self::UNIX_FORMAT)
+ protected function parseDuration(string $date, DateInterval $duration, ?string $format = self::UNIX_FORMAT)
{
$dateTime = date_create($date);
$dateTime->modify("{$duration->y} year");
@@ -1974,7 +1932,7 @@ class ICalParser
* @param string $data
* @return string
*/
- protected function removeUnprintableChars($data)
+ protected function removeUnprintableChars(string $data): string
{
return preg_replace('/[\x00-\x1F\x7F\xA0]/u', '', $data);
}
@@ -1986,7 +1944,7 @@ class ICalParser
* @param string $candidateText
* @return string
*/
- protected function escapeParamText($candidateText)
+ protected function escapeParamText(string $candidateText): string
{
if (strpbrk($candidateText, ':;,') !== false) {
return '"' . $candidateText . '"';
@@ -2002,13 +1960,12 @@ class ICalParser
* @param array $event
* @return array
*/
- public function parseExdates(array $event)
+ public function parseExdates(array $event): array
{
if (empty($event['EXDATE_array'])) {
return array();
- } else {
- $exdates = $event['EXDATE_array'];
}
+ $exdates = $event['EXDATE_array'];
$output = array();
$currentTimeZone = $this->defaultTimeZone;
@@ -2046,7 +2003,7 @@ class ICalParser
* @param string $value
* @return boolean
*/
- public function isValidDate($value)
+ public function isValidDate(string $value): bool
{
if (!$value) {
return false;
@@ -2065,10 +2022,10 @@ class ICalParser
* Returns a `DateTimeZone` object based on a string containing a time zone name.
* Falls back to the default time zone if string passed not a recognised time zone.
*
- * @param string $timeZoneString
+ * @param DateTimeZone|string $timeZoneString
* @return DateTimeZone
*/
- public function timeZoneStringToDateTimeZone($timeZoneString)
+ public function timeZoneStringToDateTimeZone($timeZoneString): DateTimeZone
{
if ($timeZoneString instanceof DateTimeZone)
return $timeZoneString;
diff --git a/modules-available/locationinfo/inc/infopanel.inc.php b/modules-available/locationinfo/inc/infopanel.inc.php
index 6deb9db5..1a0e9b67 100644
--- a/modules-available/locationinfo/inc/infopanel.inc.php
+++ b/modules-available/locationinfo/inc/infopanel.inc.php
@@ -7,16 +7,16 @@ class InfoPanel
* Gets the config of the location.
*
* @param int $locationID ID of the location
- * @param mixed $config the panel config will be returned here
- * @return string|bool paneltype, false if not exists
+ * @param ?array $config the panel config will be returned here
+ * @return ?string panel type, null if not exists
*/
- public static function getConfig($paneluuid, &$config)
+ public static function getConfig(string $paneluuid, ?array &$config): ?string
{
$panel = Database::queryFirst('SELECT panelname, panelconfig, paneltype, locationids FROM locationinfo_panel WHERE paneluuid = :paneluuid',
compact('paneluuid'));
if ($panel === false) {
- return false;
+ return null;
}
$config = LocationInfo::defaultPanelConfig($panel['paneltype']);
@@ -87,13 +87,10 @@ class InfoPanel
* @param array $array location list to populate with machine data
* @param bool $withPosition Defines if coords should be included or not.
*/
- public static function appendMachineData(&$array, $idList = false, $withPosition = false, $withHostname = false)
+ public static function appendMachineData(array &$array, array $idList, bool $withPosition = false, bool $withHostname = false): void
{
- if (empty($array) && $idList === false)
+ if (empty($idList))
return;
- if ($idList === false) {
- $idList = array_keys($array);
- }
$ignoreList = array();
if (Module::isAvailable('runmode')) {
@@ -115,7 +112,7 @@ class InfoPanel
$dbquery = Database::simpleQuery($query, array('idlist' => $idList));
// Iterate over matching machines
- while ($row = $dbquery->fetch(PDO::FETCH_ASSOC)) {
+ foreach ($dbquery as $row) {
if (isset($ignoreList[$row['machineuuid']]))
continue;
settype($row['locationid'], 'int');
@@ -165,7 +162,7 @@ class InfoPanel
* @param array $array list of locations, indexed by locationId
* @param int[] $idList list of locations
*/
- public static function appendOpeningTimes(&$array, $idList)
+ public static function appendOpeningTimes(array &$array, array $idList): void
{
// First, lets get all the parent ids for the given locations
// in case we need to get inherited opening times
@@ -175,7 +172,7 @@ class InfoPanel
$res = Database::simpleQuery("SELECT locationid, openingtime FROM location
WHERE locationid IN (:lids)", array('lids' => $allIds));
$openingTimes = array();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ foreach ($res as $row) {
$openingTimes[(int)$row['locationid']] = $row;
}
// Now we got all the calendars for locations and parents
@@ -207,7 +204,6 @@ class InfoPanel
$currentId = $locations[$currentId]['parentlocationid'];
}
}
- return;
}
@@ -218,12 +214,12 @@ class InfoPanel
* @param int[] $idList location ids
* @return int[] more location ids
*/
- private static function getLocationsWithParents($idList)
+ private static function getLocationsWithParents(array $idList): array
{
$locations = Location::getLocationsAssoc();
$allIds = $idList;
foreach ($idList as $id) {
- if (isset($locations[$id]) && isset($locations[$id]['parents'])) {
+ if (isset($locations[$id]['parents'])) {
$allIds = array_merge($allIds, $locations[$id]['parents']);
}
}
@@ -239,9 +235,9 @@ class InfoPanel
* 'HourClose' => hh, 'MinutesClose' => mm }
*
* @param array $openingtime The opening time in the db saved format.
- * @return mixed The opening time in the frontend needed format.
+ * @return array The opening time in the frontend needed format.
*/
- private static function formatOpeningtime($openingtime)
+ private static function formatOpeningtime(array $openingtime): array
{
$result = array();
foreach ($openingtime as $entry) {
diff --git a/modules-available/locationinfo/inc/locationinfo.inc.php b/modules-available/locationinfo/inc/locationinfo.inc.php
index 6427cc1a..42829a18 100644
--- a/modules-available/locationinfo/inc/locationinfo.inc.php
+++ b/modules-available/locationinfo/inc/locationinfo.inc.php
@@ -7,14 +7,14 @@ class LocationInfo
* Gets the pc data and returns it's state.
*
* @param array $pc The pc data from the db. Array('state' => xx, 'lastseen' => xxx)
- * @return int pc state
+ * @return string pc state
*/
- public static function getPcState($pc)
+ public static function getPcState(array $pc): string
{
$lastseen = (int)$pc['lastseen'];
$NOW = time();
- if ($pc['state'] === 'OFFLINE' && $NOW - $lastseen > 21 * 86400) {
+ if ($pc['state'] === 'OFFLINE' && $NOW - $lastseen > 30 * 86400) {
return "BROKEN";
}
return $pc['state'];
@@ -22,11 +22,12 @@ class LocationInfo
/**
* Return list of locationids associated with given panel.
+ *
* @param string $paneluuid panel
* @param bool $recursive if true and paneltype == SUMMARY the result is recursive with all child room ids.
* @return int[] locationIds
*/
- public static function getLocationsOr404($paneluuid, $recursive = true)
+ public static function getLocationsOr404(string $paneluuid, bool $recursive = true): array
{
$panel = Database::queryFirst('SELECT paneltype, locationids FROM locationinfo_panel WHERE paneluuid = :paneluuid',
compact('paneluuid'));
@@ -48,7 +49,7 @@ class LocationInfo
* @param int $serverId id of server
* @param string|array $message error message to set, array of error message struct, null or false clears error.
*/
- public static function setServerError($serverId, $message)
+ public static function setServerError(int $serverId, $message): void
{
if (is_array($message)) {
$fatal = false;
@@ -86,7 +87,7 @@ class LocationInfo
*
* @return array Return a default config.
*/
- public static function defaultPanelConfig($type)
+ public static function defaultPanelConfig(string $type): array
{
if ($type === 'DEFAULT') {
return array(
@@ -97,6 +98,7 @@ class LocationInfo
'prettytime' => true,
'roomplanner' => true,
'scaledaysauto' => true,
+ 'startday' => 0,
'daystoshow' => 7,
'rotation' => 0,
'scale' => 50,
@@ -127,9 +129,111 @@ class LocationInfo
'interactive' => 0,
'bookmarks' => '',
'allow-tty' => '',
+ 'url' => '',
+ 'zoom-factor' => 100,
);
}
return array();
}
+ /**
+ * Gets the calendar of the given ids.
+ *
+ * @param int[] $idList list with the location ids.
+ * @return array Calendar.
+ */
+ public static function getCalendar(array $idList, bool $forceCached = false): array
+ {
+ if (empty($idList))
+ return [];
+
+ $resultArray = array();
+
+ if ($forceCached) {
+ $res = Database::simpleQuery("SELECT locationid, calendar FROM locationinfo_locationconfig
+ WHERE Length(calendar) > 10 AND lastcalendarupdate > UNIX_TIMESTAMP() - 86400*3");
+ foreach ($res as $row) {
+ $resultArray[] = [
+ 'id' => (int)$row['locationid'],
+ 'calendar' => json_decode($row['calendar'], true),
+ ];
+ }
+ return $resultArray;
+ }
+
+ // Build SQL query for multiple ids.
+ $query = "SELECT l.locationid, l.serverid, l.serverlocationid, s.servertype, s.credentials
+ FROM `locationinfo_locationconfig` AS l
+ INNER JOIN locationinfo_coursebackend AS s ON (s.serverid = l.serverid)
+ WHERE l.locationid IN (:idlist)
+ ORDER BY s.servertype ASC";
+ $dbquery = Database::simpleQuery($query, array('idlist' => array_values($idList)));
+
+ $serverList = array();
+ foreach ($dbquery as $dbresult) {
+ if (!isset($serverList[$dbresult['serverid']])) {
+ $serverList[$dbresult['serverid']] = array(
+ 'credentials' => (array)json_decode($dbresult['credentials'], true),
+ 'type' => $dbresult['servertype'],
+ 'idlist' => array()
+ );
+ }
+ $serverList[$dbresult['serverid']]['idlist'][] = $dbresult['locationid'];
+ }
+
+ foreach ($serverList as $serverid => $server) {
+ $serverInstance = CourseBackend::getInstance($server['type']);
+ if ($serverInstance === false) {
+ EventLog::warning('Cannot fetch schedule for location (' . implode(', ', $server['idlist']) . ')'
+ . ': Backend type ' . $server['type'] . ' unknown. Disabling location.');
+ Database::exec("UPDATE locationinfo_locationconfig SET serverid = NULL WHERE locationid IN (:lid)",
+ array('lid' => $server['idlist']));
+ continue;
+ }
+ $credentialsOk = $serverInstance->setCredentials($serverid, $server['credentials']);
+
+ if ($credentialsOk) {
+ $calendarFromBackend = $serverInstance->fetchSchedule($server['idlist']);
+ } else {
+ $calendarFromBackend = array();
+ }
+
+ LocationInfo::setServerError($serverid, $serverInstance->getErrors());
+
+ if (is_array($calendarFromBackend)) {
+ foreach ($calendarFromBackend as $key => $value) {
+ $resultArray[] = array(
+ 'id' => (int)$key,
+ 'calendar' => $value,
+ );
+ }
+ }
+ }
+ return $resultArray;
+ }
+
+ public static function getAllCalendars(bool $forceCached): array
+ {
+ $locations = Database::queryColumnArray("SELECT locationid FROM location");
+ $calendars = [];
+ foreach (LocationInfo::getCalendar($locations, $forceCached) as $cal) {
+ if (empty($cal['calendar']))
+ continue;
+ $calendars[$cal['id']] = $cal['calendar'];
+ }
+ return $calendars;
+ }
+
+ public static function extractCurrentEvent(array $calendar): string
+ {
+ $NOW = time();
+ foreach ($calendar as $event) {
+ $start = strtotime($event['start']);
+ $end = strtotime($event['end']) + 60;
+ if ($NOW >= $start && $NOW <= $end)
+ return $event['title'];
+ }
+ return '';
+ }
+
}
diff --git a/modules-available/locationinfo/inc/locationinfohooks.inc.php b/modules-available/locationinfo/inc/locationinfohooks.inc.php
index 8e4975e9..8ec217cc 100644
--- a/modules-available/locationinfo/inc/locationinfohooks.inc.php
+++ b/modules-available/locationinfo/inc/locationinfohooks.inc.php
@@ -5,9 +5,9 @@ class LocationInfoHooks
/**
* @param string $uuid panel uuid
- * @return bool|string panel name if exists, false otherwise
+ * @return false|string panel name if exists, false otherwise
*/
- public static function getPanelName($uuid)
+ public static function getPanelName(string $uuid)
{
$ret = Database::queryFirst('SELECT panelname FROM locationinfo_panel WHERE paneluuid = :uuid', compact('uuid'));
if ($ret === false)
@@ -18,14 +18,11 @@ class LocationInfoHooks
/**
* Hook called by runmode module where we should modify the client config according to our
* needs. Disable standby/logout timeouts, enable autologin, set URL.
- *
- * @param $machineUuid
- * @param $panelUuid
*/
- public static function configHook($machineUuid, $panelUuid)
+ public static function configHook(string $machineUuid, string $panelUuid): void
{
$type = InfoPanel::getConfig($panelUuid, $data);
- if ($type === false)
+ if ($type === null)
return; // TODO: Invalid panel - what should we do?
if ($type === 'URL') {
// Check if we should set the insecure SSL mode (accept invalid/self signed certs etc.)
@@ -51,7 +48,7 @@ class LocationInfoHooks
RunMode::updateClientFlag($machineUuid, 'locationinfo', true);
} else { // Automatic login
RunMode::updateClientFlag($machineUuid, 'locationinfo', false);
- ConfigHolder::add('SLX_AUTOLOGIN', '1', 1000);
+ ConfigHolder::add('SLX_AUTOLOGIN', 'ON', 1000);
ConfigHolder::add('SLX_ADDONS', '', 1000);
}
if (!empty($data['browser'])) {
@@ -72,13 +69,19 @@ class LocationInfoHooks
if ($data['allow-tty'] === 'yes' || $data['allow-tty'] === 'no') {
ConfigHolder::add('SLX_TTY_SWITCH', $data['allow-tty'], 1000);
}
+ if (($data['zoom-factor'] ?? 100) != 100) {
+ ConfigHolder::add('SLX_BROWSER_ZOOM', $data['zoom-factor']);
+ }
} else {
// Not URL panel
ConfigHolder::add('SLX_BROWSER_URL', 'http://' . $_SERVER['SERVER_ADDR'] . '/panel/' . $panelUuid);
- ConfigHolder::add('SLX_BROWSER_INSECURE', '1'); // TODO: Sat server might redirect to HTTPS, which in turn could have a self-signed cert - push to client
- ConfigHolder::add('SLX_AUTOLOGIN', '1', 1000);
+ ConfigHolder::add('SLX_AUTOLOGIN', 'ON', 1000);
ConfigHolder::add('SLX_ADDONS', '', 1000);
}
+ $al = ConfigHolder::get('SLX_AUTOLOGIN');
+ if (!empty($al) && $al !== 'OFF' && $al != 0) {
+ ConfigHolder::add('SLX_SHUTDOWN_TIMEOUT', '', 1000);
+ }
ConfigHolder::add('SLX_LOGOUT_TIMEOUT', '', 1000);
ConfigHolder::add('SLX_SCREEN_STANDBY_TIMEOUT', '', 1000);
ConfigHolder::add('SLX_SYSTEM_STANDBY_TIMEOUT', '', 1000);
@@ -87,10 +90,8 @@ class LocationInfoHooks
/**
* Turn multiline list into space separated list, removing any
* comments (starting with #)
- * @param string $list
- * @return string
*/
- private static function mangleList($list)
+ private static function mangleList(string $list): string
{
return preg_replace('/\s*(#[^\n]*)?(\n|$)/', ' ', $list);
}