summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--inc/user.inc.php3
-rw-r--r--modules-available/dnbd3/baseconfig/getconfig.inc.php2
-rw-r--r--modules-available/dnbd3/inc/dnbd3util.inc.php5
-rw-r--r--modules-available/dozmod/api.inc.php6
-rw-r--r--modules-available/locationinfo/api.inc.php32
-rw-r--r--modules-available/locationinfo/inc/infopanel.inc.php9
-rw-r--r--modules-available/locationinfo/inc/locationinfo.inc.php5
-rw-r--r--modules-available/locationinfo/page.inc.php25
-rw-r--r--modules-available/locations/baseconfig/getconfig.inc.php6
-rw-r--r--modules-available/locations/inc/autolocation.inc.php4
-rw-r--r--modules-available/locations/inc/location.inc.php382
-rw-r--r--modules-available/locations/inc/locationutil.inc.php4
-rw-r--r--modules-available/locations/pages/details.inc.php15
-rw-r--r--modules-available/news/api.inc.php8
-rw-r--r--modules-available/permissionmanager/inc/getpermissiondata.inc.php33
-rw-r--r--modules-available/permissionmanager/inc/permissionutil.inc.php36
-rw-r--r--modules-available/roomplanner/inc/pvsgenerator.inc.php2
-rw-r--r--modules-available/roomplanner/inc/room.inc.php3
-rw-r--r--modules-available/serversetup-bwlp-ipxe/inc/ipxemenu.inc.php2
-rw-r--r--modules-available/statistics/api.inc.php2
-rw-r--r--modules-available/statistics/inc/statisticsfilter.inc.php2
-rw-r--r--modules-available/statistics/pages/summary.inc.php9
-rw-r--r--modules-available/sysconfig/api.inc.php6
23 files changed, 292 insertions, 309 deletions
diff --git a/inc/user.inc.php b/inc/user.inc.php
index cd35ac29..088f12c6 100644
--- a/inc/user.inc.php
+++ b/inc/user.inc.php
@@ -112,8 +112,7 @@ class User
}
if (self::$user['permissions'] & Permission::get('superadmin')) {
if (Module::isAvailable('locations')) {
- $a = array_keys(Location::getLocationsAssoc());
- $a[] = 0;
+ $a = Location::getAllLocationIds(0, true);
} else {
$a = [0];
}
diff --git a/modules-available/dnbd3/baseconfig/getconfig.inc.php b/modules-available/dnbd3/baseconfig/getconfig.inc.php
index b962106d..d93c3f4a 100644
--- a/modules-available/dnbd3/baseconfig/getconfig.inc.php
+++ b/modules-available/dnbd3/baseconfig/getconfig.inc.php
@@ -51,7 +51,7 @@ foreach ($res as $row) {
if ($defPrio === 1000 && is_null($row['locationid'])) {
// Server is not assigned to this location, try to guess it by its IP address
$serverLoc = Location::getFromIp($ip);
- if ($serverLoc !== false) {
+ if ($serverLoc !== null) {
$row['locationid'] = $serverLoc;
}
}
diff --git a/modules-available/dnbd3/inc/dnbd3util.inc.php b/modules-available/dnbd3/inc/dnbd3util.inc.php
index fe27f572..d79ae54f 100644
--- a/modules-available/dnbd3/inc/dnbd3util.inc.php
+++ b/modules-available/dnbd3/inc/dnbd3util.inc.php
@@ -126,11 +126,8 @@ class Dnbd3Util {
if (!empty($assignedLocs) && ($modeData['firewall'] ?? false)) {
// Get all sub-locations too
$recursiveLocs = $assignedLocs;
- $locations = Location::getLocationsAssoc();
foreach ($assignedLocs as $l) {
- if (isset($locations[$l])) {
- $recursiveLocs = array_merge($recursiveLocs, $locations[$l]['children']);
- }
+ $recursiveLocs = array_merge($recursiveLocs, Location::getAllLocationIds($l));
}
$res = Database::simpleQuery('SELECT startaddr, endaddr FROM subnet WHERE locationid IN (:locs)',
array('locs' => array_values($recursiveLocs)));
diff --git a/modules-available/dozmod/api.inc.php b/modules-available/dozmod/api.inc.php
index 5a8f6302..11a542ef 100644
--- a/modules-available/dozmod/api.inc.php
+++ b/modules-available/dozmod/api.inc.php
@@ -343,7 +343,11 @@ if (substr($ip, 0, 7) === '::ffff:') {
/* lookup location id(s) */
$location_ids = Location::getFromIp($ip, true);
-$location_ids = Location::getLocationRootChain($location_ids);
+if ($location_ids === null) {
+ $location_ids = [];
+} else {
+ $location_ids = Location::getLocationRootChain($location_ids);
+}
if ($resource === 'list') {
outputLectureXmlForLocation($location_ids);
diff --git a/modules-available/locationinfo/api.inc.php b/modules-available/locationinfo/api.inc.php
index ef462d83..b066779b 100644
--- a/modules-available/locationinfo/api.inc.php
+++ b/modules-available/locationinfo/api.inc.php
@@ -33,7 +33,7 @@ function HandleParameters()
$output = getPcStates($locationIds, $uuid);
} elseif ($get === "locationtree") {
$locationIds = LocationInfo::getLocationsOr404($uuid);
- $output = getLocationTree($locationIds);
+ $output = Location::getTree(...$locationIds);
} elseif ($get === "calendar") {
$locationIds = LocationInfo::getLocationsOr404($uuid);
$output = LocationInfo::getCalendar($locationIds);
@@ -127,36 +127,6 @@ function getPcStates(array $idList, string $paneluuid): array
}
/**
- * Gets the location tree of the given locations.
- *
- * @param int[] $idList Array list of the locations.
- * @return array location tree data
- */
-function getLocationTree(array $idList): array
-{
- if (in_array(0, $idList)) {
- return array_values(Location::getTree());
- }
- $locations = Location::getTree();
-
- return findLocations($locations, $idList);
-}
-
-function findLocations(array $locations, array $idList): array
-{
- $ret = array();
- foreach ($locations as $location) {
- if (in_array($location['locationid'], $idList)) {
- $ret[] = $location;
- } elseif (!empty($location['children'])) {
- $ret = array_merge($ret, findLocations($location['children'], $idList));
- }
- }
- return $ret;
-}
-
-
-/**
* Generates a web application manifest for a panel.
*
* @param string $uuid The UUID of the panel.
diff --git a/modules-available/locationinfo/inc/infopanel.inc.php b/modules-available/locationinfo/inc/infopanel.inc.php
index 1a0e9b67..2557149d 100644
--- a/modules-available/locationinfo/inc/infopanel.inc.php
+++ b/modules-available/locationinfo/inc/infopanel.inc.php
@@ -27,7 +27,6 @@ class InfoPanel
return $panel['paneltype'];
}
- $locations = Location::getLocationsAssoc();
$overrides = false;
if (!empty($panel['panelconfig'])) {
@@ -48,17 +47,17 @@ class InfoPanel
$lids = array_map('intval', explode(',', $panel['locationids']));
// Locations -
if ($panel['paneltype'] === 'SUMMARY') {
- $lids = Location::getRecursiveFlat($lids);
- $lids = array_keys($lids);
foreach ($lids as $lid) {
- $config['locations'][$lid] = array('id' => $lid);
+ foreach (Location::getAllLocationIds($lid, true) as $lid2) {
+ $config['locations'][$lid2] = ['id' => $lid2];
+ }
}
}
if ($panel['paneltype'] === 'DEFAULT') {
foreach ($lids as $lid) {
$config['locations'][$lid] = array(
'id' => $lid,
- 'name' => isset($locations[$lid]) ? $locations[$lid]['locationname'] : 'noname00.pas',
+ 'name' => Location::getName($lid) ?: 'noname00.pas',
);
// Now apply any overrides from above
if (isset($overrides[$lid]) && is_array($overrides[$lid])) {
diff --git a/modules-available/locationinfo/inc/locationinfo.inc.php b/modules-available/locationinfo/inc/locationinfo.inc.php
index 2ce557f4..b41f3a33 100644
--- a/modules-available/locationinfo/inc/locationinfo.inc.php
+++ b/modules-available/locationinfo/inc/locationinfo.inc.php
@@ -34,8 +34,9 @@ class LocationInfo
if ($panel !== false) {
$idArray = array_map('intval', explode(',', $panel['locationids']));
if ($panel['paneltype'] == "SUMMARY" && $recursive) {
- $idList = Location::getRecursiveFlat($idArray);
- $idArray = array_keys($idList);
+ foreach ($idArray as $lid) {
+ $idArray = array_merge($idArray, Location::getAllLocationIds($lid));
+ }
}
return $idArray;
}
diff --git a/modules-available/locationinfo/page.inc.php b/modules-available/locationinfo/page.inc.php
index b5171200..fd9abb18 100644
--- a/modules-available/locationinfo/page.inc.php
+++ b/modules-available/locationinfo/page.inc.php
@@ -185,11 +185,7 @@ class Page_LocationInfo extends Page
if ($changeServerRecursive) {
// Recursive overwriting of serverid
- $children = Location::getRecursiveFlat($locationid);
- $array = array();
- foreach ($children as $loc) {
- $array[] = $loc['locationid'];
- }
+ $array = Location::getAllLocationIds($locationid);
if (!empty($array)) {
Database::exec("UPDATE locationinfo_locationconfig
SET serverid = :serverid, lastcalendarupdate = IF(serverid <> :serverid, 0, lastcalendarupdate), lastchange = :now
@@ -214,7 +210,7 @@ class Page_LocationInfo extends Page
{
$locationids = Request::post('locationids', Request::REQUIRED_EMPTY, 'string');
$locationids = explode(',', $locationids);
- $all = array_map(function ($item) { return $item['locationid']; }, Location::queryLocations());
+ $all = Location::getAllLocationIds();
$locationids = array_filter($locationids, function ($item) use ($all) { return in_array($item, $all); });
if (empty($locationids)) {
Message::addError('main.parameter-empty', 'locationids');
@@ -617,7 +613,6 @@ class Page_LocationInfo extends Page
$runmodes = RunMode::getForModule(Page::getModule(), true);
}
$panels = array();
- $locations = Location::getLocationsAssoc();
foreach ($res as $row) {
if ($row['paneltype'] === 'URL') {
$url = json_decode($row['panelconfig'], true)['url'];
@@ -635,8 +630,8 @@ class Page_LocationInfo extends Page
$row['runmode_disabled'] = $assignLocations !== true && !empty(array_diff($lids, $assignLocations))
? 'disabled' : '';
// Locations
- $locs = array_map(function ($id) use ($locations) {
- return isset($locations[$id]) ? $locations[$id]['locationname'] : "<<deleted=$id>>";
+ $locs = array_map(function ($id) {
+ return Location::getName($id) ?: "<<deleted=$id>>";
}, $lids);
$row['locations'] = implode(', ', $locs);
}
@@ -1080,9 +1075,19 @@ class Page_LocationInfo extends Page
die(Render::parse('frontend-default', $data, null, $config['language']));
}
+ if ($type === 'UPCOMING') {
+ $data += array(
+ 'uuid' => $uuid,
+ 'config' => json_encode($config),
+ 'language' => $config['language'],
+ );
+
+ die(Render::parse('frontend-kg2-upcoming', $data, null, $config['language']));
+ }
+
if ($type === 'SUMMARY') {
$locations = LocationInfo::getLocationsOr404($uuid, false);
- $config['tree'] = Location::getRecursive($locations);
+ $config['tree'] = Location::getTree(...$locations);
$data += array(
'uuid' => $uuid,
'config' => json_encode($config),
diff --git a/modules-available/locations/baseconfig/getconfig.inc.php b/modules-available/locations/baseconfig/getconfig.inc.php
index 1bed5de7..b6db699a 100644
--- a/modules-available/locations/baseconfig/getconfig.inc.php
+++ b/modules-available/locations/baseconfig/getconfig.inc.php
@@ -4,7 +4,7 @@
/** @var ?string $ip */
// Location handling: figure out location
-$locationId = false;
+$locationId = null;
if (BaseConfig::hasOverride('locationid')) {
$locationId = BaseConfig::getOverride('locationid');
} elseif (Request::any('force', 0, 'int') === 1 && Request::any('module', false, 'string') === 'locations') {
@@ -14,14 +14,14 @@ if (BaseConfig::hasOverride('locationid')) {
}
}
-if ($locationId === false) {
+if ($locationId === null) {
if ($ip === null) // Required at this point, bail out if not given
return;
$locationId = Location::getFromIpAndUuid($ip, $uuid);
}
$matchingLocations = array();
-if ($locationId !== false) {
+if ($locationId !== null) {
// Get all parents
$matchingLocations = Location::getLocationRootChain($locationId);
ConfigHolder::add("SLX_LOCATIONS", implode(' ', $matchingLocations), 100);
diff --git a/modules-available/locations/inc/autolocation.inc.php b/modules-available/locations/inc/autolocation.inc.php
index f77cf714..73fa9042 100644
--- a/modules-available/locations/inc/autolocation.inc.php
+++ b/modules-available/locations/inc/autolocation.inc.php
@@ -9,7 +9,7 @@ class AutoLocation
*
* @param int[]|false $locations Locations to rebuild, or false for everything
*/
- public static function rebuildAll($locations = false)
+ public static function rebuildAll($locations = false): void
{
if (Module::get('statistics') === false)
return; // Nothing to do
@@ -24,7 +24,7 @@ class AutoLocation
$nulls = array();
foreach ($res as $row) {
$loc = Location::mapIpToLocation($row['clientip']);
- if ($loc === false) {
+ if ($loc === null) {
$nulls[] = $row['machineuuid'];
} else {
if (!isset($updates[$loc])) {
diff --git a/modules-available/locations/inc/location.inc.php b/modules-available/locations/inc/location.inc.php
index 807f8577..d4978068 100644
--- a/modules-available/locations/inc/location.inc.php
+++ b/modules-available/locations/inc/location.inc.php
@@ -3,28 +3,111 @@
class Location
{
- private static $flatLocationCache = false;
- private static $assocLocationCache = false;
- private static $treeCache = false;
- private static $subnetMapCache = false;
+ /** @var ?array */
+ private static $flatLocationCache = null;
+ /** @var ?array */
+ private static $assocLocationCache = null;
- public static function getTree(): array
+ /**
+ * Get a nested array of locations. Each element in the array represents a top-level
+ * location, and locations with have child-nodes will have a 'children' key that again
+ * contains an array of location elements, with 'children' as appropriate, and so on.
+ * If you pass one or more startLocationIds, the tree will be cut down to only contain
+ * the locations given (plus any subtrees they have).
+ * @return array<array{locationid: int, locationname: string, parentlocationd: int, children: array}>
+ */
+ public static function getTree(int ...$startLocationId): array
{
- if (self::$treeCache === false) {
- self::$treeCache = self::queryLocations();
- self::$treeCache = self::buildTree(self::$treeCache);
+ static $treeCache = null;
+ if ($treeCache === null) {
+ $treeCache = self::buildTree(self::queryLocations());
}
- return self::$treeCache;
+ if (empty($startLocationId) || in_array(0, $startLocationId))
+ return $treeCache;
+ // Subtree, search
+ return self::filterTreeByLocations($treeCache, $startLocationId);
}
- public static function queryLocations(): array
+ /**
+ * Return a filtered tree that only contains the locations present in $idList, plus their respective
+ * child locations in the 'children' array key, if applicable. Top level elements will only be those
+ * contained in $idList, but not every location given in $idList will be a top level element, as they
+ * might already be present in one of the nested 'children' arrays.
+ * @param array $locationTree part of tree to search in
+ * @param array $idList list of wanted ids
+ * @return array<array{locationid: int, locationname: string, parentlocationd: int, children: array}> filtered tree
+ */
+ private static function filterTreeByLocations(array $locationTree, array $idList): array
{
- $res = Database::simpleQuery("SELECT locationid, parentlocationid, locationname FROM location");
- $rows = array();
- foreach ($res as $row) {
- $rows[] = $row;
+ $ret = [];
+ foreach ($locationTree as $location) {
+ if (in_array($location['locationid'], $idList)) {
+ $ret[] = $location;
+ } elseif (!empty($location['children'])) {
+ $ret = array_merge($ret, self::filterTreeByLocations($location['children'], $idList));
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * @return array<array{locationid: int, locationname: string, parentlocationd: int, children: array}>
+ */
+ private static function buildTree(array $elements, int $parentId = 0): array
+ {
+ $branch = array();
+ foreach ($elements as $lid => $element) {
+ $lid = (int)$lid;
+ if ($lid === 0 || $lid === $parentId)
+ continue;
+ if ($element['parentlocationid'] === $parentId) {
+ $children = self::buildTree($elements, $lid);
+ if (!empty($children)) {
+ $element['children'] = $children;
+ }
+ $element['locationid'] = $lid;
+ $branch[] = $element;
+ }
+ }
+ ArrayUtil::sortByColumn($branch, 'locationname');
+ return $branch;
+ }
+
+ private static function queryLocations(): array
+ {
+ static $locationDbCache = null;
+ if ($locationDbCache === null) {
+ $locationDbCache = Database::queryIndexedList("SELECT locationid, parentlocationid, locationname FROM location");
+ foreach ($locationDbCache as &$loc) {
+ $loc['parentlocationid'] = (int)$loc['parentlocationid'];
+ }
+ }
+ return $locationDbCache;
+ }
+
+ /**
+ * Get all location ids starting from the specified location id.
+ * If $startId is 0, it will return all available location ids.
+ * If $withStart is true, the starting location id will be included in the result.
+ *
+ * @param int $startId The location id to start from (default is 0 to get all location ids).
+ * @param bool $withStart True to include the starting location id in the result (default is false).
+ * @return int[] An array of location ids, including the starting location id if requested.
+ */
+ public static function getAllLocationIds(int $startId = 0, bool $withStart = false): array
+ {
+ if ($startId === 0) {
+ $locs = array_keys(self::queryLocations());
+ } else {
+ $locs = self::getLocationsAssoc();
+ if (!isset($locs[$startId]))
+ return [];
+ $locs = $locs[$startId]['children'] ?? [];
+ }
+ if ($withStart) {
+ $locs[] = $startId;
}
- return $rows;
+ return $locs;
}
/**
@@ -43,79 +126,79 @@ class Location
*/
public static function getName(int $locationId)
{
- if (self::$assocLocationCache === false) {
- self::getLocationsAssoc();
- }
- if (!isset(self::$assocLocationCache[$locationId]))
- return false;
- return self::$assocLocationCache[$locationId]['locationname'];
+ $locs = self::queryLocations();
+ return $locs[$locationId]['locationname'] ?? false;
}
/**
* Get all the names of the given location and its parents, up
* to the root element. Array keys will be locationids, value the names.
- * @return array|false locations, from furthest to nearest or false if locationId doesn't exist
+ * @return ?array locations, from furthest to nearest or false if locationId doesn't exist
*/
- public static function getNameChain(int $locationId)
+ public static function getNameChain(int $locationId): ?array
{
- if (self::$assocLocationCache === false) {
- self::getLocationsAssoc();
- }
- if (!isset(self::$assocLocationCache[$locationId]))
- return false;
- $ret = array();
- while (isset(self::$assocLocationCache[$locationId])) {
- $ret[$locationId] = self::$assocLocationCache[$locationId]['locationname'];
- $locationId = self::$assocLocationCache[$locationId]['parentlocationid'];
+ $locs = self::queryLocations();
+ $ret = [];
+ while (isset($locs[$locationId])) {
+ $ret[$locationId] = $locs[$locationId]['locationname'];
+ $locationId = $locs[$locationId]['parentlocationid'];
}
+ if (empty($ret))
+ return null;
return array_reverse($ret, true);
}
- public static function getLocationsAssoc()
+ /**
+ * Get associative array of locations. Mostly used for working with locations, in contrast to ::getLocations()
+ * @return array<array{locationid: int, locationname: string, parentlocationid: int, parents: int[], children: int[], directchildren: int[], depth: int, isleaf: bool}> All the locations
+ */
+ public static function getLocationsAssoc(): array
{
- if (self::$assocLocationCache === false) {
- $rows = self::getTree();
- self::$assocLocationCache = self::flattenTreeAssoc($rows);
+ if (self::$assocLocationCache === null) {
+ self::$assocLocationCache = self::flattenTreeAssoc(self::getTree());
}
return self::$assocLocationCache;
}
- private static function flattenTreeAssoc($tree, $parents = array(), $depth = 0): array
+ /**
+ * @return array<array{locationid: int, locationname: string, parentlocationid: int, parents: int[], children: int[], directchildren: int[], depth: int, isleaf: bool}> All the locations
+ */
+ private static function flattenTreeAssoc(array $tree, array $parents = [], int $depth = 0): array
{
- if ($depth > 20) {
- ErrorHandler::traceError('Recursive location definition detected at ' . print_r($tree, true));
- }
- $output = array();
+ $output = [];
foreach ($tree as $node) {
- $cc = empty($node['children']) ? array() : array_map(function ($item) { return (int)$item['locationid']; }, $node['children']);
- $output[(int)$node['locationid']] = array(
- 'locationid' => (int)$node['locationid'],
- 'parentlocationid' => (int)$node['parentlocationid'],
+ $cc = empty($node['children']) ? [] : array_column($node['children'], 'locationid');
+ $output[$node['locationid']] = [
+ 'locationid' => $node['locationid'],
+ 'parentlocationid' => $node['parentlocationid'],
'parents' => $parents,
'children' => $cc,
'directchildren' => $cc,
'locationname' => $node['locationname'],
'depth' => $depth,
- 'isleaf' => true,
- );
+ 'isleaf' => empty($cc),
+ ];
if (!empty($node['children'])) {
- $childNodes = self::flattenTreeAssoc($node['children'], array_merge($parents, array((int)$node['locationid'])), $depth + 1);
- $output[(int)$node['locationid']]['children'] = array_merge($output[(int)$node['locationid']]['children'],
+ $childNodes = self::flattenTreeAssoc($node['children'], array_merge($parents, [$node['locationid']]), $depth + 1);
+ $output[$node['locationid']]['children'] = array_merge($output[$node['locationid']]['children'],
array_reduce($childNodes, function ($carry, $item) {
return array_merge($carry, $item['children']);
- }, array()));
+ }, []));
$output += $childNodes;
}
}
- foreach ($output as &$entry) {
- if (!isset($output[$entry['parentlocationid']]))
- continue;
- $output[$entry['parentlocationid']]['isleaf'] = false;
- }
return $output;
}
+ private static function buildFlatTree(): void
+ {
+ if (self::$flatLocationCache === null) {
+ self::$flatLocationCache = self::flattenTree(self::getTree());
+ }
+ }
+
/**
+ * get locations as flat array - mostly used for rendering a <select>.
* @param int|int[] $selected Which locationIDs to mark as selected
* @param int $excludeId Which locationID to exclude
* @param bool $addNoParent Add entry for "no location" at the top
@@ -124,13 +207,8 @@ class Location
*/
public static function getLocations($selected = 0, int $excludeId = 0, bool $addNoParent = false, bool $keepArrayKeys = false): array
{
- if (self::$flatLocationCache === false) {
- $rows = self::getTree();
- $rows = self::flattenTree($rows);
- self::$flatLocationCache = $rows;
- } else {
- $rows = self::$flatLocationCache;
- }
+ self::buildFlatTree();
+ $rows = self::$flatLocationCache;
$del = false;
unset($row);
$index = 0;
@@ -150,94 +228,37 @@ class Location
$row['sortIndex'] = $index++;
}
if ($addNoParent) {
- array_unshift($rows, array(
+ $noParent = [
'locationid' => 0,
'locationname' => '-----',
'selected' => $selected === 0,
'locationpad' => '',
- ));
+ ];
+ if ($keepArrayKeys) {
+ $rows = array_reverse($rows, true);
+ $rows[0] = $noParent;
+ $rows = array_reverse($rows, true);
+ } else {
+ array_unshift($rows, $noParent);
+ }
}
if ($keepArrayKeys)
return $rows;
return array_values($rows);
}
- /**
- * Get nested array of all the locations and children of given locationid(s).
- *
- * @param int[]|int $idList List of location ids
- * @param ?array $locationTree used in recursive calls, don't pass
- * @return array list of passed locations plus their children
- */
- public static function getRecursive($idList, ?array $locationTree = null): array
- {
- if (!is_array($idList)) {
- $idList = array($idList);
- }
- if ($locationTree === null) {
- $locationTree = self::getTree();
- }
- $ret = array();
- foreach ($locationTree as $location) {
- if (in_array($location['locationid'], $idList)) {
- $ret[] = $location;
- } elseif (!empty($location['children'])) {
- $ret = array_merge($ret, self::getRecursive($idList, $location['children']));
- }
- }
- return $ret;
- }
-
- /**
- * Get flat array of all the locations and children of given locationid(s).
- *
- * @param int[]|int $idList List of location ids
- * @return array list of passed locations plus their children
- */
- public static function getRecursiveFlat($idList): array
- {
- $ret = self::getRecursive($idList);
- if (!empty($ret)) {
- $ret = self::flattenTree($ret);
- }
- return $ret;
- }
-
- public static function buildTree(array $elements, int $parentId = 0): array
- {
- $branch = array();
- $sort = array();
- foreach ($elements as $element) {
- if ($element['locationid'] == 0 || $element['locationid'] == $parentId)
- continue;
- if ($element['parentlocationid'] == $parentId) {
- $children = self::buildTree($elements, $element['locationid']);
- if (!empty($children)) {
- $element['children'] = $children;
- }
- $branch[] = $element;
- $sort[] = $element['locationname'];
- }
- }
- array_multisort($sort, SORT_ASC, $branch);
- return $branch;
- }
-
private static function flattenTree(array $tree, int $depth = 0): array
{
- if ($depth > 20) {
- ErrorHandler::traceError('Recursive location definition detected at ' . print_r($tree, true));
- }
- $output = array();
+ $output = [];
foreach ($tree as $node) {
- $output[(int)$node['locationid']] = array(
+ $output[$node['locationid']] = [
'locationid' => $node['locationid'],
'parentlocationid' => $node['parentlocationid'],
'locationname' => $node['locationname'],
'locationpad' => str_repeat('--', $depth),
'isleaf' => empty($node['children']),
- 'depth' => $depth
- );
+ 'depth' => $depth,
+ ];
if (!empty($node['children'])) {
$output += self::flattenTree($node['children'], $depth + 1);
}
@@ -247,39 +268,28 @@ class Location
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'];
- return (bool)$result;
- }
-
- public static function extractIds(array $tree): array
- {
- $ids = array();
- foreach ($tree as $node) {
- $ids[] = (int)$node['locationid'];
- if (!empty($node['children'])) {
- $ids = array_merge($ids, self::extractIds($node['children']));
- }
- }
- return $ids;
+ if (self::$flatLocationCache !== null)
+ return self::$flatLocationCache[$locationid]['isleaf'] ?? false;
+ if (self::$assocLocationCache !== null)
+ return self::$assocLocationCache[$locationid]['isleaf'] ?? false;
+ return !empty(self::getAllLocationIds($locationid));
}
/**
* Get location id for given machine (by uuid)
*
* @param string $uuid machine uuid
- * @return false|int locationid, false if no match
+ * @return ?int locationid, null if no match
*/
- public static function getFromMachineUuid(string $uuid)
+ public static function getFromMachineUuid(string $uuid): ?int
{
// Only if we have the statistics module which supplies the machine table
if (Module::get('statistics') === false)
- return false;
- $ret = Database::queryFirst("SELECT locationid FROM machine WHERE machineuuid = :uuid", compact('uuid'));
+ return null;
+ $ret = Database::queryFirst("SELECT locationid FROM machine WHERE machineuuid = :uuid",
+ ['uuid' => $uuid]);
if ($ret === false || !$ret['locationid'])
- return false;
+ return null;
return (int)$ret['locationid'];
}
@@ -289,9 +299,9 @@ class Location
*
* @param string $ip IP address of client
* @param bool $honorRoomPlanner consider a fixed location assigned manually by roomplanner
- * @return false|int locationid, or false if no match
+ * @return ?int locationid, or null if no match
*/
- public static function getFromIp(string $ip, bool $honorRoomPlanner = false)
+ public static function getFromIp(string $ip, bool $honorRoomPlanner = false): ?int
{
if (Module::get('statistics') !== false) {
// Shortcut - try to use subnetlocationid in machine table
@@ -308,7 +318,7 @@ class Location
if ($ret['loc'] > 0) {
return (int)$ret['loc'];
}
- return false;
+ return null;
}
}
return self::mapIpToLocation($ip);
@@ -321,13 +331,13 @@ class Location
*
* @param string $ip IP address of client
* @param ?string $uuid System-UUID of client
- * @return int|false location id, or false if none matches
+ * @return ?int location id, or null if none matches
*/
- public static function getFromIpAndUuid(string $ip, ?string $uuid)
+ public static function getFromIpAndUuid(string $ip, ?string $uuid): ?int
{
$locationId = false;
$ipLoc = self::getFromIp($ip);
- if ($ipLoc !== false) {
+ if ($ipLoc !== null) {
// Set locationId to ipLoc for now, it will be overwritten later if another case applies.
$locationId = $ipLoc;
if ($uuid !== null) {
@@ -341,7 +351,7 @@ class Location
return $locationId;
}
- public static function isFixedLocationValid($uuidLoc, $ipLoc): bool
+ public static function isFixedLocationValid(?int $uuidLoc, int $ipLoc): bool
{
if ($uuidLoc === false)
return false;
@@ -368,18 +378,16 @@ class Location
*/
public static function getLocationRootChain(int $locationId): array
{
- if (self::$assocLocationCache === false) {
- self::getLocationsAssoc();
- }
- if (!isset(self::$assocLocationCache[$locationId]))
+ $locs = self::getLocationsAssoc();
+ if (!isset($locs[$locationId]))
return [];
- $chain = self::$assocLocationCache[$locationId]['parents'];
+ $chain = $locs[$locationId]['parents'];
$chain[] = $locationId;
return array_reverse($chain);
}
/**
- * @return array list of subnets as numeric array
+ * @return array<array{locationid: int, startaddr: int, endaddr: int}> list of subnets
*/
public static function getSubnets(): array
{
@@ -393,7 +401,7 @@ class Location
}
/**
- * @return array assoc array mapping from locationid to subnets
+ * @return array<array{locationid: int, subnets: array<array{startaddr:int, endaddr: int}>}> assoc array mapping from locationid to subnets
*/
public static function getSubnetsByLocation($recursive = false): array
{
@@ -429,48 +437,44 @@ class Location
* random one will be returned.
*
* @param string $ip IP to look up
- * @return false|int locationid ip matches, false = no match
+ * @return ?int locationid ip matches, null = no match
*/
- public static function mapIpToLocation(string $ip)
+ public static function mapIpToLocation(string $ip): ?int
{
- if (self::$subnetMapCache === false) {
- self::$subnetMapCache = self::getSubnetsByLocation();
+ static $subnetMapCache = null;
+ if ($subnetMapCache === null) {
+ $subnetMapCache = self::getSubnetsByLocation();
}
$long = sprintf('%u', ip2long($ip));
- $best = false;
+ $best = null;
$bestSize = 0;
- foreach (self::$subnetMapCache as $lid => $data) {
- if ($best !== false && self::$subnetMapCache[$lid]['depth'] < self::$subnetMapCache[$best]['depth'])
+ foreach ($subnetMapCache as $lid => $curLocation) {
+ if ($best !== null && $curLocation['depth'] < $subnetMapCache[$best]['depth'])
continue; // Don't even need to take a look
- foreach ($data['subnets'] as $subnet) {
+ foreach ($curLocation['subnets'] as $subnet) {
if ($long < $subnet['startaddr'] || $long > $subnet['endaddr'])
continue; // Nope
- if ($best !== false // Already have a best candidate
- && self::$subnetMapCache[$lid]['depth'] === self::$subnetMapCache[$best]['depth'] // Same depth
+ if ($best !== null // Already have a best candidate
+ && $curLocation['depth'] === $subnetMapCache[$best]['depth'] // Same depth
&& $bestSize < $subnet['endaddr'] - $subnet['startaddr']) { // Old candidate has smaller subnet
// So we ignore this one as the old one is more specific
continue;
}
$bestSize = $subnet['endaddr'] - $subnet['startaddr'];
- $best = $lid;
+ $best = (int)$lid;
}
}
- if ($best === false)
- return false;
- return (int)$best;
+ return $best;
}
/**
- * @return false|int newly determined location
+ * @return ?int newly determined location
*/
- public static function updateMapIpToLocation(string $uuid, string $ip)
+ public static function updateMapIpToLocation(string $uuid, string $ip): ?int
{
$loc = self::mapIpToLocation($ip);
- if ($loc === false) {
- Database::exec("UPDATE machine SET subnetlocationid = NULL WHERE machineuuid = :uuid", compact('uuid'));
- } else {
- Database::exec("UPDATE machine SET subnetlocationid = :loc WHERE machineuuid = :uuid", compact('loc', 'uuid'));
- }
+ Database::exec("UPDATE machine SET subnetlocationid = :loc WHERE machineuuid = :uuid",
+ ['loc' => $loc, 'uuid' => $uuid]);
return $loc;
}
diff --git a/modules-available/locations/inc/locationutil.inc.php b/modules-available/locations/inc/locationutil.inc.php
index 91117445..6114b0fb 100644
--- a/modules-available/locations/inc/locationutil.inc.php
+++ b/modules-available/locations/inc/locationutil.inc.php
@@ -149,8 +149,8 @@ class LocationUtil
$data += [
'iplocationid' => $slid,
'iplocationname' => $locs[$slid]['locationname'],
- 'ipisleaf' => empty($locs[$slid]['children']),
- 'canmove' => empty($locs[$slid]['children'])
+ 'ipisleaf' => $locs[$slid]['isleaf'],
+ 'canmove' => $locs[$slid]['isleaf']
&& (!$checkPerms || $ipLocs === true || in_array($slid, $ipLocs)), // Can machine be moved to subnet's locationid?
];
}
diff --git a/modules-available/locations/pages/details.inc.php b/modules-available/locations/pages/details.inc.php
index 26f32e68..7f3586ad 100644
--- a/modules-available/locations/pages/details.inc.php
+++ b/modules-available/locations/pages/details.inc.php
@@ -207,20 +207,21 @@ class SubPage
$newName = $location['locationname'];
}
if ($newParent === false || !User::hasPermission('location.edit.parent', $locationId)
- || !User::hasPermission('location.edit.parent', $newParent)
+ || !User::hasPermission('location.edit.*', $newParent)
|| !User::hasPermission('location.edit.*', $location['parentlocationid'])) {
- $newParent = $location['parentlocationid'];
+ $newParent = (int)$location['parentlocationid'];
} else if ($newParent !== 0) {
- $rows = Location::queryLocations();
- $all = Location::extractIds(Location::buildTree($rows));
+ $all = Location::getAllLocationIds();
if (!in_array($newParent, $all) || $newParent === $locationId) {
+ // New parent location doesn't exist
Message::addWarning('main.value-invalid', 'parent', $newParent);
- $newParent = $location['parentlocationid'];
+ $newParent = (int)$location['parentlocationid'];
} else {
- $rows = Location::extractIds(Location::buildTree($rows, $locationId));
+ $rows = Location::getAllLocationIds($locationId);
if (in_array($newParent, $rows)) {
+ // New parent location is a child location of the location itself
Message::addWarning('main.value-invalid', 'parent', $newParent);
- $newParent = $location['parentlocationid'];
+ $newParent = (int)$location['parentlocationid'];
}
}
}
diff --git a/modules-available/news/api.inc.php b/modules-available/news/api.inc.php
index 3b56c70d..e7c3129f 100644
--- a/modules-available/news/api.inc.php
+++ b/modules-available/news/api.inc.php
@@ -9,8 +9,12 @@ if (Module::isAvailable('locations')) {
if ($locationId === 0) {
$locationId = Location::getFromIp($_SERVER['REMOTE_ADDR']);
}
- $locations = Location::getLocationRootChain($locationId);
- $locations[] = 0;
+ if ($locationId === null) {
+ $locations = [0];
+ } else {
+ $locations = Location::getLocationRootChain($locationId);
+ $locations[] = 0;
+ }
} else {
$locations = [0];
}
diff --git a/modules-available/permissionmanager/inc/getpermissiondata.inc.php b/modules-available/permissionmanager/inc/getpermissiondata.inc.php
index a51619e0..83752a05 100644
--- a/modules-available/permissionmanager/inc/getpermissiondata.inc.php
+++ b/modules-available/permissionmanager/inc/getpermissiondata.inc.php
@@ -44,20 +44,33 @@ class GetPermissionData
*/
public static function getLocationData(): array
{
- $res = Database::simpleQuery("SELECT role.roleid AS roleid, rolename, GROUP_CONCAT(COALESCE(locationid, 0)) AS locationids FROM role
- INNER JOIN role_x_location ON role.roleid = role_x_location.roleid GROUP BY roleid ORDER BY rolename ASC");
- $locations = Location::getLocations(0, 0, false, true);
- foreach ($res as $row) {
- $locationids = explode(",", $row['locationids']);
- if (in_array("0", $locationids)) {
- $locationids = array_map("intval", Location::extractIds(Location::getTree()));
+ $res = Database::simpleQuery("SELECT role.roleid AS roleid, rolename, GROUP_CONCAT(COALESCE(locationid, 0)) AS locationids
+ FROM role
+ INNER JOIN role_x_location ON (role.roleid = role_x_location.roleid)
+ GROUP BY roleid, rolename ORDER BY rolename ASC");
+ $locations = Location::getLocations(0, 0, true, true);
+ $tree = Location::getLocationsAssoc();
+ $locations[0]['locationname'] = Dictionary::translate('global', true);
+ foreach ($res as $role) {
+ $locationids = explode(",", $role['locationids']);
+ if (in_array(0, $locationids)) {
+ $locationids = [0];
} else {
- $locationids = PermissionUtil::getSublocations(Location::getTree(), $locationids);
+ foreach ($locationids as $locationId) {
+ if (isset($tree[$locationId])) {
+ $locationids = array_merge($locationids, $tree[$locationId]['children']);
+ } else {
+ error_log("Unknown $locationId: " . json_encode($role));
+ }
+ }
}
foreach ($locationids as $locationid) {
+ if (!isset($locations[$locationid])) {
+ $locations[$locationid] = ['locationname' => $locationid, 'locationid' => $locationid];
+ }
$locations[$locationid]['roles'][] = array(
- 'roleid' => $row['roleid'],
- 'rolename' => $row['rolename']
+ 'roleid' => $role['roleid'],
+ 'rolename' => $role['rolename']
);
}
}
diff --git a/modules-available/permissionmanager/inc/permissionutil.inc.php b/modules-available/permissionmanager/inc/permissionutil.inc.php
index 170fd699..2dcd4d3c 100644
--- a/modules-available/permissionmanager/inc/permissionutil.inc.php
+++ b/modules-available/permissionmanager/inc/permissionutil.inc.php
@@ -165,7 +165,7 @@ class PermissionUtil
$parts = explode('.', $permissionid);
// Special case: To prevent lockout, userid === 1 always has permissionmanager.*
if ($parts[0] === 'permissionmanager' && User::getId() === 1) {
- $allowedLocations = [true];
+ $allowedLocations = [0 => true];
} else {
// Limit query to first part of permissionid, which is always the module id
$prefix = $parts[0] . '.%';
@@ -185,38 +185,18 @@ class PermissionUtil
}
}
}
- $locations = Location::getTree();
if (isset($allowedLocations[0])) {
- // Trivial case - have permission for all locations, so populate list with all valid locationds
- $allowedLocations = Location::extractIds($locations);
- $allowedLocations[] = 0; // .. plus 0 to show that we have global perms
+ // Trivial case - have permission for all locations, so populate list with all valid locationids
+ // .. plus 0 to show that we have global perms
+ $allowedLocations = Location::getAllLocationIds(0, true);
} else {
// We have a specific list of locationds - add any sublocations to list
- $allowedLocations = self::getSublocations($locations, $allowedLocations);
- }
- return $allowedLocations;
- }
-
- /**
- * Extend an array of locations by adding all sublocations.
- *
- * @param array $tree tree of all locations (structured like Location::getTree())
- * @param int[] $allowedLocations the array of locationids to extend
- * @return array extended array of locationids
- */
- public static function getSublocations(array $tree, array $allowedLocations): array
- {
- $result = $allowedLocations;
- foreach ($tree as $location) {
- if (array_key_exists("children", $location)) {
- if (isset($allowedLocations[$location["locationid"]])) {
- $result += array_flip(Location::extractIds($location["children"]));
- } else {
- $result += array_flip(self::getSublocations($location["children"], $allowedLocations));
- }
+ $allowedLocations = array_keys($allowedLocations);
+ foreach ($allowedLocations as $lid) {
+ $allowedLocations = array_merge($allowedLocations, Location::getAllLocationIds($lid));
}
}
- return array_keys($result);
+ return $allowedLocations;
}
/**
diff --git a/modules-available/roomplanner/inc/pvsgenerator.inc.php b/modules-available/roomplanner/inc/pvsgenerator.inc.php
index f3c5c838..3a699a60 100644
--- a/modules-available/roomplanner/inc/pvsgenerator.inc.php
+++ b/modules-available/roomplanner/inc/pvsgenerator.inc.php
@@ -180,7 +180,7 @@ class PvsGenerator
public static function getManagerName(int $locationId): ?string
{
$names = Location::getNameChain($locationId);
- if ($names === false)
+ if ($names === null)
return null;
return implode(' / ', $names);
}
diff --git a/modules-available/roomplanner/inc/room.inc.php b/modules-available/roomplanner/inc/room.inc.php
index 880cb6d0..c37dc24a 100644
--- a/modules-available/roomplanner/inc/room.inc.php
+++ b/modules-available/roomplanner/inc/room.inc.php
@@ -96,9 +96,8 @@ abstract class Room
public function __construct($row)
{
- $locations = Location::getLocationsAssoc();
$this->locationId = (int)$row['locationid'];
- $this->locationName = $locations[$this->locationId]['locationname'];
+ $this->locationName = Location::getName($this->locationId);
}
/**
diff --git a/modules-available/serversetup-bwlp-ipxe/inc/ipxemenu.inc.php b/modules-available/serversetup-bwlp-ipxe/inc/ipxemenu.inc.php
index 759f4cad..fd9de9ed 100644
--- a/modules-available/serversetup-bwlp-ipxe/inc/ipxemenu.inc.php
+++ b/modules-available/serversetup-bwlp-ipxe/inc/ipxemenu.inc.php
@@ -179,7 +179,7 @@ class IPxeMenu
{
$locationId = 0;
if (Module::isAvailable('locations')) {
- $locationId = Location::getFromIpAndUuid($ip, $uuid);
+ $locationId = Location::getFromIpAndUuid($ip, $uuid) ?? 0;
}
$menu = self::forLocation($locationId);
if ($uuid !== null) {
diff --git a/modules-available/statistics/api.inc.php b/modules-available/statistics/api.inc.php
index 8bc2c5ef..8f0f0810 100644
--- a/modules-available/statistics/api.inc.php
+++ b/modules-available/statistics/api.inc.php
@@ -171,7 +171,7 @@ if ($type[0] === '~') {
if (($old === false || $old['clientip'] !== $ip) && Module::isAvailable('locations')) {
// New, or ip changed (dynamic pool?), update subnetlicationid
$loc = Location::updateMapIpToLocation($uuid, $ip);
- $new['locationid'] = $loc; // For Filter Event
+ $new['locationid'] = $loc ?? 0; // For Filter Event
}
if ($json !== false) {
diff --git a/modules-available/statistics/inc/statisticsfilter.inc.php b/modules-available/statistics/inc/statisticsfilter.inc.php
index dbbb39e1..72b0cbf9 100644
--- a/modules-available/statistics/inc/statisticsfilter.inc.php
+++ b/modules-available/statistics/inc/statisticsfilter.inc.php
@@ -585,7 +585,7 @@ class LocationStatisticsFilter extends EnumStatisticsFilter
ErrorHandler::traceError('Cannot use ~ operator for location with array');
}
if ($recursive) {
- $argument = array_keys(Location::getRecursiveFlat($argument));
+ $argument = Location::getAllLocationIds((int)$argument, true);
} elseif ($argument == 0) {
return 'locationid IS ' . ($operator === '!=' ? 'NOT' : '') . ' NULL';
}
diff --git a/modules-available/statistics/pages/summary.inc.php b/modules-available/statistics/pages/summary.inc.php
index 5d6a58a8..af0e1e6a 100644
--- a/modules-available/statistics/pages/summary.inc.php
+++ b/modules-available/statistics/pages/summary.inc.php
@@ -78,7 +78,14 @@ class SubPage
$locations = null;
$op = null;
} elseif ($locFilter->op === '~') {
- $locations = array_keys(Location::getRecursiveFlat($locFilter->argument));
+ if (!is_array($locFilter->argument)) {
+ $locations = Location::getAllLocationIds((int)$locFilter->argument, true);
+ } else {
+ $locations = [];
+ foreach ($locFilter->argument as $lid) {
+ $locations = array_merge($locations, Location::getAllLocationIds((int)$lid, true));
+ }
+ }
$op = $locFilter->op;
} else {
if (is_array($locFilter->argument)) {
diff --git a/modules-available/sysconfig/api.inc.php b/modules-available/sysconfig/api.inc.php
index 90383240..209bb758 100644
--- a/modules-available/sysconfig/api.inc.php
+++ b/modules-available/sysconfig/api.inc.php
@@ -24,15 +24,15 @@ function deliverEmpty($message)
die('Config file could not be found or read!');
}
-$locationId = false;
+$locationId = null;
if (Module::isAvailable('locations')) {
$locationId = Location::getFromIpAndUuid($ip, $uuid);
- if ($locationId !== false) {
+ if ($locationId !== null) {
$locationChain = Location::getLocationRootChain($locationId);
$locationChain[] = 0;
}
}
-if ($locationId === false) {
+if ($locationId === null) {
$locationId = 0;
$locationChain = array(0);
}