summaryrefslogtreecommitdiffstats
path: root/modules-available/locations
diff options
context:
space:
mode:
Diffstat (limited to 'modules-available/locations')
-rw-r--r--modules-available/locations/baseconfig/getconfig.inc.php7
-rw-r--r--modules-available/locations/inc/abstractlocationcolumn.inc.php29
-rw-r--r--modules-available/locations/inc/autolocation.inc.php2
-rw-r--r--modules-available/locations/inc/location.inc.php87
-rw-r--r--modules-available/locations/inc/locationhooks.inc.php6
-rw-r--r--modules-available/locations/inc/locationutil.inc.php32
-rw-r--r--modules-available/locations/inc/openingtimes.inc.php62
-rw-r--r--modules-available/locations/install.inc.php7
-rw-r--r--modules-available/locations/lang/de/messages.json4
-rw-r--r--modules-available/locations/lang/de/permissions.json1
-rw-r--r--modules-available/locations/lang/de/template-tags.json19
-rw-r--r--modules-available/locations/lang/en/messages.json4
-rw-r--r--modules-available/locations/lang/en/permissions.json1
-rw-r--r--modules-available/locations/lang/en/template-tags.json15
-rw-r--r--modules-available/locations/pages/cleanup.inc.php9
-rw-r--r--modules-available/locations/pages/details.inc.php131
-rw-r--r--modules-available/locations/pages/locations.inc.php183
-rw-r--r--modules-available/locations/pages/subnets.inc.php10
-rw-r--r--modules-available/locations/permissions/permissions.json3
-rw-r--r--modules-available/locations/style.css12
-rw-r--r--modules-available/locations/templates/ajax-opening-location.html55
-rw-r--r--modules-available/locations/templates/location-subnets.html23
-rw-r--r--modules-available/locations/templates/locations.html107
-rw-r--r--modules-available/locations/templates/mismatch-cleanup.html24
24 files changed, 435 insertions, 398 deletions
diff --git a/modules-available/locations/baseconfig/getconfig.inc.php b/modules-available/locations/baseconfig/getconfig.inc.php
index 26e43ed8..1bed5de7 100644
--- a/modules-available/locations/baseconfig/getconfig.inc.php
+++ b/modules-available/locations/baseconfig/getconfig.inc.php
@@ -1,5 +1,8 @@
<?php
+/** @var ?string $uuid */
+/** @var ?string $ip */
+
// Location handling: figure out location
$locationId = false;
if (BaseConfig::hasOverride('locationid')) {
@@ -12,7 +15,7 @@ if (BaseConfig::hasOverride('locationid')) {
}
if ($locationId === false) {
- if (!$ip) // Required at this point, bail out if not given
+ if ($ip === null) // Required at this point, bail out if not given
return;
$locationId = Location::getFromIpAndUuid($ip, $uuid);
}
@@ -31,7 +34,7 @@ if (!empty($matchingLocations)) {
FROM setting_location WHERE locationid IN (:list)",
['list' => $matchingLocations]);
$tmp = array();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ foreach ($res as $row) {
$tmp[(int)$row['locationid']][$row['setting']] = $row; // Put whole row so we have value and displayvalue
}
// Callback for pretty printing
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
diff --git a/modules-available/locations/install.inc.php b/modules-available/locations/install.inc.php
index c5fd9688..46a6544c 100644
--- a/modules-available/locations/install.inc.php
+++ b/modules-available/locations/install.inc.php
@@ -15,7 +15,7 @@ $res[] = tableCreate('location', '
`locationid` INT(11) NOT NULL AUTO_INCREMENT,
`parentlocationid` INT(11) NOT NULL,
`locationname` VARCHAR(100) NOT NULL,
- `openingtime` BLOB,
+ `openingtime` BLOB DEFAULT NULL,
PRIMARY KEY (`locationid`),
KEY `locationname` (`locationname`),
KEY `parentlocationid` (`parentlocationid`)
@@ -40,7 +40,7 @@ $res[] = tableAddConstraint('setting_location', 'locationid', 'location', 'locat
// 2020-07-14 Add openingtime column to location table, then migrate data and delete the column from locationinfo
if (!tableHasColumn('location', 'openingtime')) {
- if (Database::exec("ALTER TABLE location ADD openingtime BLOB") === false) {
+ if (Database::exec("ALTER TABLE location ADD openingtime BLOB DEFAULT NULL") === false) {
finalResponse(UPDATE_FAILED, 'Could not create openingtime column');
}
$res[] = UPDATE_DONE;
@@ -60,5 +60,8 @@ if (tableHasColumn('locationinfo_locationconfig', 'openingtime')) {
$res[] = UPDATE_DONE;
}
+// 2021-03-19: Fix this. No idea how this came to be, maybe during dev only? But better be safe...
+Database::exec("UPDATE location SET openingtime = NULL WHERE openingtime = ''");
+
// Create response for browser
responseFromArray($res);
diff --git a/modules-available/locations/lang/de/messages.json b/modules-available/locations/lang/de/messages.json
index ec1cac73..7167f5d3 100644
--- a/modules-available/locations/lang/de/messages.json
+++ b/modules-available/locations/lang/de/messages.json
@@ -1,5 +1,9 @@
{
"added-x-entries": "Eintr\u00e4ge hinzugef\u00fcgt: {{0}}",
+ "ignored-invalid-end": "Zeitspanne ignoriert: Ung\u00fcltiges Ende {{0}}",
+ "ignored-invalid-range": "Zeitspanne ignoriert: {{0}} - {{1}}",
+ "ignored-invalid-start": "Zeitspanne ignoriert: Ung\u00fcltiger Start {{0}}",
+ "ignored-line-no-days": "Eine Zeitspanne wurde ignoriert, da ihr keine Tage zugewiesen wurden",
"invalid-location-id": "Ung\u00fcltige Orts-id: {{0}}",
"location-deleted": "Location wurde gel\u00f6scht (Locations: {{0}}, IDs: {{1}})",
"location-updated": "Location {{0}} wurde aktualisiert",
diff --git a/modules-available/locations/lang/de/permissions.json b/modules-available/locations/lang/de/permissions.json
index 8a9336b3..e8e3cced 100644
--- a/modules-available/locations/lang/de/permissions.json
+++ b/modules-available/locations/lang/de/permissions.json
@@ -2,6 +2,7 @@
"location.add": "R\u00e4ume\/Orte hinzuf\u00fcgen.",
"location.delete": "R\u00e4ume\/Orte l\u00f6schen.",
"location.edit.name": "Orte umbenennen.",
+ "location.edit.openingtimes": "\u00d6ffnungszeiten bearbeiten.",
"location.edit.parent": "Den \u00fcbergeordneten Ort eines Ortes \u00e4ndern.",
"location.edit.subnets": "Die IP-Ranges eines Ortes \u00e4ndern.",
"location.view": "R\u00e4ume anschauen.",
diff --git a/modules-available/locations/lang/de/template-tags.json b/modules-available/locations/lang/de/template-tags.json
index 94f95ded..ecd3a647 100644
--- a/modules-available/locations/lang/de/template-tags.json
+++ b/modules-available/locations/lang/de/template-tags.json
@@ -3,18 +3,19 @@
"lang_areYouSureNoUndo": "Sind Sie sicher? Diese Aktion kann nicht r\u00fcckg\u00e4ngig gemacht werden.",
"lang_assignSubnetExplanation": "Rechner, die in einen der hier aufgef\u00fchrten Adressbereiche fallen, werden diesem Ort zugeschrieben und erhalten damit z.B. f\u00fcr diesen Raum angepasste Veranstaltungslisten.",
"lang_assignedSubnets": "Zugeordnete Subnetze bzw. IP-Bereiche",
- "lang_bootMenu": "Bootmen\u00fc",
+ "lang_automatedMachineActions": "Automatisierte Aktionen",
"lang_closingTime": "Schlie\u00dfungszeit",
"lang_day": "Tag",
"lang_deleteChildLocations": "Untergeordnete Orte ebenfalls l\u00f6schen",
"lang_deleteLocation": "Ort l\u00f6schen",
"lang_deleteSubnet": "Bereich l\u00f6schen",
"lang_deleteSubnetWarning": "Alle zum L\u00f6schen markierten Subnetze werden gel\u00f6scht. Diese Aktion kann nicht r\u00fcckg\u00e4ngig gemacht werden.",
- "lang_editConfigVariables": "Konfig.-Variablen",
+ "lang_editNews": "News\/Hilfe bearbeiten",
"lang_editRoomplan": "Raumplan bearbeiten",
"lang_endAddress": "Endadresse",
- "lang_expertMode": "Experten Modus",
+ "lang_expertMode": "Expertenmodus",
"lang_fixMachineAssign": "Zuweisungen anzeigen\/aufheben",
+ "lang_inheritOpeningTimes": "Vom \u00fcbergeordneten Ort \u00fcbernehmen",
"lang_ip": "IP-Adresse",
"lang_listOfSubnets": "Liste der Subnetze",
"lang_location": "Ort",
@@ -28,7 +29,6 @@
"lang_locationsMainHeading": "Verwaltung von R\u00e4umen\/Orten",
"lang_machine": "Rechner",
"lang_machineCount": "Rechner",
- "lang_machineLoad": "Besetzt",
"lang_matchingMachines": "Enthaltene Rechner",
"lang_mismatchHeading": "Rechner mit widerspr\u00fcchlicher Raumzuweisung",
"lang_mismatchIntroText": "Die hier aufgelisteten Rechner wurden mittels des Raumplaners im oben genannten Raum platziert. Ihrer IP-Adresse nach fallen diese jedoch in einen anderen Raum (durch die f\u00fcr diesen definierten IP-Ranges). Wenn Sie die entsprechenden Rechner hier markieren und auf \"Zur\u00fccksetzen\" klicken, werden die Rechner aus dem oben genannten Raumplan entfernt. Wenn Sie stattdessen auf \"Verschieben\" klicken, werden die Rechner mit ihrer aktuellen Position aus dem jetzigen Raum in den eigentlichen Raum (siehe letzte Spalte der Tabelle) verschoben.",
@@ -36,19 +36,23 @@
"lang_moveMachines": "In durch Subnet zugeordneten Raum verschieben",
"lang_moveable": "Verschiebbar",
"lang_name": "Name",
- "lang_numMachinesWithOverrides": "Anzahl Rechner, bei denen mindestens eine Konfigurationsvariable \u00fcberschrieben wird",
+ "lang_nextEvent": "N\u00e4chstes geplantes Ereignis",
"lang_offsetEarly": "Min. vorher",
"lang_offsetLate": "Min. danach",
"lang_openingTime": "\u00d6ffnungszeit",
- "lang_overridenVarsForLocation": "Anzahl Variablen, die an diesem Ort \u00fcberschrieben werden",
"lang_parentLocation": "\u00dcbergeordneter Ort",
"lang_referencingLectures": "Veranstaltungen",
+ "lang_remoteAccessConstraints": "Beschr\u00e4nkungen f\u00fcr den Fernzugriff",
+ "lang_remoteAccessHelp": "Wenn dieser Raum einer Fernzugriffsgruppe zugewiesen ist, kann der Zugriff hier eingeschr\u00e4nkt werden, sodass er z.B. nur au\u00dferhalb der oben angegebenen \u00d6ffnungszeiten tats\u00e4chlich f\u00fcr den Zugriff zur Verf\u00fcgung steht. Ist der Raum in keiner Fernzugriffsgruppe, hat diese Einstellung keine Auswirkungen.",
+ "lang_remoteAccessNever": "Niemals erlauben",
+ "lang_remoteAccessNoRestriction": "Keine Einschr\u00e4nkung",
+ "lang_remoteAccessOnlyWhenClosed": "Nur, wenn der Raum geschlossen ist",
"lang_resetMachines": "Raumzuweisung zur\u00fccksetzen",
"lang_saturday": "Samstag",
"lang_shortFriday": "Fr",
"lang_shortMonday": "Mo",
"lang_shortSaturday": "Sa",
- "lang_shortSunday": "Su",
+ "lang_shortSunday": "So",
"lang_shortThursday": "Do",
"lang_shortTuesday": "Di",
"lang_shortWednesday": "Mi",
@@ -57,7 +61,6 @@
"lang_startAddress": "Startadresse",
"lang_subnet": "IP-Bereich",
"lang_sunday": "Sonntag",
- "lang_sysConfig": "Lokalisierung",
"lang_thisListByLocation": "Orte",
"lang_thisListBySubnet": "Subnetze",
"lang_unassignedMachines": "Rechner, die in keinen definierten Ort fallen",
diff --git a/modules-available/locations/lang/en/messages.json b/modules-available/locations/lang/en/messages.json
index fd7c740a..16e0ef76 100644
--- a/modules-available/locations/lang/en/messages.json
+++ b/modules-available/locations/lang/en/messages.json
@@ -1,5 +1,9 @@
{
"added-x-entries": "Entries added: {{0}}",
+ "ignored-invalid-end": "Ignoring timespan: Invalid end {{0}}",
+ "ignored-invalid-range": "Ignoring timespan: {{0}} - {{1}}",
+ "ignored-invalid-start": "Ignoring timespan: Invalid start {{0}}",
+ "ignored-line-no-days": "Ignoring timespan without any days selected",
"invalid-location-id": "Invalid location id: {{0}}",
"location-deleted": "Location has been deleted (locations: {{0}}, IDs: {{1}})",
"location-updated": "Location {{0}} has been updated",
diff --git a/modules-available/locations/lang/en/permissions.json b/modules-available/locations/lang/en/permissions.json
index 1d01a9d1..c9f7a3ab 100644
--- a/modules-available/locations/lang/en/permissions.json
+++ b/modules-available/locations/lang/en/permissions.json
@@ -2,6 +2,7 @@
"location.add": "Add locations.",
"location.delete": "Delete locations.",
"location.edit.name": "Rename locations.",
+ "location.edit.openingtimes": "Edit opening times.",
"location.edit.parent": "Change the parent location of a location.",
"location.edit.subnets": "Edit the IP ranges of a location.",
"location.view": "View locations.",
diff --git a/modules-available/locations/lang/en/template-tags.json b/modules-available/locations/lang/en/template-tags.json
index 467af8e1..55eaec86 100644
--- a/modules-available/locations/lang/en/template-tags.json
+++ b/modules-available/locations/lang/en/template-tags.json
@@ -3,18 +3,19 @@
"lang_areYouSureNoUndo": "Are you sure? This cannot be undone!",
"lang_assignSubnetExplanation": "Client machines which fall into an IP range listed below will be assigned to this location and will see an according lecture list (e.g. they will see lectures that are exclusively assigned to this location).",
"lang_assignedSubnets": "Assigned subnets \/ IP ranges",
- "lang_bootMenu": "Boot menu",
+ "lang_automatedMachineActions": "Automated actions",
"lang_closingTime": "Closing time",
"lang_day": "Day",
"lang_deleteChildLocations": "Delete child locations as well",
"lang_deleteLocation": "Delete location",
"lang_deleteSubnet": "Delete range",
"lang_deleteSubnetWarning": "All subnets marked for deletion will be deleted. This cannot be undone!",
- "lang_editConfigVariables": "Config vars",
+ "lang_editNews": "Edit news\/help",
"lang_editRoomplan": "Edit roomplan",
"lang_endAddress": "End address",
"lang_expertMode": "Expert mode",
"lang_fixMachineAssign": "Fix or remove assignment",
+ "lang_inheritOpeningTimes": "Inherit from parent location",
"lang_ip": "IP address",
"lang_listOfSubnets": "List of subnets",
"lang_location": "Location",
@@ -28,7 +29,6 @@
"lang_locationsMainHeading": "Manage rooms and locations",
"lang_machine": "Machine",
"lang_machineCount": "Clients",
- "lang_machineLoad": "In use",
"lang_matchingMachines": "Matching clients",
"lang_mismatchHeading": "Machines with mismatching room plan assignment",
"lang_mismatchIntroText": "Machines listed here are assigned to the room above, but judging from their IP address, should actually be in another room (because of the IP range(s) assigned to that room). By selecting machines below and clicking \"reset\", they will be removed from their current room plan. If you choose \"move\", they will be transferred to the plan of the room they should actually belong to (see last column of table).",
@@ -36,13 +36,17 @@
"lang_moveMachines": "Move to room designated by IP address",
"lang_moveable": "Moveable",
"lang_name": "Name",
- "lang_numMachinesWithOverrides": "Number of clients where at least one variable is overridden",
+ "lang_nextEvent": "Next scheduled event",
"lang_offsetEarly": "min. before",
"lang_offsetLate": "min. after",
"lang_openingTime": "Opening Time",
- "lang_overridenVarsForLocation": "Number of variables that get overridden for this location",
"lang_parentLocation": "Parent location",
"lang_referencingLectures": "Assigned Lectures",
+ "lang_remoteAccessConstraints": "Remote access constraints",
+ "lang_remoteAccessHelp": "If this room is part of a remote access group, you can limit its accessibility here, like only making it available outside of the business hours configured above. If this room isn't assigned to any remote access group, this setting doesn't have any effect.",
+ "lang_remoteAccessNever": "Never allow access",
+ "lang_remoteAccessNoRestriction": "No restrictions",
+ "lang_remoteAccessOnlyWhenClosed": "Only when room is not open",
"lang_resetMachines": "Reset room assignment",
"lang_saturday": "Saturday",
"lang_shortFriday": "Fri",
@@ -57,7 +61,6 @@
"lang_startAddress": "Start address",
"lang_subnet": "IP range",
"lang_sunday": "Sunday",
- "lang_sysConfig": "Localization\/Integration",
"lang_thisListByLocation": "Locations",
"lang_thisListBySubnet": "Subnets",
"lang_unassignedMachines": "Machines not matching any location",
diff --git a/modules-available/locations/pages/cleanup.inc.php b/modules-available/locations/pages/cleanup.inc.php
index d10dbac0..423d6a6b 100644
--- a/modules-available/locations/pages/cleanup.inc.php
+++ b/modules-available/locations/pages/cleanup.inc.php
@@ -3,7 +3,7 @@
class SubPage
{
- public static function doPreprocess($action)
+ public static function doPreprocess($action): bool
{
if ($action === 'resetmachines') {
self::resetMachines();
@@ -16,17 +16,20 @@ class SubPage
return false;
}
- public static function doRender($action)
+ public static function doRender($action): bool
{
$list = self::loadForLocation();
if ($list === false)
return true;
+ $list['canmove'] = array_reduce($list['clients'], function (bool $carry, array $item): bool {
+ return $carry || ($item['canmove'] ?? false);
+ }, false);
Permission::addGlobalTags($list['perms'], NULL, ['subnets.edit', 'location.view']);
Render::addTemplate('mismatch-cleanup', $list);
return true;
}
- public static function doAjax($action)
+ public static function doAjax($action): bool
{
return false;
}
diff --git a/modules-available/locations/pages/details.inc.php b/modules-available/locations/pages/details.inc.php
index 2f444157..279eee44 100644
--- a/modules-available/locations/pages/details.inc.php
+++ b/modules-available/locations/pages/details.inc.php
@@ -3,24 +3,25 @@
class SubPage
{
- public static function doPreprocess($action)
+ public static function doPreprocess($action): bool
{
if ($action === 'updatelocation') {
self::updateLocation();
return true;
- } else if ($action === 'updateOpeningtimes') {
+ }
+ if ($action === 'updateOpeningtimes') {
self::updateOpeningTimes();
return true;
}
return false;
}
- public static function doRender($action)
+ public static function doRender($action): bool
{
return false;
}
- public static function doAjax($action)
+ public static function doAjax($action): bool
{
if ($action === 'showlocation') {
self::ajaxShowLocation();
@@ -35,25 +36,29 @@ class SubPage
private static function updateOpeningTimes()
{
+ $otInherited = Request::post('openingtimes-inherited', false, 'bool');
$openingTimes = Request::post('openingtimes', Request::REQUIRED, 'string');
$locationid = Request::post('locationid', Request::REQUIRED, 'int');
$wol = Request::post('wol', false, 'bool');
- $woloffset = Request::post('wol-offset', 0, 'int');
+ $wolOffset = Request::post('wol-offset', 0, 'int');
$sd = Request::post('sd', false, 'bool');
- $sdoffset = Request::post('sd-offset', 0, 'int');
+ $sdOffset = Request::post('sd-offset', 0, 'int');
+ $raMode = Request::post('ra-mode', 'ALWAYS', 'string');
- User::assertPermission('location.edit.*', $locationid); // TODO: Introduce permission
+ User::assertPermission('location.edit.openingtimes', $locationid);
// Construct opening-times for database
- if ($openingTimes !== '') {
+ if ($otInherited || $openingTimes === '') {
+ $openingTimes = null;
+ } else {
$openingTimes = json_decode($openingTimes, true);
if (!is_array($openingTimes)) {
- $openingTimes = '';
+ $openingTimes = null;
} else {
$mangled = array();
foreach (array_keys($openingTimes) as $key) {
$entry = $openingTimes[$key];
- if (!isset($entry['days']) || !is_array($entry['days']) || empty($entry['days'])) {
+ if (empty($entry['days']) || !is_array($entry['days'])) {
Message::addError('ignored-line-no-days');
continue;
}
@@ -89,34 +94,27 @@ class SubPage
array('locationid' => $locationid, 'openingtime' => $openingTimes));
if (Module::isAvailable('rebootcontrol')) {
- if ($wol) {
- $options = array();
- // Sanity checks
- if ($woloffset > 15) $woloffset = 15;
- else if ($woloffset < 0) $woloffset = 0;
- $options['wol-offset'] = $woloffset;
- Scheduler::updateSchedule($locationid, 'wol', $options, $openingTimes);
- } else {
- Scheduler::deleteSchedule($locationid, 'wol');
- }
- if ($sd) {
- $options = array();
- // Sanity checks
- if ($sdoffset > 15) $sdoffset = 15;
- else if ($sdoffset < 0) $sdoffset = 0;
- $options['sd-offset'] = $sdoffset;
- Scheduler::updateSchedule($locationid, 'sd', $options, $openingTimes);
- } else {
- Scheduler::deleteSchedule($locationid, 'sd');
+ // Set options
+ if (!Scheduler::isValidRaMode($raMode)) {
+ $raMode = Scheduler::RA_ALWAYS;
}
+ Scheduler::setLocationOptions($locationid, [
+ 'wol' => $wol,
+ 'sd' => $sd,
+ 'wol-offset' => $wolOffset,
+ 'sd-offset' => $sdOffset,
+ 'ra-mode' => $raMode,
+ ]);
}
}
private static function getTime($str)
{
$str = explode(':', $str);
- if (count($str) !== 2) return false;
- if ($str[0] < 0 || $str[0] > 23 || $str[1] < 0 || $str[1] > 59) return false;
+ if (count($str) !== 2)
+ return false;
+ if ($str[0] < 0 || $str[0] > 23 || $str[1] < 0 || $str[1] > 59)
+ return false;
return $str[0] * 60 + $str[1];
}
@@ -175,7 +173,7 @@ class SubPage
Util::redirect('?do=Locations');
}
- private static function updateLocationData($location)
+ private static function updateLocationData(array $location): bool
{
$locationId = (int)$location['locationid'];
$newParent = Request::post('parentlocationid', false, 'integer');
@@ -219,14 +217,12 @@ class SubPage
return $newParent != $location['parentlocationid'];
}
- private static function updateLocationSubnets()
+ private static function updateLocationSubnets(): bool
{
$locationId = Request::post('locationid', false, 'integer');
if (!User::hasPermission('location.edit.subnets', $locationId))
return false;
- $change = false;
-
// Deletion first
$dels = Request::post('deletesubnet', false);
$deleteCount = 0;
@@ -245,8 +241,9 @@ class SubPage
$starts = Request::post('startaddr', false);
$ends = Request::post('endaddr', false);
if (!is_array($starts) || !is_array($ends)) {
- return $change;
+ return false;
}
+ $change = false;
$editCount = 0;
$stmt = Database::prepare('UPDATE subnet SET startaddr = :start, endaddr = :end'
. ' WHERE subnetid = :id');
@@ -261,7 +258,7 @@ class SubPage
continue;
}
$range = LocationUtil::rangeToLongVerbose($start, $end);
- if ($range === false)
+ if ($range === null)
continue;
list($startLong, $endLong) = $range;
if ($stmt->execute(array('id' => $subnetid, 'start' => $startLong, 'end' => $endLong))) {
@@ -279,18 +276,18 @@ class SubPage
return $change;
}
- private static function addNewLocationSubnets($location)
+ private static function addNewLocationSubnets(array $location): bool
{
$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;
+ return false;
}
+ $change = false;
$count = 0;
$stmt = Database::prepare('INSERT INTO subnet SET startaddr = :start, endaddr = :end, locationid = :location');
foreach ($starts as $key => $start) {
@@ -335,7 +332,7 @@ class SubPage
$res = Database::simpleQuery("SELECT subnetid, startaddr, endaddr FROM subnet WHERE locationid = :lid",
array('lid' => $locationId));
$rows = array();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ foreach ($res as $row) {
$row['startaddr'] = long2ip($row['startaddr']);
$row['endaddr'] = long2ip($row['endaddr']);
$rows[] = $row;
@@ -345,6 +342,7 @@ class SubPage
'locationname' => $loc['locationname'],
'list' => $rows,
'roomplanner' => Module::get('roomplanner') !== false,
+ 'news' => Module::get('news') !== false && User::hasPermission('.news.*', $loc['locationid']),
'parents' => Location::getLocations($loc['parentlocationid'], $locationId, true)
);
@@ -373,7 +371,7 @@ class SubPage
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)) {
+ foreach ($mres as $row) {
$count++;
if ($row['state'] === 'IDLE') {
$online++;
@@ -397,48 +395,61 @@ class SubPage
$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');
+ Permission::addGlobalTags($data['perms'], $locationId,
+ ['location.edit.name', 'location.edit.subnets', 'location.delete', 'location.edit.openingtimes', '.roomplanner.edit'],
+ 'save_button');
if (empty($allowedLocs)) {
$data['perms']['location']['edit']['parent']['disabled'] = 'disabled';
} else {
unset($data['perms']['save_button']);
}
+ if (Module::get('rebootcontrol') !== false) {
+ $res = Database::queryFirst("SELECT action, nextexecution FROM `reboot_scheduler`
+ WHERE locationid = :id", ['id' => $locationId]);
+ if ($res !== false && $res['nextexecution'] > 0) {
+ $data['next_action'] = $res['action'];
+ $data['next_time'] = Util::prettyTime($res['nextexecution']);
+ }
+ }
+
echo Render::parse('location-subnets', $data);
}
- private static function ajaxOpeningTimes($id) {
- User::assertPermission('location.edit', $id);
- $openTimes = Database::queryFirst("SELECT openingtime FROM `location` WHERE locationid = :id", array('id' => $id));
- if ($openTimes !== false) {
+ private static function ajaxOpeningTimes($id)
+ {
+ User::assertPermission('location.edit.openingtimes', $id);
+ $data = ['id' => $id];
+ $openTimes = Database::queryFirst("SELECT openingtime FROM `location`
+ WHERE locationid = :id", array('id' => $id));
+ if ($openTimes === false) {
+ Message::addError('invalid-location-id', $id);
+ return;
+ }
+ if ($openTimes['openingtime'] !== null) {
$openingTimes = json_decode($openTimes['openingtime'], true);
+ } else {
+ $openingTimes = OpeningTimes::forLocation($id);
+ $data['openingtimes_inherited'] = 'checked';
}
if (!isset($openingTimes) || !is_array($openingTimes)) {
$openingTimes = array();
}
- $data = array('id' => $id);
$data['expertMode'] = !self::isSimpleMode($openingTimes);
$data['schedule_data'] = json_encode($openingTimes);
$rebootcontrol = Module::isAvailable('rebootcontrol');
$data['rebootcontrol'] = $rebootcontrol;
if ($rebootcontrol) {
- $wol = Database::queryFirst("SELECT options FROM `reboot_scheduler` WHERE locationid = :id AND action = 'wol'", array('id' => $id));
- if ($wol !== false) {
- $data['wol'] = true;
- $data['wol-options'] = json_decode($wol['options']);
- }
- $sd = Database::queryFirst("SELECT options FROM `reboot_scheduler` WHERE locationid = :id AND action = 'sd'", array('id' => $id));
- if ($sd !== false) {
- $data['sd'] = true;
- $data['sd-options'] = json_decode($sd['options']);
- }
+ $data['scheduler-options'] = Scheduler::getLocationOptions($id);
+ $data['scheduler_' . $data['scheduler-options']['ra-mode'] . '_checked'] = 'checked';
}
echo Render::parse('ajax-opening-location', $data);
}
- private static function isSimpleMode(&$array) {
+ private static function isSimpleMode(&$array): bool
+ {
if (empty($array))
return true;
// Decompose by day
@@ -526,7 +537,7 @@ class SubPage
// $start must lie before range start, otherwise we'd have hit the case above
$e = $current[1];
unset($array[$day][$key]);
- continue;
+ //continue;
}
}
$array[$day][] = array($s, $e);
diff --git a/modules-available/locations/pages/locations.inc.php b/modules-available/locations/pages/locations.inc.php
index 8afb454a..78818328 100644
--- a/modules-available/locations/pages/locations.inc.php
+++ b/modules-available/locations/pages/locations.inc.php
@@ -3,7 +3,7 @@
class SubPage
{
- public static function doPreprocess($action)
+ public static function doPreprocess($action): bool
{
if ($action === 'addlocations') {
self::addLocations();
@@ -12,7 +12,7 @@ class SubPage
return false;
}
- public static function doRender($getAction)
+ public static function doRender($getAction): bool
{
if ($getAction === false) {
if (User::hasPermission('location.view')) {
@@ -32,7 +32,7 @@ class SubPage
return false;
}
- public static function doAjax($action)
+ public static function doAjax($action): bool
{
return false;
}
@@ -93,126 +93,43 @@ class SubPage
$unassignedIdle = $unassignedLoad = $unassignedOverrides = 0;
$allowedLocationIds = User::getAllowedLocations("location.view");
- foreach (array_keys($locationList) as $lid) {
- if (!User::hasPermission('.baseconfig.view', $lid)) {
- $locationList[$lid]['havebaseconfig'] = false;
- }
- if (!User::hasPermission('.sysconfig.config.view-list', $lid)) {
- $locationList[$lid]['havesysconfig'] = false;
- }
- if (!User::hasPermission('.statistics.view.list', $lid)) {
- $locationList[$lid]['havestatistics'] = false;
- }
- if (!User::hasPermission('.serversetup.ipxe.menu.assign', $lid)) {
- $locationList[$lid]['haveipxe'] = false;
- }
- if (!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 m.locationid, Count(*) AS cnt,
- Sum(If(m.state = 'OCCUPIED', 1, 0)) AS used, Sum(If(m.state = 'IDLE', 1, 0)) AS idle
- FROM machine m 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']);
- $locationList[$locId]['clientIdle'] = round(100 * ($row['used'] + $row['idle']) / $row['cnt']);
- } else {
- $unassigned += $row['cnt'];
- $unassignedLoad += $row['used'];
- $unassignedIdle += $row['idle'];
- }
- }
- $res = Database::simpleQuery("SELECT m.locationid, Count(DISTINCT sm.machineuuid) AS cnt FROM setting_machine sm
- INNER JOIN machine m USING (machineuuid) GROUP BY m.locationid");
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $locId = (int)$row['locationid'];
- if (isset($locationList[$locId])) {
- $locationList[$locId]['machineVarsOverrideCount'] = $row['cnt'];
- } else {
- $unassignedOverrides += $row['cnt'];
- }
- }
- 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'] = $loc['clientIdle'] = $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'];
+ $plugins = [];
+ foreach (Hook::load('locations-column') as $hook) {
+ $c = @include($hook->file);
+ if ($c instanceof AbstractLocationColumn) {
+ $plugins[sprintf('%04d.%s', $c->priority(), $hook->moduleId)] = $c;
+ } elseif (is_array($c)) {
+ foreach ($c as $i => $cc) {
+ if ($cc instanceof AbstractLocationColumn) {
+ $plugins[sprintf('%04d.%d.%s', $cc->priority(), $i, $hook->moduleId)] = $cc;
}
- 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'];
- }
+ ksort($plugins);
+ foreach ($locationList as $lid => &$loc) {
+ $loc['plugins'] = [];
+ foreach ($plugins as $pk => $plugin) {
+ $loc['plugins'][$pk] = [
+ 'url' => $plugin->getEditUrl($lid),
+ 'html' => $plugin->getColumnHtml($lid),
+ ];
+ }
+ if (!in_array($lid, $allowedLocationIds)) {
+ $locationList[$lid]['show-only'] = true;
}
- // 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'];
- }
+ unset($loc);
+ foreach ($plugins as $pk => $plugin) {
+ if ($plugin->propagateColumn()) {
+ self::propagateFields($locationList, $plugin, $pk);
}
- self::propagateFields($locationList, '', 'customMenu', 'customMenuClass');
}
+ foreach ($locationList as &$loc) {
+ $loc['plugins'] = array_values($loc['plugins']);
+ }
+ unset($loc);
$addAllowedLocs = User::getAllowedLocations("location.add");
$addAllowedList = Location::getLocations(0, 0, true);
@@ -224,44 +141,36 @@ class SubPage
unset($loc);
// Output
- $data = array(
+ $data = [
+ 'colspan' => (2 + count($plugins)),
+ 'plugins' => array_values($plugins),
'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)) : ''),
- 'unassignedIdle' => ($unassigned ? (round((($unassignedLoad + $unassignedIdle) / $unassigned) * 100)) : ''),
- 'unassignedOverrides' => $unassignedOverrides,
- '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);
Module::isAvailable('js_ip'); // For CIDR magic
}
- private static function propagateFields(&$locationList, $defaultValue, $name, $class)
+ private static function propagateFields(array &$locationList, AbstractLocationColumn $plugin, string $pluginKey)
{
$depth = array();
foreach ($locationList as &$loc) {
$d = $loc['depth'];
- if (!isset($loc[$name])) {
+ if (empty($loc['plugins'][$pluginKey]['html'])) {
// Has no explicit config assignment
- if ($d === 0) {
- $loc[$name] = $defaultValue;
- } else {
- $loc[$name] = $depth[$d - 1];
- }
- $loc[$class] = 'gray';
- }
- $depth[$d] = $loc[$name];
+ $loc['plugins'][$pluginKey]['html'] = $depth[$d - 1] ?? $plugin->propagateDefaultHtml();
+ $loc['plugins'][$pluginKey]['class'] = 'gray';
+ } elseif (empty($loc['plugins'][$pluginKey]['class'])) {
+ $loc['plugins'][$pluginKey]['class'] = 'slx-bold';
+ $loc['plugins'][$pluginKey]['html'] =
+ $plugin->propagationOverride($depth[$d - 1] ?? $plugin->propagateDefaultHtml(),
+ $loc['plugins'][$pluginKey]['html']);
+ }
+ $depth[$d] = $loc['plugins'][$pluginKey]['html'];
unset($depth[$d + 1]);
}
}
diff --git a/modules-available/locations/pages/subnets.inc.php b/modules-available/locations/pages/subnets.inc.php
index fb1e1e80..7628486b 100644
--- a/modules-available/locations/pages/subnets.inc.php
+++ b/modules-available/locations/pages/subnets.inc.php
@@ -3,7 +3,7 @@
class SubPage
{
- public static function doPreprocess($action)
+ public static function doPreprocess(string $action): bool
{
if ($action === 'updatesubnets') {
self::updateSubnets();
@@ -42,7 +42,7 @@ class SubPage
continue;
}
$range = LocationUtil::rangeToLongVerbose($start, $end);
- if ($range === false)
+ if ($range === null)
continue;
list($startLong, $endLong) = $range;
if ($stmt->execute(compact('startLong', 'endLong', 'loc', 'subnetid'))) {
@@ -59,7 +59,7 @@ class SubPage
Util::redirect('?do=Locations');
}
- public static function doRender($getAction)
+ public static function doRender($getAction): bool
{
if ($getAction === false) {
User::assertPermission('subnets.edit', NULL, '?do=locations');
@@ -67,7 +67,7 @@ class SubPage
FROM subnet
ORDER BY startaddr ASC, endaddr DESC");
$rows = array();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ foreach ($res as $row) {
$row['startaddr'] = long2ip($row['startaddr']);
$row['endaddr'] = long2ip($row['endaddr']);
$row['locations'] = Location::getLocations($row['locationid']);
@@ -81,7 +81,7 @@ class SubPage
return false;
}
- public static function doAjax($action)
+ public static function doAjax($action): bool
{
return false;
}
diff --git a/modules-available/locations/permissions/permissions.json b/modules-available/locations/permissions/permissions.json
index 18b24a73..108bf8e0 100644
--- a/modules-available/locations/permissions/permissions.json
+++ b/modules-available/locations/permissions/permissions.json
@@ -14,6 +14,9 @@
"location.edit.parent": {
"location-aware": true
},
+ "location.edit.openingtimes": {
+ "location-aware": true
+ },
"location.view": {
"location-aware": true
},
diff --git a/modules-available/locations/style.css b/modules-available/locations/style.css
index 19950a38..0de0a801 100644
--- a/modules-available/locations/style.css
+++ b/modules-available/locations/style.css
@@ -1,4 +1,4 @@
-table.locations tbody td:nth-of-type(even) {
+table.locations > tbody > tr > td:nth-of-type(even) {
background-color: rgba(0, 0, 0, 0.025);
}
@@ -16,4 +16,12 @@ table.locations tbody td:nth-of-type(even) {
.load-col {
text-align: right;
text-shadow: 1px 1px #fff;
-} \ No newline at end of file
+ margin:0 -5px;
+ min-width: 80px;
+}
+
+.edit-btn {
+ background: inherit;
+ padding:0 2px;
+ text-align: right;
+}
diff --git a/modules-available/locations/templates/ajax-opening-location.html b/modules-available/locations/templates/ajax-opening-location.html
index a2bb357d..861bef65 100644
--- a/modules-available/locations/templates/ajax-opening-location.html
+++ b/modules-available/locations/templates/ajax-opening-location.html
@@ -1,5 +1,10 @@
<div>
- <h3>{{lang_openingTime}}</h3>
+ <h4>{{lang_openingTime}}</h4>
+ <div class="checkbox">
+ <input id="oi{{id}}" class="openingtimes-inherited"
+ type="checkbox" name="openingtimes-inherited" value="1" {{openingtimes_inherited}}>
+ <label for="oi{{id}}">{{lang_inheritOpeningTimes}}</label>
+ </div>
{{^expertMode}}
<div class="simple-mode">
@@ -124,18 +129,18 @@
</div>
{{#rebootcontrol}}
-<hr>
+<h4>{{lang_automatedMachineActions}}</h4>
<div class="row wol">
<div class="col-sm-4">
<div class="checkbox checkbox-inline">
- <input id="wol-check-{{id}}" name="wol" type="checkbox" {{#wol}}checked{{/wol}}>
+ <input id="wol-check-{{id}}" name="wol" type="checkbox" {{#scheduler-options.wol}}checked{{/scheduler-options.wol}}>
<label for="wol-check-{{id}}">{{lang_wakeonlan}}</label>
</div>
</div>
<div class="col-sm-8">
<div class="input-group">
<input disabled type="number" id="wol-offset-{{id}}" name="wol-offset" class="form-control"
- value="{{wol-options.wol-offset}}" placeholder="0" min="0" max="15">
+ value="{{scheduler-options.wol-offset}}" placeholder="0" min="0" max="60">
<span class="input-group-addon slx-ga2">
<label for="wol-offset-{{id}}">{{lang_offsetEarly}}</label>
</span>
@@ -145,23 +150,46 @@
<div class="row shutdown">
<div class="col-sm-4">
<div class="checkbox checkbox-inline">
- <input id="sd-check-{{id}}" name="sd" type="checkbox" {{#sd}}checked{{/sd}}>
+ <input id="sd-check-{{id}}" name="sd" type="checkbox" {{#scheduler-options.sd}}checked{{/scheduler-options.sd}}>
<label for="sd-check-{{id}}">{{lang_shutdown}}</label>
</div>
</div>
<div class="col-sm-8">
<div class="input-group">
<input disabled type="number" id="sd-offset-{{id}}" name="sd-offset" class="form-control"
- value="{{sd-options.sd-offset}}" placeholder="0" min="0" max="15">
+ value="{{scheduler-options.sd-offset}}" placeholder="0" min="0" max="60">
<span class="input-group-addon slx-ga2">
<label for="sd-offset-{{id}}">{{lang_offsetLate}}</label>
</span>
</div>
</div>
</div>
+<h4>{{lang_remoteAccessConstraints}}</h4>
+<div class="slx-smallspace">
+ <div class="radio">
+ <input id="ra-ALWAYS-check-{{id}}" name="ra-mode" value="ALWAYS" type="radio"
+ {{scheduler_ALWAYS_checked}}>
+ <label for="ra-ALWAYS-check-{{id}}">{{lang_remoteAccessNoRestriction}}</label>
+ </div>
+</div>
+<div class="slx-smallspace">
+ <div class="radio">
+ <input id="ra-SELECTIVE-check-{{id}}" name="ra-mode" value="SELECTIVE" type="radio"
+ {{scheduler_SELECTIVE_checked}}>
+ <label for="ra-SELECTIVE-check-{{id}}">{{lang_remoteAccessOnlyWhenClosed}}</label>
+ </div>
+</div>
+<div class="slx-smallspace">
+ <div class="radio">
+ <input id="ra-NEVER-check-{{id}}" name="ra-mode" value="NEVER" type="radio"
+ {{scheduler_NEVER_checked}}>
+ <label for="ra-NEVER-check-{{id}}">{{lang_remoteAccessNever}}</label>
+ </div>
+</div>
+<p><i>{{lang_remoteAccessHelp}}</i></p>
{{/rebootcontrol}}
-<script type="application/javascript">
+<script>
(function() {
var $loc = $('#openingTimesModal{{id}}');
@@ -204,6 +232,7 @@
$loc.find('.new-openingtime').click(function (e) {
e.preventDefault();
setTimepicker(newOpeningTime($loc, {}).find('.timepicker2'));
+ setInputEnabled();
});
$loc.find('.btn-show-expert').click(function (e) {
@@ -214,9 +243,21 @@
}
$loc.find('.simple-mode').remove();
$loc.find('.expert-mode').show();
+ setInputEnabled();
});
$loc.find('form').submit(validateOpeningTimes);
+ var setInputEnabled = function () {
+ $loc.find('.expert-mode input, .simple-mode input').prop('disabled', $inheritCb.is(':checked') ? 'disabled' : false);
+ };
+ var $inheritCb = $loc.find('.openingtimes-inherited');
+ setInputEnabled();
+ $inheritCb.change(setInputEnabled);
+ $loc.find('.new-openingtime').click(function (e) {
+ if ($inheritCb.is(':checked')) {
+ $inheritCb.click();
+ }
+ });
})();
</script>
diff --git a/modules-available/locations/templates/location-subnets.html b/modules-available/locations/templates/location-subnets.html
index e954bf10..976c3cc7 100644
--- a/modules-available/locations/templates/location-subnets.html
+++ b/modules-available/locations/templates/location-subnets.html
@@ -62,7 +62,7 @@
<div class="slx-bold">{{lang_locationInfo}}</div>
<div class="row">
- <div class="col-md-3">
+ <div class="col-md-5">
{{#haveDozmod}}
<div>
<span class="slx-ga2">{{lang_referencingLectures}}:</span> {{lectures}}
@@ -80,14 +80,23 @@
{{/statsLink}}
</div>
{{/haveStatistics}}
+ {{#next_action}}
+ <div>
+ {{lang_nextEvent}}: {{next_action}} – {{next_time}}
+ </div>
+ {{/next_action}}
</div>
- <div class="col-md-3 text-center">
- <button type="button" class="btn btn-default" data-toggle="modal" data-target="#openingTimesModal{{locationid}}" onclick="loadOpeningTimes('{{locationid}}')">
+ <div class="col-md-7 text-right">
+ <button {{perms.location.edit.openingtimes.disabled}} type="button" class="btn btn-default" data-toggle="modal" data-target="#openingTimesModal{{locationid}}" onclick="loadOpeningTimes('{{locationid}}')">
<span class="glyphicon glyphicon-time"></span>
{{lang_openingTime}}
</button>
- </div>
- <div class="col-md-3 text-center">
+ {{#news}}
+ <a class="btn btn-default" href="?do=news&amp;locationid={{locationid}}">
+ <span class="glyphicon glyphicon-pencil"></span>
+ {{lang_editNews}}
+ </a>
+ {{/news}}
{{#roomplanner}}
<a class="btn btn-default" href="?do=roomplanner&amp;locationid={{locationid}}" target="_blank"
onclick="window.open(this.href, '_blank', 'toolbar=0,scrollbars,resizable');return false">
@@ -96,8 +105,6 @@
{{#perms.roomplanner.edit.disabled}}{{lang_showRoomplan}}{{/perms.roomplanner.edit.disabled}}
</a>
{{/roomplanner}}
- </div>
- <div class="col-md-3 text-right">
<button {{perms.location.delete.disabled}} type="button" class="btn btn-danger" data-toggle="modal" data-target="#deleteLocationModal{{locationid}}"><span class="glyphicon glyphicon-trash"></span> {{lang_deleteLocation}}</button>
<button onclick="deleteSubnetWarning('{{locationid}}')" {{perms.save_button.disabled}} type="button" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-disk"></span> {{lang_save}}</button>
</div>
@@ -149,7 +156,7 @@
<input type="hidden" name="openingtimes" value="">
<input type="hidden" name="locationid" value="{{locationid}}">
- <div class="modal-header">{{locationname}}</div>
+ <div class="modal-header"><h3>{{locationname}}</h3></div>
<div class="modal-body"></div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{lang_close}}</button>
diff --git a/modules-available/locations/templates/locations.html b/modules-available/locations/templates/locations.html
index 125c101a..efd48216 100644
--- a/modules-available/locations/templates/locations.html
+++ b/modules-available/locations/templates/locations.html
@@ -31,21 +31,11 @@
<table class="table table-condensed table-hover locations" style="margin-bottom:0">
<tr>
<th width="100%">{{lang_locationName}}</th>
- <th>
- {{#havestatistics}}{{lang_machineCount}}{{/havestatistics}}
- </th>
- <th>
- {{#havestatistics}}{{lang_machineLoad}}{{/havestatistics}}
- </th>
- <th class="text-nowrap">
- {{#havebaseconfig}}{{lang_editConfigVariables}}{{/havebaseconfig}}
- </th>
+ {{#plugins}}
<th class="text-nowrap">
- {{#havesysconfig}}{{lang_sysConfig}}{{/havesysconfig}}
- </th>
- <th class="text-nowrap">
- {{#haveipxe}}{{lang_bootMenu}}{{/haveipxe}}
+ {{header}}
</th>
+ {{/plugins}}
</tr>
{{#list}}
<tr>
@@ -61,85 +51,30 @@
</a>
{{/show-only}}
</td>
- <td class="text-nowrap" align="right">
- {{#havestatistics}}
- <a href="?do=Statistics&amp;show=list&amp;filters=location={{locationid}}">&nbsp;{{clientCount}}&nbsp;</a>
- <span style="display:inline-block;width:5ex">
- {{#hasChild}}
- (<a href="?do=Statistics&amp;show=list&amp;filters=location~{{locationid}}">&downarrow;{{clientCountSum}}</a>)
- {{/hasChild}}
- </span>
- {{/havestatistics}}
- </td>
- <td class="text-nowrap load-col" {{#clientCount}} style="background:linear-gradient(to right, #f97, #f97 {{clientLoad}}%, #6fa {{clientLoad}}%, #6fa {{clientIdle}}%, #eee {{clientIdle}}%)"{{/clientCount}}>
- {{#clientCount}}
- {{clientLoad}}&thinsp;%
- {{/clientCount}}
- </td>
- <td class="text-nowrap {{overriddenClass}}">
- {{#havebaseconfig}}
- <div class="pull-right" style="z-index:-1">
- <a class="btn btn-default btn-xs" href="?do=baseconfig&amp;module=locations&amp;locationid={{locationid}}"><span class="glyphicon glyphicon-edit"></span></a>
- </div>
- {{#overriddenVars}}
- <span class="badge" title="{{lang_overridenVarsForLocation}}">
- <span class="glyphicon glyphicon-home"></span> {{.}}
- </span>
- {{/overriddenVars}}
- {{#machineVarsOverrideCount}}
- <span class="badge" title="{{lang_numMachinesWithOverrides}}">
- <span class="glyphicon glyphicon-tasks"></span> {{.}}
- </span>
- {{/machineVarsOverrideCount}}
- &emsp;&emsp;
- {{/havebaseconfig}}
- </td>
- <td class="text-nowrap">
- {{#havesysconfig}}
- <div class="pull-right">
- <a class="btn btn-default btn-xs" href="?do=sysconfig&amp;locationid={{locationid}}"><span class="glyphicon glyphicon-edit"></span></a>
- </div>
- <span class="{{configClass}}">
- {{configName}}&emsp;&emsp;
- </span>
- {{/havesysconfig}}
- </td>
- <td class="text-nowrap">
- {{#haveipxe}}
- <div class="pull-right">
- <a class="btn btn-default btn-xs" href="?do=serversetup&amp;show=assignlocation&amp;locationid={{locationid}}"><span class="glyphicon glyphicon-edit"></span></a>
- </div>
- <span class="{{customMenuClass}}">
- {{customMenu}}&emsp;&emsp;
- </span>
- {{/haveipxe}}
+ {{#plugins}}
+ <td>
+ <table width="100%"><tr>
+ <td class="text-nowrap {{class}}">{{{html}}}</td>
+ {{#url}}
+ <td class="edit-btn">
+ <a class="btn btn-default btn-xs" href="{{.}}">
+ <span class="glyphicon glyphicon-edit"></span>
+ </a>
+ </td>
+ {{/url}}
+ </tr></table>
</td>
+ {{/plugins}}
</tr>
{{/list}}
- {{#unassignedCount}}
<tr>
<td>{{lang_unassignedMachines}}</td>
- <td class="text-nowrap" align="right">
- <a href="?do=Statistics&amp;show=list&amp;filters=location=0">
- &nbsp;{{unassignedCount}}&nbsp;
- </a>
- <span style="display:inline-block;width:5ex"></span>
- </td>
- <td class="text-nowrap load-col"{{#unassignedCount}} style="background:linear-gradient(to right, #f97, #f97 {{unassignedLoad}}%, #6fa {{unassignedLoad}}%, #6fa {{unassignedIdle}}%, #eee {{unassignedIdle}}%)"{{/unassignedCount}}>
- {{#unassignedCount}}
- {{unassignedLoad}}&thinsp;%
- {{/unassignedCount}}
- </td>
- <td>
- {{#unassignedOverrides}}
- <span class="badge" title="{{lang_numMachinesWithOverrides}}">
- <span class="glyphicon glyphicon-tasks"></span> {{.}}
- </span>
- {{/unassignedOverrides}}
+ {{#plugins}}
+ <td class="text-nowrap">
+ {{{propagateDefaultHtml}}}
</td>
- <td>{{defaultConfig}}</td>
+ {{/plugins}}
</tr>
- {{/unassignedCount}}
</table>
<form method="post" action="?do=Locations">
<input type="hidden" name="token" value="{{token}}">
@@ -214,7 +149,7 @@ function slxOpenLocation(e, lid) {
}
return;
}
- var td = $('<td>').attr('colspan', '6').css('padding', '0px 0px 12px');
+ var td = $('<td>').attr('colspan', '{{colspan}}').css('padding', '0px 0px 12px');
var tr = $('<tr>').attr('id', 'location-details-' + lid);
tr.append(td);
$(e).closest('tr').addClass('active slx-bold').after(tr);
diff --git a/modules-available/locations/templates/mismatch-cleanup.html b/modules-available/locations/templates/mismatch-cleanup.html
index a6f45d27..53a956df 100644
--- a/modules-available/locations/templates/mismatch-cleanup.html
+++ b/modules-available/locations/templates/mismatch-cleanup.html
@@ -32,7 +32,7 @@
<tr>
<td>
<div class="checkbox checkbox-inline">
- <input id="machine-{{machineuuid}}" type="checkbox" name="machines[]" value="{{machineuuid}}" {{disabled}}>
+ <input class="mcb {{#canmove}}cmov{{/canmove}}" id="machine-{{machineuuid}}" type="checkbox" name="machines[]" value="{{machineuuid}}" {{disabled}}>
<label for="machine-{{machineuuid}}">{{hostname}}{{^hostname}}{{clientip}}{{/hostname}}</label>
</div>
</td>
@@ -60,10 +60,28 @@
<span class="glyphicon glyphicon-repeat"></span>
{{lang_resetMachines}}
</button>
- <button type="submit" class="btn btn-success" name="action" value="movemachines">
+ {{#canmove}}
+ <button id="btn-move" type="submit" class="btn btn-success" name="action" value="movemachines">
<span class="glyphicon glyphicon-arrow-right"></span>
{{lang_moveMachines}}
</button>
+ {{/canmove}}
</div>
</form>
-<div class="clearfix"></div> \ No newline at end of file
+<div class="clearfix"></div>
+
+<script>
+ document.addEventListener('DOMContentLoaded', function () {
+ var $btn = $('#btn-move');
+ if ($btn.length === 0)
+ return;
+ var ccheck = function() {
+ var $e = $('input.mcb:checked');
+ var nope = $e.is(':not(.cmov)');
+ var yep = $e.is('.cmov');
+ $btn.prop('disabled', nope || !yep);
+ };
+ ccheck();
+ $('input.mcb').change(ccheck);
+ });
+</script> \ No newline at end of file