summaryrefslogtreecommitdiffstats
path: root/modules-available/locations/pages
diff options
context:
space:
mode:
authorSimon Rettberg2019-07-31 16:58:14 +0200
committerSimon Rettberg2019-07-31 16:58:14 +0200
commit7c539fd8736b0ff9acafe32d857b2a2021d778e6 (patch)
treeb6a44076cba8443b2840340256b9829451214229 /modules-available/locations/pages
parent[locations] Optimize some functions in Location class (diff)
downloadslx-admin-7c539fd8736b0ff9acafe32d857b2a2021d778e6.tar.gz
slx-admin-7c539fd8736b0ff9acafe32d857b2a2021d778e6.tar.xz
slx-admin-7c539fd8736b0ff9acafe32d857b2a2021d778e6.zip
[locations] Add warnings/cleanup for bad machine to roomplan mappings
Diffstat (limited to 'modules-available/locations/pages')
-rw-r--r--modules-available/locations/pages/cleanup.inc.php93
-rw-r--r--modules-available/locations/pages/details.inc.php310
-rw-r--r--modules-available/locations/pages/locations.inc.php271
-rw-r--r--modules-available/locations/pages/subnets.inc.php79
4 files changed, 753 insertions, 0 deletions
diff --git a/modules-available/locations/pages/cleanup.inc.php b/modules-available/locations/pages/cleanup.inc.php
new file mode 100644
index 00000000..d10dbac0
--- /dev/null
+++ b/modules-available/locations/pages/cleanup.inc.php
@@ -0,0 +1,93 @@
+<?php
+
+class SubPage
+{
+
+ public static function doPreprocess($action)
+ {
+ if ($action === 'resetmachines') {
+ self::resetMachines();
+ return true;
+ }
+ if ($action === 'movemachines') {
+ self::moveMachines();
+ return true;
+ }
+ return false;
+ }
+
+ public static function doRender($action)
+ {
+ $list = self::loadForLocation();
+ if ($list === false)
+ return true;
+ Permission::addGlobalTags($list['perms'], NULL, ['subnets.edit', 'location.view']);
+ Render::addTemplate('mismatch-cleanup', $list);
+ return true;
+ }
+
+ public static function doAjax($action)
+ {
+ return false;
+ }
+
+ private static function resetMachines()
+ {
+ $delete = self::getSelectedMachines(true);
+ if ($delete === false)
+ return;
+ $num = Database::exec("UPDATE machine SET fixedlocationid = NULL, position = '' WHERE machineuuid IN (:machines)",
+ ['machines' => $delete]);
+ Message::addSuccess('reset-n-machines', $num);
+ }
+
+ private static function moveMachines()
+ {
+ $move = self::getSelectedMachines(false);
+ if ($move === false)
+ return;
+ // Move to subnet's location, or NULL if position field was empty (Which should never be the case)
+ $num = Database::exec("UPDATE machine SET fixedlocationid = If(Length(position) > 0, subnetlocationid, NULL) WHERE machineuuid IN (:machines)",
+ ['machines' => $move]);
+ Message::addSuccess('moved-n-machines', $num);
+ }
+
+ private static function getSelectedMachines($forDelete)
+ {
+ $list = self::loadForLocation();
+ if ($list === false)
+ return false;
+ $machines = Request::post('machines', false, 'array');
+ if ($machines === false) {
+ Message::addError('main.parameter-missing', 'machines');
+ return false;
+ }
+ $valid = array_map(function($item) use ($forDelete) {
+ return $item['canmove'] || $forDelete ? $item['machineuuid'] : 'x';
+ }, $list['clients']);
+ $retList = array_filter($machines, function($item) use ($valid) {
+ return in_array($item, $valid);
+ });
+ if (empty($retList)) {
+ Message::addError('no-valid-machines-selected');
+ return false;
+ }
+ return $retList;
+ }
+
+ private static function loadForLocation()
+ {
+ $locationid = Request::any('locationid', false, 'int');
+ if ($locationid === false) {
+ Message::addError('main.parameter-missing', 'locationid');
+ return false;
+ }
+ $list = LocationUtil::getMachinesWithLocationMismatch($locationid, true);
+ if (empty($list)) {
+ Message::addInfo('no-mismatch-location');
+ return false;
+ }
+ return $list;
+ }
+
+} \ No newline at end of file
diff --git a/modules-available/locations/pages/details.inc.php b/modules-available/locations/pages/details.inc.php
new file mode 100644
index 00000000..6acf31bf
--- /dev/null
+++ b/modules-available/locations/pages/details.inc.php
@@ -0,0 +1,310 @@
+<?php
+
+class SubPage
+{
+
+ public static function doPreprocess($action)
+ {
+ if ($action === 'updatelocation') {
+ self::updateLocation();
+ return true;
+ }
+ return false;
+ }
+
+ public static function doRender($action)
+ {
+ return false;
+ }
+
+ public static function doAjax($action)
+ {
+ if ($action === 'showlocation') {
+ self::ajaxShowLocation();
+ return true;
+ }
+ return false;
+ }
+
+ private static function updateLocation()
+ {
+ $locationId = Request::post('locationid', false, 'integer');
+ $del = Request::post('deletelocation', false, 'integer');
+ if ($locationId === false) {
+ Message::addError('parameter-missing', 'locationid');
+ Util::redirect('?do=Locations');
+ }
+ $location = Database::queryFirst('SELECT locationid, parentlocationid, locationname FROM location'
+ . ' WHERE locationid = :lid', array('lid' => $locationId));
+ if ($location === false) {
+ Message::addError('main.value-invalid', 'locationid', $locationId);
+ Util::redirect('?do=Locations');
+ }
+ $change = false;
+ // Delete location?
+ if ($locationId === $del) {
+ User::assertPermission("location.delete", $locationId, '?do=locations');
+ self::deleteLocation($location);
+ $change = true;
+ }
+ // Update subnets
+ $change |= self::updateLocationSubnets();
+ // Insert subnets
+ $change |= self::addNewLocationSubnets($location);
+ // Update location!
+ $change |= self::updateLocationData($location);
+
+ if ($change) {
+ // In case subnets or tree layout changed, recalc this
+ AutoLocation::rebuildAll();
+ }
+ Util::redirect('?do=Locations');
+ }
+
+ private static function deleteLocation($location)
+ {
+ $locationId = (int)$location['locationid'];
+ if (Request::post('recursive', false) === 'on') {
+ $rows = Location::queryLocations();
+ $rows = Location::buildTree($rows, $locationId);
+ $ids = Location::extractIds($rows);
+ } else {
+ $ids = [$locationId];
+ }
+ $locs = Database::exec("DELETE FROM location WHERE locationid IN (:ids)", ['ids' => $ids]);
+ Database::exec('UPDATE location SET parentlocationid = :newparent WHERE parentlocationid = :oldparent', array(
+ 'newparent' => $location['parentlocationid'],
+ 'oldparent' => $location['locationid']
+ ));
+ AutoLocation::rebuildAll($ids);
+ Message::addSuccess('location-deleted', $locs, implode(', ', $ids));
+ Util::redirect('?do=Locations');
+ }
+
+ private static function updateLocationData($location)
+ {
+ $locationId = (int)$location['locationid'];
+ $newParent = Request::post('parentlocationid', false, 'integer');
+ $newName = Request::post('locationname', false, 'string');
+ if (!User::hasPermission('location.edit.name', $locationId)) {
+ $newName = $location['locationname'];
+ } elseif ($newName === false || preg_match('/^\s*$/', $newName)) {
+ if ($newName !== false) {
+ Message::addWarning('main.value-invalid', 'location name', $newName);
+ }
+ $newName = $location['locationname'];
+ }
+ if ($newParent === false || !User::hasPermission('location.edit.parent', $locationId)
+ || !User::hasPermission('location.edit.parent', $newParent)
+ || !User::hasPermission('location.edit.*', $location['parentlocationid'])) {
+ $newParent = $location['parentlocationid'];
+ } else if ($newParent !== 0) {
+ $rows = Location::queryLocations();
+ $all = Location::extractIds(Location::buildTree($rows));
+ if (!in_array($newParent, $all) || $newParent === $locationId) {
+ Message::addWarning('main.value-invalid', 'parent', $newParent);
+ $newParent = $location['parentlocationid'];
+ } else {
+ $rows = Location::extractIds(Location::buildTree($rows, $locationId));
+ if (in_array($newParent, $rows)) {
+ Message::addWarning('main.value-invalid', 'parent', $newParent);
+ $newParent = $location['parentlocationid'];
+ }
+ }
+ }
+ // TODO: Check permissions for new parent (only if changed)
+ $ret = Database::exec('UPDATE location SET parentlocationid = :parent, locationname = :name'
+ . ' WHERE locationid = :lid', array(
+ 'lid' => $locationId,
+ 'parent' => $newParent,
+ 'name' => $newName
+ ));
+ if ($ret > 0) {
+ Message::addSuccess('location-updated', $newName);
+ }
+ return $newParent != $location['parentlocationid'];
+ }
+
+ private static function updateLocationSubnets()
+ {
+ $locationId = Request::post('locationid', false, 'integer');
+ if (!User::hasPermission('location.edit.subnets', $locationId))
+ return false;
+
+ $change = false;
+
+ // Deletion first
+ $dels = Request::post('deletesubnet', false);
+ if (is_array($dels)) {
+ $count = 0;
+ $stmt = Database::prepare('DELETE FROM subnet WHERE subnetid = :id');
+ foreach ($dels as $key => $value) {
+ if (!is_numeric($key) || $value !== 'on')
+ continue;
+ if ($stmt->execute(array('id' => $key))) {
+ $count += $stmt->rowCount();
+ }
+ }
+ if ($count > 0) {
+ Message::addInfo('subnets-deleted', $count);
+ $change = true;
+ }
+ }
+
+ // Now actual updates
+ $starts = Request::post('startaddr', false);
+ $ends = Request::post('endaddr', false);
+ if (!is_array($starts) || !is_array($ends)) {
+ return $change;
+ }
+ $count = 0;
+ $stmt = Database::prepare('UPDATE subnet SET startaddr = :start, endaddr = :end'
+ . ' WHERE subnetid = :id');
+ foreach ($starts as $key => $start) {
+ if (!isset($ends[$key]) || !is_numeric($key))
+ continue;
+ $end = $ends[$key];
+ $range = LocationUtil::rangeToLongVerbose($start, $end);
+ if ($range === false)
+ continue;
+ list($startLong, $endLong) = $range;
+ if ($stmt->execute(array('id' => $key, 'start' => $startLong, 'end' => $endLong))) {
+ $count += $stmt->rowCount();
+ }
+ }
+ if ($count > 0) {
+ Message::addInfo('subnets-updated', $count);
+ $change = true;
+ }
+ return $change;
+ }
+
+ private static function addNewLocationSubnets($location)
+ {
+ $locationId = (int)$location['locationid'];
+ if (!User::hasPermission('location.edit.subnets', $locationId))
+ return false;
+
+ $change = false;
+ $starts = Request::post('newstartaddr', false);
+ $ends = Request::post('newendaddr', false);
+ if (!is_array($starts) || !is_array($ends)) {
+ return $change;
+ }
+ $count = 0;
+ $stmt = Database::prepare('INSERT INTO subnet SET startaddr = :start, endaddr = :end, locationid = :location');
+ foreach ($starts as $key => $start) {
+ if (!isset($ends[$key]) || !is_numeric($key))
+ continue;
+ $end = $ends[$key];
+ list($startLong, $endLong) = LocationUtil::rangeToLong($start, $end);
+ if ($startLong === false) {
+ Message::addWarning('main.value-invalid', 'new start addr', $start);
+ }
+ if ($endLong === false) {
+ Message::addWarning('main.value-invalid', 'new end addr', $start);
+ }
+ if ($startLong === false || $endLong === false)
+ continue;
+ if ($startLong > $endLong) {
+ Message::addWarning('main.value-invalid', 'range', $start . ' - ' . $end);
+ continue;
+ }
+ if ($stmt->execute(array('location' => $locationId, 'start' => $startLong, 'end' => $endLong))) {
+ $count += $stmt->rowCount();
+ }
+ }
+ if ($count > 0) {
+ Message::addInfo('subnets-created', $count);
+ $change = true;
+ }
+ return $change;
+ }
+
+ private static function ajaxShowLocation()
+ {
+ $locationId = Request::any('locationid', 0, 'integer');
+
+ User::assertPermission("location.view", $locationId);
+
+ $loc = Database::queryFirst('SELECT locationid, parentlocationid, locationname FROM location WHERE locationid = :lid',
+ array('lid' => $locationId));
+ if ($loc === false) {
+ die('Unknown locationid');
+ }
+ $res = Database::simpleQuery("SELECT subnetid, startaddr, endaddr FROM subnet WHERE locationid = :lid",
+ array('lid' => $locationId));
+ $rows = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $row['startaddr'] = long2ip($row['startaddr']);
+ $row['endaddr'] = long2ip($row['endaddr']);
+ $rows[] = $row;
+ }
+ $data = array(
+ 'locationid' => $loc['locationid'],
+ 'locationname' => $loc['locationname'],
+ 'list' => $rows,
+ 'roomplanner' => Module::get('roomplanner') !== false,
+ 'parents' => Location::getLocations($loc['parentlocationid'], $locationId, true)
+ );
+
+ // Disable locations in the parent selector where the user cannot change to
+ if (!User::hasPermission('location.edit.*', $loc['parentlocationid'])
+ || !User::hasPermission('location.edit.parent', $locationId)) {
+ $allowedLocs = [];
+ } else {
+ $allowedLocs = User::getAllowedLocations("location.edit.*");
+ foreach ($data['parents'] as &$parent) {
+ if (!(in_array($parent["locationid"], $allowedLocs) || $parent["locationid"] == $loc['parentlocationid'])) {
+ $parent["disabled"] = "disabled";
+ }
+ }
+ }
+
+ if (Module::get('dozmod') !== false) {
+ $lectures = Database::queryFirst('SELECT Count(*) AS cnt FROM sat.lecture l '
+ . ' INNER JOIN sat.lecture_x_location ll ON (l.lectureid = ll.lectureid AND ll.locationid = :lid)',
+ array('lid' => $locationId));
+ $data['lectures'] = $lectures['cnt'];
+ $data['haveDozmod'] = true;
+ }
+ // Get clients matching this location's subnet(s)
+ $count = $online = $used = 0;
+ if (Module::get('statistics') !== false) {
+ $mres = Database::simpleQuery("SELECT state FROM machine"
+ . " WHERE machine.locationid = :lid", array('lid' => $locationId));
+ while ($row = $mres->fetch(PDO::FETCH_ASSOC)) {
+ $count++;
+ if ($row['state'] === 'IDLE') {
+ $online++;
+ }
+ if ($row['state'] === 'OCCUPIED') {
+ $online++;
+ $used++;
+ }
+ }
+ $data['haveStatistics'] = true;
+ // Link
+ if (User::hasPermission('.statistics.view.list')) {
+ $data['statsLink'] = 'list';
+ } elseif (User::hasPermission('.statistics.view.summary')) {
+ $data['statsLink'] = 'summary';
+ }
+ }
+ $data['machines'] = $count;
+ $data['machines_online'] = $online;
+ $data['machines_used'] = $used;
+ $data['used_percent'] = $count === 0 ? 0 : round(($used / $count) * 100);
+
+
+ Permission::addGlobalTags($data['perms'], $locationId, ['location.edit.name', 'location.edit.subnets', 'location.delete', '.roomplanner.edit'], 'save_button');
+ if (empty($allowedLocs)) {
+ $data['perms']['location']['edit']['parent']['disabled'] = 'disabled';
+ } else {
+ unset($data['perms']['save_button']);
+ }
+
+ echo Render::parse('location-subnets', $data);
+ }
+
+} \ No newline at end of file
diff --git a/modules-available/locations/pages/locations.inc.php b/modules-available/locations/pages/locations.inc.php
new file mode 100644
index 00000000..275aafdb
--- /dev/null
+++ b/modules-available/locations/pages/locations.inc.php
@@ -0,0 +1,271 @@
+<?php
+
+class SubPage
+{
+
+ public static function doPreprocess($action)
+ {
+ if ($action === 'addlocations') {
+ self::addLocations();
+ return true;
+ }
+ return false;
+ }
+
+ public static function doRender($getAction)
+ {
+ if ($getAction === false) {
+ if (User::hasPermission('location.view')) {
+ // OK
+ } elseif (User::hasPermission('subnets.edit')) {
+ // Fallback to something else?
+ Util::redirect('?do=locations&page=subnets');
+ } else {
+ // Trigger permission denied by asserting non-existent permission
+ User::assertPermission('location.view');
+ }
+ }
+ if ($getAction === false) {
+ self::showLocationList();
+ return true;
+ }
+ return false;
+ }
+
+ public static function doAjax($action)
+ {
+ return false;
+ }
+
+ private static function addLocations()
+ {
+ $names = Request::post('newlocation', false);
+ $parents = Request::post('newparent', []);
+ if (!is_array($names) || !is_array($parents)) {
+ Message::addError('main.empty-field');
+ Util::redirect('?do=Locations');
+ }
+ $locs = Location::getLocations();
+ $count = 0;
+ foreach ($names as $idx => $name) {
+ $name = trim($name);
+ if (empty($name))
+ continue;
+ $parent = isset($parents[$idx]) ? (int)$parents[$idx] : 0;
+ if (!User::hasPermission("location.add", $parent)) {
+ Message::addError('no-permission-location', isset($locs[$parent]) ? $locs[$parent]['locationname'] : $parent);
+ continue;
+ }
+ if ($parent !== 0) {
+ $ok = false;
+ foreach ($locs as $loc) {
+ if ($loc['locationid'] == $parent) {
+ $ok = true;
+ }
+ }
+ if (!$ok) {
+ Message::addWarning('main.value-invalid', 'parentlocationid', $parent);
+ continue;
+ }
+ }
+ Database::exec("INSERT INTO location (parentlocationid, locationname)"
+ . " VALUES (:parent, :name)", array(
+ 'parent' => $parent,
+ 'name' => $name
+ ));
+ $count++;
+ }
+ Message::addSuccess('added-x-entries', $count);
+ Util::redirect('?do=Locations');
+ }
+
+ public static function showLocationList()
+ {
+ // Warn admin about overlapping subnet definitions
+ $overlapSelf = $overlapOther = true;
+ LocationUtil::getOverlappingSubnets($overlapSelf, $overlapOther);
+ // Find machines assigned to a room with a UUID mismatch
+ $mismatchMachines = LocationUtil::getMachinesWithLocationMismatch(0, true);
+ $locationList = Location::getLocationsAssoc();
+ unset($locationList[0]);
+ // Statistics: Count machines for each subnet
+ $unassigned = false;
+ $unassignedLoad = 0;
+
+ // Filter view: Remove locations we can't reach at all, but show parents to locations
+ // we have permission to, so the tree doesn't look all weird
+ $visibleLocationIds = $allowedLocationIds = User::getAllowedLocations("location.view");
+ foreach ($allowedLocationIds as $lid) {
+ if (!isset($locationList[$lid]))
+ continue;
+ $visibleLocationIds = array_merge($visibleLocationIds, $locationList[$lid]['parents']);
+ }
+ $visibleLocationIds = array_unique($visibleLocationIds);
+ foreach (array_keys($locationList) as $lid) {
+ if (User::hasPermission('.baseconfig.view', $lid)) {
+ $visibleLocationIds[] = $lid;
+ } else {
+ $locationList[$lid]['havebaseconfig'] = false;
+ }
+ if (User::hasPermission('.sysconfig.config.view-list', $lid)) {
+ $visibleLocationIds[] = $lid;
+ } else {
+ $locationList[$lid]['havesysconfig'] = false;
+ }
+ if (User::hasPermission('.statistics.view.list', $lid)) {
+ $visibleLocationIds[] = $lid;
+ } else {
+ $locationList[$lid]['havestatistics'] = false;
+ }
+ if (User::hasPermission('.serversetup.ipxe.menu.assign', $lid)) {
+ $visibleLocationIds[] = $lid;
+ } else {
+ $locationList[$lid]['haveipxe'] = false;
+ }
+ if (!in_array($lid, $visibleLocationIds)) {
+ unset($locationList[$lid]);
+ } elseif (!in_array($lid, $allowedLocationIds)) {
+ $locationList[$lid]['show-only'] = true;
+ }
+ }
+
+ // Client statistics
+ if (Module::get('statistics') !== false) {
+ $unassigned = 0;
+ $extra = '';
+ if (in_array(0, $allowedLocationIds)) {
+ $extra = ' OR locationid IS NULL';
+ }
+ $res = Database::simpleQuery("SELECT locationid, Count(*) AS cnt, Sum(If(state = 'OCCUPIED', 1, 0)) AS used
+ FROM machine WHERE (locationid IN (:allowedLocationIds) $extra) GROUP BY locationid", compact('allowedLocationIds'));
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $locId = (int)$row['locationid'];
+ if (isset($locationList[$locId])) {
+ $locationList[$locId]['clientCount'] = $row['cnt'];
+ $locationList[$locId]['clientLoad'] = round(100 * $row['used'] / $row['cnt']) . ' %';
+ } else {
+ $unassigned += $row['cnt'];
+ $unassignedLoad += $row['used'];
+ }
+ }
+ unset($loc);
+ foreach ($locationList as &$loc) {
+ if (!in_array($loc['locationid'], $allowedLocationIds))
+ continue;
+ if (!isset($loc['clientCountSum'])) {
+ $loc['clientCountSum'] = 0;
+ }
+ if (!isset($loc['clientCount'])) {
+ $loc['clientCount'] = 0;
+ $loc['clientLoad'] = '0%';
+ }
+ $loc['clientCountSum'] += $loc['clientCount'];
+ foreach ($loc['parents'] as $pid) {
+ if (!in_array($pid, $allowedLocationIds))
+ continue;
+ $locationList[(int)$pid]['hasChild'] = true;
+ $locationList[(int)$pid]['clientCountSum'] += $loc['clientCount'];
+ }
+ }
+ unset($loc);
+ }
+ // Show currently active sysconfig for each location
+ $defaultConfig = false;
+ if (Module::isAvailable('sysconfig')) {
+ $confs = SysConfig::getAll();
+ foreach ($confs as $conf) {
+ if (strlen($conf['locs']) === 0)
+ continue;
+ $confLocs = explode(',', $conf['locs']);
+ foreach ($confLocs as $locId) {
+ settype($locId, 'int');
+ if ($locId === 0) {
+ $defaultConfig = $conf['title'];
+ }
+ if (!isset($locationList[$locId]))
+ continue;
+ $locationList[$locId] += array('configName' => $conf['title'], 'configClass' => 'slx-bold');
+ }
+ }
+ self::propagateFields($locationList, $defaultConfig, 'configName', 'configClass');
+ }
+ // Count overridden config vars
+ if (Module::get('baseconfig') !== false) {
+ $res = Database::simpleQuery("SELECT locationid, Count(*) AS cnt FROM `setting_location`
+ WHERE locationid IN (:allowedLocationIds) GROUP BY locationid", compact('allowedLocationIds'));
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $lid = (int)$row['locationid'];
+ if (isset($locationList[$lid])) {
+ $locationList[$lid]['overriddenVars'] = $row['cnt'];
+ }
+ }
+ // Confusing because the count might be inaccurate within a branch
+ //$this->propagateFields($locationList, '', 'overriddenVars', 'overriddenClass');
+ }
+ // Show ipxe menu
+ if (Module::isAvailable('serversetup') && class_exists('IPxe')) {
+ $res = Database::simpleQuery("SELECT ml.locationid, m.title, ml.defaultentryid FROM serversetup_menu m
+ INNER JOIN serversetup_menu_location ml USING (menuid)
+ WHERE locationid IN (:allowedLocationIds) GROUP BY locationid", compact('allowedLocationIds'));
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $lid = (int)$row['locationid'];
+ if (isset($locationList[$lid])) {
+ if ($row['defaultentryid'] !== null) {
+ $row['title'] .= '(*)';
+ }
+ $locationList[$lid]['customMenu'] = $row['title'];
+ }
+ }
+ self::propagateFields($locationList, '', 'customMenu', 'customMenuClass');
+ }
+
+ $addAllowedLocs = User::getAllowedLocations("location.add");
+ $addAllowedList = Location::getLocations(0, 0, true);
+ foreach ($addAllowedList as &$loc) {
+ if (!in_array($loc["locationid"], $addAllowedLocs)) {
+ $loc["disabled"] = "disabled";
+ }
+ }
+ unset($loc);
+
+ // Output
+ $data = array(
+ 'list' => array_values($locationList),
+ 'havestatistics' => Module::get('statistics') !== false,
+ 'havebaseconfig' => Module::get('baseconfig') !== false,
+ 'havesysconfig' => Module::get('sysconfig') !== false,
+ 'haveipxe' => Module::isAvailable('serversetup') && class_exists('IPxe'),
+ 'overlapSelf' => $overlapSelf,
+ 'overlapOther' => $overlapOther,
+ 'mismatchMachines' => $mismatchMachines,
+ 'unassignedCount' => $unassigned,
+ 'unassignedLoad' => ($unassigned ? (round(($unassignedLoad / $unassigned) * 100) . ' %') : ''),
+ 'defaultConfig' => $defaultConfig,
+ 'addAllowedList' => array_values($addAllowedList),
+ );
+ // TODO: Buttons for config vars and sysconfig are currently always shown, as their availability
+ // depends on permissions in the according modules, not this one
+ Permission::addGlobalTags($data['perms'], NULL, ['subnets.edit', 'location.add']);
+ Render::addTemplate('locations', $data);
+ }
+
+ private static function propagateFields(&$locationList, $defaultValue, $name, $class)
+ {
+ $depth = array();
+ foreach ($locationList as &$loc) {
+ $d = $loc['depth'];
+ if (!isset($loc[$name])) {
+ // Has no explicit config assignment
+ if ($d === 0) {
+ $loc[$name] = $defaultValue;
+ } else {
+ $loc[$name] = $depth[$d - 1];
+ }
+ $loc[$class] = 'gray';
+ }
+ $depth[$d] = $loc[$name];
+ unset($depth[$d + 1]);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/modules-available/locations/pages/subnets.inc.php b/modules-available/locations/pages/subnets.inc.php
new file mode 100644
index 00000000..6c37129a
--- /dev/null
+++ b/modules-available/locations/pages/subnets.inc.php
@@ -0,0 +1,79 @@
+<?php
+
+class SubPage
+{
+
+ public static function doPreprocess($action)
+ {
+ if ($action === 'updatesubnets') {
+ self::updateSubnets();
+ return true;
+ }
+ return false;
+ }
+
+ private static function updateSubnets()
+ {
+ User::assertPermission('subnets.edit', NULL, '?do=locations');
+ $count = 0;
+ $starts = Request::post('startaddr', false);
+ $ends = Request::post('endaddr', false);
+ $locs = Request::post('location', false);
+ if (!is_array($starts) || !is_array($ends) || !is_array($locs)) {
+ Message::addError('main.empty-field');
+ Util::redirect('?do=Locations');
+ }
+ $existingLocs = Location::getLocationsAssoc();
+ $stmt = Database::prepare("UPDATE subnet SET startaddr = :startLong, endaddr = :endLong, locationid = :loc WHERE subnetid = :subnetid");
+ foreach ($starts as $subnetid => $start) {
+ if (!isset($ends[$subnetid]) || !isset($locs[$subnetid]))
+ continue;
+ $loc = (int)$locs[$subnetid];
+ $end = $ends[$subnetid];
+ if (!isset($existingLocs[$loc])) {
+ Message::addError('main.value-invalid', 'locationid', $loc);
+ continue;
+ }
+ $range = LocationUtil::rangeToLongVerbose($start, $end);
+ if ($range === false)
+ continue;
+ list($startLong, $endLong) = $range;
+ if ($stmt->execute(compact('startLong', 'endLong', 'loc', 'subnetid'))) {
+ $count += $stmt->rowCount();
+ }
+ }
+ AutoLocation::rebuildAll();
+ Message::addSuccess('subnets-updated', $count);
+ Util::redirect('?do=Locations');
+ }
+
+ public static function doRender($getAction)
+ {
+ if ($getAction === false) {
+ User::assertPermission('subnets.edit', NULL, '?do=locations');
+ $res = Database::simpleQuery("SELECT subnetid, startaddr, endaddr, locationid FROM subnet");
+ $rows = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $row['startaddr'] = long2ip($row['startaddr']);
+ $row['endaddr'] = long2ip($row['endaddr']);
+ $row['locations'] = Location::getLocations($row['locationid']);
+ $rows[] = $row;
+ }
+ $data = array('list' => $rows);
+ Permission::addGlobalTags($data['perms'], NULL, ['location.view']);
+ Render::addTemplate('subnets', $data);
+ return true;
+ }
+ return false;
+ }
+
+ public static function doAjax($action)
+ {
+ return false;
+ }
+
+ /*
+ * Helpers
+ */
+
+} \ No newline at end of file