diff options
Diffstat (limited to 'modules-available/locations/inc')
6 files changed, 149 insertions, 69 deletions
diff --git a/modules-available/locations/inc/abstractlocationcolumn.inc.php b/modules-available/locations/inc/abstractlocationcolumn.inc.php new file mode 100644 index 00000000..65224da9 --- /dev/null +++ b/modules-available/locations/inc/abstractlocationcolumn.inc.php @@ -0,0 +1,29 @@ +<?php + +abstract class AbstractLocationColumn +{ + + public abstract function getColumnHtml(int $locationId): string; + + public abstract function getEditUrl(int $locationId): string; + + public abstract function header(): string; + + public abstract function priority(): int; + + public function propagateColumn(): bool + { + return false; + } + + public function propagationOverride(string $parent, string $data): string + { + return $data; + } + + public function propagateDefaultHtml(): string + { + return $this->getColumnHtml(0); + } + +}
\ No newline at end of file diff --git a/modules-available/locations/inc/autolocation.inc.php b/modules-available/locations/inc/autolocation.inc.php index 82c61251..f77cf714 100644 --- a/modules-available/locations/inc/autolocation.inc.php +++ b/modules-available/locations/inc/autolocation.inc.php @@ -22,7 +22,7 @@ class AutoLocation } $updates = array(); $nulls = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + foreach ($res as $row) { $loc = Location::mapIpToLocation($row['clientip']); if ($loc === false) { $nulls[] = $row['machineuuid']; diff --git a/modules-available/locations/inc/location.inc.php b/modules-available/locations/inc/location.inc.php index 700edaf8..807f8577 100644 --- a/modules-available/locations/inc/location.inc.php +++ b/modules-available/locations/inc/location.inc.php @@ -8,7 +8,7 @@ class Location private static $treeCache = false; private static $subnetMapCache = false; - public static function getTree() + public static function getTree(): array { if (self::$treeCache === false) { self::$treeCache = self::queryLocations(); @@ -17,11 +17,11 @@ class Location return self::$treeCache; } - public static function queryLocations() + public static function queryLocations(): array { $res = Database::simpleQuery("SELECT locationid, parentlocationid, locationname FROM location"); $rows = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + foreach ($res as $row) { $rows[] = $row; } return $rows; @@ -29,10 +29,9 @@ class Location /** * Return row from location table for $locationId. - * @param $locationId * @return array|bool row from DB, false if not found */ - public static function get($locationId) + public static function get(int $locationId) { return Database::queryFirst("SELECT * FROM location WHERE locationid = :locationId", compact('locationId')); } @@ -42,9 +41,8 @@ class Location * @param int $locationId id of location to get name for * @return string|false Name of location, false if locationId doesn't exist */ - public static function getName($locationId) + public static function getName(int $locationId) { - $locationId = (int)$locationId; if (self::$assocLocationCache === false) { self::getLocationsAssoc(); } @@ -56,15 +54,13 @@ class Location /** * Get all the names of the given location and its parents, up * to the root element. Array keys will be locationids, value the names. - * @param int $locationId * @return array|false locations, from furthest to nearest or false if locationId doesn't exist */ - public static function getNameChain($locationId) + public static function getNameChain(int $locationId) { if (self::$assocLocationCache === false) { self::getLocationsAssoc(); } - $locationId = (int)$locationId; if (!isset(self::$assocLocationCache[$locationId])) return false; $ret = array(); @@ -84,10 +80,10 @@ class Location return self::$assocLocationCache; } - private static function flattenTreeAssoc($tree, $parents = array(), $depth = 0) + private static function flattenTreeAssoc($tree, $parents = array(), $depth = 0): array { if ($depth > 20) { - Util::traceError('Recursive location definition detected at ' . print_r($tree, true)); + ErrorHandler::traceError('Recursive location definition detected at ' . print_r($tree, true)); } $output = array(); foreach ($tree as $node) { @@ -121,12 +117,12 @@ class Location /** * @param int|int[] $selected Which locationIDs to mark as selected - * @param int $excludeId Which locationID to explude + * @param int $excludeId Which locationID to exclude * @param bool $addNoParent Add entry for "no location" at the top * @param bool $keepArrayKeys Keep location IDs as array index * @return array Locations */ - public static function getLocations($selected = 0, $excludeId = 0, $addNoParent = false, $keepArrayKeys = false) + public static function getLocations($selected = 0, int $excludeId = 0, bool $addNoParent = false, bool $keepArrayKeys = false): array { if (self::$flatLocationCache === false) { $rows = self::getTree(); @@ -170,15 +166,15 @@ class Location * Get nested array of all the locations and children of given locationid(s). * * @param int[]|int $idList List of location ids - * @param bool $locationTree used in recursive calls, don't pass + * @param ?array $locationTree used in recursive calls, don't pass * @return array list of passed locations plus their children */ - public static function getRecursive($idList, $locationTree = false) + public static function getRecursive($idList, ?array $locationTree = null): array { if (!is_array($idList)) { $idList = array($idList); } - if ($locationTree === false) { + if ($locationTree === null) { $locationTree = self::getTree(); } $ret = array(); @@ -198,7 +194,7 @@ class Location * @param int[]|int $idList List of location ids * @return array list of passed locations plus their children */ - public static function getRecursiveFlat($idList) + public static function getRecursiveFlat($idList): array { $ret = self::getRecursive($idList); if (!empty($ret)) { @@ -207,7 +203,7 @@ class Location return $ret; } - public static function buildTree($elements, $parentId = 0) + public static function buildTree(array $elements, int $parentId = 0): array { $branch = array(); $sort = array(); @@ -227,10 +223,10 @@ class Location return $branch; } - private static function flattenTree($tree, $depth = 0) + private static function flattenTree(array $tree, int $depth = 0): array { if ($depth > 20) { - Util::traceError('Recursive location definition detected at ' . print_r($tree, true)); + ErrorHandler::traceError('Recursive location definition detected at ' . print_r($tree, true)); } $output = array(); foreach ($tree as $node) { @@ -249,16 +245,16 @@ class Location return $output; } - public static function isLeaf($locationid) { + public static function isLeaf(int $locationid): bool + { $result = Database::queryFirst('SELECT COUNT(locationid) = 0 AS isleaf ' . 'FROM location ' . 'WHERE parentlocationid = :locationid', ['locationid' => $locationid]); $result = $result['isleaf']; - $result = (bool)$result; - return $result; + return (bool)$result; } - public static function extractIds($tree) + public static function extractIds(array $tree): array { $ids = array(); foreach ($tree as $node) { @@ -272,10 +268,11 @@ class Location /** * Get location id for given machine (by uuid) + * * @param string $uuid machine uuid - * @return bool|int locationid, false if no match + * @return false|int locationid, false if no match */ - public static function getFromMachineUuid($uuid) + public static function getFromMachineUuid(string $uuid) { // Only if we have the statistics module which supplies the machine table if (Module::get('statistics') === false) @@ -292,9 +289,9 @@ class Location * * @param string $ip IP address of client * @param bool $honorRoomPlanner consider a fixed location assigned manually by roomplanner - * @return bool|int locationid, or false if no match + * @return false|int locationid, or false if no match */ - public static function getFromIp($ip, $honorRoomPlanner = false) + public static function getFromIp(string $ip, bool $honorRoomPlanner = false) { if (Module::get('statistics') !== false) { // Shortcut - try to use subnetlocationid in machine table @@ -323,17 +320,17 @@ class Location * client, so if it seems too fishy, the UUID will be ignored. * * @param string $ip IP address of client - * @param string $uuid System-UUID of client - * @return int|bool location id, or false if none matches + * @param ?string $uuid System-UUID of client + * @return int|false location id, or false if none matches */ - public static function getFromIpAndUuid($ip, $uuid) + public static function getFromIpAndUuid(string $ip, ?string $uuid) { $locationId = false; $ipLoc = self::getFromIp($ip); if ($ipLoc !== false) { // Set locationId to ipLoc for now, it will be overwritten later if another case applies. $locationId = $ipLoc; - if ($uuid !== false) { + if ($uuid !== null) { // Machine ip maps to a location, and we have a client supplied uuid (which might not be known if the client boots for the first time) $uuidLoc = self::getFromMachineUuid($uuid); if (self::isFixedLocationValid($uuidLoc, $ipLoc)) { @@ -344,7 +341,7 @@ class Location return $locationId; } - public static function isFixedLocationValid($uuidLoc, $ipLoc) + public static function isFixedLocationValid($uuidLoc, $ipLoc): bool { if ($uuidLoc === false) return false; @@ -367,12 +364,10 @@ class Location /** * Get all location IDs from the given location up to the root. * - * @param int $locationId * @return int[] location ids, including $locationId */ - public static function getLocationRootChain($locationId) + public static function getLocationRootChain(int $locationId): array { - $locationId = (int)$locationId; if (self::$assocLocationCache === false) { self::getLocationsAssoc(); } @@ -386,11 +381,11 @@ class Location /** * @return array list of subnets as numeric array */ - public static function getSubnets() + public static function getSubnets(): array { $res = Database::simpleQuery("SELECT startaddr, endaddr, locationid FROM subnet"); $subnets = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + foreach ($res as $row) { settype($row['locationid'], 'int'); $subnets[] = $row; } @@ -398,9 +393,9 @@ class Location } /** - * @return array|bool assoc array mapping from locationid to subnets + * @return array assoc array mapping from locationid to subnets */ - public static function getSubnetsByLocation($recursive = false) + public static function getSubnetsByLocation($recursive = false): array { $locs = self::getLocationsAssoc(); $subnets = self::getSubnets(); @@ -434,9 +429,9 @@ class Location * random one will be returned. * * @param string $ip IP to look up - * @return bool|int locationid ip matches, false = no match + * @return false|int locationid ip matches, false = no match */ - public static function mapIpToLocation($ip) + public static function mapIpToLocation(string $ip) { if (self::$subnetMapCache === false) { self::$subnetMapCache = self::getSubnetsByLocation(); @@ -465,7 +460,10 @@ class Location return (int)$best; } - public static function updateMapIpToLocation($uuid, $ip) + /** + * @return false|int newly determined location + */ + public static function updateMapIpToLocation(string $uuid, string $ip) { $loc = self::mapIpToLocation($ip); if ($loc === false) { @@ -473,6 +471,7 @@ class Location } else { Database::exec("UPDATE machine SET subnetlocationid = :loc WHERE machineuuid = :uuid", compact('loc', 'uuid')); } + return $loc; } } diff --git a/modules-available/locations/inc/locationhooks.inc.php b/modules-available/locations/inc/locationhooks.inc.php index 5ce3bbfe..f6ef02da 100644 --- a/modules-available/locations/inc/locationhooks.inc.php +++ b/modules-available/locations/inc/locationhooks.inc.php @@ -6,7 +6,7 @@ class LocationHooks /** * Resolve baseconfig id to locationid -- noop in this case */ - public static function baseconfigLocationResolver($id) + public static function baseconfigLocationResolver(int $id): int { return $id; } @@ -15,13 +15,13 @@ class LocationHooks * Hook to get inheritance tree for all config vars * @param int $id Locationid currently being edited */ - public static function baseconfigInheritance($id) + public static function baseconfigInheritance(int $id): array { $locs = Location::getLocationsAssoc(); if ($locs === false || !isset($locs[$id])) return []; BaseConfig::prepareWithOverrides([ - 'locationid' => $locs[$id]['parentlocationid'] + 'locationid' => $locs[$id]['parentlocationid'] ?? 0 ]); return ConfigHolder::getRecursiveConfig(true); } diff --git a/modules-available/locations/inc/locationutil.inc.php b/modules-available/locations/inc/locationutil.inc.php index 708cc8a2..91117445 100644 --- a/modules-available/locations/inc/locationutil.inc.php +++ b/modules-available/locations/inc/locationutil.inc.php @@ -48,7 +48,7 @@ class LocationUtil if ($overlapOther) { $overlapOther = array(); foreach ($other as $entry) { - if (!isset($locs[$entry['lid1']]) || !isset($locs[$entry['lid2']])) + if (!isset($locs[$entry['lid1']]) && !isset($locs[$entry['lid2']])) continue; if (in_array($entry['lid1'], $locs[$entry['lid2']]['parents']) || in_array($entry['lid2'], $locs[$entry['lid1']]['parents'])) continue; @@ -70,12 +70,9 @@ class LocationUtil * grouped by the location the client was assigned to via roomplanner. * Otherwise, just return an assoc array with the requested locationid, name * and a list of all clients that are wrongfully assigned to that room. - * @param int $locationId - * @return array */ - public static function getMachinesWithLocationMismatch($locationId = 0, $checkPerms = false) + public static function getMachinesWithLocationMismatch(int $locationId = 0, bool $checkPerms = false): array { - $locationId = (int)$locationId; if ($checkPerms) { if ($locationId !== 0) { // Query details for specific location -- use assert and fake array @@ -124,7 +121,7 @@ class LocationUtil $res = Database::simpleQuery($query, $params); $return = []; $locs = false; - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + foreach ($res as $row) { if (Location::isFixedLocationValid($row['fixedlocationid'], $row['subnetlocationid'])) continue; $lid = (int)$row['fixedlocationid']; @@ -162,19 +159,17 @@ class LocationUtil } if (empty($return)) return $return; - if ($locationId === 0) { + if ($locationId === 0) return array_values($return); - } else { - return $return[$locationId]; - } + return $return[$locationId]; } - private static function overlap($net1, $net2) + private static function overlap(array $net1, array $net2): bool { return ($net1['startaddr'] <= $net2['endaddr'] && $net1['endaddr'] >= $net2['startaddr']); } - public static function rangeToLongVerbose($start, $end) + public static function rangeToLongVerbose(string $start, string $end): ?array { $result = self::rangeToLong($start, $end); list($startLong, $endLong) = $result; @@ -185,24 +180,19 @@ class LocationUtil Message::addWarning('main.value-invalid', 'end addr', $start); } if ($startLong === false || $endLong === false) - return false; + return null; if ($startLong > $endLong) { Message::addWarning('main.value-invalid', 'range', $start . ' - ' . $end); - return false; + return null; } return $result; } - public static function rangeToLong($start, $end) + /** @return array{0: int, 1: int} */ + public static function rangeToLong(string $start, string $end): array { $startLong = ip2long($start); $endLong = ip2long($end); - if ($startLong !== false) { - $startLong = sprintf("%u", $startLong); - } - if ($endLong !== false) { - $endLong = sprintf("%u", $endLong); - } return array($startLong, $endLong); } diff --git a/modules-available/locations/inc/openingtimes.inc.php b/modules-available/locations/inc/openingtimes.inc.php new file mode 100644 index 00000000..74dae7c3 --- /dev/null +++ b/modules-available/locations/inc/openingtimes.inc.php @@ -0,0 +1,62 @@ +<?php + +class OpeningTimes +{ + + /** + * Get opening times for given location. + * Format is the decoded JSON from DB column, i.e. currently a list of entries: + * <pre>{ + * "days": ["Monday", "Tuesday", ...], + * "openingtime": "8:00", + * "closingtime": "20:00" + * }</pre> + */ + public static function forLocation(int $locationId): ?array + { + static $openingTimesList = false; + if ($openingTimesList === false) { + $openingTimesList = Database::queryKeyValueList("SELECT locationid, openingtime FROM location + WHERE openingtime IS NOT NULL"); + } + $chain = Location::getLocationRootChain($locationId); + foreach ($chain as $lid) { + if (isset($openingTimesList[$lid])) { + if (is_string($openingTimesList[$lid])) { + $openingTimesList[$lid] = json_decode($openingTimesList[$lid], true); + } + return $openingTimesList[$lid]; + } + } + return null; + } + + /** + * Check whether given location is open according to openingtimes. + * @param int $locationId location + * @param int $openOffsetMin offset to apply to opening times when checking. this is subtracted from opening time + * @param int $closeOffsetMin offset to apply to closing times when checking. this is added to closing time + */ + public static function isRoomOpen(int $locationId, int $openOffsetMin = 0, int $closeOffsetMin = 0): bool + { + $openingTimes = self::forLocation($locationId); + if ($openingTimes === null) + return true; // No opening times should mean room is always open + $now = time(); + $today = date('l', $now); + foreach ($openingTimes as $row) { + foreach ($row['days'] as $day) { + if ($day !== $today) + continue; // Not today! + if (strtotime("today {$row['openingtime']} -$openOffsetMin minutes") > $now) + continue; + if (strtotime("today {$row['closingtime']} +$closeOffsetMin minutes") < $now) + continue; + // Bingo! + return true; + } + } + return false; + } + +}
\ No newline at end of file |