summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2016-06-22 17:56:04 +0200
committerSimon Rettberg2016-06-22 17:56:04 +0200
commita6a484ea37aeb91f848c11cb818e2d7d4351d391 (patch)
tree03cc7b9df8dabf61bdb1bc84137601ce187339ef
parent[location] Support passing array of selected locs to getLocations() (diff)
downloadslx-admin-a6a484ea37aeb91f848c11cb818e2d7d4351d391.tar.gz
slx-admin-a6a484ea37aeb91f848c11cb818e2d7d4351d391.tar.xz
slx-admin-a6a484ea37aeb91f848c11cb818e2d7d4351d391.zip
[locations/sysconfig] Implement location specific sysconfig
-rw-r--r--modules-available/locations/inc/location.inc.php8
-rw-r--r--modules-available/locations/page.inc.php54
-rw-r--r--modules-available/locations/templates/locations.html35
-rw-r--r--modules-available/sysconfig/api.inc.php78
-rw-r--r--modules-available/sysconfig/clientscript.js62
-rw-r--r--modules-available/sysconfig/inc/sysconfig.inc.php17
-rw-r--r--modules-available/sysconfig/install.inc.php6
-rw-r--r--modules-available/sysconfig/page.inc.php160
-rw-r--r--modules-available/sysconfig/templates/_page.html227
-rw-r--r--modules-available/sysconfig/templates/list-configs.html115
-rw-r--r--modules-available/sysconfig/templates/list-legend.html44
-rw-r--r--modules-available/sysconfig/templates/list-modules.html59
-rw-r--r--style/default.css4
13 files changed, 589 insertions, 280 deletions
diff --git a/modules-available/locations/inc/location.inc.php b/modules-available/locations/inc/location.inc.php
index 788fc588..b1474279 100644
--- a/modules-available/locations/inc/location.inc.php
+++ b/modules-available/locations/inc/location.inc.php
@@ -64,7 +64,7 @@ class Location
return $output;
}
- public static function getLocations($selected = 0, $excludeId = 0, $addNoParent = false)
+ public static function getLocations($selected = 0, $excludeId = 0, $addNoParent = false, $keepArrayKeys = false)
{
if (is_string($selected)) {
settype($selected, 'int');
@@ -99,6 +99,8 @@ class Location
'selected' => $selected === 0
));
}
+ if ($keepArrayKeys)
+ return $rows;
return array_values($rows);
}
@@ -129,14 +131,14 @@ class Location
}
$output = array();
foreach ($tree as $node) {
- $output[] = array(
+ $output[(int)$node['locationid']] = array(
'locationid' => $node['locationid'],
'locationname' => $node['locationname'],
'locationpad' => str_repeat('--', $depth),
'depth' => $depth
);
if (!empty($node['children'])) {
- $output = array_merge($output, self::flattenTree($node['children'], $depth + 1));
+ $output += self::flattenTree($node['children'], $depth + 1);
}
}
return $output;
diff --git a/modules-available/locations/page.inc.php b/modules-available/locations/page.inc.php
index 84bc11a9..de724350 100644
--- a/modules-available/locations/page.inc.php
+++ b/modules-available/locations/page.inc.php
@@ -166,7 +166,6 @@ class Page_Locations extends Page
}
}
// 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)) {
@@ -263,7 +262,8 @@ class Page_Locations extends Page
{
$overlapSelf = $overlapOther = true;
$subnets = Location::getSubnetsByLocation($overlapSelf, $overlapOther);
- $locs = Location::getLocations();
+ $locs = Location::getLocations(0, 0, false, true);
+ // Statistics: Count machines for each subnet
$unassigned = false;
if (Module::get('statistics') !== false) {
foreach ($locs as &$location) {
@@ -289,15 +289,61 @@ class Page_Locations extends Page
$unassigned = $res['cnt'];
}
unset($loc, $location);
+ // Show currently active sysconfig for each location
+ $defaultConfig = false;
+ if (Module::isAvailable('sysconfig')) {
+ $confs = SysConfig::getAll();
+ foreach ($confs as $conf) {
+ $confLocs = explode(',', $conf['locs']);
+ foreach ($confLocs as $loc) {
+ settype($loc, 'int');
+ if ($loc === 0) {
+ $defaultConfig = $conf['title'];
+ }
+ if (!isset($locs[$loc]))
+ continue;
+ $locs[$loc] += array('configName' => $conf['title'], 'configClass' => 'slx-bold');
+ }
+ }
+ $depth = array();
+ foreach ($locs as &$loc) {
+ $d = $loc['depth'];
+ if (!isset($loc['configName'])) {
+ // Has no explicit config assignment
+ if ($d === 0) {
+ $loc['configName'] = $defaultConfig;
+ } else {
+ $loc['configName'] = $depth[$d - 1];
+ }
+ $loc['configClass'] = 'gray';
+ }
+ $depth[$d] = $loc['configName'];
+ unset($depth[$d + 1]);
+ }
+ unset($loc);
+ }
+ // Count overridden config vars
+ if (Module::get('baseconfig') !== false) {
+ $res = Database::simpleQuery("SELECT locationid, Count(*) AS cnt FROM `setting_location` GROUP BY locationid");
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $lid = (int)$row['locationid'];
+ if (isset($locs[$lid])) {
+ $locs[$lid]['overriddenVars'] = $row['cnt'];
+ }
+ }
+ }
+ // Output
Render::addTemplate('locations', array(
- 'list' => $locs,
+ 'list' => array_values($locs),
'havestatistics' => Module::get('statistics') !== false,
'havebaseconfig' => Module::get('baseconfig') !== false,
+ 'havesysconfig' => Module::get('sysconfig') !== false,
'overlapSelf' => $overlapSelf,
'overlapOther' => $overlapOther,
'haveOverlapSelf' => !empty($overlapSelf),
'haveOverlapOther' => !empty($overlapOther),
- 'unassignedCount' => $unassigned
+ 'unassignedCount' => $unassigned,
+ 'defaultConfig' => $defaultConfig,
));
}
diff --git a/modules-available/locations/templates/locations.html b/modules-available/locations/templates/locations.html
index e8b5d707..11142ef8 100644
--- a/modules-available/locations/templates/locations.html
+++ b/modules-available/locations/templates/locations.html
@@ -6,34 +6,59 @@
<table class="table table-condensed" style="margin-bottom:0px">
<tr>
<th width="100%">{{lang_locationName}}</th>
- <th></th>
+ <th>{{#havestatistics}}{{lang_machineCount}}{{/havestatistics}}</th>
+ <th class="slx-nowrap">{{lang_editConfigVariables}}</th>
+ {{#havesysconfig}}
+ <th class="slx-nowrap">
+ {{#havebaseconfig}}{{lang_sysConfig}}{{/havebaseconfig}}
+ </th>
+ {{/havesysconfig}}
</tr>
{{#list}}
<tr>
<td>
<div style="display:inline-block;width:{{depth}}em"></div>
- <a href="#" onclick="slxOpenLocation(this, {{locationid}})">{{locationname}}<b class="caret"></b></a>
+ <a href="#" onclick="slxOpenLocation(this, {{locationid}})">{{locationname}} <b class="caret"></b></a>
</td>
<td class="slx-nowrap" align="right">
{{#havestatistics}}
{{clientCount}}
<a class="btn btn-default btn-xs" href="?do=Statistics&amp;filter=location&amp;argument={{locationid}}"><span class="glyphicon glyphicon-eye-open"></span></a>
{{/havestatistics}}
+ </td>
+ <td class="slx-nowrap">
{{#havebaseconfig}}
- <a class="btn btn-success btn-xs" href="?do=baseconfig&amp;module=locations&amp;locationid={{locationid}}"><span class="glyphicon glyphicon-edit"></span> {{lang_editConfigVariables}}</a>
+ <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}}
+ {{lang_overrideCount}}: {{overriddenVars}}
+ {{/overriddenVars}}
{{/havebaseconfig}}
</td>
+ <td class="slx-nowrap">
+ <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}}
+ </span>
+ {{#havesysconfig}}
+ {{/havesysconfig}}
+ </td>
</tr>
{{/list}}
{{#unassignedCount}}
<tr>
- <td></td>
+ <td>{{lang_unassignedMachines}}</td>
<td class="slx-nowrap" align="right">
- {{lang_unassignedMachines}}: {{unassignedCount}}
+ {{unassignedCount}}
<a class="btn btn-default btn-xs" href="?do=Statistics&amp;filter=location&amp;argument=0">
<span class="glyphicon glyphicon-eye-open"></span>
</a>
</td>
+ <td></td>
+ <td>{{defaultConfig}}</td>
</tr>
{{/unassignedCount}}
</table>
diff --git a/modules-available/sysconfig/api.inc.php b/modules-available/sysconfig/api.inc.php
new file mode 100644
index 00000000..556e99f3
--- /dev/null
+++ b/modules-available/sysconfig/api.inc.php
@@ -0,0 +1,78 @@
+<?php
+
+$ip = $_SERVER['REMOTE_ADDR'];
+if (substr($ip, 0, 7) === '::ffff:') {
+ $ip = substr($ip, 7);
+}
+
+$uuid = Request::any('uuid', false, 'string');
+if ($uuid !== false && strlen($uuid) !== 36) {
+ $uuid = false;
+}
+$locationId = false;
+if (Module::isAvailable('locations')) {
+ $locationId = Location::getFromIpAndUuid($ip, $uuid);
+ if ($locationId !== false) {
+ $locationChain = Location::getLocationRootChain($locationId);
+ $locationChain[] = 0;
+ }
+}
+if ($locationId === false) {
+ $locationId = 0;
+ $locationChain = array(0);
+}
+
+// What we do if we can't supply the requested config
+function deliverEmpty($message)
+{
+ EventLog::failure($message);
+ Header('HTTP/1.1 404 Not found');
+ die('Config file could not be found or read!');
+}
+
+// Get config module path
+
+// We get all the configs for the whole location chain up to root
+$res = Database::simpleQuery("SELECT c.title, c.filepath, c.status, cl.locationid FROM configtgz c"
+ . " INNER JOIN configtgz_location cl USING (configid)"
+ . " WHERE cl.locationid IN (" . implode(',', $locationChain) . ")");
+$best = 1000;
+$row = false;
+while ($r = $res->fetch(PDO::FETCH_ASSOC)) {
+ settype($r['locationid'], 'int');
+ $index = array_search($r['locationid'], $locationChain);
+ if ($index === false || $index > $best)
+ continue;
+ if (!file_exists($r['filepath'])) {
+ if ($r['locationid'] === 0) {
+ EventLog::failure("The global config.tgz '{$r['title']}' was not found at '{$r['filepath']}'. Please regenerate the system configuration");
+ } else {
+ EventLog::warning("config.tgz '{$r['title']}' for location $locationId not found at '{$r['filepath']}', trying fallback....");
+ }
+ continue;
+ }
+ $best = $index;
+ $row = $r;
+}
+
+if ($row === false) {
+ // TODO Not found in DB
+ deliverEmpty("No config.tgz for location $locationId found");
+}
+
+if (!file_exists($row['filepath'])) {
+ // TODO Does not exist
+ deliverEmpty();
+}
+
+Header('Content-Type: application/gzip');
+Header('Content-Disposition: attachment; filename=' . Util::sanitizeFilename($row['title']) . '.tgz');
+$ret = readfile($row['filepath']);
+
+if ($ret === false || $ret === 0) {
+ // TODO didn't send anything/everything
+ // Cannot deliver empty, don't know what has been send already
+ EventLog::warning("Could not deliver config.tgz to client $ip: readfile() returned " . ($ret === false ? 'false' : $ret));
+}
+
+exit; \ No newline at end of file
diff --git a/modules-available/sysconfig/clientscript.js b/modules-available/sysconfig/clientscript.js
index 2a133353..47761ea5 100644
--- a/modules-available/sysconfig/clientscript.js
+++ b/modules-available/sysconfig/clientscript.js
@@ -1,4 +1,6 @@
+// Ugly hack to get the ellipsisized fields to work
+
function forceTable(t)
{
var pwidth = t.parent().innerWidth();
@@ -19,3 +21,63 @@ function forceTable(t)
w -= 3;
} while (t.width() > pwidth);
}
+
+// Mouseover and clicking
+
+var boldItem = false;
+
+function showmod(e, action) {
+ var list = $(e).attr('data-modlist');
+ list = list.split(',');
+ if (action === 'bold') {
+ $(boldItem).removeClass("slx-bold");
+ if (boldItem === e) {
+ action = 'fade';
+ boldItem = false;
+ }
+ } else if (boldItem !== false) {
+ return;
+ }
+ $('.modrow').each(function () {
+ var elem = $(this);
+ elem.removeClass("slx-fade slx-bold");
+ if (action === 'reset')
+ return;
+ if (action === 'bold' && list.indexOf(elem.attr('data-id')) !== -1)
+ elem.addClass("slx-bold");
+ if (list.indexOf(elem.attr('data-id')) === -1)
+ elem.addClass("slx-fade");
+ });
+ if (action === 'bold') {
+ boldItem = e;
+ $(e).addClass("slx-bold");
+ }
+}
+
+// Polling for updated status (outdated, missing, ok)
+
+var statusChecks = 0;
+
+function checkBuildStatus() {
+ var mods = [];
+ var confs = [];
+ $(".refmod.btn-primary").each(function (index) {
+ mods.push($(this).val());
+ });
+ $(".refconf.btn-primary").each(function (index) {
+ confs.push($(this).val());
+ });
+ if (mods.length === 0 && confs.length === 0) return;
+ if (++statusChecks < 10) setTimeout(checkBuildStatus, 200 + 50 * statusChecks);
+ $.post('?do=SysConfig', { mods: mods.join(), confs: confs.join(), token: TOKEN, action: 'status' }, function (data) {
+ if (typeof data === 'undefined') return;
+ if (typeof data.mods === 'object') updateButtonColor($(".refmod.btn-primary"), data.mods);
+ if (typeof data.confs === 'object') updateButtonColor($(".refconf.btn-primary"), data.confs);
+ }, 'json');
+}
+
+function updateButtonColor(list,ids) {
+ list.each(function() {
+ if (ids.indexOf($(this).val()) >= 0) $(this).removeClass('btn-primary').addClass('btn-default');
+ });
+}
diff --git a/modules-available/sysconfig/inc/sysconfig.inc.php b/modules-available/sysconfig/inc/sysconfig.inc.php
new file mode 100644
index 00000000..15bd4104
--- /dev/null
+++ b/modules-available/sysconfig/inc/sysconfig.inc.php
@@ -0,0 +1,17 @@
+<?php
+
+class SysConfig
+{
+
+ public static function getAll()
+ {
+ $res = Database::simpleQuery("SELECT c.configid, c.title, c.filepath, c.status, Group_Concat(cl.locationid) AS locs FROM configtgz c"
+ . " LEFT JOIN configtgz_location cl USING (configid) GROUP BY c.configid");
+ $ret = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $ret[] = $row;
+ }
+ return $ret;
+ }
+
+} \ No newline at end of file
diff --git a/modules-available/sysconfig/install.inc.php b/modules-available/sysconfig/install.inc.php
index 35697d1f..0cde39c2 100644
--- a/modules-available/sysconfig/install.inc.php
+++ b/modules-available/sysconfig/install.inc.php
@@ -30,6 +30,12 @@ $res[] = tableCreate('configtgz_x_module', "
KEY `moduleid` (`moduleid`)
");
+$res[] = tableCreate('configtgz_location', "
+ `locationid` int(11) NOT NULL,
+ `configid` int(10) unsigned NOT NULL,
+ PRIMARY KEY (`locationid`),
+ KEY `configid` (`configid`)
+");
// Constraints
if (in_array(UPDATE_DONE, $res)) {
diff --git a/modules-available/sysconfig/page.inc.php b/modules-available/sysconfig/page.inc.php
index 121e4cee..9bf9cacd 100644
--- a/modules-available/sysconfig/page.inc.php
+++ b/modules-available/sysconfig/page.inc.php
@@ -10,6 +10,18 @@ class Page_SysConfig extends Page
protected static $moduleTypes = array();
/**
+ * @var int current locationid, 0 if global
+ */
+ private $currentLoc;
+
+ /**
+ * @var array Associative list of known locations
+ */
+ private $locations;
+
+ private $haveOverriddenLocations = false;
+
+ /**
* Add a known configuration module. Every addmoule_* file should call this
* for its module provided.
*
@@ -51,6 +63,22 @@ class Page_SysConfig extends Page
Util::redirect('?do=Main');
}
+ // Determine location we're editing
+ if (!Module::isAvailable('locations')) {
+ $this->locations = array();
+ $this->currentLoc = 0;
+ } else {
+ $this->locations = Location::getLocationsAssoc();
+ $this->currentLoc = Request::any('locationid', 0, 'int');
+ }
+ // Location valid?
+ if ($this->currentLoc !== 0 && !isset($this->locations[$this->currentLoc])) {
+ Message::addError('locations.invalid-location-id', $this->currentLoc);
+ Util::redirect('?do=sysconfig');
+ }
+
+ // Action handling
+
$action = Request::any('action', 'list');
// Load all addmodule classes, as they populate the $moduleTypes array
@@ -115,7 +143,13 @@ class Page_SysConfig extends Page
AddConfig_Base::render();
return;
case 'list':
+ Render::openTag('div', array('class' => 'row'));
$this->listConfigs();
+ if ($this->currentLoc === 0) {
+ $this->listModules();
+ }
+ Render::closeTag('div');
+ Render::addTemplate('list-legend', array('showLocationBadge' => $this->haveOverriddenLocations));
return;
case 'module':
$listid = Request::post('list');
@@ -135,27 +169,79 @@ class Page_SysConfig extends Page
Message::addError('invalid-action', $action, 'main');
}
+ private function getLocationNames($locations, $ids)
+ {
+ $ret = array();
+ foreach ($ids as $id) {
+ settype($id, 'int');
+ if (isset($locations[$id])) {
+ $ret[] = $locations[$id]['locationname'];
+ }
+ }
+ return implode(', ', $ret);
+ }
+
/**
* List all configurations and configuration modules.
*/
private function listConfigs()
{
// Configs
- $res = Database::simpleQuery("SELECT configtgz.configid, configtgz.title, configtgz.filepath, configtgz.status, GROUP_CONCAT(configtgz_x_module.moduleid) AS modlist"
- . " FROM configtgz"
- . " INNER JOIN configtgz_x_module USING (configid)"
+ $res = Database::simpleQuery("SELECT c.configid, c.title, c.filepath, c.status,"
+ . " GROUP_CONCAT(DISTINCT cl.locationid) AS loclist, GROUP_CONCAT(cxm.moduleid) AS modlist"
+ . " FROM configtgz c"
+ . " INNER JOIN configtgz_x_module cxm USING (configid)"
+ . " LEFT JOIN configtgz_location cl ON (c.configid = cl.configid)"
. " GROUP BY configid"
. " ORDER BY title ASC");
$configs = array();
+ if ($this->currentLoc !== 0) {
+ $locationName = $this->locations[$this->currentLoc]['locationname'];
+ } else {
+ $locationName = false;
+ }
+ $hasDefault = false;
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ if (is_null($row['loclist'])) {
+ $locList = array();
+ } else {
+ $locList = explode(',', $row['loclist']);
+ }
+ $isDefault = in_array((string)$this->currentLoc, $locList, true);
+ $hasDefault |= $isDefault;
+ if ($this->currentLoc !== 0) {
+ $locCount = 0;
+ } else {
+ $locCount = count($locList);
+ if ($isDefault) {
+ $locCount--;
+ }
+ }
+ if ($locCount > 0) {
+ $this->haveOverriddenLocations = true;
+ }
$configs[] = array(
'configid' => $row['configid'],
'config' => $row['title'],
'modlist' => $row['modlist'],
- 'current' => readlink(CONFIG_HTTP_DIR . '/default/config.tgz') === $row['filepath'],
+ 'current' => $isDefault,
+ 'loclist' => $row['loclist'],
+ 'readableLocList' => $this->getLocationNames($this->locations, $locList),
+ 'locationCount' => $locCount,
'needrebuild' => ($row['status'] !== 'OK')
);
}
+ Render::addTemplate('list-configs', array(
+ 'locationid' => $this->currentLoc,
+ 'locationname' => $locationName,
+ 'havelocations' => Module::isAvailable('locations'),
+ 'configs' => $configs,
+ 'inheritConfig' => !$hasDefault,
+ ));
+ }
+
+ private function listModules()
+ {
// Config modules
$res = Database::simpleQuery("SELECT moduleid, title, moduletype, status FROM configtgz_module ORDER BY moduletype ASC, title ASC");
$modules = array();
@@ -168,15 +254,10 @@ class Page_SysConfig extends Page
'needrebuild' => ($row['status'] !== 'OK')
);
}
- Render::addTemplate('_page', array(
- 'configs' => $configs,
+ Render::addTemplate('list-modules', array(
'modules' => $modules,
'havemodules' => (count($modules) > 0)
));
- Render::addFooter('<script> $(window).load(function (e) {
- forceTable($("#modtable"));
- forceTable($("#conftable"));
- }); // </script>');
}
private function listModuleContents($moduleid)
@@ -185,7 +266,7 @@ class Page_SysConfig extends Page
$row = Database::queryFirst("SELECT title, filepath FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleid));
if ($row === false) {
Message::addError('config-invalid', $moduleid);
- Util::redirect('?do=SysConfig');
+ Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
}
// find files in that archive
@@ -196,7 +277,7 @@ class Page_SysConfig extends Page
$status = Taskmanager::waitComplete($status, 4000);
if (!Taskmanager::isFinished($status) || Taskmanager::isFailed($status)) {
Taskmanager::addErrorMessage($status);
- Util::redirect('?do=SysConfig');
+ Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
}
// Sort files for better display
@@ -232,7 +313,7 @@ class Page_SysConfig extends Page
$config = Database::queryFirst("SELECT title FROM configtgz WHERE configid = :configid LIMIT 1", array('configid' => $configid));
if ($config === false) {
Message::addError('config-invalid', $configid);
- Util::redirect('?do=SysConfig');
+ Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
}
// fetch the data
$res = Database::simpleQuery("SELECT module.moduleid, module.title AS moduletitle"
@@ -257,25 +338,22 @@ class Page_SysConfig extends Page
private function activateConfig()
{
- $configid = Request::post('activate', 'MISSING');
- $row = Database::queryFirst("SELECT title, filepath FROM configtgz WHERE configid = :configid LIMIT 1", array('configid' => $configid));
- if ($row === false) {
- Message::addError('config-invalid', $configid);
- Util::redirect('?do=SysConfig');
+ $configid = Request::post('activate', false, 'int');
+ if ($configid === false) {
+ Message::addError('main.empty-field');
+ Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
}
- $task = Taskmanager::submit('LinkConfigTgz', array(
- 'destination' => $row['filepath']
- ));
- if (isset($task['statusCode']) && $task['statusCode'] === TASK_WAITING) {
- $task = Taskmanager::waitComplete($task['id']);
- }
- if (!isset($task['statusCode']) || $task['statusCode'] === TASK_ERROR) {
- Message::addError('main.task-error', $task['data']['error']);
- } elseif ($task['statusCode'] === TASK_FINISHED) {
- Message::addSuccess('config-activated', $row['title']);
- Event::activeConfigChanged();
+ if ($this->currentLoc === 0 || $configid !== 0) {
+ $row = Database::queryFirst("SELECT title, filepath FROM configtgz WHERE configid = :configid LIMIT 1", array('configid' => $configid));
+ if ($row === false) {
+ Message::addError('config-invalid', $configid);
+ Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
+ }
}
- Util::redirect('?do=SysConfig');
+ $locationid = $this->currentLoc;
+ Database::exec("INSERT INTO configtgz_location (locationid, configid) VALUES (:locationid, :configid)"
+ . " ON DUPLICATE KEY UPDATE configid = :configid", compact('locationid', 'configid'));
+ Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
}
private function rebuildConfig()
@@ -284,7 +362,7 @@ class Page_SysConfig extends Page
$config = ConfigTgz::get($configid);
if ($config === false) {
Message::addError('config-invalid', $configid);
- Util::redirect('?do=SysConfig');
+ Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
}
$ret = $config->generate(false, 350); // TODO
if ($ret === true)
@@ -293,7 +371,7 @@ class Page_SysConfig extends Page
Message::addError('module-rebuild-failed', $config->title());
else
Message::addInfo('module-rebuilding', $config->title());
- Util::redirect('?do=SysConfig');
+ Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
}
private function delModule()
@@ -302,14 +380,14 @@ class Page_SysConfig extends Page
$row = Database::queryFirst("SELECT title, filepath FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleid));
if ($row === false) {
Message::addError('config-invalid', $moduleid);
- Util::redirect('?do=SysConfig');
+ Util::redirect('?do=sysconfig');
}
$existing = Database::queryFirst("SELECT title FROM configtgz_x_module"
. " INNER JOIN configtgz USING (configid)"
. " WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleid));
if ($existing !== false) {
Message::addError('module-in-use', $row['title'], $existing['title']);
- Util::redirect('?do=SysConfig');
+ Util::redirect('?do=sysconfig');
}
$task = Taskmanager::submit('DeleteFile', array(
'file' => $row['filepath']
@@ -323,7 +401,7 @@ class Page_SysConfig extends Page
Message::addSuccess('module-deleted', $row['title']);
}
Database::exec("DELETE FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleid));
- Util::redirect('?do=SysConfig');
+ Util::redirect('?do=sysconfig');
}
private function downloadModule()
@@ -332,10 +410,10 @@ class Page_SysConfig extends Page
$row = Database::queryFirst("SELECT title, filepath FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleid));
if ($row === false) {
Message::addError('config-invalid', $moduleid);
- Util::redirect('?do=SysConfig');
+ Util::redirect('?do=sysconfig');
}
if (!Util::sendFile($row['filepath'], $row['title'] . '.tgz'))
- Util::redirect('?do=SysConfig');
+ Util::redirect('?do=sysconfig');
exit(0);
}
@@ -345,7 +423,7 @@ class Page_SysConfig extends Page
$module = ConfigModule::get($moduleid);
if ($module === false) {
Message::addError('config-invalid', $moduleid);
- Util::redirect('?do=SysConfig');
+ Util::redirect('?do=sysconfig');
}
$ret = $module->generate(false, 250);
if ($ret === true)
@@ -354,7 +432,7 @@ class Page_SysConfig extends Page
Message::addError('module-rebuild-failed', $module->title());
else
Message::addInfo('module-rebuilding', $module->title());
- Util::redirect('?do=SysConfig');
+ Util::redirect('?do=sysconfig');
}
private function delConfig()
@@ -363,10 +441,10 @@ class Page_SysConfig extends Page
$config = ConfigTgz::get($configid);
if ($config === false) {
Message::addError('config-invalid', $configid);
- Util::redirect('?do=SysConfig');
+ Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
}
$config->delete();
- Util::redirect('?do=SysConfig');
+ Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
}
private function initAddModule()
diff --git a/modules-available/sysconfig/templates/_page.html b/modules-available/sysconfig/templates/_page.html
index 1a2f64d6..e69de29b 100644
--- a/modules-available/sysconfig/templates/_page.html
+++ b/modules-available/sysconfig/templates/_page.html
@@ -1,227 +0,0 @@
-<div class="row">
- <div class="col-md-6">
- <div class="panel panel-default">
- <div class="panel-heading">
- {{lang_availableSystem}}
- <a class="btn btn-default" data-toggle="modal" data-target="#help-config"><span class="glyphicon glyphicon-question-sign"></span></a>
- </div>
- <div class="panel-body">
- <form method="post" action="?do=SysConfig">
- <input type="hidden" name="token" value="{{token}}">
- <input type="hidden" name="action" value="config">
- <table id="conftable" class="slx-table" style="max-width:100px !important">
- {{#configs}}
- <tr>
- <td data-modlist="{{modlist}}" class="slx-pointer slx-width-ignore slx-nowrap"onclick="showmod(this, 'bold')" onmouseover="showmod(this, 'fade')" onmouseout="showmod(this, 'reset')">
- <div class="slx-dyn-ellipsis">{{config}}</div>
- </td>
- <td>
- {{^current}}
- <button class="btn btn-primary btn-xs" name="activate" value="{{configid}}">
- <span class="glyphicon glyphicon-flag"></span>
- {{lang_activate}}
- </button>
- {{/current}}
- {{#current}}
- <span class="btn btn-success btn-xs slx-nopointer">
- <span class="glyphicon glyphicon-ok"></span>
- {{lang_active}}
- </span>
- {{/current}}
- </td>
- <td class="slx-nowrap">
- <button
- {{#needrebuild}}
- class="refconf btn btn-primary btn-xs"
- {{/needrebuild}}
- {{^needrebuild}}
- class="refconf btn btn-default btn-xs"
- {{/needrebuild}}
- name="rebuild" value="{{configid}}" title="{{lang_rebuild}}"><span class="glyphicon glyphicon-refresh"></span></button>
- </td>
- <td class="slx-nowrap">
- <a class="btn btn-success btn-xs" href="?do=SysConfig&amp;action=addconfig&amp;edit={{configid}}" title="{{lang_edit}}"><span class="glyphicon glyphicon-edit"></span></a>
- <button class="btn btn-danger btn-xs" name="del" value="{{configid}}" title="{{lang_delete}}"><span class="glyphicon glyphicon-trash"></span></button>
- </td>
- </tr>
- {{/configs}}
- </table>
- {{^configs}}
- <div class="alert alert-warning">
- {{lang_systemConfigurationNotFound}}
- </div>
- {{^modules}}
- <div class="alert alert-danger">
- {{lang_systemConfigurationAlert}}
- </div>
- {{/modules}}
- {{/configs}}
- </form>
- </div>
- {{#havemodules}}
- <div class="panel-footer">
- <a class="btn btn-primary" href="?do=SysConfig&amp;action=addconfig">{{lang_newConfiguration}}</a>
- </div>
- {{/havemodules}}
- </div>
- </div>
-
- <div class="col-md-6">
- <div class="panel panel-default">
- <div class="panel-heading">
- {{lang_availableModules}}
- <a class="btn btn-default" data-toggle="modal" data-target="#help-module"><span class="glyphicon glyphicon-question-sign"></span></a>
- </div>
- <div class="panel-body">
- <form method="post" action="?do=SysConfig">
- <input type="hidden" name="token" value="{{token}}">
- <input type="hidden" name="action" value="module">
- <table id="modtable" class="slx-table" style="max-width:100px !important">
- {{#modules}}
- <tr>
- <td class="badge slx-nowrap">{{moduletype}}</td>
- <td data-id="{{moduleid}}" class="modrow slx-width-ignore slx-nowrap"><div class="slx-dyn-ellipsis">{{module}}</div></td>
- <td class="slx-nowrap">
- {{#iscustom}}
- <button class="btn btn-default btn-xs" name="list" value="{{moduleid}}" title="{{lang_show}}"><span class="glyphicon glyphicon-eye-open"></span></button>
- <!-- a class="btn btn-default btn-xs"><span class="glyphicon glyphicon-edit"></span> Bearbeiten</a -->
- <button class="btn btn-default btn-xs" name="download" value="{{moduleid}}" title="{{lang_download}}"><span class="glyphicon glyphicon-download-alt"></span></button>
- {{/iscustom}}
- </td>
- <td class="slx-nowrap">
- <button
- {{#needrebuild}}
- class="refmod btn btn-primary btn-xs"
- {{/needrebuild}}
- {{^needrebuild}}
- class="refmod btn btn-default btn-xs"
- {{/needrebuild}}
- name="rebuild" value="{{moduleid}}" title="{{lang_rebuild}}"><span class="glyphicon glyphicon-refresh"></span></button>
- <a class="btn btn-success btn-xs" href="?do=SysConfig&amp;action=addmodule&amp;step={{moduletype}}_Start&amp;edit={{moduleid}}" title="{{lang_edit}}"><span class="glyphicon glyphicon-edit"></span></a>
- <button class="btn btn-danger btn-xs" name="del" value="{{moduleid}}" title="{{lang_delete}}"><span class="glyphicon glyphicon-trash"></span></button>
- </td>
- </tr>
- {{/modules}}
- </table>
- {{^modules}}
- <div class="alert alert-warning">{{lang_configurationModuleNotFound}}</div>
- {{/modules}}
- </form>
- </div>
- <div class="panel-footer">
- <a class="btn btn-primary" href="?do=SysConfig&amp;action=addmodule">{{lang_newModule}}</a>
- </div>
- </div>
- </div>
-</div>
-
-<div class="panel panel-default">
- <div class="panel-heading">{{lang_legend}}</div>
- <div class="panel-body">
- <p>
- <span class="btn btn-default btn-xs" title="{{lang_show}}"><span class="glyphicon glyphicon-eye-open"></span></span>
- {{lang_showLong}}
- </p>
- <p>
- <span class="btn btn-default btn-xs" title="{{lang_download}}"><span class="glyphicon glyphicon-download-alt"></span></span>
- {{lang_downloadLong}}
- </p>
- <p>
- <span class="btn btn-default btn-xs" title="{{lang_rebuild}}"><span class="glyphicon glyphicon-refresh"></span></span>
- {{lang_rebuildLong}}
- </p>
- <p>
- <span class="btn btn-primary btn-xs" title="{{lang_rebuild}}"><span class="glyphicon glyphicon-refresh"></span></span>
- {{lang_rebuildOutdatedLong}}
- </p>
- <p>
- <span class="btn btn-success btn-xs" title="{{lang_edit}}"><span class="glyphicon glyphicon-edit"></span></span>
- {{lang_editLong}}
- </p>
- <div>
- <span class="btn btn-danger btn-xs" title="{{lang_delete}}"><span class="glyphicon glyphicon-trash"></span></span>
- {{lang_deleteLong}}
- </div>
- </div>
-</div>
-
-<div class="modal fade" id="help-config" tabindex="-1" role="dialog">
- <div class="modal-dialog">
- <div class="modal-content">
- <div class="modal-header">{{lang_systemConfiguration}}</div>
- <div class="modal-body">
- {{lang_helpSystemConfiguration}}
- </div>
- <div class="modal-footer"><a class="btn btn-primary" data-dismiss="modal">{{lang_close}}</a></div>
- </div>
- </div>
-</div>
-
-<div class="modal fade" id="help-module" tabindex="-1" role="dialog">
- <div class="modal-dialog">
- <div class="modal-content">
- <div class="modal-header">{{lang_moduleConfiguration}}</div>
- <div class="modal-body">
- {{lang_helpModuleConfiguration}}
- </div>
- <div class="modal-footer"><a class="btn btn-primary" data-dismiss="modal">{{lang_close}}</a></div>
- </div>
- </div>
-</div>
-
-<script type="text/javascript"><!--
- var boldItem = false;
- function showmod(e, action) {
- var list = $(e).attr('data-modlist');
- list = list.split(',');
- if (action === 'bold') {
- $(boldItem).removeClass("slx-bold");
- if (boldItem === e) {
- action = 'fade';
- boldItem = false;
- }
- } else if (boldItem !== false) {
- return;
- }
- $('.modrow').each(function () {
- var elem = $(this);
- elem.removeClass("slx-fade slx-bold");
- if (action === 'reset')
- return;
- if (action === 'bold' && list.indexOf(elem.attr('data-id')) !== -1)
- elem.addClass("slx-bold");
- if (list.indexOf(elem.attr('data-id')) === -1)
- elem.addClass("slx-fade");
- });
- if (action === 'bold') {
- boldItem = e;
- $(e).addClass("slx-bold");
- }
- }
-
- var statusChecks = 0;
- function checkBuildStatus() {
- var mods = [];
- var confs = [];
- $(".refmod.btn-primary").each(function (index) {
- mods.push($(this).val());
- });
- $(".refconf.btn-primary").each(function (index) {
- confs.push($(this).val());
- });
- if (mods.length === 0 && confs.length === 0) return;
- if (++statusChecks < 10) setTimeout(checkBuildStatus, 200 + 50 * statusChecks);
- console.log("POSTING");
- $.post('?do=SysConfig', { mods: mods.join(), confs: confs.join(), token: TOKEN, action: 'status' }, function (data) {
- if (typeof data === 'undefined') return;
- if (typeof data.mods === 'object') updateButtonColor($(".refmod.btn-primary"), data.mods);
- if (typeof data.confs === 'object') updateButtonColor($(".refconf.btn-primary"), data.confs);
- }, 'json');
- }
- function updateButtonColor(list,ids) {
- list.each(function() {
- if (ids.indexOf($(this).val()) >= 0) $(this).removeClass('btn-primary').addClass('btn-default');
- });
- }
- document.addEventListener("DOMContentLoaded", checkBuildStatus, false);
-// --></script> \ No newline at end of file
diff --git a/modules-available/sysconfig/templates/list-configs.html b/modules-available/sysconfig/templates/list-configs.html
new file mode 100644
index 00000000..ed027385
--- /dev/null
+++ b/modules-available/sysconfig/templates/list-configs.html
@@ -0,0 +1,115 @@
+{{#locationid}}
+<div class="col-md-12">
+ <h1>{{locationname}}</h1>
+ <div class="alert alert-info">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
+ {{lang_editingLocationInfo}}
+ </div>
+</div>
+{{/locationid}}
+<div class="col-md-6">
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ {{lang_availableSystem}}
+ <a class="btn btn-default" data-toggle="modal" data-target="#help-config"><span class="glyphicon glyphicon-question-sign"></span></a>
+ </div>
+ <div class="panel-body">
+ <form method="post" action="?do=SysConfig">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="action" value="config">
+ <input type="hidden" name="locationid" value="{{locationid}}">
+ <table id="conftable" class="slx-table" style="max-width:100px !important">
+ {{#configs}}
+ <tr>
+ <td data-modlist="{{modlist}}" class="slx-pointer slx-width-ignore slx-nowrap"onclick="showmod(this, 'bold')" onmouseover="showmod(this, 'fade')" onmouseout="showmod(this, 'reset')" width="100%">
+ <div class="slx-dyn-ellipsis">{{config}}</div>
+ </td>
+ <td>
+ {{^current}}
+ <button class="btn btn-primary btn-xs" name="activate" value="{{configid}}">
+ <span class="glyphicon glyphicon-flag"></span>
+ {{lang_activate}}
+ </button>
+ {{/current}}
+ {{#current}}
+ <span class="btn btn-success btn-xs slx-nopointer">
+ <span class="glyphicon glyphicon-ok"></span>
+ {{lang_active}}
+ </span>
+ {{/current}}
+ </td>
+ <td>
+ {{#locationCount}}
+ <span class="badge" {{#readableLocList}} data-toggle="tooltip" data-placement="top" title="{{readableLocList}}" {{/readableLocList}} >
+ +{{locationCount}}
+ </span>
+ {{/locationCount}}
+ </td>
+ <td class="slx-nowrap">
+ <button
+ {{#needrebuild}}
+ class="refconf btn btn-primary btn-xs"
+ {{/needrebuild}}
+ {{^needrebuild}}
+ class="refconf btn btn-default btn-xs"
+ {{/needrebuild}}
+ name="rebuild" value="{{configid}}" title="{{lang_rebuild}}"><span class="glyphicon glyphicon-refresh"></span></button>
+ </td>
+ <td class="slx-nowrap">
+ <a class="btn btn-success btn-xs" href="?do=SysConfig&amp;action=addconfig&amp;edit={{configid}}" title="{{lang_edit}}"><span class="glyphicon glyphicon-edit"></span></a>
+ <button class="btn btn-danger btn-xs" name="del" value="{{configid}}" title="{{lang_delete}}"><span class="glyphicon glyphicon-trash"></span></button>
+ </td>
+ </tr>
+ {{/configs}}
+ {{#locationid}}
+ <tr>
+ <td><i>Vererben von übergeordnetem Raum</i></td>
+ <td>
+ {{^inheritConfig}}
+ <button class="btn btn-primary btn-xs" name="activate" value="0">
+ <span class="glyphicon glyphicon-flag"></span>
+ {{lang_activate}}
+ </button>
+ {{/inheritConfig}}
+ {{#inheritConfig}}
+ <span class="btn btn-success btn-xs slx-nopointer">
+ <span class="glyphicon glyphicon-ok"></span>
+ {{lang_active}}
+ </span>
+ {{/inheritConfig}}
+ </td>
+ <td></td>
+ <td></td>
+ </tr>
+ {{/locationid}}
+ </table>
+ {{^configs}}
+ <div class="alert alert-warning">
+ {{lang_systemConfigurationNotFound}}
+ </div>
+ {{^modules}}
+ <div class="alert alert-danger">
+ {{lang_systemConfigurationAlert}}
+ </div>
+ {{/modules}}
+ {{/configs}}
+ </form>
+ </div>
+ <div class="panel-footer">
+ <a class="btn btn-primary" href="?do=SysConfig&amp;action=addconfig">{{lang_newConfiguration}}</a>
+ </div>
+ </div>
+
+ <div class="modal fade" id="help-config" tabindex="-1" role="dialog">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">{{lang_systemConfiguration}}</div>
+ <div class="modal-body">
+ {{lang_helpSystemConfiguration}}
+ </div>
+ <div class="modal-footer"><a class="btn btn-primary" data-dismiss="modal">{{lang_close}}</a></div>
+ </div>
+ </div>
+ </div>
+
+</div> \ No newline at end of file
diff --git a/modules-available/sysconfig/templates/list-legend.html b/modules-available/sysconfig/templates/list-legend.html
new file mode 100644
index 00000000..090ef526
--- /dev/null
+++ b/modules-available/sysconfig/templates/list-legend.html
@@ -0,0 +1,44 @@
+<div class="panel panel-default">
+ <div class="panel-heading">{{lang_legend}}</div>
+ <div class="panel-body">
+ <p>
+ <span class="btn btn-default btn-xs" title="{{lang_show}}"><span class="glyphicon glyphicon-eye-open"></span></span>
+ {{lang_showLong}}
+ </p>
+ <p>
+ <span class="btn btn-default btn-xs" title="{{lang_download}}"><span class="glyphicon glyphicon-download-alt"></span></span>
+ {{lang_downloadLong}}
+ </p>
+ <p>
+ <span class="btn btn-default btn-xs" title="{{lang_rebuild}}"><span class="glyphicon glyphicon-refresh"></span></span>
+ {{lang_rebuildLong}}
+ </p>
+ <p>
+ <span class="btn btn-primary btn-xs" title="{{lang_rebuild}}"><span class="glyphicon glyphicon-refresh"></span></span>
+ {{lang_rebuildOutdatedLong}}
+ </p>
+ <p>
+ <span class="btn btn-success btn-xs" title="{{lang_edit}}"><span class="glyphicon glyphicon-edit"></span></span>
+ {{lang_editLong}}
+ </p>
+ <p>
+ <span class="btn btn-danger btn-xs" title="{{lang_delete}}"><span class="glyphicon glyphicon-trash"></span></span>
+ {{lang_deleteLong}}
+ </p>
+ {{#showLocationBadge}}
+ <p>
+ <span class="badge">+4</span>
+ {{lang_configLocationLong}}
+ </p>
+ {{/showLocationBadge}}
+ </div>
+</div>
+
+<script type="application/javascript"><!--
+document.addEventListener("DOMContentLoaded", function () {
+ forceTable($("#modtable"));
+ forceTable($("#conftable"));
+ checkBuildStatus();
+ $('[data-toggle="tooltip"]').tooltip();
+}, false);
+// --></script> \ No newline at end of file
diff --git a/modules-available/sysconfig/templates/list-modules.html b/modules-available/sysconfig/templates/list-modules.html
new file mode 100644
index 00000000..c3e2d736
--- /dev/null
+++ b/modules-available/sysconfig/templates/list-modules.html
@@ -0,0 +1,59 @@
+<div class="col-md-6">
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ {{lang_availableModules}}
+ <a class="btn btn-default" data-toggle="modal" data-target="#help-module"><span class="glyphicon glyphicon-question-sign"></span></a>
+ </div>
+ <div class="panel-body">
+ <form method="post" action="?do=SysConfig">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="action" value="module">
+ <table id="modtable" class="slx-table" style="max-width:100px !important">
+ {{#modules}}
+ <tr>
+ <td class="badge slx-nowrap">{{moduletype}}</td>
+ <td data-id="{{moduleid}}" class="modrow slx-width-ignore slx-nowrap" width="100%"><div class="slx-dyn-ellipsis">{{module}}</div></td>
+ <td class="slx-nowrap">
+ {{#iscustom}}
+ <button class="btn btn-default btn-xs" name="list" value="{{moduleid}}" title="{{lang_show}}"><span class="glyphicon glyphicon-eye-open"></span></button>
+ <button class="btn btn-default btn-xs" name="download" value="{{moduleid}}" title="{{lang_download}}"><span class="glyphicon glyphicon-download-alt"></span></button>
+ {{/iscustom}}
+ </td>
+ <td class="slx-nowrap">
+ <button
+ {{#needrebuild}}
+ class="refmod btn btn-primary btn-xs"
+ {{/needrebuild}}
+ {{^needrebuild}}
+ class="refmod btn btn-default btn-xs"
+ {{/needrebuild}}
+ name="rebuild" value="{{moduleid}}" title="{{lang_rebuild}}"><span class="glyphicon glyphicon-refresh"></span></button>
+ <a class="btn btn-success btn-xs" href="?do=SysConfig&amp;action=addmodule&amp;step={{moduletype}}_Start&amp;edit={{moduleid}}" title="{{lang_edit}}"><span class="glyphicon glyphicon-edit"></span></a>
+ <button class="btn btn-danger btn-xs" name="del" value="{{moduleid}}" title="{{lang_delete}}"><span class="glyphicon glyphicon-trash"></span></button>
+ </td>
+ </tr>
+ {{/modules}}
+ </table>
+ {{^modules}}
+ <div class="alert alert-warning">{{lang_configurationModuleNotFound}}</div>
+ {{/modules}}
+ </form>
+ </div>
+ <div class="panel-footer">
+ <a class="btn btn-primary" href="?do=SysConfig&amp;action=addmodule">{{lang_newModule}}</a>
+ </div>
+ </div>
+
+ <div class="modal fade" id="help-module" tabindex="-1" role="dialog">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">{{lang_moduleConfiguration}}</div>
+ <div class="modal-body">
+ {{lang_helpModuleConfiguration}}
+ </div>
+ <div class="modal-footer"><a class="btn btn-primary" data-dismiss="modal">{{lang_close}}</a></div>
+ </div>
+ </div>
+ </div>
+
+</div> \ No newline at end of file
diff --git a/style/default.css b/style/default.css
index 9b93bfda..0fa8fe26 100644
--- a/style/default.css
+++ b/style/default.css
@@ -107,6 +107,10 @@ body {
color: #0b0;
}
+.gray {
+ color: gray;
+}
+
.slx-label {
font-weight: bold;
margin: auto;