summaryrefslogtreecommitdiffstats
path: root/modules-available/locations
diff options
context:
space:
mode:
authorSimon Rettberg2016-05-03 19:03:09 +0200
committerSimon Rettberg2016-05-03 19:03:09 +0200
commit50404f3b23b7fd6aeae4c9d2f6df0ea25e984e66 (patch)
tree05e99fdffa696434960d7c77966c0bc36d6339e8 /modules-available/locations
parentSecond half of merge.... (diff)
downloadslx-admin-50404f3b23b7fd6aeae4c9d2f6df0ea25e984e66.tar.gz
slx-admin-50404f3b23b7fd6aeae4c9d2f6df0ea25e984e66.tar.xz
slx-admin-50404f3b23b7fd6aeae4c9d2f6df0ea25e984e66.zip
WIP
Diffstat (limited to 'modules-available/locations')
-rw-r--r--modules-available/locations/config.json4
-rw-r--r--modules-available/locations/lang/de/templates/location-subnets.json18
-rw-r--r--modules-available/locations/lang/de/templates/locations.json10
-rw-r--r--modules-available/locations/lang/de/templates/subnets.json7
-rw-r--r--modules-available/locations/lang/en/module.json3
-rw-r--r--modules-available/locations/lang/en/templates/location-subnets.json18
-rw-r--r--modules-available/locations/lang/en/templates/locations.json10
-rw-r--r--modules-available/locations/lang/en/templates/subnets.json7
-rw-r--r--modules-available/locations/page.inc.php348
-rw-r--r--modules-available/locations/templates/location-subnets.html73
-rw-r--r--modules-available/locations/templates/locations.html96
-rw-r--r--modules-available/locations/templates/subnets.html35
12 files changed, 629 insertions, 0 deletions
diff --git a/modules-available/locations/config.json b/modules-available/locations/config.json
new file mode 100644
index 00000000..6c189b63
--- /dev/null
+++ b/modules-available/locations/config.json
@@ -0,0 +1,4 @@
+{
+ "category":"main.content",
+ "enabled":"true"
+}
diff --git a/modules-available/locations/lang/de/templates/location-subnets.json b/modules-available/locations/lang/de/templates/location-subnets.json
new file mode 100644
index 00000000..6caa1991
--- /dev/null
+++ b/modules-available/locations/lang/de/templates/location-subnets.json
@@ -0,0 +1,18 @@
+{
+ "lang_addNewSubnet": "Neues Subnetz hinzuf\u00fcgen",
+ "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_deleteChildLocations": "Untergeordnete Orte ebenfalls l\u00f6schen",
+ "lang_deleteLocation": "Ort l\u00f6schen",
+ "lang_deleteSubnet": "Bereich l\u00f6schen",
+ "lang_endAddress": "Endadresse",
+ "lang_locationInfo": "Details zu diesem Ort",
+ "lang_locationSettings": "Raum\/Ort bearbeiten",
+ "lang_matchingMachines": "Enthaltene Rechner",
+ "lang_name": "Name",
+ "lang_parentLocation": "\u00dcbergeordneter Ort",
+ "lang_referencingLectures": "Veranstaltungen",
+ "lang_save": "Speichern",
+ "lang_startAddress": "Startadresse",
+ "lang_subnet": "IP-Bereich"
+} \ No newline at end of file
diff --git a/modules-available/locations/lang/de/templates/locations.json b/modules-available/locations/lang/de/templates/locations.json
new file mode 100644
index 00000000..3e25ef45
--- /dev/null
+++ b/modules-available/locations/lang/de/templates/locations.json
@@ -0,0 +1,10 @@
+{
+ "lang_areYouSureNoUndo": "Sind Sie sicher? Diese Aktion kann nicht r\u00fcckg\u00e4ngig gemacht werden.",
+ "lang_edit": "Bearbeiten",
+ "lang_location": "Ort",
+ "lang_locationName": "Name",
+ "lang_locationsMainHeading": "Verwaltung von R\u00e4umen\/Orten",
+ "lang_noParent": "Kein \u00fcbergeordneter Ort",
+ "lang_save": "Speichern",
+ "lang_thisListBySubnet": "Nach Subnetzen auflisten"
+} \ No newline at end of file
diff --git a/modules-available/locations/lang/de/templates/subnets.json b/modules-available/locations/lang/de/templates/subnets.json
new file mode 100644
index 00000000..b57f87ce
--- /dev/null
+++ b/modules-available/locations/lang/de/templates/subnets.json
@@ -0,0 +1,7 @@
+{
+ "lang_endAddress": "Ende",
+ "lang_listOfSubnets": "Liste der Subnetze",
+ "lang_location": "Ort",
+ "lang_startAddress": "Start",
+ "lang_thisListByLocation": "Zur Ortsansicht"
+} \ No newline at end of file
diff --git a/modules-available/locations/lang/en/module.json b/modules-available/locations/lang/en/module.json
new file mode 100644
index 00000000..b2a837b6
--- /dev/null
+++ b/modules-available/locations/lang/en/module.json
@@ -0,0 +1,3 @@
+{
+ "module_name": "Room\/Locations"
+} \ No newline at end of file
diff --git a/modules-available/locations/lang/en/templates/location-subnets.json b/modules-available/locations/lang/en/templates/location-subnets.json
new file mode 100644
index 00000000..2ba94384
--- /dev/null
+++ b/modules-available/locations/lang/en/templates/location-subnets.json
@@ -0,0 +1,18 @@
+{
+ "lang_addNewSubnet": "Add new subnet",
+ "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_deleteChildLocations": "Delete child locations aswell",
+ "lang_deleteLocation": "Delete location",
+ "lang_deleteSubnet": "Delete range",
+ "lang_endAddress": "End address",
+ "lang_locationInfo": "Location details",
+ "lang_locationSettings": "Edit this room or location",
+ "lang_matchingMachines": "Matching clients",
+ "lang_name": "Name",
+ "lang_parentLocation": "Parent location",
+ "lang_referencingLectures": "Assigned Lectures",
+ "lang_save": "Save",
+ "lang_startAddress": "Start address",
+ "lang_subnet": "IP range"
+} \ No newline at end of file
diff --git a/modules-available/locations/lang/en/templates/locations.json b/modules-available/locations/lang/en/templates/locations.json
new file mode 100644
index 00000000..db4fd0a7
--- /dev/null
+++ b/modules-available/locations/lang/en/templates/locations.json
@@ -0,0 +1,10 @@
+{
+ "lang_areYouSureNoUndo": "Are you sure? This cannot be undone!",
+ "lang_edit": "Edit",
+ "lang_location": "Ort",
+ "lang_locationName": "Name",
+ "lang_locationsMainHeading": "Manage rooms and locations",
+ "lang_noParent": "No parent",
+ "lang_save": "Save",
+ "lang_thisListBySubnet": "List by subnet"
+} \ No newline at end of file
diff --git a/modules-available/locations/lang/en/templates/subnets.json b/modules-available/locations/lang/en/templates/subnets.json
new file mode 100644
index 00000000..65da254b
--- /dev/null
+++ b/modules-available/locations/lang/en/templates/subnets.json
@@ -0,0 +1,7 @@
+{
+ "lang_endAddress": "End",
+ "lang_listOfSubnets": "List of subnets",
+ "lang_location": "Location",
+ "lang_startAddress": "Start",
+ "lang_thisListByLocation": "List by location"
+} \ No newline at end of file
diff --git a/modules-available/locations/page.inc.php b/modules-available/locations/page.inc.php
new file mode 100644
index 00000000..60af719b
--- /dev/null
+++ b/modules-available/locations/page.inc.php
@@ -0,0 +1,348 @@
+<?php
+
+class Page_Locations extends Page
+{
+
+ private $action;
+
+ /*
+ * Action handling
+ */
+
+ protected function doPreprocess()
+ {
+ User::load();
+ if (!User::hasPermission('superadmin')) {
+ Message::addError('no-permission');
+ Util::redirect('?do=Main');
+ }
+ $this->action = Request::post('action');
+ if ($this->action === 'updatelocation') {
+ $this->updateLocation();
+ } elseif ($this->action === 'addlocations') {
+ $this->addLocations();
+ }
+ }
+
+ private function addLocations()
+ {
+ $names = Request::post('newlocation', false);
+ $parents = Request::post('newparent', false);
+ if (!is_array($names) || !is_array($parents)) {
+ Message::addError('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 ($parent !== 0) {
+ $ok = false;
+ foreach ($locs as $loc) {
+ if ($loc['locationid'] == $parent) {
+ $ok = true;
+ }
+ }
+ if (!$ok) {
+ Message::addWarning('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');
+ }
+
+ private 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('value-invalid', 'locationid', $locationId);
+ Util::redirect('?do=Locations');
+ }
+ // Delete location?
+ if ($locationId === $del) {
+ $this->deleteLocation($location);
+ }
+ // Update subnets
+ $this->updateLocationSubnets($location);
+ // Insert subnets
+ $this->addNewLocationSubnets($location); // TODO
+ // Update location!
+ $this->updateLocationData($location);
+ Util::redirect('?do=Locations');
+ }
+
+ private function deleteLocation($location)
+ {
+ $locationId = (int)$location['locationid'];
+ $ids = $locationId;
+ if (Request::post('recursive', false) === 'on') {
+ $rows = Location::queryLocations();
+ $rows = Location::buildTree($rows, $locationId);
+ $rows = Location::extractIds($rows);
+ if (!empty($rows)) {
+ $ids .= ',' . implode(',', $rows);
+ }
+ }
+ $subs = Database::exec("DELETE FROM subnet WHERE locationid IN ($ids)");
+ $locs = Database::exec("DELETE FROM location WHERE locationid IN ($ids)");
+ Database::exec('UPDATE location SET parentlocationid = :newparent WHERE parentlocationid = :oldparent', array(
+ 'newparent' => $location['parentlocationid'],
+ 'oldparent' => $location['locationid']
+ ));
+ Message::addSuccess('location-deleted', $locs, $subs);
+ Util::redirect('?do=Locations');
+ }
+
+ private function updateLocationData($location)
+ {
+ $locationId = (int)$location['locationid'];
+ $newParent = Request::post('parentlocationid', false, 'integer');
+ $newName = Request::post('locationname', false, 'string');
+ if ($newName === false || preg_match('/^\s*$/', $newName)) {
+ if ($newName !== false) {
+ Message::addWarning('value-invalid', 'location name', $newName);
+ }
+ $newName = $location['locationname'];
+ }
+ if ($newParent === false) {
+ $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('value-invalid', 'parent', $newParent);
+ $newParent = $location['parentlocationid'];
+ } else {
+ $rows = Location::extractIds(Location::buildTree($rows, $locationId));
+ if (in_array($newParent, $rows)) {
+ Message::addWarning('value-invalid', 'parent', $newParent);
+ $newParent = $location['parentlocationid'];
+ }
+ }
+ }
+ $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);
+ }
+ }
+
+ private function updateLocationSubnets($location)
+ {
+ $locationId = (int)$location['locationid'];
+ // 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);
+ }
+ }
+ // Now actual updates
+ // TODO: Warn on mismatch/overlap (should lie entirely in parent's subnet, not overlap with others)
+ $starts = Request::post('startaddr', false);
+ $ends = Request::post('endaddr', false);
+ if (!is_array($starts) || !is_array($ends)) {
+ return;
+ }
+ $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];
+ list($startLong, $endLong) = $this->rangeToLong($start, $end);
+ if ($startLong === false) {
+ Message::addWarning('value-invalid', 'start addr', $start);
+ }
+ if ($endLong === false) {
+ Message::addWarning('value-invalid', 'end addr', $start);
+ }
+ if ($startLong === false || $endLong === false) continue;
+ if ($startLong > $endLong) {
+ Message::addWarning('value-invalid', 'range', $start . ' - ' . $end);
+ continue;
+ }
+ if ($stmt->execute(array('id' => $key, 'start' => $startLong, 'end' => $endLong))) {
+ $count += $stmt->rowCount();
+ }
+ }
+ if ($count > 0) {
+ Message::addInfo('subnets-updated', $count);
+ }
+ }
+
+ private function addNewLocationSubnets($location)
+ {
+ $locationId = (int)$location['locationid'];
+ $starts = Request::post('newstartaddr', false);
+ $ends = Request::post('newendaddr', false);
+ if (!is_array($starts) || !is_array($ends)) {
+ return;
+ }
+ $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) = $this->rangeToLong($start, $end);
+ if ($startLong === false) {
+ Message::addWarning('value-invalid', 'new start addr', $start);
+ }
+ if ($endLong === false) {
+ Message::addWarning('value-invalid', 'new end addr', $start);
+ }
+ if ($startLong === false || $endLong === false) continue;
+ if ($startLong > $endLong) {
+ Message::addWarning('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);
+ }
+ }
+
+ /*
+ * Rendering normal pages
+ */
+
+ protected function doRender()
+ {
+ //Render::setTitle(Dictionary::translate('lang_titleBackup'));
+ $getAction = Request::get('action');
+ if (empty($getAction)) {
+ // Until we have a main landing page?
+ Util::redirect('?do=Locations&action=showlocations');
+ }
+ if ($getAction === 'showsubnets') {
+ $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;
+ }
+ Render::addTemplate('subnets', array('list' => $rows));
+ } elseif ($getAction === 'showlocations') {
+ $locs = Location::getLocations();
+ Render::addTemplate('locations', array('list' => $locs));
+ }
+ }
+
+ /*
+ * Ajax
+ */
+
+ protected function doAjax()
+ {
+ User::load();
+ if (!User::isLoggedIn()) {
+ die('Unauthorized');
+ }
+ $action = Request::any('action');
+ if ($action === 'showlocation') {
+ $this->ajaxShowLocation();
+ }
+ }
+
+ private function ajaxShowLocation()
+ {
+ $locationId = Request::any('locationid', 0, 'integer');
+ $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,
+ 'parents' => Location::getLocations($loc['parentlocationid'], $locationId, true)
+ );
+ // if (moduleEnabled(DOZMOD) {
+ $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'];
+ // }
+ // Get clients matching this location's subnet(s)
+ $mres = Database::simpleQuery("SELECT lastseen, logintime FROM machine"
+ . " INNER JOIN subnet ON (INET_ATON(machine.clientip) BETWEEN startaddr AND endaddr)"
+ . " WHERE subnet.locationid = :lid OR machine.locationid = :lid", array('lid' => $locationId));
+ $count = $online = $used = 0;
+ $DL = time() - 605;
+ while ($row = $mres->fetch(PDO::FETCH_ASSOC)) {
+ $count++;
+ if ($row['lastseen'] > $DL) {
+ $online++;
+ if ($row['logintime'] != 0) {
+ $used++;
+ }
+ }
+ }
+ $data['machines'] = $count;
+ $data['machines_online'] = $online;
+ $data['machines_used'] = $used;
+ $data['used_percent'] = round(100 * $used / $online);
+ echo Render::parse('location-subnets', $data);
+ }
+
+ /*
+ * Helpers
+ */
+
+ private function rangeToLong($start, $end)
+ {
+ $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/templates/location-subnets.html b/modules-available/locations/templates/location-subnets.html
new file mode 100644
index 00000000..76b7442a
--- /dev/null
+++ b/modules-available/locations/templates/location-subnets.html
@@ -0,0 +1,73 @@
+<div class="slx-well">
+ <div class="slx-bold">{{lang_locationSettings}}</div>
+ <form method="post" action="?do=Locations">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="action" value="updatelocation">
+ <input type="hidden" name="locationid" value="{{locationid}}">
+ <div style="display:none">
+ <button type="submit" class="btn btn-primary">Save</button>
+ </div>
+ <div class="row">
+ <div class="col-sm-6">
+ <div class="input-group">
+ <span class="input-group-addon slx-ga2">{{lang_name}}</span>
+ <input class="form-control" type="text" name="locationname" value="{{locationname}}" pattern=".*\S.*">
+ </div>
+ </div>
+ <div class="col-sm-6">
+ <div class="input-group">
+ <span class="input-group-addon slx-ga2">{{lang_parentLocation}}</span>
+ <select class="form-control" name="parentlocationid">
+ {{#parents}}
+ <option value="{{locationid}}" {{#selected}}selected="selected"{{/selected}}>{{locationpad}} {{locationname}}</option>
+ {{/parents}}
+ </select>
+ </div>
+ </div>
+ </div>
+ <div>
+ <div class="pull-right">
+ <label><input type="checkbox" name="recursive" value="on"> {{lang_deleteChildLocations}}</label>
+ <button type="submit" class="btn btn-sm btn-danger" name="deletelocation" value="{{locationid}}" onclick="return slxConfirm()">{{lang_deleteLocation}}</button>
+ </div>
+ <div class="clearfix"></div>
+ </div>
+ <br>
+ <div class="slx-bold">{{lang_assignedSubnets}}</div>
+ <div><i>{{lang_assignSubnetExplanation}}</i></div>
+ <table class="table table-condensed table-striped">
+ <tr>
+ <th>#</th>
+ <th>{{lang_startAddress}}</th>
+ <th>{{lang_endAddress}}</th>
+ <th title="{{lang_deleteSubnet}}"><span class="glyphicon glyphicon-trash"></span></th>
+ </tr>
+ {{#list}}
+ <tr>
+ <td>{{subnetid}}</td>
+ <td><input class="form-control" type="text" name="startaddr[{{subnetid}}]" value="{{startaddr}}" pattern="\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"></td>
+ <td><input class="form-control" type="text" name="endaddr[{{subnetid}}]" value="{{endaddr}}" pattern="\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"></td>
+ <td class="danger" align="center"><input type="checkbox" name="deletesubnet[{{subnetid}}]" value="on"></td>
+ </tr>
+ {{/list}}
+ <tr id="loc-sub-{{locationid}}">
+ <td colspan="2">
+ <button class="btn btn-success btn-sm" type="button" onclick="slxAddSubnetRow(this, {{locationid}})" title="{{lang_addNewSubnet}}">
+ <span class="glyphicon glyphicon-plus-sign"></span> {{lang_subnet}}
+ </button>
+ </td>
+ <td colspan="2" align="right">
+ <button type="submit" class="btn btn-primary">{{lang_save}}</button>
+ </div>
+ </tr>
+ </table>
+ </form>
+ <br>
+ <div class="slx-bold">{{lang_locationInfo}}</div>
+ <div>
+ <span class="slx-ga2">{{lang_referencingLectures}}:</span> {{lectures}}
+ </div>
+ <div>
+ <span class="slx-ga2">{{lang_matchingMachines}}:</span> <a href="?do=Statistics&amp;filter=location&amp;argument={{locationid}}">{{machines}} / {{machines_online}} / {{machines_used}} ({{used_percent}}%)</a>
+ </div>
+</div> \ No newline at end of file
diff --git a/modules-available/locations/templates/locations.html b/modules-available/locations/templates/locations.html
new file mode 100644
index 00000000..76c8f97c
--- /dev/null
+++ b/modules-available/locations/templates/locations.html
@@ -0,0 +1,96 @@
+<div>
+ <div class="pull-right">
+ <a href="?do=Locations&amp;action=showsubnets">{{lang_thisListBySubnet}}</a>
+ </div>
+ <h1>{{lang_locationsMainHeading}}</h1>
+ <table class="table table-condensed" style="margin-bottom:0px">
+ <tr>
+ <th>#</th>
+ <th width="100%">{{lang_locationName}}</th>
+ <th></th>
+ </tr>
+ {{#list}}
+ <tr>
+ <td>{{locationid}}</td>
+ <td><div style="display:inline-block;width:{{depth}}em"></div>{{locationname}}</td>
+ <td align="right">
+ <a class="btn btn-success btn-xs" onclick="slxOpenLocation(this, {{locationid}})"><span class="glyphicon glyphicon-edit"></span> {{lang_edit}}</a>
+ </td>
+ </tr>
+ {{/list}}
+ </table>
+ <form method="post" action="?do=Locations">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="action" value="addlocations">
+ <table class="table table-condensed table-hover">
+ <tr id="lasttr">
+ <td>
+ <button class="btn btn-success btn-sm" type="button" onclick="slxAddLocationRow()">
+ <span class="glyphicon glyphicon-plus-sign"></span> {{lang_location}}
+ </button>
+ </td>
+ <td width="80%">&emsp;</td>
+ <td width="20%" align="right">
+ <button type="submit" class="btn btn-primary">{{lang_save}}</button>
+ </td>
+ </tr>
+ </table>
+ </form>
+</div>
+<script type="text/javascript"><!--
+var slxAddCounter = 0;
+var slxLastLocation = false;
+
+function slxAddLocationRow() {
+ var tr = $('#lasttr');
+ tr.before('<tr>\
+ <td>#</td>\
+ <td><input class="form-control" type="text" name="newlocation[' + slxAddCounter + ']" placeholder="{{lang_locationName}}" pattern=".*\\S.*"></td>\
+ <td><select class="form-control" name="newparent[' + slxAddCounter + ']">\
+ <option value="0">{{lang_noParent}}</option>\
+ {{#list}}<option value="{{locationid}}">{{locationpad}} {{locationname}}</option>{{/list}}\
+ </select></td>\
+ </tr>');
+ slxAddCounter++;
+}
+
+function slxOpenLocation(e, lid) {
+ if (slxLastLocation !== false) {
+ slxLastLocation.hide();
+ $(slxLastLocation).prev().removeClass('active slx-bold');
+ }
+ var existing = $('#location-details-' + lid);
+ if (existing.length > 0) {
+ if (existing.is(slxLastLocation)) {
+ slxLastLocation = false;
+ } else {
+ existing.show();
+ $(e).closest('tr').addClass('active slx-bold');
+ slxLastLocation = existing;
+ }
+ return;
+ }
+ var td = $('<td>').attr('colspan', '12').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);
+ td.load('?do=Locations&action=showlocation&locationid=' + lid);
+ slxLastLocation = tr;
+}
+
+function slxAddSubnetRow(e, lid) {
+ var tr = $('#loc-sub-' + lid);
+ tr.before('<tr>\
+ <td>#</td>\
+ <td><input class="form-control" type="text" name="newstartaddr[' + slxAddCounter + ']" pattern="\\d{1,3}\.\\d{1,3}\.\\d{1,3}\.\\d{1,3}"></td>\
+ <td><input class="form-control" type="text" name="newendaddr[' + slxAddCounter + ']" pattern="\\d{1,3}\.\\d{1,3}\.\\d{1,3}\.\\d{1,3}"></td>\
+ <td></td>\
+ </tr>');
+ slxAddCounter++;
+}
+
+function slxConfirm() {
+ return confirm('{{lang_areYouSureNoUndo}}');
+}
+ // -->
+</script>
diff --git a/modules-available/locations/templates/subnets.html b/modules-available/locations/templates/subnets.html
new file mode 100644
index 00000000..2294f42b
--- /dev/null
+++ b/modules-available/locations/templates/subnets.html
@@ -0,0 +1,35 @@
+<div>
+ <div class="pull-right">
+ <a href="?do=Locations&amp;action=showlocations">{{lang_thisListByLocation}}</a>
+ </div>
+ <h1>{{lang_listOfSubnets}}</h1>
+ <form method="post" action="?do=Locations">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="action" value="updatesubnets">
+ <table class="table table-condensed table-striped">
+ <tr>
+ <th>#</th>
+ <th>{{lang_startAddress}}</th>
+ <th>{{lang_endAddress}}</th>
+ <th>{{lang_location}}</th>
+ </tr>
+ {{#list}}
+ <tr>
+ <td>{{subnetid}}</td>
+ <td><input class="form-control" type="text" name="startaddr[{{subnetid}}]" value="{{startaddr}}"></td>
+ <td><input class="form-control" type="text" name="endaddr[{{subnetid}}]" value="{{endaddr}}"></td>
+ <td>
+ <select class="form-control" name="location[{{subnetid}}]">
+ {{#locations}}
+ <option value="{{locationid}}" {{#selected}}selected="selected"{{/selected}}>{{locationpad}} {{locationname}}</option>
+ {{/locations}}
+ </select>
+ </td>
+ </tr>
+ {{/list}}
+ </table>
+ <div>
+ <button type="submit" class="btn btn-primary">Späschohn (geht noch nicht!)</button>
+ </div>
+ </form>
+</div>