From 50404f3b23b7fd6aeae4c9d2f6df0ea25e984e66 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 3 May 2016 19:03:09 +0200 Subject: WIP --- modules-available/locations/config.json | 4 + .../lang/de/templates/location-subnets.json | 18 ++ .../locations/lang/de/templates/locations.json | 10 + .../locations/lang/de/templates/subnets.json | 7 + modules-available/locations/lang/en/module.json | 3 + .../lang/en/templates/location-subnets.json | 18 ++ .../locations/lang/en/templates/locations.json | 10 + .../locations/lang/en/templates/subnets.json | 7 + modules-available/locations/page.inc.php | 348 +++++++++++++++++++++ .../locations/templates/location-subnets.html | 73 +++++ .../locations/templates/locations.html | 96 ++++++ modules-available/locations/templates/subnets.html | 35 +++ 12 files changed, 629 insertions(+) create mode 100644 modules-available/locations/config.json create mode 100644 modules-available/locations/lang/de/templates/location-subnets.json create mode 100644 modules-available/locations/lang/de/templates/locations.json create mode 100644 modules-available/locations/lang/de/templates/subnets.json create mode 100644 modules-available/locations/lang/en/module.json create mode 100644 modules-available/locations/lang/en/templates/location-subnets.json create mode 100644 modules-available/locations/lang/en/templates/locations.json create mode 100644 modules-available/locations/lang/en/templates/subnets.json create mode 100644 modules-available/locations/page.inc.php create mode 100644 modules-available/locations/templates/location-subnets.html create mode 100644 modules-available/locations/templates/locations.html create mode 100644 modules-available/locations/templates/subnets.html (limited to 'modules-available/locations') 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 @@ +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 @@ +
+
{{lang_locationSettings}}
+
+ + + +
+ +
+
+
+
+ {{lang_name}} + +
+
+
+
+ {{lang_parentLocation}} + +
+
+
+
+
+ + +
+
+
+
+
{{lang_assignedSubnets}}
+
{{lang_assignSubnetExplanation}}
+ + + + + + + + {{#list}} + + + + + + + {{/list}} + + + +
#{{lang_startAddress}}{{lang_endAddress}}
{{subnetid}}
+ + + + +
+
+
+
{{lang_locationInfo}}
+
+ {{lang_referencingLectures}}: {{lectures}} +
+
+ {{lang_matchingMachines}}: {{machines}} / {{machines_online}} / {{machines_used}} ({{used_percent}}%) +
+
\ 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 @@ +
+
+ {{lang_thisListBySubnet}} +
+

{{lang_locationsMainHeading}}

+ + + + + + + {{#list}} + + + + + + {{/list}} +
#{{lang_locationName}}
{{locationid}}
{{locationname}}
+ {{lang_edit}} +
+
+ + + + + + + + +
+ + + +
+
+
+ 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 @@ +
+
+ {{lang_thisListByLocation}} +
+

{{lang_listOfSubnets}}

+
+ + + + + + + + + + {{#list}} + + + + + + + {{/list}} +
#{{lang_startAddress}}{{lang_endAddress}}{{lang_location}}
{{subnetid}} + +
+
+ +
+
+
-- cgit v1.2.3-55-g7522