summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2017-12-01 13:42:04 +0100
committerSimon Rettberg2017-12-01 13:42:04 +0100
commit5393703f6e1485ddff94a50f63bcdd216ab629f4 (patch)
treece2db7130302bd4fb78b170e1fbd75496a1b4510
parentMerge remote-tracking branch 'origin/permission-manager' into permission-manager (diff)
parent[roomplanner] Sort already placed machines to the bottom (diff)
downloadslx-admin-5393703f6e1485ddff94a50f63bcdd216ab629f4.tar.gz
slx-admin-5393703f6e1485ddff94a50f63bcdd216ab629f4.tar.xz
slx-admin-5393703f6e1485ddff94a50f63bcdd216ab629f4.zip
Merge branch 'master' into permission-manager
-rw-r--r--.idea/inspectionProfiles/Project_Default.xml21
-rw-r--r--inc/database.inc.php17
-rw-r--r--inc/paginate.inc.php23
-rw-r--r--inc/pagination.inc.php47
-rw-r--r--inc/render.inc.php5
-rw-r--r--install.php63
-rw-r--r--modules-available/baseconfig/api.inc.php2
-rw-r--r--modules-available/baseconfig/page.inc.php2
-rw-r--r--modules-available/baseconfig_bwlp/baseconfig/settings.json35
-rw-r--r--modules-available/baseconfig_bwlp/lang/de/config-variables.json12
-rw-r--r--modules-available/baseconfig_bwlp/lang/en/config-variables.json10
-rw-r--r--modules-available/dnbd3/inc/dnbd3util.inc.php1
-rw-r--r--modules-available/dnbd3/install.inc.php41
-rw-r--r--modules-available/dnbd3/lang/de/template-tags.json5
-rw-r--r--modules-available/dnbd3/lang/en/messages.json8
-rw-r--r--modules-available/dnbd3/lang/en/template-tags.json58
-rw-r--r--modules-available/locationinfo/api.inc.php55
-rw-r--r--modules-available/locationinfo/inc/infopanel.inc.php17
-rw-r--r--modules-available/locationinfo/inc/locationinfo.inc.php88
-rw-r--r--modules-available/locationinfo/lang/de/messages.json5
-rw-r--r--modules-available/locationinfo/lang/de/template-tags.json46
-rw-r--r--modules-available/locationinfo/lang/en/backend-hisinone.json6
-rw-r--r--modules-available/locationinfo/lang/en/messages.json5
-rw-r--r--modules-available/locationinfo/lang/en/template-tags.json54
-rw-r--r--modules-available/locationinfo/page.inc.php48
-rwxr-xr-xmodules-available/locationinfo/templates/frontend-default.html84
-rw-r--r--modules-available/locationinfo/templates/frontend-summary.html77
-rw-r--r--modules-available/locationinfo/templates/page-config-panel-summary.html208
-rw-r--r--modules-available/locations/inc/location.inc.php25
-rw-r--r--modules-available/locations/lang/en/template-tags.json2
-rw-r--r--modules-available/main/lang/de/messages.json2
-rw-r--r--modules-available/main/lang/de/template-tags.json3
-rw-r--r--modules-available/main/lang/en/messages.json4
-rw-r--r--modules-available/main/lang/en/template-tags.json5
-rw-r--r--modules-available/main/page.inc.php3
-rw-r--r--modules-available/main/templates/page-main.html16
-rw-r--r--modules-available/news/install.inc.php4
-rw-r--r--modules-available/rebootcontrol/inc/rebootqueries.inc.php10
-rw-r--r--modules-available/roomplanner/baseconfig/getconfig.inc.php15
-rw-r--r--modules-available/roomplanner/clientscript.js40
-rw-r--r--modules-available/roomplanner/config.json2
-rw-r--r--modules-available/roomplanner/hooks/runmode/config.json6
-rw-r--r--modules-available/roomplanner/inc/pvsgenerator.inc.php48
-rw-r--r--modules-available/roomplanner/install.inc.php43
-rw-r--r--modules-available/roomplanner/js/grid.js9
-rw-r--r--modules-available/roomplanner/page.inc.php46
-rw-r--r--modules-available/runmode/inc/runmode.inc.php141
-rw-r--r--modules-available/runmode/install.inc.php24
-rw-r--r--modules-available/runmode/lang/de/template-tags.json10
-rw-r--r--modules-available/runmode/lang/en/template-tags.json10
-rw-r--r--modules-available/runmode/page.inc.php29
-rw-r--r--modules-available/runmode/templates/machine-selector.html15
-rw-r--r--modules-available/runmode/templates/module-machine-list.html2
-rw-r--r--modules-available/session/lang/en/messages.json4
-rw-r--r--modules-available/statistics/api.inc.php207
-rw-r--r--modules-available/statistics/hooks/cron.inc.php25
-rw-r--r--modules-available/statistics/inc/filter.inc.php42
-rw-r--r--modules-available/statistics/inc/filterset.inc.php14
-rw-r--r--modules-available/statistics/inc/machine.inc.php5
-rw-r--r--modules-available/statistics/inc/statistics.inc.php51
-rw-r--r--modules-available/statistics/install.inc.php20
-rw-r--r--modules-available/statistics/lang/de/messages.json4
-rw-r--r--modules-available/statistics/lang/de/template-tags.json5
-rw-r--r--modules-available/statistics/lang/en/messages.json4
-rw-r--r--modules-available/statistics/lang/en/template-tags.json5
-rw-r--r--modules-available/statistics/page.inc.php93
-rw-r--r--modules-available/statistics/templates/clientlist.html18
-rw-r--r--modules-available/statistics/templates/machine-main.html23
-rw-r--r--modules-available/statistics/templates/machine-usage.html80
-rw-r--r--modules-available/statistics/templates/summary.html8
-rw-r--r--modules-available/statistics/templates/syslog.html2
-rw-r--r--modules-available/sysconfig/hooks/cron.inc.php7
-rw-r--r--modules-available/sysconfig/install.inc.php26
-rw-r--r--modules-available/sysconfig/lang/de/module.json1
-rw-r--r--modules-available/sysconfig/lang/en/module.json3
-rw-r--r--modules-available/syslog/page.inc.php22
-rw-r--r--modules-available/syslog/templates/page-syslog.html2
-rw-r--r--script/collapse.js3
-rw-r--r--script/fileselect.js20
-rw-r--r--script/slx-fixes.js42
80 files changed, 1653 insertions, 640 deletions
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index a50453af..b86f71cc 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -1,9 +1,30 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
+ <inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
+ <option name="myValues">
+ <value>
+ <list size="7">
+ <item index="0" class="java.lang.String" itemvalue="nobr" />
+ <item index="1" class="java.lang.String" itemvalue="noembed" />
+ <item index="2" class="java.lang.String" itemvalue="comment" />
+ <item index="3" class="java.lang.String" itemvalue="noscript" />
+ <item index="4" class="java.lang.String" itemvalue="embed" />
+ <item index="5" class="java.lang.String" itemvalue="script" />
+ <item index="6" class="java.lang.String" itemvalue="th" />
+ </list>
+ </value>
+ </option>
+ <option name="myCustomValuesEnabled" value="true" />
+ </inspection_tool>
<inspection_tool class="PhpDivisionByZeroInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PhpDocMissingReturnTagInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpDocMissingThrowsInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpDocSignatureInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
+ <inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
+ <option name="processCode" value="false" />
+ <option name="processLiterals" value="false" />
+ <option name="processComments" value="true" />
+ </inspection_tool>
</profile>
</component> \ No newline at end of file
diff --git a/inc/database.inc.php b/inc/database.inc.php
index 150f828a..79c945b8 100644
--- a/inc/database.inc.php
+++ b/inc/database.inc.php
@@ -17,6 +17,7 @@ class Database
private static $statements = array();
private static $returnErrors;
private static $lastError = false;
+ private static $explainList = array();
/**
* Connect to the DB if not already connected.
@@ -37,6 +38,11 @@ class Database
return false;
Util::traceError('Connecting to the local database failed: ' . $e->getMessage());
}
+ if (CONFIG_DEBUG) {
+ register_shutdown_function(function() {
+ self::examineLoggedQueries();
+ });
+ }
return true;
}
@@ -115,7 +121,7 @@ class Database
{
self::init();
if (CONFIG_DEBUG && preg_match('/^\s*SELECT/is', $query)) {
- self::explainQuery($query, $args);
+ self::$explainList[] = [$query, $args];
}
// Support passing nested arrays for IN statements, automagically refactor
self::handleArrayArgument($query, $args);
@@ -141,6 +147,13 @@ class Database
return false;
}
+ public static function examineLoggedQueries()
+ {
+ foreach (self::$explainList as $e) {
+ self::explainQuery($e[0], $e[1]);
+ }
+ }
+
private static function explainQuery($query, $args)
{
$res = self::simpleQuery('EXPLAIN ' . $query, $args, true);
@@ -155,7 +168,7 @@ class Database
$lens[$key] = strlen($key);
}
foreach ($rows as $row) {
- if (!$log && preg_match('/filesort|temporary/i', $row['Extra'])) {
+ if (!$log && $row['rows'] > 20 && preg_match('/filesort|temporary/i', $row['Extra'])) {
$log = true;
}
foreach ($row as $key => $col) {
diff --git a/inc/paginate.inc.php b/inc/paginate.inc.php
index 91f52077..cdb4adf1 100644
--- a/inc/paginate.inc.php
+++ b/inc/paginate.inc.php
@@ -38,11 +38,9 @@ class Paginate
if (preg_match('/(\-\-|;)(\s|[^\'"`])*$/is', $query)) {
Util::traceError("Your query must not end in a comment or semi-colon!");
}
- $query .= ' LIMIT ' . ($this->currentPage * $this->perPage) . ', ' . $this->perPage;
- // Use SQL_CALC_FOUND_ROWS
- if (!preg_match('/^\s*SELECT\s+SQL_CALC_FOUND_ROWS/is', $query)) {
- $query = preg_replace('/^\s*SELECT/is', 'SELECT SQL_CALC_FOUND_ROWS ', $query);
- }
+ // Don't use SQL_CALC_FOUND_ROWS as it leads to filesort frequently thus being slower than two queries
+ // See https://www.percona.com/blog/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/
+
} else {
Util::traceError('Unsupported database engine');
}
@@ -55,7 +53,7 @@ class Paginate
if (substr($url, -1) !== '&') $url .= '&';
}
//
- $this->query =$query;
+ $this->query = $query;
$this->url = $url;
}
@@ -64,11 +62,14 @@ class Paginate
*/
public function exec($args = array())
{
- $args[':limit_start'] = $this->currentPage;
- $args[':limit_count'] = $this->perPage;
- $retval = Database::simpleQuery($this->query, $args);
- $res = Database::queryFirst('SELECT FOUND_ROWS() AS rowcount');
- $this->totalRows = (int)$res['rowcount'];
+ $countQuery = preg_replace('/ORDER\s+BY\s.*?(\sASC|\sDESC|$)/is', '', $this->query);
+ $countQuery = preg_replace('/SELECT\s.*?\sFROM\s/is', 'SELECT Count(*) AS rowcount FROM ', $countQuery);
+ $countRes = Database::queryFirst($countQuery, $args);
+ $args['limit_start'] = $this->currentPage;
+ $args['limit_count'] = $this->perPage;
+ $query = $this->query . ' LIMIT ' . ($this->currentPage * $this->perPage) . ', ' . $this->perPage;
+ $retval = Database::simpleQuery($query, $args);
+ $this->totalRows = (int)$countRes['rowcount'];
return $retval;
}
diff --git a/inc/pagination.inc.php b/inc/pagination.inc.php
deleted file mode 100644
index 65785a36..00000000
--- a/inc/pagination.inc.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-/**
- * TODO: Why does this class exist?
- * There's already the Paginate class which works more efficient by using the LIMIT statement
- * for the query, and has more options. Consider refactoring the places where this class is
- * used (see syslog or eventlog for usage examples), then get rid of this one.
- */
-class Pagination
-{
- private $items;
- private $page;
- private $maxItems;
-
- public function __construct($par1, $par2)
- {
- $this->items = $par1;
- $this->page = $par2;
-
- $this->maxItems = 5;
- }
-
- public function getPagination()
- {
- $ret = array();
- $n = ceil(count($this->items) / $this->maxItems);
- for ($i = 1; $i <= $n; $i++) {
- $class = ($i == $this->page) ? 'active' : '';
- $ret[] = array(
- 'class' => $class,
- 'page' => $i
- );
- }
- return $ret;
- }
-
- public function getItems()
- {
- $ret = array();
- $first = ($this->page - 1) * $this->maxItems;
- for ($i = 0; $i < $this->maxItems; $i++) {
- if ($first + $i < count($this->items))
- $ret[] = $this->items[$first + $i];
- }
- return $ret;
- }
-} \ No newline at end of file
diff --git a/inc/render.inc.php b/inc/render.inc.php
index 53e2f314..13262c1d 100644
--- a/inc/render.inc.php
+++ b/inc/render.inc.php
@@ -107,8 +107,7 @@ class Render
<script src="script/jquery.js"></script>
<script src="script/bootstrap.min.js"></script>
<script src="script/taskmanager.js"></script>
- <script src="script/fileselect.js"></script>
- <script src="script/collapse.js"></script>
+ <script src="script/slx-fixes.js"></script>
';
foreach ($modules as $module) {
$files = $module->getScripts($module != $pageModule);
@@ -213,7 +212,7 @@ class Render
*/
public static function parse($template, $params = false, $module = false)
{
- if ($module === false) {
+ if ($module === false && class_exists('Page')) {
$module = Page::getModule()->getIdentifier();
}
// Load html snippet
diff --git a/install.php b/install.php
index 7937ec38..b736713f 100644
--- a/install.php
+++ b/install.php
@@ -99,7 +99,7 @@ function tableRename($old, $new) {
* @param string $refColumn referenced column
* @return false|string[] list of constraints matching the request, false on error
*/
-function tableGetContraints($table, $column, $refTable, $refColumn)
+function tableGetConstraints($table, $column, $refTable, $refColumn)
{
$db = 'openslx';
if (defined('CONFIG_SQL_DB')) {
@@ -120,6 +120,51 @@ function tableGetContraints($table, $column, $refTable, $refColumn)
}
/**
+ * Because I'm stupid and can't type properly.
+ */
+function tableGetContraints($table, $column, $refTable, $refColumn)
+{
+ return tableGetConstraints($table, $column, $refTable, $refColumn);
+}
+
+/**
+ * Add constraint to table if it doesn't exist already.
+ * On failure, trigger finalResponse with error message.
+ *
+ * @param string $table table to add constraint to
+ * @param string $column foreign key column of that table
+ * @param string $refTable destination table
+ * @param string $refColumn primary key column in destination table
+ * @param string $actions "ON xxx ON yyy" string
+ * @return string UPDATE_* result code
+ */
+function tableAddConstraint($table, $column, $refTable, $refColumn, $actions)
+{
+ $test = tableExists($refTable) && tableHasColumn($refTable, $refColumn);
+ if ($test === false) {
+ // Most likely, destination table does not exist yet or isn't up to date
+ return UPDATE_RETRY;
+ }
+ $test = tableGetConstraints($table, $column, $refTable, $refColumn);
+ if ($test === false) {
+ // Should never happen!?
+ finalResponse(UPDATE_FAILED, 'DB: Cannot query constraints: ' . Database::lastError());
+ }
+ if (!empty($test)) {
+ // Already exists
+ return UPDATE_NOOP;
+ }
+ // Need to create
+ $ret = Database::exec("ALTER TABLE `$table` ADD CONSTRAINT FOREIGN KEY (`$column`)
+ REFERENCES `$refTable` (`$refColumn`)
+ $actions");
+ if ($ret === false) {
+ finalResponse(UPDATE_FAILED, 'DB: Cannot add constraint: ' . Database::lastError());
+ }
+ return UPDATE_DONE;
+}
+
+/**
* Drop constraint from a table.
*
* @param string $table table name
@@ -146,6 +191,22 @@ function tableCreate($table, $structure, $fatalOnError = true)
return UPDATE_FAILED;
}
+function responseFromArray($array)
+{
+ if (in_array(UPDATE_FAILED, $array)) {
+ finalResponse(UPDATE_FAILED, 'Update failed!');
+ }
+ if (in_array(UPDATE_RETRY, $array)) {
+ finalResponse(UPDATE_RETRY, 'Temporary failure, will try again.');
+ }
+ if (in_array(UPDATE_DONE, $array)) {
+ finalResponse(UPDATE_DONE, 'Tables created/updated successfully');
+ }
+
+ finalResponse(UPDATE_NOOP, 'Everything already up to date');
+
+}
+
/*
* Rest of install script....
*/
diff --git a/modules-available/baseconfig/api.inc.php b/modules-available/baseconfig/api.inc.php
index 64204774..a4024c5e 100644
--- a/modules-available/baseconfig/api.inc.php
+++ b/modules-available/baseconfig/api.inc.php
@@ -172,12 +172,14 @@ if (Request::any('save') === 'true') {
} else {
echo "# Error saving config to $path\n";
}
+ echo "SLX_NOW='", time(), "'\n";
}
}
// Output to browser
echo $lines;
} else {
// Only output to client
+ ConfigHolder::add('SLX_NOW', time(), PHP_INT_MAX);
ConfigHolder::outputConfig();
}
diff --git a/modules-available/baseconfig/page.inc.php b/modules-available/baseconfig/page.inc.php
index bd9d6683..366a1238 100644
--- a/modules-available/baseconfig/page.inc.php
+++ b/modules-available/baseconfig/page.inc.php
@@ -178,7 +178,7 @@ class Page_BaseConfig extends Page
$settings[$var['catid']]['settings'][$key]['displayvalue'] = $var['defaultvalue'];
}
if (!isset($settings[$var['catid']]['settings'][$key]['shadows'])) {
- $settings[$var['catid']]['settings'][$key]['shadows'] = null;
+ $settings[$var['catid']]['settings'][$key]['shadows'] = isset($var['shadows']) ? $var['shadows'] : null;
}
//echo "<pre>";
//var_dump($settings[$var['catid']]['settings'][$key]);
diff --git a/modules-available/baseconfig_bwlp/baseconfig/settings.json b/modules-available/baseconfig_bwlp/baseconfig/settings.json
index d0da2253..32d9aa88 100644
--- a/modules-available/baseconfig_bwlp/baseconfig/settings.json
+++ b/modules-available/baseconfig_bwlp/baseconfig/settings.json
@@ -73,6 +73,12 @@
"permissions": "2",
"validator": "list:socks4|socks5|http-connect|http-relay"
},
+ "SLX_BRIDGE_OTHER_NICS": {
+ "catid": "networking",
+ "defaultvalue": "no",
+ "permissions": "2",
+ "validator": "list:no|yes"
+ },
"SLX_REMOTE_LOG_SESSIONS": {
"catid": "other",
"defaultvalue": "anonymous",
@@ -115,6 +121,18 @@
"permissions": "2",
"validator": "regex:\/^\\d*$\/"
},
+ "SLX_SYSTEM_STANDBY_TIMEOUT": {
+ "catid": "power",
+ "defaultvalue": "",
+ "permissions": "2",
+ "validator": "regex:\/^\\d*$\/"
+ },
+ "SLX_WAKEUP_SCHEDULE": {
+ "catid": "power",
+ "defaultvalue": "",
+ "permissions": "2",
+ "validator": "regex:\/^(\\s*\\d{1,2}:\\d{1,2})*\\s*$\/"
+ },
"SLX_VMCHOOSER_TAB": {
"catid": "vmchooser",
"defaultvalue": "AUTO",
@@ -133,6 +151,12 @@
"permissions": "2",
"validator": "list:IGNORE|BUMP|EXCLUSIVE"
},
+ "SLX_VMCHOOSER_TIMEOUT": {
+ "catid": "vmchooser",
+ "defaultvalue": "120",
+ "permissions": "2",
+ "validator": "regex:\/^\\d*$\/"
+ },
"SLX_PRINT_USER_PREFIX": {
"catid": "sysconfig",
"defaultvalue": "",
@@ -145,6 +169,17 @@
"permissions": "2",
"validator": "regex:\/^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|)$\/i"
},
+ "SLX_AUTOLOGIN": {
+ "catid": "sysconfig",
+ "defaultvalue": "OFF",
+ "permissions": "2",
+ "validator": "list:ON|OFF",
+ "shadows": {
+ "ON": [
+ "SLX_VMCHOOSER_TIMEOUT"
+ ]
+ }
+ },
"SLX_PVS_DEFAULT": {
"catid": "vmchooser",
"defaultvalue": "OFF",
diff --git a/modules-available/baseconfig_bwlp/lang/de/config-variables.json b/modules-available/baseconfig_bwlp/lang/de/config-variables.json
index 06681a58..b88fddba 100644
--- a/modules-available/baseconfig_bwlp/lang/de/config-variables.json
+++ b/modules-available/baseconfig_bwlp/lang/de/config-variables.json
@@ -1,10 +1,13 @@
{
"SLX_ADDONS": "Zu ladende Addons. Zur Zeit steht nur *vmware* zur Verf\u00fcgung.",
+ "SLX_AUTOLOGIN": "Anmeldemaske \u00fcberspringen und direkt zum vmChooser gehen, bzw. die in SLX_AUTOSTART_UUID gesetzte Veranstaltung starten.",
"SLX_AUTOSTART_UUID": "ID einer Veranstaltung die automatisch gestartet werden soll. Die Veranstaltungs-ID finden Sie im Detailfenster innerhalb der bwLehrpool-Suite.\r\n\r\n*Hinweis: Diese Option ist eine tempor\u00e4re \u00dcbergangsl\u00f6sung. In sp\u00e4teren Versionen wird die Funktionalit\u00e4t einfacher erreichbar sein.*",
"SLX_BIOS_CLOCK": "Legt fest, ob und wie die interne Uhr des Rechners im Bezug auf die Systemzeit des \/MiniLinux\/ gesetzt werden soll.\r\n*off* = Die interne Uhr des Rechners wird nicht ver\u00e4ndert.\r\n*local* = Die interne Uhr wird auf die Lokalzeit gesetzt. Bevorzugt wenn z.B. noch eine native Windows-Installation auf dem PC vorhanden ist.\r\n*utc* = Die interne Uhr wird auf die \/Koordinierte Weltzeit\/ gesetzt. Dies ist die g\u00e4ngige Einstellung in einem reinen Linux-Umfeld.",
+ "SLX_BRIDGE_OTHER_NICS": "Sofern ein Client mehrere Netzwerkkarten besitzt, k\u00f6nnen Sie mittels dieser Option alle weiteren gefundenen Karten in die VM durchreichen.",
"SLX_DEMO_PASS": "Passwort f\u00fcr den eingebauten *demo*-Account. Leer lassen, um das Einloggen zu verbieten.\r\nDas Passwort wird wie das root-Passwort nur gehasht an den Client \u00fcbertragen.",
- "SLX_LOGOUT_TIMEOUT": "Zeit \/in Sekunden\/, die eine Benutzersitzung ohne Aktion sein darf, bevor sie beendet wird.Feld leer lassen, um die Funktion zu deaktivieren.",
+ "SLX_LOGOUT_TIMEOUT": "Zeit in Sekunden, die eine Benutzersitzung ohne Aktion sein darf, bevor sie beendet wird.Feld leer lassen, um die Funktion zu deaktivieren.",
"SLX_NET_DOMAIN": "DNS-Dom\u00e4ne, in die sich die Clients eingliedern, sofern der DHCP Server keine solche vorgibt.",
+ "SLX_NET_SEARCH": "Per Leerzeichen getrennte Liste von Suchdom\u00e4nen, die der Client verwenden soll, sofern der DHCP-Server keine Vorgabe macht.",
"SLX_NTP_SERVER": "Adresse des NTP-Zeitservers. Es k\u00f6nnen mehrere Server mit Leerzeichen getrennt angegeben werden.Die Server werden der Reihe nach angefragt, bis ein antwortender Server gefunden wird.",
"SLX_PRINT_USER_PREFIX": "Pr\u00e4fix, was im Authentifizierungsdialog der PrinterGUI dem Benutzernamen vorangestellt wird.\r\nWenn das Drucksystem auf einem AD-Server l\u00e4uft und der Dom\u00e4nenname vorangestellt werden muss, tragen Sie hier *domain\\* ein. Achten Sie auf die Angabe des Backslashes, er wird nicht automatisch angeh\u00e4ngt. Falls das Drucksystem mit dem reinen Benutzernamen zurecht kommt, k\u00f6nnen Sie das Feld leer lassen.",
"SLX_PROXY_BLACKLIST": "Adressen bzw. Adressbereiche, f\u00fcr die der Proxyserver nicht verwendet werden soll (z.B. der Adressbereich der Einrichtung). G\u00fcltige Angaben sind einzelne IP-Adressen, sowie IP-Bereiche in CIDR-Notation (z.B. 1.2.0.0\/16). Mehrere Angaben k\u00f6nnen durch Leerzeichen getrennt werden.",
@@ -18,8 +21,11 @@
"SLX_ROOT_PASS": "Das root-Passwort des Grundsystems. Wird nur f\u00fcr Diagnosezwecke am Client ben\u00f6tigt.\r\nFeld leer lassen, um root-Logins zu verbieten.\r\n\/Hinweis\/: Das Passwort wird im Klartext in der lokalen Datenbank hinterlegt, jedoch immer gehasht an die Clients \u00fcbermittelt (SHA-512 mit Salt). Wenn Sie das Passwort auch im Satelliten nicht im Klartext speichern wollen, k\u00f6nnen Sie hier auch ein vorgehashtes Passwort eintragen (im *$6$....*-Format).",
"SLX_SCREEN_STANDBY_TIMEOUT": "Zeit in Sekunden, nach der der Bildschirm bei Inaktivit\u00e4t des Rechners in den Standby-Modus versetzt wird.",
"SLX_SHUTDOWN_SCHEDULE": "Feste Uhrzeit, zu der sich die Rechner ausschalten, auch wenn noch ein Benutzer aktiv ist.\r\nMehrere Zeitpunkte k\u00f6nnen durch Leerzeichen getrennt angegeben werden.",
- "SLX_SHUTDOWN_TIMEOUT": "Zeit in Sekunden, nach dem ein Rechner abgeschaltet wird, sofern kein Benutzer angemeldet ist.\r\nFeld leer lassen, um die Funktion zu deaktivieren.",
+ "SLX_SHUTDOWN_TIMEOUT": "Zeit in Sekunden, nach der ein Rechner abgeschaltet wird, sofern kein Benutzer angemeldet ist.\r\nFeld leer lassen, um die Funktion zu deaktivieren.",
+ "SLX_SYSTEM_STANDBY_TIMEOUT": "Zeit in Sekunden, nach der ein Rechner in den Standby-Modus versetzt wird, sofern kein Benutzer angemeldet ist.\r\nFeld leer lassen, um die Funktion zu deaktivieren.\r\n\r\nBitte beachten Sie, dass der Standby-Modus auf bestimmter Hardware nicht oder nur unzuverl\u00e4ssig funktioniert. Es empfiehlt sich, diese Funktion nur in R\u00e4umen zu aktivieren bei denen vorher \u00fcberpr\u00fcft wurde, dass der Standby-Modus ordnungsgem\u00e4\u00df funktioniert.\r\nDer Standby-Modus kann manuell ausgel\u00f6st werden, indem auf dem Client als root-Benutzer der Befehl *systemctl suspend* ausgef\u00fchrt wird.",
"SLX_VMCHOOSER_FORLOCATION": "Legt das Verhalten fest, wenn es Veranstaltungen gibt, die an einen bestimmten Ort\/Raum gebunden sind.\r\n*IGNORE*: Mit den restlichen, globalen Veranstaltungen alphabetisch sortiert auflisten.\r\n*BUMP*: Die spezifischen Veranstaltungen oben auflisten, die globalen darunter.\r\n*EXCLUSIVE*: Spezifische Veranstaltungen oben auflisten, globale Veranstaltungen zun\u00e4chst ausblenden. Die globalen Veranstaltungen befinden sich unter einem eingeklappten Listenknoten.",
"SLX_VMCHOOSER_TAB": "Bestimmt, welcher Karteireiter im vmChooser standardm\u00e4\u00dfig ausgew\u00e4hlt wird.\r\n*0*: Native Linux-Sessions\r\n*1*: Nutzerspezifische Kurse\r\n*2*: Alle Kurse\r\n*AUTO*: Hat der Rechner beschr\u00e4nkte Ressourcen, werden die Linux-Sitzungen angezeigt, sonst alle Kurse\r\n\r\nHat der Benutzer ein persistentes Home-Verzeichnis, wirkt sich diese Einstellung nur beim ersten Anmelden aus. Bei sp\u00e4teren Sitzungen markiert der vmChooser die zuletzt gestartete Sitzung und wechselt zum entsprechenden Karteireiter.",
- "SLX_VMCHOOSER_TEMPLATES": "Legt fest, wie Veranstaltungen in der Sortierung behandelt werden, welche auf eine VM linken, die eine Vorlage ist.\r\n*IGNORE*: Wie regul\u00e4re Veranstaltungen behandeln\r\n*BUMP*: Weiter oben in der Liste einsortieren"
+ "SLX_VMCHOOSER_TEMPLATES": "Legt fest, wie Veranstaltungen in der Sortierung behandelt werden, welche auf eine VM linken, die eine Vorlage ist.\r\n*IGNORE*: Wie regul\u00e4re Veranstaltungen behandeln\r\n*BUMP*: Weiter oben in der Liste einsortieren",
+ "SLX_VMCHOOSER_TIMEOUT": "Zeit in Sekunden, die der Nutzer zur Auswahl einer Sitzung im vmChooser hat. Dieser Z\u00e4hler wird bei Maus-\/Tastaturaktivit\u00e4t zur\u00fcckgesetzt.",
+ "SLX_WAKEUP_SCHEDULE": "Feste Uhrzeit, zu der sich die Rechner einschalten. Der Zeitpunkt gilt derzeit f\u00fcr jeden Tag - auch am Wochenende. Mehrere Zeitpunkte k\u00f6nnen durch Leerzeichen getrennt angegeben werden.\r\n\r\nBitte beachten Sie, dass diese Funktion auf bestimmter Hardware nicht oder nur unzuverl\u00e4ssig funktioniert. Es empfiehlt sich, diese Funktion nur in R\u00e4umen zu aktivieren bei denen vorher \u00fcberpr\u00fcft wurde, dass das Aufwecken ordnungsgem\u00e4\u00df funktioniert."
} \ No newline at end of file
diff --git a/modules-available/baseconfig_bwlp/lang/en/config-variables.json b/modules-available/baseconfig_bwlp/lang/en/config-variables.json
index 6aee946b..4778ae67 100644
--- a/modules-available/baseconfig_bwlp/lang/en/config-variables.json
+++ b/modules-available/baseconfig_bwlp/lang/en/config-variables.json
@@ -1,10 +1,13 @@
{
"SLX_ADDONS": "Addons to load. Currently, only *vmware* is available.",
+ "SLX_AUTOLOGIN": "Skip login screen and directly show vmChooser (or start the lecture specified in SLX_AUTOSTART_UUID).",
"SLX_AUTOSTART_UUID": "ID of a lecture which is automatically started. The lecture-ID is found in the detail window of a lecture in the bwLehrpool-Suite. \r\n\r\n*This solution is only temporary. In later versions this feature will probably be moved to another section*",
"SLX_BIOS_CLOCK": "Specifies whether and how the internal clock of the computer should be set in relation to the system time of the \/MiniLinux\/.\r\n*off* = The internal clock of the computer is not changed.\r\n*local* = The internal clock is set to local time. Preferably if, for example, there is still a native Windows installation available on the PC.\r\n*utc* = The internal clock is set to the \/Coordinated Universal Time\/. This is the most common setup in a pure Linux environment.",
+ "SLX_BRIDGE_OTHER_NICS": "If enabled, additional network cards installed in the Client will be bridged to the VM. ",
"SLX_DEMO_PASS": "Password for the *demo* account. Leave empty to disallow logging in as the demo user.\r\nLike the root password, the demo user's password will be sent to the client in its hashed form.",
- "SLX_LOGOUT_TIMEOUT": "Time \/in seconds\/, in which a user session may remain without action before it is terminated.Leave field blank to disable the function.",
+ "SLX_LOGOUT_TIMEOUT": "Time in seconds, in which a user session may remain without action before it is terminated.Leave field blank to disable the function.",
"SLX_NET_DOMAIN": "DNS domain in which the client integrate, provided the DHCP server does not specifies such.",
+ "SLX_NET_SEARCH": "Space separated list of DNS search domains to use in case the DHCP server doesn't supply any.",
"SLX_NTP_SERVER": "Address of the NTP time server. Multiple servers can be specified separated by spaces.The servers are queried in sequence until a responding server is found.",
"SLX_PRINT_USER_PREFIX": "Prefix to add to the user name in the authentication dialog of PrinterGUI.\r\nIf your print server belongs to a Windows domain and requires the domain name prefixed, set this field to *domainname\\*. Note the trailing backslash, it will not be inserted automatically. If your print server just wants the plain user name, this field should be left blank.",
"SLX_PROXY_BLACKLIST": "Address or addresses ranges in which the proxy server is not used (for example the address range of the device). Valid entries are individual IP addresses and IP ranges in CIDR notation (for example 1.2.0.0\/16). Multiple selections can be separated by spaces.",
@@ -19,7 +22,10 @@
"SLX_SCREEN_STANDBY_TIMEOUT": "Time in seconds after which the screen will enter power saving mode, if the client is not in use.",
"SLX_SHUTDOWN_SCHEDULE": "Fixed time to turn off the computer, even if there is a user active.\r\nSeveral times can be specified, separated by spaces.",
"SLX_SHUTDOWN_TIMEOUT": "Time in seconds after which a computer is switched off, if no user is logged on.\r\nLeave blank to disable the function.",
+ "SLX_SYSTEM_STANDBY_TIMEOUT": "Timeout in seconds after which the computer enters stand-by mode if no user is logged on.\r\nLeave blank to disable.\r\n\r\nNote that some hardware might not properly support stand-by and crash or freeze on wakeup. It's recommended to only enable this feature for rooms where it's known that the hardware supports stand-by mode. You can manually trigger stand-by mode on a client by logging in as root and executing *systemctl suspend*.",
"SLX_VMCHOOSER_FORLOCATION": "Defines how lectures special to the user's location are handled in the vmChooser.\r\n*IGNORE*: Sort them alphabetically among the global lectures.\r\n*BUMP*: Put them atop the global lectures.\r\n*EXCLUSIVE*: Put them atop the global lectures and aditionally collapse the node which contains the global lectures.",
"SLX_VMCHOOSER_TAB": "Defines which tab is show by default, if the user doesn't have stored a last used session on his persistent home directory.\r\n*0*: Native Linux sessions\r\n*1*: User specific lectures\r\n*2*: All lectures\r\n*AUTO*: If the computer has low system specs, show the Linux sessions, otherwise, show all lectures",
- "SLX_VMCHOOSER_TEMPLATES": "Defines how lectures that link to template VMs are treated wrt sorting.\r\n*IGNORE*: Sort among regular lectures\r\n*BUMP*: Move to top of list"
+ "SLX_VMCHOOSER_TEMPLATES": "Defines how lectures that link to template VMs are treated wrt sorting.\r\n*IGNORE*: Sort among regular lectures\r\n*BUMP*: Move to top of list",
+ "SLX_VMCHOOSER_TIMEOUT": "Timeout in seconds after which the session will be closed if the user doesn't make any selection in vmChooser. Mouse or keyboard activity resets this timeout.",
+ "SLX_WAKEUP_SCHEDULE": "Fixed time to turn on the computer. At the moment this feature takes effect on every day - also at the weekend. Several times can be specified, separated by spaces.\r\n\r\nNote that some hardware might not properly support wakeup. It's recommended to only enable this feature for rooms where it's known that the hardware supports it."
} \ No newline at end of file
diff --git a/modules-available/dnbd3/inc/dnbd3util.inc.php b/modules-available/dnbd3/inc/dnbd3util.inc.php
index a9b9241e..02ca89c2 100644
--- a/modules-available/dnbd3/inc/dnbd3util.inc.php
+++ b/modules-available/dnbd3/inc/dnbd3util.inc.php
@@ -171,6 +171,7 @@ class Dnbd3Util {
ConfigHolder::add('SLX_SHUTDOWN_SCHEDULE', '', 1000);
ConfigHolder::add('SLX_REBOOT_TIMEOUT', '', 1000);
ConfigHolder::add('SLX_REBOOT_SCHEDULE', '', 1000);
+ ConfigHolder::add('SLX_SYSTEM_STANDBY_TIMEOUT', '', 1000);
}
/**
diff --git a/modules-available/dnbd3/install.inc.php b/modules-available/dnbd3/install.inc.php
new file mode 100644
index 00000000..c5096cfe
--- /dev/null
+++ b/modules-available/dnbd3/install.inc.php
@@ -0,0 +1,41 @@
+<?php
+
+$res = array();
+
+$res[] = tableCreate('dnbd3_server', "
+ `serverid` int(11) NOT NULL AUTO_INCREMENT,
+ `machineuuid` char(36) CHARACTER SET ascii DEFAULT NULL,
+ `fixedip` varchar(45) CHARACTER SET ascii DEFAULT NULL,
+ `runid` bigint(20) NOT NULL DEFAULT '0',
+ `lastseen` bigint(20) NOT NULL DEFAULT '0',
+ `uptime` bigint(20) NOT NULL DEFAULT '0',
+ `totalup` bigint(20) NOT NULL DEFAULT '0',
+ `totaldown` bigint(20) NOT NULL DEFAULT '0',
+ `lastup` bigint(20) NOT NULL DEFAULT '0',
+ `lastdown` bigint(20) NOT NULL DEFAULT '0',
+ `clientcount` int(11) NOT NULL DEFAULT '0',
+ `disktotal` bigint(20) NOT NULL DEFAULT '0',
+ `diskfree` bigint(20) NOT NULL DEFAULT '0',
+ `errormsg` varchar(200) DEFAULT NULL,
+ PRIMARY KEY (`serverid`),
+ UNIQUE KEY `machineuuid` (`machineuuid`),
+ UNIQUE KEY `fixedip` (`fixedip`)
+");
+
+$res[] = tableCreate('dnbd3_server_x_location', '
+ `serverid` int(11) NOT NULL,
+ `locationid` int(11) NOT NULL,
+ PRIMARY KEY (`serverid`,`locationid`),
+ KEY `locationid` (`locationid`)
+');
+
+$res[] = tableAddConstraint('dnbd3_server', 'machineuuid', 'machine', 'machineuuid',
+ 'ON UPDATE CASCADE ON DELETE CASCADE');
+
+$res[] = tableAddConstraint('dnbd3_server_x_location', 'locationid', 'location', 'locationid',
+ 'ON UPDATE CASCADE ON DELETE CASCADE');
+
+$res[] = tableAddConstraint('dnbd3_server_x_location', 'serverid', 'dnbd3_server', 'serverid',
+ 'ON UPDATE CASCADE ON DELETE CASCADE');
+
+responseFromArray($res);
diff --git a/modules-available/dnbd3/lang/de/template-tags.json b/modules-available/dnbd3/lang/de/template-tags.json
index 0573a68e..a178df6d 100644
--- a/modules-available/dnbd3/lang/de/template-tags.json
+++ b/modules-available/dnbd3/lang/de/template-tags.json
@@ -1,5 +1,6 @@
{
"lang_addServer": "Server hinzuf\u00fcgen",
+ "lang_allowNfsFallback": "NFS-Fallback aktivieren",
"lang_allowedSubnets": "Zum Zugriff freigegebene Subnets",
"lang_altservers": "Uplinks",
"lang_backgroundReplication": "Replikation im Hintergrund",
@@ -14,7 +15,7 @@
"lang_count": "Anzahl",
"lang_disabled": "Deaktiviert",
"lang_diskFree": "Freier Speicher",
- "lang_dnbd3IntroText": "DNBD3 ist ganz toll. Einfach einschalten und los gehts, zu beachten gibt es rein gar nichts!",
+ "lang_dnbd3IntroText": "DNBD3 ist ein verteiltes Speichersystem speziell f\u00fcr die Anforderungen von bwLehrpool. Erst in Verbindung mit mindestens einem Proxy (zus\u00e4tzlich zum Satellitenserver) kann das System seine Geschwindigkeitsvorteile gegen\u00fcber NFS\/CIFS ausspielen. F\u00fcr schlecht angebundene Poolr\u00e4ume empfiehlt sich jeweils ein eigener Proxy-Server.\r\nBitte beachten Sie die Hinweise im Wiki.",
"lang_dnbd3Management": "DNBD3 Verwaltung",
"lang_dnbd3Status": "DNBD3 Status",
"lang_editProxyHeading": "Proxy-Einstellungen bearbeiten",
@@ -38,7 +39,7 @@
"lang_managedServerHelp": "Automatisch konfigurierte DNBD3-Proxies booten wie gew\u00f6hnliche bwLehrpool-Clients via PXE \u00fcber den Satelliten-Server. Sobald ein bwLehrpool-Client als DNBD3-Proxy konfiguriert wird, erh\u00e4lt er beim Booten eine gesonderte Konfiguration, sodass er fortan exklusiv als DNBD3-Proxy arbeitet, und nicht mehr als Arbeitsstation zur Verf\u00fcgung steht.\r\nDer Vorteil ist, dass die Konfiguration automatisiert erfolgt, und durch w\u00f6chentliche Reboots sichergestellt wird, dass eventuelle Updates des MiniLinux angewendet werden.\r\nIn diesem Fall legen Sie bitte eine Partition mit der ID 45 auf der Festplatte des Proxy-Servers an; diese wird persistent Behandelt und im Gegensatz zur ID44-Partition nicht beim Booten formatiert. Generell sollte diese Partition so gro\u00df wie m\u00f6glich sein, abh\u00e4ngig von der Anzahl der genutzten VMs. Bei Platzmangel l\u00f6scht der Proxy automatisch die VM, die am l\u00e4ngsten nicht verwendet wurde, um neuen VMs Platz zu machen.\r\nWeitere Informationen dazu finden Sie im Wiki.",
"lang_numFails": "Fehler",
"lang_proxyConfig": "Konfiguration",
- "lang_proxyLocationText": "Bla bla bla BLA!!!!",
+ "lang_proxyLocationText": "Hier k\u00f6nnen Sie festlegen, dass nur Clients aus bestimmten R\u00e4umen\/Orten diesen Proxy verwenden. Damit vermeiden Sie die Metrikmessung zwischen Client und Proxy, wenn aufgrund der Infrastruktur bereits bekannt ist, dass dieser Proxy nur f\u00fcr bestimmte R\u00e4ume sinnvoll ist. ",
"lang_proxyServerTHead": "Server\/Proxy",
"lang_reboot": "Reboot",
"lang_rebootProxyHeading": "Proxy neustarten",
diff --git a/modules-available/dnbd3/lang/en/messages.json b/modules-available/dnbd3/lang/en/messages.json
index 4c658e11..731b4ad9 100644
--- a/modules-available/dnbd3/lang/en/messages.json
+++ b/modules-available/dnbd3/lang/en/messages.json
@@ -1,3 +1,11 @@
{
+ "dnbd3-proxy-unreachable": "DNBD3-Proxy {{0}} is offline since {{2}}. ({{1}}) ",
+ "invalid-ipv4": "{{0}} is no valid IPv4-Address",
+ "main-dnbd3-unreachable": "Main DNBD3-Server of the satellite server is offline since {{1}}. ({{0}})",
+ "not-automatic-server": "{{0}} is not managed by this satellite server",
+ "server-added": "Server {{0}} added",
+ "server-already-exists": "Server {{0}} already exists",
+ "server-deleted": "Server {{0}} deleted",
+ "server-non-existent": "Server {{0}} doesn't exist",
"server-unreachable": "Server not reachable"
} \ No newline at end of file
diff --git a/modules-available/dnbd3/lang/en/template-tags.json b/modules-available/dnbd3/lang/en/template-tags.json
new file mode 100644
index 00000000..0441521f
--- /dev/null
+++ b/modules-available/dnbd3/lang/en/template-tags.json
@@ -0,0 +1,58 @@
+{
+ "lang_addServer": "Add server",
+ "lang_allowNfsFallback": "NFS fallback",
+ "lang_allowedSubnets": "Allowed subnets",
+ "lang_altservers": "Uplinks",
+ "lang_backgroundReplication": "Background replication",
+ "lang_backgroundReplicationInfo": "If a VM is requested by this proxy, the proxy mirrors the complete VM in the background, not only the requested data blocks.",
+ "lang_bytesSent": "Sent",
+ "lang_changeDnbd3Status": "Enable\/disable DNBD3",
+ "lang_client": "Client",
+ "lang_clientCount": "Clients",
+ "lang_clientList": "List of clients",
+ "lang_clientsByLocation": "Clients by location",
+ "lang_comment": "Comment",
+ "lang_count": "Count",
+ "lang_disabled": "Disabled",
+ "lang_diskFree": "Free space",
+ "lang_dnbd3IntroText": "DNBD3 is a distributed storage system for the special requirements of bwLehrpool. To see speed improvements over NFS\/CIFS you need at least one proxy (besides the satellite server). If you have locations with slow network connection it's advisable to have one proxy per location. Please mind the information in the wiki.",
+ "lang_dnbd3Management": "DNBD3 management",
+ "lang_dnbd3Status": "DNBD3 status",
+ "lang_editProxyHeading": "Edit proxy settings",
+ "lang_enableDnbd3": "Enable DNBD3",
+ "lang_enabled": "Enabled",
+ "lang_enterIpOfServer": "Please enter the ip address ot the server",
+ "lang_externalServer": "External DNBD3-Server",
+ "lang_externalServerAdd": "Add external server",
+ "lang_externalServerHelp": "An external server is not configured and managed by the satellite server. The installation, configuration and update of the DNBD3 software has to be done manually. This is a more flexible approach which is preferable for RAID or bcache setups or if the DNBD3-Server offers additional services. More information in the wiki.",
+ "lang_firewallInfo": "If the proxy is restricted to one or more locations, clients from other locations won't use that specific proxy. But technically it's still possible to gain access from other locations. If you activate this setting the access from other locations is blocked with iptables.",
+ "lang_firewalled": "Limit access to corresponding locations",
+ "lang_flags": "Flags",
+ "lang_global": "Global",
+ "lang_lastSeen": "Last seen",
+ "lang_latency": "Latency",
+ "lang_location": "Location",
+ "lang_locations": "Locations",
+ "lang_manageAccessTo": "Manage access to server:",
+ "lang_managedServer": "Automatically configured DNBD3-Proxy",
+ "lang_managedServerAdd": "Add automatically configured proxy",
+ "lang_managedServerHelp": "Automatically configured DNBD3-Proxies will boot like normal bwLehrpool-Clients over PXE and the satellite server. If a client is configured as proxy it will boot with a different configuration and acts exclusively as proxy. The client can therefore not be used as a normal working station.\r\nThe advantage is that you don't need to install or configure anything else. The client will reboot every week to get possible updates ot the minilinux.\r\nIf you want to use this feature, please create a partition with ID 45 on the local hard disk of the proxy server. In contrast to the ID 44 partition which is formated after every reboot, this partition is persistent. As a rule of thumb the partition should be as big as possible. If there is no space left the proxy will delete the VM which hasn't be used for the longest time. More information in the wiki.",
+ "lang_numFails": "Errors",
+ "lang_proxyConfig": "Configuration",
+ "lang_proxyLocationText": "Here you can restrict the usage of this proxy to certain locations. This can be useful if the usage is only reasonable from some locations. That may be because of the network infrastructure.",
+ "lang_proxyServerTHead": "Server\/Proxy",
+ "lang_reboot": "Reboot",
+ "lang_rebootProxyHeading": "Reboot proxy",
+ "lang_rebootProxyText": "If the configuration has changed, the server needs a reboot to adapt to it.",
+ "lang_recursiveCount": "Recursive",
+ "lang_rxTotal": "Total received",
+ "lang_serverList": "List of servers",
+ "lang_sessionRx": "Received since boot",
+ "lang_sessionTx": "Sent since boot",
+ "lang_settings": "Settings",
+ "lang_storageSize": "Storage size",
+ "lang_test": "Test",
+ "lang_txTotal": "Total sent",
+ "lang_uptime": "Uptime",
+ "lang_wantToDelete": "Do you really want to delete this server? (Reboot\/Shutdown has to be done manually)"
+} \ No newline at end of file
diff --git a/modules-available/locationinfo/api.inc.php b/modules-available/locationinfo/api.inc.php
index e14fe9d7..a89f16ed 100644
--- a/modules-available/locationinfo/api.inc.php
+++ b/modules-available/locationinfo/api.inc.php
@@ -1,33 +1,5 @@
<?php
-if (Request::get('redirect', false, 'int') !== false) {
- // Redirect to actual panel from uuid
- $uuid = Request::get('uuid', false, 'string');
- if ($uuid === false) {
- http_response_code(400);
- die('Missing uuid parameter');
- }
- $row = Database::queryFirst('SELECT paneltype, panelconfig FROM locationinfo_panel WHERE paneluuid = :uuid', compact('uuid'));
- if ($row === false) {
- http_response_code(404);
- die('Panel not found');
- }
- if ($row['paneltype'] === 'DEFAULT') {
- Util::redirect(dirname($_SERVER['SCRIPT_NAME']) . '/modules/locationinfo/frontend/doorsign.html?uuid=' . $uuid);
- } elseif ($row['paneltype'] === 'SUMMARY') {
- Util::redirect(dirname($_SERVER['SCRIPT_NAME']) . '/modules/locationinfo/frontend/overview.html?uuid=' . $uuid);
- } elseif ($row['paneltype'] === 'URL') {
- $data = json_decode($row['panelconfig'], true);
- if (!$data || !isset($data['url'])) {
- http_response_code('500');
- die('Panel config corrupted on server');
- }
- Util::redirect($data['url']);
- }
- http_response_code('500');
- die('Panel has invalid type "' . $row['paneltype'] . '"');
-}
-
/*
* vvv - API to Panel - vvv
*/
@@ -46,7 +18,7 @@ function HandleParameters()
if ($get === "timestamp") {
$output = array('ts' => getLastChangeTs($uuid));
} elseif ($get === "machines") {
- $locationIds = getLocationsOr404($uuid);
+ $locationIds = LocationInfo::getLocationsOr404($uuid);
$output = array();
InfoPanel::appendMachineData($output, $locationIds, false);
$output = array_values($output);
@@ -57,13 +29,13 @@ function HandleParameters()
die('Panel not found');
}
} elseif ($get === "pcstates") {
- $locationIds = getLocationsOr404($uuid);
+ $locationIds = LocationInfo::getLocationsOr404($uuid);
$output = getPcStates($locationIds);
} elseif ($get === "locationtree") {
- $locationIds = getLocationsOr404($uuid);
+ $locationIds = LocationInfo::getLocationsOr404($uuid);
$output = getLocationTree($locationIds);
} elseif ($get === "calendar") {
- $locationIds = getLocationsOr404($uuid);
+ $locationIds = LocationInfo::getLocationsOr404($uuid);
$output = getCalendar($locationIds);
}
if ($output !== false) {
@@ -76,22 +48,6 @@ function HandleParameters()
}
/**
- * Return list of locationids associated with given panel.
- * @param string $paneluuid panel
- * @return int[] locationIds
- */
-function getLocationsOr404($paneluuid)
-{
- $panel = Database::queryFirst('SELECT locationids FROM locationinfo_panel WHERE paneluuid = :paneluuid',
- compact('paneluuid'));
- if ($panel !== false) {
- return array_map('intval', explode(',', $panel['locationids']));
- }
- http_response_code(404);
- die('Panel not found');
-}
-
-/**
* Get last config modification timestamp for given panel.
* This was planned to be smart and check the involved locations,
* even going up the location tree if the opening time schedule
@@ -132,8 +88,9 @@ function getPcStates($idList)
'id' => $id,
'idle' => 0,
'occupied' => 0,
- 'off' => 0,
+ 'offline' => 0,
'broken' => 0,
+ 'standby' => 0,
);
}
diff --git a/modules-available/locationinfo/inc/infopanel.inc.php b/modules-available/locationinfo/inc/infopanel.inc.php
index 66ee0ae7..94f264bb 100644
--- a/modules-available/locationinfo/inc/infopanel.inc.php
+++ b/modules-available/locationinfo/inc/infopanel.inc.php
@@ -20,6 +20,7 @@ class InfoPanel
}
if ($panel['paneltype'] === 'URL') {
+ // Shortcut for URL redirect
$config = json_decode($panel['panelconfig'], true);
return $panel['paneltype'];
}
@@ -31,6 +32,7 @@ class InfoPanel
if (!empty($panel['panelconfig'])) {
$json = json_decode($panel['panelconfig'], true);
if (is_array($json)) {
+ // Put location-specific overrides in separate variable for later use
if (isset($json['overrides']) && is_array($json['overrides'])) {
$overrides = $json['overrides'];
}
@@ -48,6 +50,7 @@ class InfoPanel
'id' => $lid,
'name' => isset($locations[$lid]) ? $locations[$lid]['locationname'] : 'noname00.pas',
);
+ // Now apply any overrides from above
if (isset($overrides[$lid]) && is_array($overrides[$lid])) {
$config['locations'][$lid]['config'] = $overrides[$lid];
}
@@ -80,13 +83,21 @@ class InfoPanel
$idList = array_keys($array);
}
+ $ignoreList = array();
+ if (Module::isAvailable('runmode')) {
+ // Ignore clients with special runmode not marked as still being a client
+ $ignoreList = RunMode::getAllClients(false, false);
+ }
+
$positionCol = $withPosition ? 'm.position,' : '';
- $query = "SELECT m.locationid, m.machineuuid, $positionCol m.logintime, m.lastseen, m.lastboot FROM machine m
+ $query = "SELECT m.locationid, m.machineuuid, $positionCol m.logintime, m.lastseen, m.lastboot, m.state FROM machine m
WHERE m.locationid IN (:idlist)";
$dbquery = Database::simpleQuery($query, array('idlist' => $idList));
// Iterate over matching machines
while ($row = $dbquery->fetch(PDO::FETCH_ASSOC)) {
+ if (isset($ignoreList[$row['machineuuid']]))
+ continue;
settype($row['locationid'], 'int');
if (!isset($array[$row['locationid']])) {
$array[$row['locationid']] = array('id' => $row['locationid'], 'machines' => array());
@@ -107,7 +118,7 @@ class InfoPanel
}
}
$pc['pcState'] = LocationInfo::getPcState($row);
- //$pc['pcState'] = ['BROKEN', 'OFF', 'IDLE', 'OCCUPIED'][mt_rand(0,3)]; // XXX
+ //$pc['pcState'] = ['BROKEN', 'OFFLINE', 'IDLE', 'OCCUPIED', 'STANDBY'][mt_rand(0,4)]; // XXX
// Add the array to the machines list.
$array[$row['locationid']]['machines'][] = $pc;
@@ -216,4 +227,4 @@ class InfoPanel
return $result;
}
-} \ No newline at end of file
+}
diff --git a/modules-available/locationinfo/inc/locationinfo.inc.php b/modules-available/locationinfo/inc/locationinfo.inc.php
index 830fb050..64070cd4 100644
--- a/modules-available/locationinfo/inc/locationinfo.inc.php
+++ b/modules-available/locationinfo/inc/locationinfo.inc.php
@@ -6,7 +6,7 @@ class LocationInfo
/**
* Gets the pc data and returns it's state.
*
- * @param array $pc The pc data from the db. Array('logintime' =>, 'lastseen' =>, 'lastboot' =>)
+ * @param array $pc The pc data from the db. Array('state' => xx, 'lastseen' => xxx)
* @return int pc state
*/
public static function getPcState($pc)
@@ -16,16 +16,36 @@ class LocationInfo
$lastboot = (int)$pc['lastboot'];
$NOW = time();
- if ($NOW - $lastseen > 14 * 86400) {
+ if ($pc['state'] === 'OFFLINE' && $NOW - $lastseen > 21 * 86400) {
return "BROKEN";
- } elseif (($NOW - $lastseen > 610) || $lastboot === 0) {
- return "OFF";
- } elseif ($logintime === 0) {
- return "IDLE";
- } elseif ($logintime > 0) {
- return "OCCUPIED";
}
- return -1;
+ return $pc['state'];
+ }
+
+ /**
+ * Return list of locationids associated with given panel.
+ * @param string $paneluuid panel
+ * @param bool $recursive if true and paneltype == SUMMARY the result is recursive with all child room ids.
+ * @return int[] locationIds
+ */
+ public static function getLocationsOr404($paneluuid, $recursive = true)
+ {
+ $panel = Database::queryFirst('SELECT paneltype, locationids FROM locationinfo_panel WHERE paneluuid = :paneluuid',
+ compact('paneluuid'));
+ if ($panel !== false) {
+ $idArray = array_map('intval', explode(',', $panel['locationids']));
+ if ($panel['paneltype'] == "SUMMARY" && $recursive) {
+ $idList = Location::getRecursiveFlat($idArray);
+ $idArray = array();
+
+ foreach ($idList as $key => $value) {
+ $idArray[] = $key;
+ }
+ }
+ return $idArray;
+ }
+ http_response_code(404);
+ die('Panel not found');
}
/**
@@ -53,27 +73,38 @@ class LocationInfo
}
/**
- * Creates and returns a default config for room that didn't saved a config yet.
+ * Creates and returns a default config for room that didn't save a config yet.
*
* @return array Return a default config.
*/
public static function defaultPanelConfig($type)
{
- return array(
- 'language' => 'en',
- 'mode' => 1,
- 'vertical' => false,
- 'eco' => false,
- 'prettytime' => true,
- 'scaledaysauto' => true,
- 'daystoshow' => 7,
- 'rotation' => 0,
- 'scale' => 50,
- 'switchtime' => 20,
- 'calupdate' => 30,
- 'roomupdate' => 15,
- 'configupdate' => 180,
- );
+ if ($type === 'DEFAULT') {
+ return array(
+ 'language' => 'en',
+ 'mode' => 1,
+ 'vertical' => false,
+ 'eco' => false,
+ 'prettytime' => true,
+ 'scaledaysauto' => true,
+ 'daystoshow' => 7,
+ 'rotation' => 0,
+ 'scale' => 50,
+ 'switchtime' => 20,
+ 'calupdate' => 30,
+ 'roomupdate' => 15,
+ 'configupdate' => 180,
+ );
+ }
+ if ($type === 'SUMMARY') {
+ return array(
+ 'language' => 'en',
+ 'calupdate' => 30,
+ 'roomupdate' => 15,
+ 'configupdate' => 180,
+ );
+ }
+ return array();
}
/**
@@ -87,6 +118,12 @@ class LocationInfo
return $ret['panelname'];
}
+ /**
+ * Hook called by runmode module where we should modify the client config according to our
+ * needs. Disable standby/logout timeouts, enable autologin, set URL.
+ * @param $machineUuid
+ * @param $panelUuid
+ */
public static function configHook($machineUuid, $panelUuid)
{
$row = Database::queryFirst('SELECT paneltype, panelconfig FROM locationinfo_panel WHERE paneluuid = :uuid',
@@ -104,6 +141,7 @@ class LocationInfo
ConfigHolder::add('SLX_ADDONS', '', 1000);
ConfigHolder::add('SLX_LOGOUT_TIMEOUT', '', 1000);
ConfigHolder::add('SLX_SCREEN_STANDBY_TIMEOUT', '', 1000);
+ ConfigHolder::add('SLX_SYSTEM_STANDBY_TIMEOUT', '', 1000);
ConfigHolder::add('SLX_AUTOLOGIN', '1', 1000);
}
diff --git a/modules-available/locationinfo/lang/de/messages.json b/modules-available/locationinfo/lang/de/messages.json
index 8da5bd33..622b94ac 100644
--- a/modules-available/locationinfo/lang/de/messages.json
+++ b/modules-available/locationinfo/lang/de/messages.json
@@ -5,6 +5,9 @@
"ignored-invalid-range": "Eintrag mit ung\u00fcltiger Range ignoriert",
"ignored-invalid-start": "Eintrag mit ung\u00fcltiger Startzeit ignoriert",
"ignored-line-no-days": "Eintrag ohne ausgew\u00e4hlte Tage ignoriert",
+ "invalid-backend-type": "Ung\u00fcltiger Backend-Typ '{{0}}'",
"invalid-panel-id": "Ung\u00fcltige Panel-ID '{{0}}'",
- "invalid-server-id": "Ung\u00fcltige Server-ID '{{0}}'"
+ "invalid-panel-type": "Ung\u00fcltiger Panel-Typ '{{0}}'",
+ "invalid-server-id": "Ung\u00fcltige Server-ID '{{0}}'",
+ "server-id-missing": "Server-ID fehlt"
} \ No newline at end of file
diff --git a/modules-available/locationinfo/lang/de/template-tags.json b/modules-available/locationinfo/lang/de/template-tags.json
index 1574d9e8..be2814d6 100644
--- a/modules-available/locationinfo/lang/de/template-tags.json
+++ b/modules-available/locationinfo/lang/de/template-tags.json
@@ -8,6 +8,7 @@
"lang_calendar": "Kalender",
"lang_calupdateTooltip": "Zeit nachdem der Kalender aktualisiert wird (in Minuten)",
"lang_checkConnection": "Verbindung pr\u00fcfen",
+ "lang_closed": "Geschlossen",
"lang_closingTime": "Schlie\u00dfungszeit",
"lang_config": "Einstellungen",
"lang_configupdateTooltip": "Zeit nach der die Einstellungen aktualisiert werden (in Minuten)",
@@ -24,12 +25,17 @@
"lang_ecoMode": "E-Ink Modus",
"lang_ecoTooltip": "Anstelle der Farb-basierten PC-Status Bilder, werden Symbol-basierte PC Bilder verwendet",
"lang_editDefaultPanelHints": "Hier k\u00f6nnen Sie ein Panel (z.B. digitales T\u00fcrschild) in Aussehen und Funktionsweise definieren. Um im Kalender \u00d6ffnungszeiten anzeigen zu k\u00f6nnen, m\u00fcssen Sie im Tab \"Raum-\/Ortsbezogene Einstellungen\" f\u00fcr den ausgew\u00e4hlten Raum entsprechend \u00d6ffnungszeiten eintragen. Damit im Kalender Veranstaltungen und andere Termine angezeigt werden k\u00f6nnen, muss ein funktionierendes Backend konfiguriert und den ausgew\u00e4hlten R\u00e4umen zugewiesen worden sein.",
+ "lang_editSummaryPanelHints": "Hier k\u00f6nnen Sie ein Summary-Panel definieren. Das Panel zeigt eine Übersicht der in den R\u00e4umen enthalten PCs.",
"lang_editPanel": "Panel bearbeiten",
+ "lang_editUrlPanelHints": "Hier k\u00f6nnen Sie konfigurieren, welche URL das Panel aufrufen soll. Dies erm\u00f6glicht Ihnen z.B. in Eingangsbereichen aktuelle Meldungen der Hochschule oder sonstige Webseiten anzuzeigen.",
"lang_entryName": "Name",
"lang_error": "Fehler",
"lang_expertMode": "Expertenmodus",
"lang_fourLocsHint": "Hier k\u00f6nnen Sie bis zu vier Orte ausw\u00e4hlen, die in diesem Panel angezeigt werden.",
+ "lang_free": "Frei",
"lang_general": "Allgemein",
+ "lang_ignoreSslTooltip": "Akzeptiere ung\u00fcltige, abgelaufene oder selbstsignierte SSL-Zertifikate",
+ "lang_insecureSsl": "Unsicheres SSL",
"lang_language": "Sprache",
"lang_languageTooltip": "Legt die Sprache der angezeigten Oberfl\u00e4che fest",
"lang_locationName": "Name",
@@ -37,6 +43,13 @@
"lang_locations": "Orte",
"lang_locationsTable": "R\u00e4ume \/ Orte",
"lang_locationsTableHints": "Hier k\u00f6nnen Sie f\u00fcr die R\u00e4ume und Orte Ihrer Einrichtung \u00d6ffnungszeiten hinterlegen, sowie die Verkn\u00fcpfung mit Raum-IDs aus konfigurierten Backends (z.B. HISinOne) vornehmen, damit Belegungspl\u00e4ne abgerufen werden k\u00f6nnen.",
+ "lang_longFri": "Freitag",
+ "lang_longMon": "Montag",
+ "lang_longSat": "Samstag",
+ "lang_longSun": "Sonntag",
+ "lang_longThu": "Donnerstag",
+ "lang_longTue": "Dienstag",
+ "lang_longWed": "Mittwoch",
"lang_mode": "Modus",
"lang_mode1": "Kalender & Raum",
"lang_mode2": "Kalender",
@@ -55,6 +68,8 @@
"lang_panelsTable": "Panels verwalten",
"lang_panelsTableHints": "Hier sehen Sie alle erstellen Anzeigetafeln, die per Browser an beliebiger Stelle angezeigt werden k\u00f6nnen, z.B. als digitales T\u00fcrschild.",
"lang_pleaseSelect": "Bitte w\u00e4hlen\u2026",
+ "lang_prettytime": "PrettyTime",
+ "lang_prettytimeTooltip": "Verwende ein anderes Anzeigeformat f\u00fcr die Uhrzeit",
"lang_recursiveServerSet": "Auch f\u00fcr alle untergeordneten R\u00e4ume setzen",
"lang_recursiveSetTooltip": "Wenn aktiviert, wird der Backend-Server auch f\u00fcr alle untergeordneten R\u00e4ume auf den hier gew\u00e4hlten Wert gesetzt",
"lang_remoteSchedule": "Abruf Belegungsplan",
@@ -68,6 +83,7 @@
"lang_rotation2": "180\u00b0",
"lang_rotation3": "90\u00b0 \u27f3",
"lang_rotationTooltip": "Rotiert den angezeigten Raum",
+ "lang_runmodeTHead": "Clients",
"lang_saturday": "Samstag",
"lang_scale": "Kalenderbreite",
"lang_scaleTooltip": "[10-90] Legt die Kalenderbreite fest (in Prozent)",
@@ -77,36 +93,30 @@
"lang_serverTableHints": "Liste aller definierten Backend-Server. Diese werden ben\u00f6tigt, um Belegungspl\u00e4ne f\u00fcr R\u00e4ume abzurufen.",
"lang_serverTooltip": "Legt fest, von welchem Backend-Server die Kalenderdaten bezogen werden",
"lang_serverType": "Typ",
+ "lang_shortFri": "Fr",
"lang_shortFriday": "Fr",
+ "lang_shortMon": "Mo",
"lang_shortMonday": "Mo",
+ "lang_shortSat": "Sa",
"lang_shortSaturday": "Sa",
+ "lang_shortSun": "So",
"lang_shortSunday": "So",
+ "lang_shortThu": "Do",
"lang_shortThursday": "Do",
+ "lang_shortTue": "Di",
"lang_shortTuesday": "Di",
+ "lang_shortWed": "Mi",
"lang_shortWednesday": "Mi",
"lang_summaryPanel": "\u00dcbersichts-Panel",
"lang_sunday": "Sonntag",
"lang_switchTime": "Wechselintervall",
"lang_switchTimeTooltip": "[1-120] Legt die Zeit fest, die vergeht bis ein Wechsel erfolgt (in Sekunden)",
+ "lang_to": "bis",
"lang_typeTooltip": "Legt fest um welchen Server-Typ es sich handelt",
"lang_updateRates": "Aktualisierungsintervall",
+ "lang_url": "URL",
+ "lang_urlPanel": "URL-Panel",
+ "lang_urlTooltip": "URL die aufgerufen wird",
"lang_vertical": "Vertikaler Modus",
- "lang_verticalTooltip": "Legt fest, ob Kalender und Raum \u00fcbereinander angezeigt werden sollen",
- "lang_closed": "Geschlossen",
- "lang_free": "Frei",
- "lang_shortSun": "So",
- "lang_shortMon": "Mo",
- "lang_shortTue": "Di",
- "lang_shortWed": "Mi",
- "lang_shortThu": "Do",
- "lang_shortFri": "Fr",
- "lang_shortSat": "Sa",
- "lang_longSun": "Sonntag",
- "lang_longMon": "Montag",
- "lang_longTue": "Dienstag",
- "lang_longWed": "Mittwoch",
- "lang_longThu": "Donnerstag",
- "lang_longFri": "Freitag",
- "lang_longSat": "Samstag",
- "lang_to": "bis"
+ "lang_verticalTooltip": "Legt fest, ob Kalender und Raum \u00fcbereinander angezeigt werden sollen"
} \ No newline at end of file
diff --git a/modules-available/locationinfo/lang/en/backend-hisinone.json b/modules-available/locationinfo/lang/en/backend-hisinone.json
index 0ff12c18..616b4c83 100644
--- a/modules-available/locationinfo/lang/en/backend-hisinone.json
+++ b/modules-available/locationinfo/lang/en/backend-hisinone.json
@@ -10,7 +10,7 @@
"username": "Username",
"username_helptext": "Authenticating user (only required for CourseService).",
"verifyCert": "Verify certificate",
- "verifyCert_helptext": "Wenn das Zertifikat abgelaufen ist, oder von keiner bekannten CA ausgestellt wurde, wird die Verbindung abgelehnt.",
+ "verifyCert_helptext": "If the certificate expired or was not signed by a known CA, the connection will be aborted.",
"verifyHostname": "Verify host name",
- "verifyHostname_helptext": "Der im Zertifikat angegebene Hostname muss mit dem Hostnamen aus der URL \u00fcbereinstimmen, sonst wird die Verbindung abgelehnt."
-} \ No newline at end of file
+ "verifyHostname_helptext": "The certificate's host name must match the host name given in the URL, otherwise the connection will be aborted."
+}
diff --git a/modules-available/locationinfo/lang/en/messages.json b/modules-available/locationinfo/lang/en/messages.json
index 6767bfcf..348390dd 100644
--- a/modules-available/locationinfo/lang/en/messages.json
+++ b/modules-available/locationinfo/lang/en/messages.json
@@ -5,6 +5,9 @@
"ignored-invalid-range": "Ignored entry with invalid range",
"ignored-invalid-start": "Ignored entry with invalid start time",
"ignored-line-no-days": "Ignored entry with no days selected",
+ "invalid-backend-type": "Invalid backend type '{{0}}'",
"invalid-panel-id": "Invalid panel id '{{0}}'",
- "invalid-server-id": "Invalid server id '{{0}}'"
+ "invalid-panel-type": "Invalid panel type '{{0}}'",
+ "invalid-server-id": "Invalid server id '{{0}}'",
+ "server-id-missing": "Server id is missing"
} \ No newline at end of file
diff --git a/modules-available/locationinfo/lang/en/template-tags.json b/modules-available/locationinfo/lang/en/template-tags.json
index 900839a9..0d2b42a3 100644
--- a/modules-available/locationinfo/lang/en/template-tags.json
+++ b/modules-available/locationinfo/lang/en/template-tags.json
@@ -1,7 +1,5 @@
{
- "lang_defaultPanel": "Default panel",
"lang_addServer": "Server",
- "lang_summaryPanel": "Summary panel",
"lang_areYouSure": "Are you sure?",
"lang_autoScale": "Auto Days",
"lang_autoscaleTooltip": "Calculates the optimum amount of days to show from the display width",
@@ -10,6 +8,7 @@
"lang_calendar": "Calendar",
"lang_calupdateTooltip": "Time the calender querys for updates (in minutes)",
"lang_checkConnection": "Check connection",
+ "lang_closed": "Closed",
"lang_closingTime": "Closing time",
"lang_config": "Config",
"lang_configupdateTooltip": "Time interval the config gets updated (in minutes)",
@@ -18,24 +17,39 @@
"lang_day": "Day",
"lang_daysToShow": "Days",
"lang_daysToShowTooltip": "Defines the amount of days to show in the calendar",
+ "lang_defaultPanel": "Default panel",
"lang_deleteConfirmation": "Are you sure?",
"lang_display": "Display",
"lang_displayName": "Name",
"lang_displayNameTooltip": "Display name for this panel",
"lang_ecoMode": "E-Ink mode",
"lang_ecoTooltip": "Symbolic based pc state pictures are used instead of the colour based ones",
+ "lang_editDefaultPanelHints": "Here you can define panel properties for e.g. a digital door sign. To show opening times for a room you need to define corresponding times in the settings.\r\nIf you want to show calendar events you have to define a functioning backend first and link it to corresponding rooms.",
"lang_editPanel": "Edit panel",
+ "lang_editSummaryPanelHints": "Here you can define a summary panel which shows a overview of clients in your locations.",
+ "lang_editUrlPanelHints": "Here you can define which URL is opened by the panel. This enables you to show news about your university or any other website.",
"lang_entryName": "Name",
"lang_error": "Error",
"lang_expertMode": "Expert mode",
"lang_fourLocsHint": "You can pick up to four locations that will be shown in this panel.",
+ "lang_free": "Free",
"lang_general": "General",
+ "lang_ignoreSslTooltip": "Accept invalid, expired or self-signed ssl certificates",
+ "lang_insecureSsl": "Insecure SSL",
"lang_language": "Language",
"lang_languageTooltip": "The language the frontend uses",
"lang_locationName": "Name",
"lang_locationSettings": "Settings",
"lang_locations": "Locations",
"lang_locationsTable": "Rooms \/ Locations",
+ "lang_locationsTableHints": "Here you can define opening times for your locations and link the location ID to a configured backend (e.g. HISinOne) to show calendar events.",
+ "lang_longFri": "Friday",
+ "lang_longMon": "Monday",
+ "lang_longSat": "Saturday",
+ "lang_longSun": "Sunday",
+ "lang_longThu": "Thursday",
+ "lang_longTue": "Tuesday",
+ "lang_longWed": "Wednesday",
"lang_mode": "Mode",
"lang_mode1": "Calendar & Room",
"lang_mode2": "Calendar",
@@ -52,7 +66,10 @@
"lang_panelType": "Type",
"lang_panels": "Panels",
"lang_panelsTable": "Manage panels",
+ "lang_panelsTableHints": "This shows you all panels which can be shown anywhere by a browser of your choice, e.g. for a digital door sign.",
"lang_pleaseSelect": "Please select...",
+ "lang_prettytime": "PrettyTime",
+ "lang_prettytimeTooltip": "Use a different display format for the time",
"lang_recursiveServerSet": "Also set for all child locations",
"lang_recursiveSetTooltip": "If checked, all direct and indirect child locations will be configured to use the backend server selected above",
"lang_remoteSchedule": "Time table retrieval",
@@ -66,43 +83,40 @@
"lang_rotation2": "180\u00b0",
"lang_rotation3": "90\u00b0 \u27f3",
"lang_rotationTooltip": "Rotates the room",
+ "lang_runmodeTHead": "Clients",
"lang_saturday": "Saturday",
"lang_scale": "Calendar width",
"lang_scaleTooltip": "[10-90] Defines the calendar width (in percent)",
"lang_sec": "sec",
"lang_server": "Server",
"lang_serverTable": "Manage backend servers",
+ "lang_serverTableHints": "List of all configured server backends. Those are required if you want to show calendar events for your locations.",
"lang_serverTooltip": "Defines from which server the room queries the calendar data",
"lang_serverType": "Type",
+ "lang_shortFri": "Fri",
"lang_shortFriday": "Fri",
+ "lang_shortMon": "Mon",
"lang_shortMonday": "Mon",
+ "lang_shortSat": "Sat",
"lang_shortSaturday": "Sat",
+ "lang_shortSun": "Sun",
"lang_shortSunday": "Sun",
+ "lang_shortThu": "Thu",
"lang_shortThursday": "Thu",
+ "lang_shortTue": "Tue",
"lang_shortTuesday": "Tue",
+ "lang_shortWed": "Wed",
"lang_shortWednesday": "Wed",
+ "lang_summaryPanel": "Summary panel",
"lang_sunday": "Sunday",
"lang_switchTime": "Switchtime",
"lang_switchTimeTooltip": "[1-120] Sets the time between switching (in seconds)",
+ "lang_to": "to",
"lang_typeTooltip": "Defines on which type of server you want to connect to",
"lang_updateRates": "Update rates",
+ "lang_url": "URL",
+ "lang_urlPanel": "URL panel",
+ "lang_urlTooltip": "URL which is shown by the panel",
"lang_vertical": "Vertical mode",
- "lang_verticalTooltip": "Defines whether the room and calendar are shown above each other",
- "lang_closed": "Closed",
- "lang_free": "Free",
- "lang_shortSun": "Sun",
- "lang_shortMon": "Mon",
- "lang_shortTue": "Tue",
- "lang_shortWed": "Wed",
- "lang_shortThu": "Thu",
- "lang_shortFri": "Fri",
- "lang_shortSat": "Sat",
- "lang_longSun": "Sunday",
- "lang_longMon": "Monday",
- "lang_longTue": "Tuesday",
- "lang_longWed": "Wednesday",
- "lang_longThu": "Thursday",
- "lang_longFri": "Friday",
- "lang_longSat": "Saturday",
- "lang_to": "to"
+ "lang_verticalTooltip": "Defines whether the room and calendar are shown above each other"
} \ No newline at end of file
diff --git a/modules-available/locationinfo/page.inc.php b/modules-available/locationinfo/page.inc.php
index 30c38362..311b269d 100644
--- a/modules-available/locationinfo/page.inc.php
+++ b/modules-available/locationinfo/page.inc.php
@@ -223,14 +223,14 @@ class Page_LocationInfo extends Page
if ($locationids === false) {
if (!$failIfEmpty)
return array();
- Message::addError('main.paramter-missing', 'locationids');
+ Message::addError('main.parameter-missing', 'locationids');
Util::redirect('?do=locationinfo');
}
$locationids = explode(',', $locationids);
$all = array_map(function ($item) { return $item['locationid']; }, Location::queryLocations());
$locationids = array_filter($locationids, function ($item) use ($all) { return in_array($item, $all); });
if ($failIfEmpty && empty($locationids)) {
- Message::addError('main.paramter-empty', 'locationids');
+ Message::addError('main.parameter-empty', 'locationids');
Util::redirect('?do=locationinfo');
}
return $locationids;
@@ -254,11 +254,14 @@ class Page_LocationInfo extends Page
$params = $this->preparePanelConfigDefault();
} elseif ($paneltype === 'URL') {
$params = $this->preparePanelConfigUrl();
+ } elseif ($paneltype === 'SUMMARY') {
+ $params = $this->preparePanelConfigSummary();
} else {
Message::addError('invalid-panel-type', $paneltype);
Util::redirect('?do=locationinfo');
}
+
if ($paneluuid === 'new') {
$paneluuid = Util::randomUuid();
$query = "INSERT INTO `locationinfo_panel` (paneluuid, panelname, locationids, paneltype, panelconfig, lastchange)
@@ -320,6 +323,13 @@ class Page_LocationInfo extends Page
return array('config' => $conf, 'locationids' => []);
}
+ private function preparePanelConfigSummary()
+ {
+ // Check locations
+ $locationids = self::getLocationIdsFromRequest(true);
+ return array('locationids' => $locationids);
+ }
+
/**
* Updates the server settings in the db.
*/
@@ -832,7 +842,7 @@ class Page_LocationInfo extends Page
'url' => $config['url'],
'ssl_checked' => $config['insecure-ssl'] ? 'checked' : '',
));
- } else { // TODO
+ } else {
Render::addTemplate('page-config-panel-summary', array(
'new' => $id === 'new',
'uuid' => $id,
@@ -862,19 +872,37 @@ class Page_LocationInfo extends Page
Util::redirect($config['url']);
}
- $data = array(
- 'uuid' => $uuid,
- 'config' => json_encode($config),
- 'language' => $config['language'],
- );
-
+ $data = array();
preg_match('#^(.*)/#', $_SERVER['PHP_SELF'], $script);
preg_match('#^([^?]+)/#', $_SERVER['REQUEST_URI'], $request);
if ($script[1] !== $request[1]) {
$data['dirprefix'] = $script[1] . '/';
}
- echo Render::parse('frontend-default', $data);
+ if ($type === 'DEFAULT') {
+ $data += array(
+ 'uuid' => $uuid,
+ 'config' => json_encode($config),
+ 'language' => $config['language'],
+ );
+
+ die(Render::parse('frontend-default', $data));
+ }
+
+ if ($type === 'SUMMARY') {
+ $locations = LocationInfo::getLocationsOr404($uuid, false);
+ $config['tree'] = Location::getRecursive($locations);
+ $data += array(
+ 'uuid' => $uuid,
+ 'config' => json_encode($config),
+ 'language' => $config['language'],
+ );
+
+ die(Render::parse('frontend-summary', $data));
+ }
+
+ http_response_code(500);
+ die('Unknown panel type ' . $type);
}
}
diff --git a/modules-available/locationinfo/templates/frontend-default.html b/modules-available/locationinfo/templates/frontend-default.html
index fc9c3eac..4147e4e2 100755
--- a/modules-available/locationinfo/templates/frontend-default.html
+++ b/modules-available/locationinfo/templates/frontend-default.html
@@ -29,7 +29,7 @@ optional:
<link rel='stylesheet' type='text/css' href='{{dirprefix}}modules/js_jqueryui/style.css'/>
<link rel='stylesheet' type='text/css' href='{{dirprefix}}modules/js_weekcalendar/style.css'/>
- <style type='text/css'>
+ <style type="text/css">
body {
margin: 0;
@@ -237,17 +237,12 @@ optional:
background: #000;
}
- .OFF .screen-inner {
+ .OFFLINE .screen-inner {
background: #332;
}
- /*
- .OFF .screen-inner:after {
- content: "\01F4A4";
- }
- */
-
- .IDLE .screen-inner {
+ .IDLE .screen-inner,
+ .STANDBY .screen-inner {
background: #250;
}
@@ -390,6 +385,10 @@ optional:
})();
$(document).ready(function () {
+ if (!SetUpDate) {
+ fatalError("js_weekcalendar not loaded");
+ return;
+ }
applyConfig({{{config}}});
});
@@ -429,13 +428,13 @@ optional:
var time = false;
var p = result.time.split('-');
if (p.length === 6) {
- time = new Date(p[0], p[1], p[2], p[3], p[4], p[5]);
+ time = new Date(p[0], (p[1] - 1), p[2], p[3], p[4], p[5]);
console.log(time);
}
- if (time === false || isNaN(time.getTime()) || time.getYear() < 2010) {
+ if (time === false || isNaN(time.getTime()) || time.getFullYear() < 2010) {
time = new Date(result.time);
}
- if (isNaN(time.getTime()) || time.getYear() < 2010) {
+ if (isNaN(time.getTime()) || time.getFullYear() < 2010) {
time = new Date();
}
SetUpDate(time);
@@ -1066,8 +1065,10 @@ optional:
if (globalConfig.prettytime) {
var str = '';
if (hours > 0) {
- str += hours + 'muh';
+ str += hours + 'h ';
}
+ str += minutes + 'min ';
+ return str;
}
if (minutes < 10) {
@@ -1351,7 +1352,6 @@ optional:
function initRoomLayout(room) {
var maxX = false, maxY = false;
var minX = false, minY = false;
- var xDifference, yDifference;
var x, y;
generateRoomLayoutDiv((100 - room.config.scale) + "%", room);
@@ -1380,15 +1380,12 @@ optional:
}
}
- xDifference = maxX - minX;
- yDifference = maxY - minY;
-
- room.xDifference = xDifference;
- room.yDifference = yDifference;
room.minX = minX;
room.minY = minY;
- room.maxX = maxX;
- room.maxY = maxY;
+ room.maxX = maxX + picSizeX;
+ room.maxY = maxY + picSizeY;
+ room.xDifference = (room.maxX - room.minX);
+ room.yDifference = (room.maxY - room.minY);
setUpRoom(room, layout);
scaleRoom(room);
@@ -1401,7 +1398,7 @@ optional:
* @param room Room Object
*/
function generateOffsetAndScale(room) {
- var clientHeight;
+ var clientHeight, clientWidth;
if (room.config.vertical && room.config.mode === 1) {
clientHeight = room.$.container.height() - (room.$.calendar.position().top + room.$.calendar.height());
@@ -1409,37 +1406,21 @@ optional:
clientHeight = room.$.container.height() - (room.$.header.height() + 5);
}
- var clientWidth = room.$.layout.width();
+ clientWidth = room.$.layout.width();
- var scaleX;
- if (room.xDifference !== 0) {
- scaleX = clientWidth / room.xDifference;
- } else {
- scaleX = clientWidth;
- }
- var scaleY;
- if (room.yDifference !== 0) {
- scaleY = clientHeight / room.yDifference;
- } else {
- scaleY = clientHeight;
+ var scaleX = clientWidth / picSizeX, scaleY = clientHeight / picSizeY;
+ if (room.xDifference > 0) {
+ scaleX = (clientWidth - 20) / room.xDifference;
}
- var scaleYs = (clientHeight - (picSizeY * scaleY)) / room.yDifference;
- var scaleXs = (clientWidth - (picSizeX * scaleX)) / room.xDifference;
- if (scaleYs <= 0) {
- scaleYs = 9999;
- }
- if (scaleXs <= 0) {
- scaleXs = 9999;
+ if (room.yDifference > 0) {
+ scaleY = (clientHeight - 20) / room.yDifference;
}
- room.scale = Math.min(scaleYs, scaleY, scaleXs, scaleX, (clientHeight * 0.9) / picSizeY, (clientWidth * 0.9) / picSizeX);
- room.xOffset = 0 - room.minX;
- room.yOffset = 0 - room.minY;
- room.xOffset += ((1 / 2 * (clientWidth - (((room.maxX + room.xOffset) * room.scale) + picSizeX * room.scale))) / room.scale);
- room.yOffset += ((1 / 2 * (clientHeight - (((room.maxY + room.yOffset) * room.scale) + picSizeY * room.scale))) / room.scale);
+ room.scale = Math.min(scaleY, scaleX);
+ room.xOffset = -room.minX * room.scale + (clientWidth - room.xDifference * room.scale) / 2;
+ room.yOffset = -room.minY * room.scale + (clientHeight - room.yDifference * room.scale) / 2;
}
-
/**
* adds images for each pc to Room Layout
* @param room Room Object
@@ -1575,11 +1556,11 @@ optional:
for (var i = 0; i < update.length; i++) {
var $div = $("#pc_" + room.id + "_" + update[i].id);
// Pc free
- if (update[i].pcState === "IDLE" || update[i].pcState === "OFF") {
+ if (update[i].pcState === "IDLE" || update[i].pcState === "OFFLINE" || update[i].pcState === "STANDBY") {
freePcs++;
}
- $div.removeClass('BROKEN OFF IDLE OCCUPIED'.replace(update[i].pcState, '')).addClass(update[i].pcState);
+ $div.removeClass('BROKEN OFFLINE IDLE OCCUPIED STANDBY'.replace(update[i].pcState, '')).addClass(update[i].pcState);
}
room.freePcs = freePcs;
UpdateRoomHeader(room);
@@ -1608,6 +1589,7 @@ optional:
function scaleRoom(room) {
if (!room.$.layout || !room.$.layout.is(':visible')) return;
room.resizeRoom = false;
+ if (!room.layout) return;
generateOffsetAndScale(room);
room.$.layout.css('font-size', Math.floor(room.scale) + 'pt');
for (var i = 0; i < room.layout.length; i++) {
@@ -1617,8 +1599,8 @@ optional:
room.layout[i].$div.css({
width: pcWidth,
height: pcHeight,
- top: ((room.layout[i].y + room.yOffset) * room.scale) + "px",
- left: ((room.layout[i].x + room.xOffset) * room.scale) + "px"
+ top: (room.layout[i].y * room.scale + room.yOffset) + "px",
+ left: (room.layout[i].x * room.scale + room.xOffset) + "px"
});
}
}
diff --git a/modules-available/locationinfo/templates/frontend-summary.html b/modules-available/locationinfo/templates/frontend-summary.html
index dd5fc25d..7faa01e5 100644
--- a/modules-available/locationinfo/templates/frontend-summary.html
+++ b/modules-available/locationinfo/templates/frontend-summary.html
@@ -2,7 +2,8 @@
<html lang="de">
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8">
<head>
- <script type='text/javascript' src='../../../script/jquery.js'></script>
+ <script type='text/javascript' src='{{dirprefix}}script/jquery.js'></script>
+
<style type='text/css'>
body {
@@ -59,7 +60,7 @@
border-style: solid;
}
- .pc-idle, .pc-occupied, .pc-off, .pc-broken {
+ .pc-idle, .pc-occupied, .pc-offline, .pc-broken, .pc-standby {
padding: 2px 1px;
text-align: center;
font-size: 90%;
@@ -78,10 +79,15 @@
border-radius: 3px 0px 0px 3px;
}
- .pc-off {
+ .pc-offline {
background-color: darkgrey;
}
+ .pc-standby {
+ background-color: darkgreen;
+ }
+
+
.pc-broken {
background-color: black;
color: white;
@@ -106,7 +112,7 @@
var rooms = {};
var startdate;
var roomidsString = "";
-
+ var config = {{{config}}};
$(document).ready(function () {
//temp
@@ -115,13 +121,17 @@
});
function init() {
- var ids = getUrlParameter("id");
+ // var ids = getUrlParameter("id");
+
+ /*
$.getJSON("../../../api.php?do=locationinfo&action=locationtree&id=" + ids, function (result) {
generateLayout(result);
setTimeout(update, 1000);
});
-
+ */
+ generateLayout(config.tree);
+ update();
}
function SetUpDate(d) {
@@ -140,7 +150,7 @@
}
/**
- * generates the divs, decidecs if parent or child
+ * generates the divs, decides if parent or child
* @param json Room tree json
* @param myParent parent div
* @param outermost if the object is a root node
@@ -183,13 +193,16 @@
var rommUpdateIds = "";
var count = 0;
var nextUpdate = 15000;
+ // TODO: Only query a few rooms is not possible with the new api stuff ...
for (var property in rooms) {
if (rooms[property].lastCalendarUpdate === null || rooms[property].lastCalendarUpdate + CALUPDATE_MS < MyDate().getTime()) {
+ // TODO: NOT NECESSARY ANYMORE?!
calendarUpdateIds = addIdToUpdateList(calendarUpdateIds, rooms[property].id);
count++;
rooms[property].lastCalendarUpdate = MyDate().getTime();
}
if (rooms[property].lastRoomUpdate === null || rooms[property].lastRoomUpdate + ROOMUPDATE_MS < MyDate().getTime()) {
+ // TODO: NOT NECESSARY ANYMORE?!
rommUpdateIds = addIdToUpdateList(rommUpdateIds, rooms[property].id);
count++;
rooms[property].lastRoomUpdate = MyDate().getTime();
@@ -197,11 +210,11 @@
if (count > 7) break;
}
if (calendarUpdateIds !== "") {
- queryCalendars(calendarUpdateIds);
+ queryCalendars();
nextUpdate = 1000;
}
if (rommUpdateIds !== "") {
- queryRooms(rommUpdateIds);
+ queryRooms();
nextUpdate = 1000;
}
for (var property in rooms) {
@@ -214,6 +227,9 @@
function UpdateTimeTables(json) {
var l = json.length;
for (var i = 0; i < l; i++) {
+ if (rooms[json[i].id] == null) {
+ continue;
+ }
rooms[json[i].id].timetable = json[i].calendar;
for (var property in rooms[json[i].id].timetable) {
rooms[json[i].id].timetable[property].start = new Date(rooms[json[i].id].timetable[property].start);
@@ -225,11 +241,11 @@
/**
* Querys Pc states
- * @param ids Room ID's which should be queried. Format for e.g.: "20,5,6"
+ * Room are queried with the {{uuid}} of the panel.
*/
- function queryRooms(ids) {
+ function queryRooms() {
$.ajax({
- url: "../../../api.php?do=locationinfo&action=pcstates&id=" + ids,
+ url: "{{dirprefix}}api.php?do=locationinfo&get=pcstates&uuid={{uuid}}",
dataType: 'json',
cache: false,
timeout: 30000,
@@ -241,6 +257,7 @@
return;
}
updatePcStates(result);
+
}, error: function () {
}
@@ -282,7 +299,7 @@
function updatePcStates(json) {
var l = json.length;
for (var i = 0; i < l; i++) {
- updateRoomUsage(json[i].id, json[i].idle, json[i].occupied, json[i].off, json[i].broken)
+ updateRoomUsage(json[i].id, json[i].idle, json[i].occupied, json[i].offline, json[i].broken, json[i].standby)
}
}
@@ -522,20 +539,21 @@
* @param id of the child
* @param idle PC's on
* @param occupied PC's used
- * @param off PC's that are off
+ * @param offline PC's that are off
* @param broken PC's that are broken
*/
- function updateRoomUsage(id, idle, occupied, off, broken) {
- if (idle == 0 && occupied == 0 && off == 0) {
+ function updateRoomUsage(id, idle, occupied, offline, broken, standby) {
+ if (idle == 0 && occupied == 0 && offline == 0 && broken == 0 && standby == 0) {
$('#parent_' + id).parent().hide();
return;
}
$('#parent_' + id).parent().show();
- var total = parseInt(idle) + parseInt(occupied) + parseInt(off) + parseInt(broken);
+ var total = parseInt(idle) + parseInt(occupied) + parseInt(offline) + parseInt(broken) + parseInt(standby);
$("#pc_Idle_" + id).text(idle).width((idle / total) * 100 + '%');
$("#pc_Occupied_" + id).text(occupied).width((occupied / total) * 100 + '%');
- $("#pc_Off_" + id).text(off).width((off / total) * 100 + '%');
+ $("#pc_Offline_" + id).text(offline).width((offline / total) * 100 + '%');
$("#pc_Broken_" + id).text(broken).width((broken / total) * 100 + '%');
+ $("#pc_Standby_" + id).text(standby).width((standby / total) * 100 + '%');
}
/**
@@ -553,7 +571,13 @@
* @param time Time value
*/
function updateCoursTimer(id, time) {
- $("#div_Time_" + id).text(time);
+ // TODO: Add seconds again with a better update rate.
+ var time_split = time.split(":");
+ if (time != "") {
+ $("#div_Time_" + id).text(time_split[0] + ":" + time_split[1]);
+ } else {
+ $("#div_Time_" + id).text(time);
+ }
}
/**
@@ -577,7 +601,8 @@
"<div class='pc-state-wrapper'>" +
"<div id = 'pc_Occupied_" + id + "' class='pc-occupied'>?</div>" +
"<div id = 'pc_Idle_" + id + "' class='pc-idle'>?</div>" +
- "<div id = 'pc_Off_" + id + "' class='pc-off'>?</div>" +
+ "<div id = 'pc_Standby_" + id + "' class='pc-standby'>?</div>" +
+ "<div id = 'pc_Offline_" + id + "' class='pc-offline'>?</div>" +
"<div id = 'pc_Broken_" + id + "' class='pc-broken'>?</div>" +
"</div>" +
"<div class='aroundCourse'>" +
@@ -585,8 +610,7 @@
"<div id = 'div_Time_" + id + "'class='courseFont'></div></div></div></div>";
var obj = $(target).append(text);
addRoom(id, name);
- return obj
-
+ return obj;
}
/**
@@ -634,10 +658,11 @@
/**
* querys the Calendar data
- * @param ids ID'S of rooms to query as string, for e.g.: "5,17,8" or "5"
+ * Calender is queried with the {{uuid}} of the panel.
+ * api.inc.php / page.inc.php is getting the ids with the panel uuid.
*/
- function queryCalendars(ids) {
- var url = "../../../api.php?do=locationinfo&action=calendar&id=" + ids;
+ function queryCalendars() {
+ var url = "{{dirprefix}}api.php?do=locationinfo&get=calendar&uuid={{uuid}}";
// Todo reimplement Frontend methode if needed
/*
@@ -652,8 +677,6 @@
timeout: 30000,
success: function (result) {
UpdateTimeTables(result);
-
-
}, error: function () {
}
diff --git a/modules-available/locationinfo/templates/page-config-panel-summary.html b/modules-available/locationinfo/templates/page-config-panel-summary.html
new file mode 100644
index 00000000..2a968fc2
--- /dev/null
+++ b/modules-available/locationinfo/templates/page-config-panel-summary.html
@@ -0,0 +1,208 @@
+<h2>
+ {{#new}}{{lang_createPanel}}{{/new}}
+ {{^new}}{{lang_editPanel}}{{/new}}
+</h2>
+
+<p>{{lang_editSummaryPanelHints}}</p>
+
+<form method="post" action="?do=locationinfo" id="config-form">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="action" value="writePanelConfig">
+ <input type="hidden" name="ptype" value="SUMMARY">
+ <input type="hidden" name="uuid" value="{{uuid}}">
+
+ <div class="row">
+
+ <div class="col-md-6">
+ <div class="modify-inputs panel panel-default">
+ <div class="panel-heading">{{lang_display}}</div>
+ <div class="panel-body">
+ <div class="list-group">
+
+ <div class="list-group-item">
+ <div class="row">
+ <div class="col-sm-3">
+ <label for="panel-title">{{lang_displayName}}</label>
+ </div>
+ <div class="col-sm-7">
+ <input class="form-control" name="name" id="panel-title" type="text" value="{{panelname}}">
+ </div>
+ <div class="col-sm-2">
+ <a class="btn btn-default helptext" title="{{lang_displayNameTooltip}}">
+ <span class="glyphicon glyphicon-question-sign"></span>
+ </a>
+ </div>
+ </div>
+ </div>
+
+ <div class="list-group-item">
+ <div class="row">
+ <div class="col-sm-3">
+ <label for="language">{{lang_language}}</label>
+ </div>
+ <div class="col-sm-7">
+ <select class="form-control" name="language" id="language">
+ {{#languages}}
+ <option value="{{cc}}" id="lang-{{cc}}" {{selected}}>{{name}}</option>
+ {{/languages}}
+ </select>
+ </div>
+ <div class="col-sm-2">
+ <a class="btn btn-default helptext" title="{{lang_languageTooltip}}">
+ <span class="glyphicon glyphicon-question-sign"></span>
+ </a>
+ </div>
+ </div>
+ </div>
+
+ <div class="list-group-item">
+ <div class="row">
+ <div class="col-sm-3">
+ <label for="input-eco">{{lang_ecoMode}}</label>
+ </div>
+ <div class="col-sm-7">
+ <input id="input-eco" type="checkbox" name="eco" {{eco_checked}}>
+ </div>
+ <div class="col-sm-2">
+ <a class="btn btn-default helptext" title="{{lang_ecoTooltip}}">
+ <span class="glyphicon glyphicon-question-sign"></span>
+ </a>
+ </div>
+ </div>
+ </div>
+<!--
+ <div class="list-group-item">
+ <div class="row">
+ <div class="col-sm-3">
+ <label for="input-prettytime">{{lang_prettytime}}</label>
+ </div>
+ <div class="col-sm-7">
+ <input id="input-prettytime" type="checkbox" name="prettytime" {{prettytime_checked}}>
+ </div>
+ <div class="col-sm-2">
+ <a class="btn btn-default helptext" title="{{lang_prettytimeTooltip}}">
+ <span class="glyphicon glyphicon-question-sign"></span>
+ </a>
+ </div>
+ </div>
+ </div>
+-->
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="modify-inputs">
+ <div class="row">
+
+ <div class="col-md-6">
+ <div class="panel panel-default">
+ <div class="panel-heading">{{lang_locations}}</div>
+ <div class="panel-body">
+ <input type="hidden" name="locationids" value="{{locationids}}" id="locationids">
+ <p>{{lang_fourLocsHint}}</p>
+ <ul id="selected-locations" class="list-unstyled">
+
+ </ul>
+ <div class="dropdown pull-right">
+ <button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown">
+ <span class="glyphicon glyphicon-plus"></span>
+ </button>
+ <ul class="dropdown-menu" id="location-list">
+ {{#locations}}
+ <li><a href="#" data-lid="{{locationid}}">{{locationpad}} <span class="name">{{locationname}}</span></a></li>
+ {{/locations}}
+ </ul>
+ </div>
+ <div class="clearfix"></div>
+ </div>
+ </div>
+ </div>
+
+ </div>
+ </div>
+ <button type="submit" class="btn btn-primary">{{lang_save}}</button>
+ <a href="?do=locationinfo&amp;show=panels" class="btn btn-default">{{lang_cancel}}</a>
+</form>
+
+<div class="modal fade" id="no-locations-message" tabindex="-1" role="dialog">
+ <div class="modal-dialog"> <!--style="min-width:600px;width:70%"-->
+
+ <div class="modal-content">
+ <div class="modal-header">{{lang_error}}</div>
+ <div class="modal-body">
+ {{lang_noLocationsWarning}}
+ </div>
+ <div class="modal-footer">
+ <a class="btn btn-primary pull-right" data-dismiss="modal">{{lang_close}}</a>
+ <div class="clearfix"></div>
+ </div>
+ </div>
+
+ </div>
+</div>
+
+<script type="text/javascript"><!--
+
+document.addEventListener("DOMContentLoaded", function () {
+ var $selLocs = $('#selected-locations');
+ var $locList = $('#location-list');
+ var $locInput = $('#locationids');
+
+ // Initialize fancy tooltips
+ $('a.helptext').tooltip();
+ // Add listener to range sliders so their label can be updated
+ $('input[type="range"]').change(function () {
+ $(this).siblings().find('.range-display').text($(this).val());
+ });
+ // Set state of input controls that aren't statically initialized server side
+ $('.modify-inputs input[type="checkbox"]')
+ .bootstrapSwitch({size: 'small'});
+
+ var lids = $locInput.val().split(',');
+ $selLocs.empty();
+ for (var i = 0; i < lids.length; ++i) {
+ var $name = $locList.find('a[data-lid="' + lids[i] + '"] .name');
+ if ($name.length === 0) continue;
+ addLocation(lids[i], $name.text());
+ }
+
+ // Adding/removing locations
+ $locList.find('a').click(function(ev) {
+ ev.preventDefault();
+ var $this = $(this);
+ var name = $this.find('.name').text();
+ var id = $this.data('lid');
+ addLocation(id, name);
+ serializeLocs();
+ });
+
+ $('#config-form').submit(function(ev) {
+ if ($locInput.val().length > 0)
+ return;
+ ev.preventDefault();
+ $('#no-locations-message').modal('show');
+ });
+
+ function addLocation(id, name) {
+ $selLocs.find('li[data-lid="' + id + '"]').remove();
+ var delButton = $('<button class="btn btn-danger btn-xs" type="button">').append($('<span class="glyphicon glyphicon-remove">')).click(delParent);
+ $selLocs.append($('<li>').attr('data-lid', id).text(name).prepend(delButton));
+ }
+
+ function delParent() {
+ $(this).parent().remove();
+ serializeLocs();
+ }
+
+ function serializeLocs() {
+ var res = $selLocs.find('li[data-lid]').map( function() {
+ return $(this).data('lid');
+ }).get().join(',');
+ $locInput.val(res);
+ }
+
+});
+
+//--></script>
diff --git a/modules-available/locations/inc/location.inc.php b/modules-available/locations/inc/location.inc.php
index 1581f744..42026f8b 100644
--- a/modules-available/locations/inc/location.inc.php
+++ b/modules-available/locations/inc/location.inc.php
@@ -37,6 +37,11 @@ class Location
return Database::queryFirst("SELECT * FROM location WHERE locationid = :locationId", compact('locationId'));
}
+ /**
+ * Get name of 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)
{
self::getLocationsAssoc();
@@ -46,6 +51,26 @@ class Location
return self::$assocLocationCache[$locationId]['locationname'];
}
+ /**
+ * 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)
+ {
+ self::getLocationsAssoc();
+ settype($locationId, 'int');
+ if (!isset(self::$assocLocationCache[$locationId]))
+ return false;
+ $ret = array();
+ while (isset(self::$assocLocationCache[$locationId])) {
+ $ret[$locationId] = self::$assocLocationCache[$locationId]['locationname'];
+ $locationId = self::$assocLocationCache[$locationId]['parentlocationid'];
+ }
+ return array_reverse($ret, true);
+ }
+
public static function getLocationsAssoc()
{
if (self::$assocLocationCache === false) {
diff --git a/modules-available/locations/lang/en/template-tags.json b/modules-available/locations/lang/en/template-tags.json
index e3c0b056..ddb90f83 100644
--- a/modules-available/locations/lang/en/template-tags.json
+++ b/modules-available/locations/lang/en/template-tags.json
@@ -32,4 +32,4 @@
"lang_thisListByLocation": "Locations",
"lang_thisListBySubnet": "Subnets",
"lang_unassignedMachines": "Machines not matching any location"
-} \ No newline at end of file
+}
diff --git a/modules-available/main/lang/de/messages.json b/modules-available/main/lang/de/messages.json
index 274a97bd..b6c2a5b3 100644
--- a/modules-available/main/lang/de/messages.json
+++ b/modules-available/main/lang/de/messages.json
@@ -7,6 +7,8 @@
"module-missing-deps": "Modul {{0}} hat fehlende Abh\u00e4ngigkeiten",
"no-permission": "Keine ausreichenden Rechte, um auf diese Seite zuzugreifen",
"no-such-module": "Modul {{0}} existiert nicht oder ist nicht aktiv",
+ "parameter-empty": "Parameter {{0}} ist leer",
+ "parameter-missing": "Parameter {{0}} wurde nicht gesetzt",
"task-error": "Ausf\u00fchrung fehlgeschlagen: {{0}}",
"taskmanager-error": "Verbindung zum Taskmanager fehlgeschlagen",
"taskmanager-format": "Taskmanager hat ung\u00fcltige Daten zur\u00fcckgeliefert",
diff --git a/modules-available/main/lang/de/template-tags.json b/modules-available/main/lang/de/template-tags.json
index e4de5737..00e27502 100644
--- a/modules-available/main/lang/de/template-tags.json
+++ b/modules-available/main/lang/de/template-tags.json
@@ -1,5 +1,7 @@
{
+ "lang_browserTime": "Browser",
"lang_changePassword": "Passwort \u00e4ndern",
+ "lang_clockDriftWarn": "Die Uhrzeit des Satelliten-Servers weicht von der Uhrzeit des lokalen Systems\/Browsers ab. Bitte stellen Sie sicher, dass die Uhrzeit des Servers korrekt ist, da sonst zeitabh\u00e4ngige Einstellungen und Aufgaben evtl. nicht korrekt durchgef\u00fchrt werden.",
"lang_goTo": "Gehe zu",
"lang_intro": "Dies ist die bwLehrpool Konfigurationsoberfl\u00e4che.",
"lang_introGuest": "Dies ist das Administrations-Interface der lokalen bwLehrpool-Installation. Bitte authentifizieren Sie sich, um Einstellungen vorzunehmen.",
@@ -11,6 +13,7 @@
"lang_needsSetup": "Einrichtung unvollst\u00e4ndig",
"lang_noExistingAccount": "Es existiert noch kein Administrator-Zugang f\u00fcr diesen Satelliten-Server.",
"lang_register": "Registrieren",
+ "lang_serverTime": "Server",
"lang_toggleNavigation": "Navigation ein\/ausblenden",
"lang_translations": "\u00dcbersetzungen",
"lang_warning": "Warnung",
diff --git a/modules-available/main/lang/en/messages.json b/modules-available/main/lang/en/messages.json
index e7314685..2a9c9c0d 100644
--- a/modules-available/main/lang/en/messages.json
+++ b/modules-available/main/lang/en/messages.json
@@ -6,7 +6,9 @@
"invalid-action": "Invalid action '{{0}}'",
"module-missing-deps": "Module {{0}} has missing dependencies",
"no-permission": "No sufficient privileges to access this page",
- "no-such-module": "Modul {{0}} existiert nicht",
+ "no-such-module": "Module {{0}} doesn't exist",
+ "parameter-empty": "Parameter {{0}} is empty",
+ "parameter-missing": "Parameter {{0}} is missing",
"task-error": "Execution failed: {{0}}",
"taskmanager-error": "Failed to connect to the Task Manager",
"taskmanager-format": "Task Manager has returned invalid data",
diff --git a/modules-available/main/lang/en/template-tags.json b/modules-available/main/lang/en/template-tags.json
index 0798290c..fdcbce06 100644
--- a/modules-available/main/lang/en/template-tags.json
+++ b/modules-available/main/lang/en/template-tags.json
@@ -1,6 +1,8 @@
{
+ "lang_browserTime": "Browser",
"lang_changePassword": "Change password",
- "lang_goTo": "Gehe zu",
+ "lang_clockDriftWarn": "The local system's\/browser's time doesn't match the server's time. Please make sure the server's clock is running correctly, otherwise time sensitive settings or tasks might not work properly.",
+ "lang_goTo": "Go to",
"lang_intro": "This is the bwLehrpool configuration interface.",
"lang_introGuest": "This is the administration interface of the local bwLehrpool intallation. Please authenticate yourself to adjust settings.",
"lang_language": "Language",
@@ -11,6 +13,7 @@
"lang_needsSetup": "Setup incomplete",
"lang_noExistingAccount": "No account has been created yet. Sign up to become the administrator.",
"lang_register": "Register",
+ "lang_serverTime": "Server",
"lang_toggleNavigation": "toggle navigation",
"lang_translations": "Translations",
"lang_warning": "Warning",
diff --git a/modules-available/main/page.inc.php b/modules-available/main/page.inc.php
index 08e8b5a6..70296a59 100644
--- a/modules-available/main/page.inc.php
+++ b/modules-available/main/page.inc.php
@@ -19,7 +19,8 @@ class Page_Main extends Page
// Logged in here
Render::addTemplate('page-main', array(
- 'user' => User::getName()
+ 'user' => User::getName(),
+ 'now' => time(),
));
// Warnings
diff --git a/modules-available/main/templates/page-main.html b/modules-available/main/templates/page-main.html
index a0b2d3b0..1b7cc62d 100644
--- a/modules-available/main/templates/page-main.html
+++ b/modules-available/main/templates/page-main.html
@@ -1,5 +1,19 @@
<div class="jumbotron">
<h1>{{lang_welcome}}, {{user}}</h1>
<p>{{lang_intro}}</p>
-
</div>
+
+<script type="application/javascript"><!--
+document.addEventListener("DOMContentLoaded", function () {
+ if (Date.now && Math.abs(Date.now() / 1000 - {{now}}) > 300) {
+ $('#browser-time').text(new Date().toLocaleString());
+ $('#server-time').text(new Date({{now}} * 1000).toLocaleString());
+ $('#time-warner').show();
+ }
+});
+//--></script>
+
+<div class="alert alert-warning collapse" id="time-warner">
+ {{lang_clockDriftWarn}}<br>
+ <b>{{lang_browserTime}}</b>: <span id="browser-time"></span>, <b>{{lang_serverTime}}</b>: <span id="server-time"></span>
+</div> \ No newline at end of file
diff --git a/modules-available/news/install.inc.php b/modules-available/news/install.inc.php
index 620a4580..e5e52256 100644
--- a/modules-available/news/install.inc.php
+++ b/modules-available/news/install.inc.php
@@ -27,9 +27,11 @@ $res[] = tableCreate('vmchooser_pages', "
`content` text,
`type` varchar(10),
PRIMARY KEY (`newsid`),
- KEY `dateline` (`dateline`)
+ KEY `type` (`type`, `dateline`)
");
+Database::exec('ALTER TABLE vmchooser_pages DROP KEY `dateline`, ADD KEY `type` (`type`, `dateline`)');
+
// Create response for browser
if (in_array(UPDATE_DONE, $res)) {
diff --git a/modules-available/rebootcontrol/inc/rebootqueries.inc.php b/modules-available/rebootcontrol/inc/rebootqueries.inc.php
index 8f65b756..1bdcb9a2 100644
--- a/modules-available/rebootcontrol/inc/rebootqueries.inc.php
+++ b/modules-available/rebootcontrol/inc/rebootqueries.inc.php
@@ -21,7 +21,7 @@ class RebootQueries
}
$res = Database::simpleQuery("
SELECT machine.machineuuid, machine.hostname, machine.clientip,
- machine.lastboot, machine.lastseen, machine.logintime,
+ machine.lastboot, machine.lastseen, machine.logintime, machine.state,
$sessionField, machine.currentuser, machine.locationid
FROM machine
$leftJoin
@@ -29,12 +29,12 @@ class RebootQueries
$ret = $res->fetchAll(PDO::FETCH_ASSOC);
$NOW = time();
foreach ($ret as &$row) {
- if ($row['lastboot'] == 0 || $NOW - $row['lastseen'] > 600) {
- $row['status'] = 0;
- } else {
+ if ($row['state'] === 'IDLE' || $row['state'] === 'OCCUPIED') {
$row['status'] = 1;
+ } else {
+ $row['status'] = 0;
}
- if ($row['status'] === 0 || $row['logintime'] == 0) {
+ if ($row['state'] !== 'OCCUPIED') {
$row['currentuser'] = '';
$row['currentsession'] = '';
}
diff --git a/modules-available/roomplanner/baseconfig/getconfig.inc.php b/modules-available/roomplanner/baseconfig/getconfig.inc.php
deleted file mode 100644
index aa8721ef..00000000
--- a/modules-available/roomplanner/baseconfig/getconfig.inc.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-ConfigHolder::add("SLX_PVS_CONFIG_URL", 'http://' . $_SERVER['SERVER_ADDR'] . $_SERVER['SCRIPT_NAME'] . '?do=roomplanner');
-
-$res = Database::queryFirst('SELECT dedicatedmgr FROM location_roomplan WHERE managerip = :ip LIMIT 1', ['ip' => $ip]);
-if ($res !== false) {
- if ((int)$res['dedicatedmgr'] !== 0) {
- // TODO: Runmode
- ConfigHolder::add("SLX_PVS_DEDICATED", 'yes');
- ConfigHolder::add("SLX_EXAM", false, 100000);
- ConfigHolder::add("SLX_SYSTEMD_TARGET", false, 100000);
- } else {
- ConfigHolder::add("SLX_PVS_HYBRID", 'yes');
- }
-} \ No newline at end of file
diff --git a/modules-available/roomplanner/clientscript.js b/modules-available/roomplanner/clientscript.js
index 1cd65132..722e3909 100644
--- a/modules-available/roomplanner/clientscript.js
+++ b/modules-available/roomplanner/clientscript.js
@@ -11,10 +11,16 @@ var selectMachinInitialized = false;
var placedMachines = [];
+function makeCombinedFieldSingle(item)
+{
+ item.combined = (item.machineuuid + " " + item.hostname + " " + item.clientip + " " + item.macaddr + " " + item.macaddr.replace(/-/g, ':')).toLocaleLowerCase();
+ item.sortField = (item.fixedlocationid === null ? 'a' : 'z') + item.hostname;
+}
+
function makeCombinedField(machineArray)
{
machineArray.forEach(function (v,i,a){
- machineArray[i].combined = (v.machineuuid + " " + v.hostname + " " + v.clientip + " " + v.macaddr + " " + v.macaddr.replace(/-/g, ':')).toLocaleLowerCase();
+ makeCombinedFieldSingle(machineArray[i]);
});
return machineArray;
}
@@ -25,15 +31,14 @@ function renderMachineEntry(item, escape) {
// console.log('used uuids is ');
// console.log(placedMachines);
- var isUsed = $.inArray(item.machineuuid, placedMachines) > -1;
var extraClass = '';
var extraText = '';
- if (isUsed) {
- extraText = ' (already placed)';
- extraClass = 'used';
- } else if (item.otherroom) {
+ if (item.otherroom) {
extraText = ' (in ' + item.otherroom + ')';
extraClass = 'used';
+ } else if (item.fixedlocationid !== null) {
+ extraText = ' (already placed)';
+ extraClass = 'used';
}
return '<div class="machine-entry ' + extraClass +'">'
//+ ' <div class="machine-logo"><i class="glyphicon glyphicon-hdd"></i></div>'
@@ -120,7 +125,7 @@ function initSelectize() {
render : { option : renderMachineEntry, item: renderMachineSelected},
load: loadMachines,
maxItems: 1,
- sortField: 'hostname',
+ sortField: 'sortField',
sortDirection: 'asc',
onChange: clearSubnetBox
});
@@ -136,7 +141,7 @@ function initSelectize() {
create: false,
render : { option : renderMachineEntry, item: renderMachineSelected},
maxItems: 1,
- sortField: 'hostname',
+ sortField: 'sortField',
sortDirection: 'asc',
onChange: clearSearchBox
});
@@ -148,13 +153,16 @@ function initSelectize() {
}
function onBtnSelect() {
/* check which one has a value */
- console.assert($selectizeSubnet.length == 1);
- console.assert($selectizeSearch.length == 1);
+ console.assert($selectizeSubnet.length === 1);
+ console.assert($selectizeSearch.length === 1);
var bySubnet = machineCache[$selectizeSubnet[0].selectize.getValue()];
var bySearch = machineCache[$selectizeSearch[0].selectize.getValue()];
- var value = (bySubnet === undefined || bySubnet == "") ? bySearch : bySubnet;
+ var value = !bySubnet ? bySearch : bySubnet;
+ value.fixedlocationid = -1;
+ makeCombinedFieldSingle(value);
+
var result = {muuid: value.machineuuid, ip: value.clientip, mac_address : value.macaddr, hostname: value.hostname};
currentCallback(result);
@@ -165,6 +173,14 @@ function onBtnSelect() {
clearSearchBox();
}
+function onPcDelete(muuid) {
+ var bySubnet = machineCache[muuid];
+ var bySearch = machineCache[muuid];
+ var value = !bySubnet ? bySearch : bySubnet;
+ value.fixedlocationid = null;
+ makeCombinedFieldSingle(value);
+}
+
/* to be called from berryous' code */
function selectMachine(usedUuids, callback) {
initSelectize();
@@ -172,7 +188,7 @@ function selectMachine(usedUuids, callback) {
placedMachines = usedUuids;
$modal.modal('show');
$modal.one('hidden.bs.modal', function () {
- if (currentCallback != null) {
+ if (currentCallback) {
currentCallback(false);
}
});
diff --git a/modules-available/roomplanner/config.json b/modules-available/roomplanner/config.json
index f620405a..537714c3 100644
--- a/modules-available/roomplanner/config.json
+++ b/modules-available/roomplanner/config.json
@@ -1,3 +1,3 @@
{
- "dependencies": ["js_jqueryui", "js_selectize", "bootstrap_dialog", "statistics", "locations"]
+ "dependencies": ["js_jqueryui", "js_selectize", "bootstrap_dialog", "statistics", "locations", "runmode"]
}
diff --git a/modules-available/roomplanner/hooks/runmode/config.json b/modules-available/roomplanner/hooks/runmode/config.json
new file mode 100644
index 00000000..0f2ce493
--- /dev/null
+++ b/modules-available/roomplanner/hooks/runmode/config.json
@@ -0,0 +1,6 @@
+{
+ "getModeName": "PvsGenerator::getManagerName",
+ "isClient": false,
+ "configHook": "PvsGenerator::runmodeConfigHook",
+ "allowGenericEditor": false
+} \ No newline at end of file
diff --git a/modules-available/roomplanner/inc/pvsgenerator.inc.php b/modules-available/roomplanner/inc/pvsgenerator.inc.php
index d61e826b..6df7c10e 100644
--- a/modules-available/roomplanner/inc/pvsgenerator.inc.php
+++ b/modules-available/roomplanner/inc/pvsgenerator.inc.php
@@ -54,9 +54,18 @@ class PvsGenerator
/* collect names and build room blocks - filter empty rooms while at it */
$roomNames = array();
$roomBlocks = '';
+ $overrides = [];
foreach ($rooms as $room) {
- if (is_null($room['notnull']) || isset($room['skip']) // Not leaf
- || empty($room['managerip'])) // rooms without managerips don't make sense
+ if (is_null($room['notnull']) || isset($room['skip'])) // Not leaf
+ continue;
+ if (Module::isAvailable('runmode')) {
+ $pc = RunMode::getForMode('roomplanner', $room['locationid']);
+ if (!empty($pc)) {
+ $pc = array_pop($pc);
+ $room['managerip'] = $pc['clientip'];
+ }
+ }
+ if (empty($room['managerip'])) // rooms without managerips don't make sense
continue;
$roomBlock = PvsGenerator::generateRoomBlock($room);
if ($roomBlock === false)
@@ -189,7 +198,7 @@ class PvsGenerator
private static function boundingBox($grid, &$minX, &$minY, &$maxX, &$maxY)
{
- $minX = PHP_INT_MAX; /* PHP_INT_MIN is only avaiable since PHP 7 */
+ $minX = PHP_INT_MAX; /* PHP_INT_MIN is only available since PHP 7 */
$maxX = ~PHP_INT_MAX;
$minY = PHP_INT_MAX;
$maxY = ~PHP_INT_MAX;
@@ -202,4 +211,37 @@ class PvsGenerator
}
}
+ public static function runmodeConfigHook($machineUuid, $locationId, $data)
+ {
+ if (!empty($data)) {
+ $data = json_decode($data, true);
+ }
+ if (!is_array($data)) {
+ $data = array();
+ }
+ ConfigHolder::add("SLX_PVS_CONFIG_URL", 'http://' . $_SERVER['SERVER_ADDR'] . $_SERVER['SCRIPT_NAME'] . '?do=roomplanner');
+
+ if (isset($data['dedicatedmgr']) && $data['dedicatedmgr']) {
+ ConfigHolder::add("SLX_ADDONS", false, 100000);
+ ConfigHolder::add("SLX_PVS_DEDICATED", 'yes');
+ ConfigHolder::add("SLX_EXAM", false, 100000);
+ ConfigHolder::add("SLX_AUTOLOGIN", 'ON', 100000);
+ } else {
+ ConfigHolder::add("SLX_PVS_HYBRID", 'yes');
+ }
+ }
+
+ /**
+ * Get display name for manager of given locationId.
+ * @param $locationId
+ * @return bool|string
+ */
+ public static function getManagerName($locationId)
+ {
+ $names = Location::getNameChain($locationId);
+ if ($names === false)
+ return false;
+ return implode(' / ', $names);
+ }
+
}
diff --git a/modules-available/roomplanner/install.inc.php b/modules-available/roomplanner/install.inc.php
index a6d98384..13365fe1 100644
--- a/modules-available/roomplanner/install.inc.php
+++ b/modules-available/roomplanner/install.inc.php
@@ -6,7 +6,6 @@ $res = array();
$res[] = tableCreate('location_roomplan', "
`locationid` INT(11) NOT NULL,
`managerip` varchar(45) CHARACTER SET ascii DEFAULT '',
- `dedicatedmgr` tinyint(1) NOT NULL DEFAULT 0,
`tutoruuid` char(36) CHARACTER SET ascii DEFAULT NULL,
`roomplan` BLOB DEFAULT NULL,
PRIMARY KEY (`locationid`),
@@ -29,24 +28,40 @@ if (!tableHasColumn('location_roomplan', 'tutoruuid')) {
}
$res[] = UPDATE_DONE;
}
-if (!tableHasColumn('location_roomplan', 'dedicatedmgr')) {
- $ret = Database::exec("ALTER TABLE `location_roomplan` ADD `dedicatedmgr` tinyint(1) NOT NULL DEFAULT 0 AFTER `managerip`") !== false;
- if ($ret === false) {
- finalResponse(UPDATE_FAILED, 'Adding dedicatedmgr to location_roomplan failed: ' . Database::lastError());
- }
- $res[] = UPDATE_DONE;
-}
if (in_array(UPDATE_DONE, $res)) {
Database::exec("ALTER TABLE `location_roomplan`
- ADD CONSTRAINT `location_roomplan_ibfk_1` FOREIGN KEY (`locationid`) REFERENCES `location` (`locationid`) ON DELETE CASCADE");
+ ADD CONSTRAINT `location_roomplan_ibfk_1` FOREIGN KEY (`locationid`) REFERENCES `location` (`locationid`) ON DELETE CASCADE");
Database::exec("ALTER TABLE `location_roomplan`
- ADD CONSTRAINT `location_roomplan_ibfk_2` FOREIGN KEY (`tutoruuid`) REFERENCES `machine` (`machineuuid`) ON DELETE SET NULL ON UPDATE CASCADE");
+ ADD CONSTRAINT `location_roomplan_ibfk_2` FOREIGN KEY (`tutoruuid`) REFERENCES `machine` (`machineuuid`) ON DELETE SET NULL ON UPDATE CASCADE");
}
-if (in_array(UPDATE_DONE, $res)) {
- finalResponse(UPDATE_DONE, 'Table created successfully');
+// 2017-11-30: Refactor to runmode
+// managerip, dedicatedmgr, --> runmode
+if (tableHasColumn('location_roomplan', 'dedicatedmgr')) {
+ if (!tableExists('runmode') || !tableExists('machine')) {
+ $res[] = UPDATE_RETRY;
+ } else {
+ $ret = Database::simpleQuery('SELECT lr.locationid, lr.managerip, lr.dedicatedmgr, m.machineuuid
+ FROM location_roomplan lr INNER JOIN machine m ON (m.clientip = lr.managerip)');
+ if ($ret === false) {
+ $res[] = UPDATE_FAILED;
+ } else {
+ while ($row = $ret->fetch(PDO::FETCH_ASSOC)) {
+ $dedi = $row['dedicatedmgr'] != 0;
+ $data = json_encode(array('dedicatedmgr' => $dedi));
+ Database::exec("INSERT IGNORE INTO runmode (machineuuid, module, modeid, modedata, isclient)
+ VALUES (:machineuuid, 'roomplanner', :locationid, :modedata, :isclient)", array(
+ 'machineuuid' => $row['machineuuid'],
+ 'locationid' => $row['locationid'],
+ 'modedata' => $data,
+ 'isclient' => ($dedi ? 0 : 1)
+ ));
+ }
+ Database::exec('ALTER TABLE location_roomplan DROP COLUMN dedicatedmgr');
+ $res[] = UPDATE_DONE;
+ }
+ }
}
-finalResponse(UPDATE_NOOP, 'Everything already up to date');
-
+responseFromArray($res);
diff --git a/modules-available/roomplanner/js/grid.js b/modules-available/roomplanner/js/grid.js
index 334057bf..466e42aa 100644
--- a/modules-available/roomplanner/js/grid.js
+++ b/modules-available/roomplanner/js/grid.js
@@ -74,10 +74,13 @@ if (!roomplanner) var roomplanner = {
initDelete: function(el) {
$(el).append('<div class="deleteHandle glyphicon glyphicon-remove-sign"></div>');
$(el).find('.deleteHandle').click(function() {
- if ($(this).parent().attr('itemtype') == "pc") {
+ if ($(this).parent().attr('itemtype') === "pc") {
var self = this;
BootstrapDialog.confirm(__('are you sure'),function(result) {
if (result) {
+ if (onPcDelete) {
+ onPcDelete($(self).parent().attr('muuid'));
+ }
$(self).parent().remove();
}
});
@@ -169,8 +172,8 @@ if (!roomplanner) var roomplanner = {
var mw = $(this).resizable("option","maxWidth");
var mh = $(this).resizable("option","maxHeight");
- var hLimit = ($(this).attr('scalable') == 'v');
- var vLimit = ($(this).attr('scalable') == 'h');
+ var hLimit = ($(this).attr('scalable') === 'v');
+ var vLimit = ($(this).attr('scalable') === 'h');
if(collides.length) {
$(collides).each(function(idx,item) {
diff --git a/modules-available/roomplanner/page.inc.php b/modules-available/roomplanner/page.inc.php
index 4e36d3ba..bae9db7b 100644
--- a/modules-available/roomplanner/page.inc.php
+++ b/modules-available/roomplanner/page.inc.php
@@ -58,7 +58,17 @@ class Page_Roomplanner extends Page
if ($this->action === 'show') {
/* do nothing */
Dashboard::disable();
- $config = Database::queryFirst('SELECT roomplan, managerip, dedicatedmgr, tutoruuid FROM location_roomplan WHERE locationid = :locationid', ['locationid' => $this->locationid]);
+ $config = Database::queryFirst('SELECT roomplan, managerip, tutoruuid FROM location_roomplan WHERE locationid = :locationid', ['locationid' => $this->locationid]);
+ $runmode = RunMode::getForMode(Page::getModule(), $this->locationid, true);
+ if (empty($runmode)) {
+ $config['dedicatedmgr'] = false;
+ } else {
+ $runmode = array_pop($runmode);
+ $config['managerip'] = $runmode['clientip'];
+ $config['manageruuid'] = $runmode['machineuuid'];
+ $data = json_decode($runmode['modedata'], true);
+ $config['dedicatedmgr'] = (isset($data['dedicatedmgr']) && $data['dedicatedmgr']);
+ }
if ($config !== false) {
$managerIp = $config['managerip'];
$dediMgr = $config['dedicatedmgr'] ? 'checked' : '';
@@ -90,7 +100,7 @@ class Page_Roomplanner extends Page
$query = Request::get('query', false, 'string');
$aquery = preg_replace('/[^\x01-\x7f]+/', '%', $query);
- $result = Database::simpleQuery('SELECT machineuuid, macaddr, clientip, hostname '
+ $result = Database::simpleQuery('SELECT machineuuid, macaddr, clientip, hostname, fixedlocationid '
. 'FROM machine '
. 'WHERE machineuuid LIKE :aquery '
. ' OR macaddr LIKE :aquery '
@@ -101,6 +111,9 @@ class Page_Roomplanner extends Page
$returnObject = ['machines' => []];
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
+ if (empty($row['hostname'])) {
+ $row['hostname'] = $row['clientip'];
+ }
$returnObject['machines'][] = $row;
}
echo json_encode($returnObject);
@@ -207,16 +220,30 @@ class Page_Roomplanner extends Page
protected function saveRoomConfig($furniture, $tutorUuid)
{
$obj = json_encode(['furniture' => $furniture]);
- Database::exec('INSERT INTO location_roomplan (locationid, roomplan, managerip, tutoruuid, dedicatedmgr)'
- . ' VALUES (:locationid, :roomplan, :managerip, :tutoruuid, :dedicatedmgr)'
+ $managerIp = Request::post('managerip', '', 'string');
+ Database::exec('INSERT INTO location_roomplan (locationid, roomplan, managerip, tutoruuid)'
+ . ' VALUES (:locationid, :roomplan, :managerip, :tutoruuid)'
. ' ON DUPLICATE KEY UPDATE '
- . ' roomplan=VALUES(roomplan), managerip=VALUES(managerip), tutoruuid=VALUES(tutoruuid), dedicatedmgr=VALUES(dedicatedmgr)', [
+ . ' roomplan=VALUES(roomplan), managerip=VALUES(managerip), tutoruuid=VALUES(tutoruuid)', [
'locationid' => $this->locationid,
'roomplan' => $obj,
- 'managerip' => Request::post('managerip', '', 'string'),
- 'dedicatedmgr' => (Request::post('dedimgr') === 'on' ? 1 : 0),
+ 'managerip' => $managerIp,
'tutoruuid' => $tutorUuid
]);
+ // See if the client is known, set run-mode
+ if (empty($managerIp)) {
+ RunMode::deleteMode(Page::getModule(), $this->locationid);
+ } else {
+ RunMode::deleteMode(Page::getModule(), $this->locationid);
+ $pc = Statistics::getMachinesByIp($managerIp, Machine::NO_DATA, 'lastseen DESC');
+ if (!empty($pc)) {
+ $dedicated = (Request::post('dedimgr') === 'on');
+ $pc = array_shift($pc);
+ RunMode::setRunMode($pc->machineuuid, Page::getModule()->getIdentifier(), $this->locationid, json_encode([
+ 'dedicatedmgr' => $dedicated
+ ]), !$dedicated);
+ }
+ }
}
protected function getFurniture($config)
@@ -262,7 +289,7 @@ class Page_Roomplanner extends Page
protected function getPotentialMachines()
{
- $result = Database::simpleQuery('SELECT m.machineuuid, m.macaddr, m.clientip, m.hostname, l.locationname AS otherroom
+ $result = Database::simpleQuery('SELECT m.machineuuid, m.macaddr, m.clientip, m.hostname, l.locationname AS otherroom, m.fixedlocationid
FROM machine m
LEFT JOIN location l ON (m.fixedlocationid = l.locationid AND m.subnetlocationid <> m.fixedlocationid)
WHERE subnetlocationid = :locationid', ['locationid' => $this->locationid]);
@@ -270,6 +297,9 @@ class Page_Roomplanner extends Page
$machines = [];
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
+ if (empty($row['hostname'])) {
+ $row['hostname'] = $row['clientip'];
+ }
$machines[] = $row;
}
diff --git a/modules-available/runmode/inc/runmode.inc.php b/modules-available/runmode/inc/runmode.inc.php
index 271542b8..d333af58 100644
--- a/modules-available/runmode/inc/runmode.inc.php
+++ b/modules-available/runmode/inc/runmode.inc.php
@@ -2,6 +2,9 @@
class RunMode
{
+ const DATA_DETAILED = 1;
+ const DATA_MACHINE_DATA = 2;
+ const DATA_STRINGS = 4;
private static $moduleConfigs = array();
@@ -29,58 +32,86 @@ class RunMode
* @param string|null $modeId an ID specific to the module to further specify the run mode, NULL to delete the run mode entry
* @param string|null $modeData optional, additional data for the run mode
* @param bool|null $isClient whether to count the machine as a client (in statistics etc.) NULL for looking at module's general runmode config
- * @return bool whether it was set
+ * @return bool whether it was set/deleted
*/
- public static function setRunMode($machineuuid, $moduleId, $modeId, $modeData, $isClient)
+ public static function setRunMode($machineuuid, $moduleId, $modeId, $modeData = null, $isClient = null)
{
- // - Check if module provides runmode config at all
- $config = self::getModuleConfig($moduleId);
- if ($config === false)
- return false;
+ if (is_object($moduleId)) {
+ $moduleId = $moduleId->getIdentifier();
+ }
// - Check if machine exists
$machine = Statistics::getMachine($machineuuid, Machine::NO_DATA);
if ($machine === false)
return false;
+ // - Delete entry if mode is null
+ if ($modeId === null) {
+ return Database::exec('DELETE FROM runmode WHERE machineuuid = :machineuuid', compact('machineuuid')) > 0;
+ }
// - Add/replace entry in runmode table
- if (is_null($modeId)) {
- Database::exec('DELETE FROM runmode WHERE machineuuid = :machineuuid', compact('machineuuid'));
- } else {
- if ($isClient === null) {
- $isClient = $config->isClient;
- }
- Database::exec('INSERT INTO runmode (machineuuid, module, modeid, modedata, isclient)'
- . ' VALUES (:uuid, :module, :modeid, :modedata, :isclient)'
- . ' ON DUPLICATE KEY'
- . ' UPDATE module = VALUES(module), modeid = VALUES(modeid), modedata = VALUES(modedata), isclient = VALUES(isclient)', array(
- 'uuid' => $machineuuid,
- 'module' => $moduleId,
- 'modeid' => $modeId,
- 'modedata' => $modeData,
- 'isclient' => ($isClient ? 1 : 0),
- ));
+ // - Check if module provides runmode config at all
+ $config = self::getModuleConfig($moduleId);
+ if ($config === false)
+ return false;
+ if ($isClient === null) {
+ $isClient = $config->isClient;
}
+ Database::exec('INSERT INTO runmode (machineuuid, module, modeid, modedata, isclient)'
+ . ' VALUES (:uuid, :module, :modeid, :modedata, :isclient)'
+ . ' ON DUPLICATE KEY'
+ . ' UPDATE module = VALUES(module), modeid = VALUES(modeid), modedata = VALUES(modedata), isclient = VALUES(isclient)', array(
+ 'uuid' => $machineuuid,
+ 'module' => $moduleId,
+ 'modeid' => $modeId,
+ 'modedata' => $modeData,
+ 'isclient' => ($isClient ? 1 : 0),
+ ));
return true;
}
/**
* @param string $machineuuid
- * @param bool $detailed whether to return meta data about machine, not just machineuuid
- * @param bool $assoc use machineuuid as array key
+ * @param int $returnData bitfield of data to return
* @return false|array {'machineuuid', 'isclient', 'module', 'modeid', 'modedata',
* <'hostname', 'clientip', 'macaddr', 'locationid', 'lastseen'>}
*/
- public static function getRunMode($machineuuid, $detailed = false)
+ public static function getRunMode($machineuuid, $returnData = self::DATA_MACHINE_DATA)
{
- if ($detailed) {
- $sel = ', m.hostname, m.clientip, m.macaddr, m.locationid, m.lastseen';
+ if ($returnData === true) {
+ $returnData = self::DATA_MACHINE_DATA | self::DATA_DETAILED;
+ }
+ if ($returnData & self::DATA_MACHINE_DATA) {
+ if ($returnData & self::DATA_DETAILED) {
+ $sel = ', m.hostname, m.clientip, m.macaddr, m.locationid, m.lastseen';
+ } else {
+ $sel = '';
+ }
+ $res = Database::queryFirst(
+ "SELECT m.machineuuid, r.isclient, r.module, r.modeid, r.modedata $sel
+ FROM machine m INNER JOIN runmode r USING (machineuuid)
+ WHERE m.machineuuid = :machineuuid LIMIT 1",
+ compact('machineuuid'));
} else {
- $sel = '';
+ $res = Database::queryFirst('SELECT r.machineuuid, r.isclient, r.module, r.modeid, r.modedata
+ FROM runmode r
+ WHERE r.machineuuid = :machineuuid LIMIT 1',
+ compact('machineuuid'));
+ }
+ if ($res === false)
+ return false;
+ if ($returnData & self::DATA_STRINGS) {
+ $module = Module::get($res['module']);
+ if ($module === false) {
+ $res['moduleName'] = $res['module'];
+ } else {
+ $res['moduleName'] = $module->getDisplayName();
+ }
+ $mode = self::getModeName($res['module'], $res['modeid']);
+ if ($mode === false) {
+ $mode = '???? unknown';
+ }
+ $res['modeName'] = $mode;
}
- return Database::queryFirst(
- "SELECT m.machineuuid, r.isclient, r.module, r.modeid, r.modedata $sel
- FROM machine m INNER JOIN runmode r USING (machineuuid)
- WHERE m.machineuuid = :machineuuid LIMIT 1",
- compact('machineuuid'));
+ return $res;
}
/**
@@ -129,7 +160,7 @@ class RunMode
$join = $sel = '';
}
$res = Database::simpleQuery(
- "SELECT r.machineuuid, r.modedata $sel
+ "SELECT r.machineuuid, r.modedata, r.isclient $sel
FROM runmode r $join
WHERE module = :module AND modeid = :modeId",
compact('module', 'modeId'));
@@ -148,6 +179,28 @@ class RunMode
}
/**
+ * Return assoc array of all configured clients.
+ * @param bool $withData also return data field?
+ * @param bool $isClient true = return clients only, false = return non-clients only, null = return both
+ * @return array all the entries from the table
+ */
+ public static function getAllClients($withData = false, $isClient = null)
+ {
+ $xtra = '';
+ if ($withData) {
+ $xtra .= ', modedata';
+ }
+ $res = Database::simpleQuery("SELECT machineuuid, module, modeid, isclient $xtra FROM runmode");
+ $ret = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ if (!is_null($isClient) && ($row['isclient'] != 0) !== $isClient)
+ continue;
+ $ret[$row['machineuuid']] = $row;
+ }
+ return $ret;
+ }
+
+ /**
* Get display name of a module's mode. If the module doesn't have a getModeName
* method configured, the modeId is simply returned. Otherwise the return value of
* that method is passed through. getModeName by contract should return false if
@@ -168,6 +221,21 @@ class RunMode
return call_user_func($conf->getModeName, $modeId);
}
+ /**
+ * Delete given runmode.
+ *
+ * @param string|\Module $module Module runmode belongs to
+ * @param string $modeId run mode id
+ */
+ public static function deleteMode($module, $modeId)
+ {
+ if (is_object($module)) {
+ $module = $module->getIdentifier();
+ }
+ Database::exec('DELETE FROM runmode WHERE module = :module AND modeid = :modeId',
+ compact('module', 'modeId'));
+ }
+
}
/* *\
@@ -207,6 +275,10 @@ class RunModeModuleConfig
* @var bool If true, config.tgz should not be downloaded by the client
*/
public $noSysconfig = false;
+ /**
+ * @var bool Allow adding and removing machines to this mode via the generic form
+ */
+ public $allowGenericEditor = true;
public function __construct($file)
{
@@ -220,6 +292,7 @@ class RunModeModuleConfig
$this->loadType($data, 'configHook', 'string');
$this->loadType($data, 'isClient', 'boolean');
$this->loadType($data, 'noSysconfig', 'boolean');
+ $this->loadType($data, 'allowGenericEditor', 'boolean');
}
private function loadType($data, $key, $type)
diff --git a/modules-available/runmode/install.inc.php b/modules-available/runmode/install.inc.php
index ec710bfa..db7e07f6 100644
--- a/modules-available/runmode/install.inc.php
+++ b/modules-available/runmode/install.inc.php
@@ -12,21 +12,8 @@ $res[] = tableCreate('runmode', "
KEY `module` (`module`,`modeid`)
");
-if (!tableExists('machine')) {
- // Cannot add constraint yet
- $res[] = UPDATE_RETRY;
-} else {
- $c = tableGetContraints('runmode', 'machineuuid', 'machine', 'machineuuid');
- if ($c === false)
- finalResponse(UPDATE_FAILED, 'Cannot get constraints of runmode table: ' . Database::lastError());
- if (empty($c)) {
- $alter = Database::exec('ALTER TABLE runmode ADD FOREIGN KEY (machineuuid) REFERENCES machine (machineuuid)
- ON DELETE CASCADE ON UPDATE CASCADE');
- if ($alter === false)
- finalResponse(UPDATE_FAILED, 'Cannot add machineuuid constraint to runmode table: ' . Database::lastError());
- $res[] = UPDATE_DONE;
- }
-}
+$res[] = tableAddConstraint('runmode', 'machineuuid', 'machine', 'machineuuid',
+ 'ON DELETE CASCADE ON UPDATE CASCADE');
if (!tableHasColumn('runmode', 'isclient')) {
$ret = Database::exec("ALTER TABLE runmode ADD COLUMN isclient bool DEFAULT '1'");
@@ -38,9 +25,4 @@ if (!tableHasColumn('runmode', 'isclient')) {
}
// Create response for browser
-
-if (in_array(UPDATE_DONE, $res)) {
- finalResponse(UPDATE_DONE, 'Tables created successfully');
-}
-
-finalResponse(UPDATE_NOOP, 'Everything already up to date'); \ No newline at end of file
+responseFromArray($res);
diff --git a/modules-available/runmode/lang/de/template-tags.json b/modules-available/runmode/lang/de/template-tags.json
new file mode 100644
index 00000000..6b45b82c
--- /dev/null
+++ b/modules-available/runmode/lang/de/template-tags.json
@@ -0,0 +1,10 @@
+{
+ "lang_addNewMachines": "Clients",
+ "lang_assignMachineIntroText": "Definieren Sie hier Clients, die in einem speziellen Betriebsmodus gestartet werden sollen. Sie k\u00f6nnen Rechner anhand der UUID, IP, Hostname oder MAC-Adresse suchen.",
+ "lang_assignRunmodeToMachine": "Betriebsmodus",
+ "lang_confirmDelete": "Wollen Sie den Betriebsmodus f\u00fcr diesen Client entfernen?",
+ "lang_isclient": "Pool-Client",
+ "lang_machine": "Client",
+ "lang_mode": "Modus",
+ "lang_specialMachinesForMode": "Betriebsmodus"
+} \ No newline at end of file
diff --git a/modules-available/runmode/lang/en/template-tags.json b/modules-available/runmode/lang/en/template-tags.json
new file mode 100644
index 00000000..43fd3da5
--- /dev/null
+++ b/modules-available/runmode/lang/en/template-tags.json
@@ -0,0 +1,10 @@
+{
+ "lang_addNewMachines": "Clients",
+ "lang_assignMachineIntroText": "Define the clients which should start a special runmode configuration. You can search for clients by UUID, IP address, host name or MAC address.",
+ "lang_assignRunmodeToMachine": "Runmode",
+ "lang_confirmDelete": "Do you want to delete the runmode for this client?",
+ "lang_isclient": "Pool-Client",
+ "lang_machine": "Client",
+ "lang_mode": "Mode",
+ "lang_specialMachinesForMode": "Runmode"
+} \ No newline at end of file
diff --git a/modules-available/runmode/page.inc.php b/modules-available/runmode/page.inc.php
index 05f32f81..ef42e7be 100644
--- a/modules-available/runmode/page.inc.php
+++ b/modules-available/runmode/page.inc.php
@@ -26,20 +26,33 @@ class Page_RunMode extends Page
$machines = array_filter(Request::post('machines', [], 'array'), 'is_string');
$module = Request::post('module', false, 'string');
$modeId = Request::post('modeid', false, 'string');
- // TODO Validate
+ $modConfig = RunMode::getModuleConfig($module);
+ if ($modConfig === false) {
+ Message::addError('module-hasnt-runmode', $module);
+ return;
+ }
+ if (!$modConfig->allowGenericEditor) {
+ Message::addError('cannot-edit-module', $module);
+ return;
+ }
+ $test = RunMode::getModeName($module, $modeId);
+ if ($test === false) {
+ Message::addError('invalid-modeid', $module, $modeId);
+ return;
+ }
$active = 0;
foreach ($machines as $machine) {
$ret = RunMode::setRunMode($machine, $module, $modeId, null, null);
if ($ret) {
$active++;
} else {
- Message::addError('invalid-module-or-machine', $module, $machine);
+ Message::addError('runmode.machine-not-found', $machine);
}
}
$deleted = Database::exec('DELETE FROM runmode
WHERE module = :module AND modeid = :modeId AND machineuuid NOT IN (:machines)',
compact('module', 'modeId', 'machines'));
- Message::addError('enabled-removed-save', $active, $deleted);
+ Message::addSuccess('runmode.enabled-removed-save', $active, $deleted);
$redirect = Request::post('redirect', false, 'string');
if ($redirect !== false) {
Util::redirect($redirect);
@@ -48,7 +61,13 @@ class Page_RunMode extends Page
} elseif ($action === 'delete-machine') {
$machineuuid = Request::post('machineuuid', false, 'string');
if ($machineuuid === false) {
-
+ Message::addError('machine-not-found', $machineuuid);
+ } else {
+ if (RunMode::setRunMode($machineuuid, null, null)) {
+ Message::addSuccess('machine-removed', $machineuuid);
+ } else {
+ Message::addWarning('machine-not-runmode', $machineuuid);
+ }
}
}
}
@@ -135,7 +154,7 @@ class Page_RunMode extends Page
$moduleId = $module->getIdentifier();
$modeName = RunMode::getModeName($moduleId, $modeId);
if ($modeName === false) {
- Message::addError('invalid-modeid', $modeId);
+ Message::addError('invalid-modeid', $moduleId, $modeId);
Util::redirect('?do=runmode');
}
Render::addTemplate('machine-selector', [
diff --git a/modules-available/runmode/templates/machine-selector.html b/modules-available/runmode/templates/machine-selector.html
index d3ff7378..7f37f5a2 100644
--- a/modules-available/runmode/templates/machine-selector.html
+++ b/modules-available/runmode/templates/machine-selector.html
@@ -108,11 +108,22 @@ function renderMachineSelected(item, escape) {
}
document.addEventListener('DOMContentLoaded', function () {
+ Selectize.define("no_bs", function (options) {
+ var original = this.deleteSelection;
+ this.deleteSelection = (function() {
+ return function (e) {
+ if (!e || e.keyCode !== 8) {
+ return original.apply(this, arguments);
+ }
+ return false;
+ };
+ })();
+ });
var old = {{{machines}}} || [];
var $box = $('#machine-sel').selectize({
options: old,
items: old.map(function(x) { return x.machineuuid; }),
- plugins: ["remove_button"],
+ plugins: ["remove_button", "no_bs"],
valueField: 'machineuuid',
searchField: "combined",
openOnFocus: false,
@@ -121,7 +132,7 @@ document.addEventListener('DOMContentLoaded', function () {
load: loadMachines,
maxItems: null,
sortField: 'hostname',
- sortDirection: 'asc'
+ sortDirection: 'asc',
});
});
diff --git a/modules-available/runmode/templates/module-machine-list.html b/modules-available/runmode/templates/module-machine-list.html
index a749a4a7..45f574ef 100644
--- a/modules-available/runmode/templates/module-machine-list.html
+++ b/modules-available/runmode/templates/module-machine-list.html
@@ -3,7 +3,7 @@
<a href="?do={{module}}">{{modulename}}</a>
</h2>
-<form method="post" action="?do=runmode" onclick="return confirm('{{lang_confirmDelete}}')">
+<form method="post" action="?do=runmode" onsubmit="return confirm('{{lang_confirmDelete}}')">
<input type="hidden" name="token" value="{{token}}">
<input type="hidden" name="action" value="delete-machine">
<table class="table">
diff --git a/modules-available/session/lang/en/messages.json b/modules-available/session/lang/en/messages.json
index de7ee4f3..a7b14d61 100644
--- a/modules-available/session/lang/en/messages.json
+++ b/modules-available/session/lang/en/messages.json
@@ -3,5 +3,5 @@
"pass-too-short": "Password too short",
"password-changed": "Password successfully changed",
"password-unchanged": "Password unchanged",
- "wrong-password": "Wrong passwort"
-} \ No newline at end of file
+ "wrong-password": "Wrong password"
+}
diff --git a/modules-available/statistics/api.inc.php b/modules-available/statistics/api.inc.php
index c395220a..a614658a 100644
--- a/modules-available/statistics/api.inc.php
+++ b/modules-available/statistics/api.inc.php
@@ -34,7 +34,7 @@ if ($type{0} === '~') {
// External mode of operation?
$mode = Request::post('mode', false, 'string');
$NOW = time();
- $old = Database::queryFirst('SELECT clientip, logintime, lastseen, lastboot FROM machine WHERE machineuuid = :uuid', array('uuid' => $uuid));
+ $old = Database::queryFirst('SELECT clientip, logintime, lastseen, lastboot, state, mbram, cpumodel FROM machine WHERE machineuuid = :uuid', array('uuid' => $uuid));
if ($old !== false) {
settype($old['logintime'], 'integer');
settype($old['lastseen'], 'integer');
@@ -43,7 +43,7 @@ if ($type{0} === '~') {
// Handle event type
if ($mode === false && $type === '~poweron') {
// Poweron & hw stats
- $uptime = Request::post('uptime', '', 'integer');
+ $uptime = Request::post('uptime', 0, 'integer');
if (strlen($macaddr) > 17) die("Invalid MAC.\n");
if ($uptime < 0 || $uptime > 4000000) die("Implausible uptime.\n");
$realcores = Request::post('realcores', 0, 'integer');
@@ -64,6 +64,65 @@ if ($type{0} === '~') {
$hostname = '';
}
$data = Request::post('data', '', 'string');
+ // Prepare insert/update to machine table
+ $new = array(
+ 'uuid' => $uuid,
+ 'macaddr' => $macaddr,
+ 'clientip' => $ip,
+ 'lastseen' => $NOW,
+ 'lastboot' => $NOW - $uptime,
+ 'realcores' => $realcores,
+ 'mbram' => $mbram,
+ 'kvmstate' => $kvmstate,
+ 'cpumodel' => $cpumodel,
+ 'systemmodel'=> $systemmodel,
+ 'id44mb' => $id44mb,
+ 'badsectors' => $badsectors,
+ 'data' => $data,
+ 'state' => 'IDLE',
+ );
+ // Create/update machine entry
+ if ($old === false) {
+ $new['firstseen'] = $NOW;
+ $new['hostname'] = $hostname;
+ $res = Database::exec('INSERT INTO machine '
+ . '(machineuuid, macaddr, clientip, firstseen, lastseen, logintime, position, lastboot, realcores, mbram,'
+ . ' kvmstate, cpumodel, systemmodel, id44mb, badsectors, data, hostname, state) VALUES '
+ . "(:uuid, :macaddr, :clientip, :firstseen, :lastseen, 0, '', :lastboot, :realcores, :mbram,"
+ . ' :kvmstate, :cpumodel, :systemmodel, :id44mb, :badsectors, :data, :hostname, :state)', $new, true);
+ if ($res === false) {
+ die("Concurrent insert, ignored. (RESULT=0)\n");
+ }
+ } else {
+ // Update
+ $moresql = ($uptime < 180 ? ' logintime = 0, currentuser = NULL, currentsession = NULL,' : '');
+ if (!empty($hostname)) {
+ $new['hostname'] = $hostname;
+ $moresql .= ' hostname = :hostname,';
+ }
+ $new['oldstate'] = $old['state'];
+ $new['oldlastseen'] = $old['lastseen'];
+ $res = Database::exec('UPDATE machine SET '
+ . ' macaddr = :macaddr,'
+ . ' clientip = :clientip,'
+ . ' lastseen = :lastseen,'
+ . ' lastboot = :lastboot,'
+ . $moresql
+ . ' realcores = :realcores,'
+ . ' mbram = :mbram,'
+ . ' kvmstate = :kvmstate,'
+ . ' cpumodel = :cpumodel,'
+ . ' systemmodel = :systemmodel,'
+ . ' id44mb = :id44mb,'
+ . ' badsectors = :badsectors,'
+ . ' data = :data,'
+ . ' state = :state '
+ . " WHERE machineuuid = :uuid AND state = :oldstate AND lastseen = :oldlastseen", $new);
+ if ($res === 0) {
+ die("Concurrent update, ignored. (RESULT=0)\n");
+ }
+ }
+ // Maybe log old crashed session
if ($uptime < 120) {
// See if we have a lingering session, create statistic entry if so
if ($old !== false && $old['logintime'] !== 0) {
@@ -93,49 +152,17 @@ if ($type{0} === '~') {
}
}
}
- // Create/update machine entry
- Database::exec('INSERT INTO machine '
- . '(machineuuid, macaddr, clientip, firstseen, lastseen, logintime, position, lastboot, realcores, mbram,'
- . ' kvmstate, cpumodel, systemmodel, id44mb, badsectors, data, hostname) VALUES '
- . "(:uuid, :macaddr, :clientip, :firstseen, :lastseen, 0, '', :lastboot, :realcores, :mbram,"
- . ' :kvmstate, :cpumodel, :systemmodel, :id44mb, :badsectors, :data, :hostname)'
- . ' ON DUPLICATE KEY UPDATE'
- . ' macaddr = VALUES(macaddr),'
- . ' clientip = VALUES(clientip),'
- . ' lastseen = VALUES(lastseen),'
- . ($uptime < 180 ? ' logintime = 0, currentuser = NULL, currentsession = NULL,' : '')
- . ' lastboot = VALUES(lastboot),'
- . ' realcores = VALUES(realcores),'
- . ' mbram = VALUES(mbram),'
- . ' kvmstate = VALUES(kvmstate),'
- . ' cpumodel = VALUES(cpumodel),'
- . ' systemmodel = VALUES(systemmodel),'
- . ' id44mb = VALUES(id44mb),'
- . ' badsectors = VALUES(badsectors),'
- . ' data = VALUES(data),'
- . " hostname = If(VALUES(hostname) = '', hostname, VALUES(hostname))", array(
- 'uuid' => $uuid,
- 'macaddr' => $macaddr,
- 'clientip' => $ip,
- 'firstseen' => $NOW,
- 'lastseen' => $NOW,
- 'lastboot' => $NOW - $uptime,
- 'realcores' => $realcores,
- 'mbram' => $mbram,
- 'kvmstate' => $kvmstate,
- 'cpumodel' => $cpumodel,
- 'systemmodel'=> $systemmodel,
- 'id44mb' => $id44mb,
- 'badsectors' => $badsectors,
- 'data' => $data,
- 'hostname' => $hostname,
- ));
if (($old === false || $old['clientip'] !== $ip) && Module::isAvailable('locations')) {
// New, or ip changed (dynamic pool?), update subnetlicationid
Location::updateMapIpToLocation($uuid, $ip);
}
+ // Check for suspicious hardware changes
+ if ($old !== false) {
+ checkHardwareChange($old, $new);
+ }
+
// Write statistics data
} else if ($type === '~runstate') {
@@ -147,7 +174,7 @@ if ($type{0} === '~') {
die("Address changed.\n");
}
$used = Request::post('used', 0, 'integer');
- if ($old['lastboot'] === 0 && $NOW - $old['lastseen'] > 300) {
+ if ($old['state'] === 'OFFLINE' && $NOW - $old['lastseen'] > 600) {
$strUpdateBoottime = ' lastboot = UNIX_TIMESTAMP(), ';
} else {
$strUpdateBoottime = '';
@@ -161,31 +188,42 @@ if ($type{0} === '~') {
}
$old['logintime'] = 0;
}
- $old['lastboot'] = 0;
}
// Figure out what's happening - state changes
- if ($used === 0 && $old['logintime'] !== 0) {
+ $params = array(
+ 'uuid' => $uuid,
+ 'oldlastseen' => $old['lastseen'],
+ 'oldstate' => $old['state'],
+ );
+ if ($used === 0 && $old['state'] !== 'IDLE') {
// Is not in use, was in use before
$sessionLength = $NOW - $old['logintime'];
- Database::exec('UPDATE machine SET lastseen = UNIX_TIMESTAMP(),'
+ $res = Database::exec('UPDATE machine SET lastseen = UNIX_TIMESTAMP(),'
. $strUpdateBoottime
- . ' logintime = 0, currentuser = NULL WHERE machineuuid = :uuid', array('uuid' => $uuid));
- } elseif ($used === 1 && $old['logintime'] === 0) {
+ . " logintime = 0, currentuser = NULL, state = 'IDLE' "
+ . " WHERE machineuuid = :uuid AND lastseen = :oldlastseen AND state = :oldstate",
+ $params);
+ } elseif ($used === 1 && $old['state'] !== 'OCCUPIED') {
// Machine is in use, was free before
if ($sessionLength !== 0 || $old['logintime'] === 0) {
// This event is a start of a new session, rather than an update
- Database::exec('UPDATE machine SET lastseen = UNIX_TIMESTAMP(),'
+ $params['user'] = Request::post('user', null, 'string');
+ $res = Database::exec('UPDATE machine SET lastseen = UNIX_TIMESTAMP(),'
. $strUpdateBoottime
- . ' logintime = UNIX_TIMESTAMP(), currentuser = :user WHERE machineuuid = :uuid', array(
- 'uuid' => $uuid,
- 'user' => Request::post('user', null, 'string'),
- ));
+ . " logintime = UNIX_TIMESTAMP(), currentuser = :user, currentsession = NULL, state = 'OCCUPIED' "
+ . " WHERE machineuuid = :uuid AND lastseen = :oldlastseen AND state = :oldstate", $params);
+ } else {
+ $res = 0;
}
} else {
// Nothing changed, simple lastseen update
- Database::exec('UPDATE machine SET '
+ $res = Database::exec('UPDATE machine SET '
. $strUpdateBoottime
- . ' lastseen = UNIX_TIMESTAMP() WHERE machineuuid = :uuid', array('uuid' => $uuid));
+ . ' lastseen = UNIX_TIMESTAMP() WHERE machineuuid = :uuid AND lastseen = :oldlastseen AND state = :oldstate', $params);
+ }
+ // Did we update, or was there a concurrent update?
+ if ($res === 0) {
+ die("Concurrent update, ignored. (RESULT=0)\n");
}
// 9) Log last session length if applicable
if ($mode === false && $sessionLength > 0 && $sessionLength < 86400*2 && $old['logintime'] !== 0) {
@@ -215,8 +253,11 @@ if ($type{0} === '~') {
));
}
}
- Database::exec('UPDATE machine SET logintime = 0, lastseen = UNIX_TIMESTAMP(), lastboot = 0 WHERE machineuuid = :uuid', array('uuid' => $uuid));
+ Database::exec("UPDATE machine SET logintime = 0, lastseen = UNIX_TIMESTAMP(), state = 'OFFLINE'
+ WHERE machineuuid = :uuid AND state = :oldstate AND lastseen = :oldlastseen",
+ array('uuid' => $uuid, 'oldlastseen' => $old['lastseen'], 'oldstate' => $old['state']));
} elseif ($mode === false && $type === '~screens') {
+ if ($old === false) die("Unknown machine.\n");
$screens = Request::post('screen', false, 'array');
if (is_array($screens)) {
// `devicetype`, `devicename`, `subid`, `machineuuid`
@@ -287,6 +328,47 @@ if ($type{0} === '~') {
}
}
+ } else if ($type === '~suspend') {
+ // Client entering suspend
+ if ($old === false) die("Unknown machine.\n");
+ if ($old['clientip'] !== $ip) {
+ EventLog::warning("[suspend] IP address of client $uuid seems to have changed ({$old['clientip']} -> $ip)");
+ die("Address changed.\n");
+ }
+ if ($NOW - $old['lastseen'] < 610 && $old['state'] !== 'OFFLINE') {
+ Database::exec("UPDATE machine SET lastseen = UNIX_TIMESTAMP(), state = 'STANDBY'
+ WHERE machineuuid = :uuid AND state = :oldstate AND lastseen = :oldlastseen",
+ array('uuid' => $uuid, 'oldlastseen' => $old['lastseen'], 'oldstate' => $old['state']));
+ } else {
+ EventLog::info("[suspend] Client $uuid reported switch to standby when it wasn't powered on first. Was: " . $old['state']);
+ }
+ } else if ($type === '~resume') {
+ // Waking up from suspend
+ if ($old === false) die("Unknown machine.\n");
+ if ($old['clientip'] !== $ip) {
+ EventLog::info("[resume] IP address of client $uuid seems to have changed ({$old['clientip']} -> $ip), allowed on resume.");
+ }
+ if ($old['state'] === 'STANDBY') {
+ $res = Database::exec("UPDATE machine SET state = 'IDLE', clientip = :ip, lastseen = UNIX_TIMESTAMP()
+ WHERE machineuuid = :uuid AND state = :oldstate AND lastseen = :oldlastseen",
+ array('uuid' => $uuid, 'ip' => $ip, 'oldlastseen' => $old['lastseen'], 'oldstate' => $old['state']));
+ // Write standby period length to statistic table
+ if ($mode === false && $res > 0 && $old['lastseen'] !== 0) {
+ $lastSeen = $old['lastseen'];
+ $duration = $NOW - $lastSeen;
+ if ($duration > 500 && $duration < 86400 * 14) {
+ Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)'
+ . " VALUES (:suspend, '~suspend-length', :uuid, :clientip, '', :length)", array(
+ 'suspend' => $lastSeen,
+ 'uuid' => $uuid,
+ 'clientip' => $ip,
+ 'length' => $duration
+ ));
+ }
+ }
+ } else {
+ EventLog::info("[resume] Client $uuid reported wakeup from standby when it wasn't logged as being in standby. Was: " . $old['state']);
+ }
} else {
die("INVALID ACTION '$type'\n");
}
@@ -315,6 +397,7 @@ function writeStatisticLog($type, $username, $data)
));
}
+
// For backwards compat, we require the . prefix
if ($type{0} === '.') {
if ($type === '.vmchooser-session') {
@@ -336,4 +419,22 @@ if ($type{0} === '.') {
}
}
+/**
+ * @param array $old row from DB with client's old data
+ * @param array $new new data to be written
+ */
+function checkHardwareChange($old, $new)
+{
+ if ($new['mbram'] !== 0) {
+ if ($new['mbram'] + 1000 < $old['mbram']) {
+ $ram1 = round($old['mbram'] / 512) / 2;
+ $ram2 = round($new['mbram'] / 512) / 2;
+ EventLog::warning('[poweron] Client ' . $new['uuid'] . ' (' . $new['clientip'] . "): RAM decreased from {$ram1}GB to {$ram2}GB");
+ }
+ if (!empty($old['cpumodel']) && !empty($new['cpumodel']) && $new['cpumodel'] !== $old['cpumodel']) {
+ EventLog::warning('[poweron] Client ' . $new['uuid'] . ' (' . $new['clientip'] . "): CPU changed from '{$old['cpumodel']}' to '{$new['cpumodel']}'");
+ }
+ }
+}
+
echo "OK.\n";
diff --git a/modules-available/statistics/hooks/cron.inc.php b/modules-available/statistics/hooks/cron.inc.php
index 575ab6ba..4df7b0d4 100644
--- a/modules-available/statistics/hooks/cron.inc.php
+++ b/modules-available/statistics/hooks/cron.inc.php
@@ -1,18 +1,33 @@
<?php
-function logstats() {
+function logstats()
+{
$NOW = time();
$cutoff = $NOW - 86400 * 30;
- $online = $NOW - 610;
- $known = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $cutoff");
- $on = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $online");
- $used = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $online AND logintime <> 0");
+ $join = $where = '';
+ if (Module::get('runmode') !== false) {
+ $join = 'LEFT JOIN runmode r USING (machineuuid)';
+ $where = 'AND (r.isclient IS NULL OR r.isclient <> 0)';
+ }
+ $known = Database::queryFirst("SELECT Count(*) AS val FROM machine m $join WHERE m.lastseen > $cutoff $where");
+ $on = Database::queryFirst("SELECT Count(*) AS val FROM machine m $join WHERE m.state IN ('IDLE', 'OCCUPIED') $where");
+ $used = Database::queryFirst("SELECT Count(*) AS val FROM machine m $join WHERE m.state = 'OCCUPIED' $where");
Database::exec("INSERT INTO statistic (dateline, typeid, clientip, username, data) VALUES (:now, '~stats', '', '', :vals)", array(
'now' => $NOW,
'vals' => $known['val'] . '#' . $on['val'] . '#' . $used['val'],
));
}
+function state_cleanup()
+{
+ // Fix online state of machines that crashed
+ $standby = time() - 86400 * 2; // Reset standby machines after two days
+ $on = time() - 610; // Reset others after ~10 minutes
+ Database::exec("UPDATE machine SET state = 'OFFLINE' WHERE lastseen < If(state = 'STANDBY', $standby, $on) AND state <> 'OFFLINE'");
+}
+
+state_cleanup();
+
logstats();
if (mt_rand(1, 10) === 1) {
diff --git a/modules-available/statistics/inc/filter.inc.php b/modules-available/statistics/inc/filter.inc.php
index 0afce572..6e437a71 100644
--- a/modules-available/statistics/inc/filter.inc.php
+++ b/modules-available/statistics/inc/filter.inc.php
@@ -29,7 +29,7 @@ class Filter
$addendum = '';
/* check if we have to do some parsing*/
- if (Page_Statistics::$columns[$this->column]['type'] == 'date') {
+ if (Page_Statistics::$columns[$this->column]['type'] === 'date') {
$args[$key] = strtotime($this->argument);
} else {
$args[$key] = $this->argument;
@@ -180,21 +180,18 @@ class StateFilter extends Filter
{
public function __construct($operator, $argument)
{
- $this->operator = $operator;
- $this->argument = $argument;
+ parent::__construct(null, $operator, $argument);
}
public function whereClause(&$args, &$joins)
{
+ $map = [ 'on' => ['IDLE', 'OCCUPIED'], 'off' => ['OFFLINE'], 'idle' => ['IDLE'], 'occupied' => ['OCCUPIED'], 'standby' => ['STANDBY'] ];
$neg = $this->operator == '!=' ? 'NOT ' : '';
- if ($this->argument === 'on') {
- return " $neg (lastseen + 600 > UNIX_TIMESTAMP() ) ";
- } elseif ($this->argument === 'off') {
- return " $neg (lastseen + 600 < UNIX_TIMESTAMP() ) ";
- } elseif ($this->argument === 'idle') {
- return " $neg (lastseen + 600 > UNIX_TIMESTAMP() AND logintime = 0 ) ";
- } elseif ($this->argument === 'occupied') {
- return " $neg (lastseen + 600 > UNIX_TIMESTAMP() AND logintime <> 0 ) ";
+ if (array_key_exists($this->argument, $map)) {
+ global $unique_key;
+ $key = $this->column . '_arg' . ($unique_key++);
+ $args[$key] = $map[$this->argument];
+ return " machine.state $neg IN ( :$key ) ";
} else {
Message::addError('invalid-filter-argument', 'state', $this->argument);
return ' 1';
@@ -216,8 +213,10 @@ class LocationFilter extends Filter
$neg = $this->operator === '=' ? '' : 'NOT';
return "machine.locationid IS $neg NULL";
} else {
- $args['lid'] = $this->argument;
- return "machine.locationid {$this->operator} :lid";
+ global $unique_key;
+ $key = $this->column . '_arg' . ($unique_key++);
+ $args[$key] = $this->argument;
+ return "machine.locationid {$this->operator} :$key";
}
}
}
@@ -236,3 +235,20 @@ class SubnetFilter extends Filter
}
}
+class IsClientFilter extends Filter
+{
+ public function __construct($argument)
+ {
+ parent::__construct(null, null, $argument);
+ }
+
+ public function whereClause(&$args, &$joins)
+ {
+ if ($this->argument) {
+ $joins[] = ' LEFT JOIN runmode USING (machineuuid)';
+ return "(runmode.isclient <> 0 OR runmode.isclient IS NULL)";
+ }
+ $joins[] = ' INNER JOIN runmode USING (machineuuid)';
+ return "runmode.isclient = 0";
+ }
+}
diff --git a/modules-available/statistics/inc/filterset.inc.php b/modules-available/statistics/inc/filterset.inc.php
index c73feeef..25c5c8fa 100644
--- a/modules-available/statistics/inc/filterset.inc.php
+++ b/modules-available/statistics/inc/filterset.inc.php
@@ -2,6 +2,9 @@
class FilterSet
{
+ /**
+ * @var \Filter[]
+ */
private $filters;
private $sortDirection;
private $sortColumn;
@@ -39,7 +42,7 @@ class FilterSet
$where .= $sep . $filter->whereClause($args, $joins);
}
}
- $join = implode('', array_unique($joins));
+ $join = implode(' ', array_unique($joins));
$col = $this->sortColumn;
$isMapped = array_key_exists('map_sort', Page_Statistics::$columns[$col]);
@@ -72,4 +75,13 @@ class FilterSet
{
return $this->sortColumn;
}
+
+ public function filterNonClients()
+ {
+ if (Module::get('runmode') === false)
+ return;
+ // Runmode module exists, add filter
+ $this->filters[] = new IsClientFilter(true);
+ }
+
}
diff --git a/modules-available/statistics/inc/machine.inc.php b/modules-available/statistics/inc/machine.inc.php
index 8cb5e884..8605749b 100644
--- a/modules-available/statistics/inc/machine.inc.php
+++ b/modules-available/statistics/inc/machine.inc.php
@@ -51,6 +51,11 @@ class Machine
public $logintime;
/**
+ * @var string state of machine (OFFLINE, IDLE, OCCUPIED, STANDBY)
+ */
+ public $state;
+
+ /**
* @var string json data of position inside room (if any), null/empty otherwise
*/
public $position;
diff --git a/modules-available/statistics/inc/statistics.inc.php b/modules-available/statistics/inc/statistics.inc.php
index 1c9ebf07..2500f16f 100644
--- a/modules-available/statistics/inc/statistics.inc.php
+++ b/modules-available/statistics/inc/statistics.inc.php
@@ -7,17 +7,12 @@ class Statistics
private static $machineFields = false;
- /**
- * @param string $machineuuid
- * @param int $returnData
- * @return \Machine|false
- */
- public static function getMachine($machineuuid, $returnData)
+ private static function initFields($returnData)
{
if (self::$machineFields === false) {
$r = new ReflectionClass('Machine');
$props = $r->getProperties(ReflectionProperty::IS_PUBLIC);
- self::$machineFields = array_flip(array_map(function($e) { return $e->getName(); }, $props));
+ self::$machineFields = array_flip(array_map(function(/* @var ReflectionProperty $e */ $e) { return $e->getName(); }, $props));
}
if ($returnData === Machine::NO_DATA) {
unset(self::$machineFields['data']);
@@ -26,8 +21,19 @@ class Statistics
} else {
Util::traceError('Invalid $returnData option passed');
}
- $fields = implode(',', array_keys(self::$machineFields));
- $row = Database::queryFirst("SELECT * FROM machine WHERE machineuuid = :machineuuid", compact('machineuuid'));
+ return implode(',', array_keys(self::$machineFields));
+ }
+
+ /**
+ * @param string $machineuuid
+ * @param int $returnData What kind of data to return Machine::NO_DATA, Machine::RAW_DATA, ...
+ * @return \Machine|false
+ */
+ public static function getMachine($machineuuid, $returnData)
+ {
+ $fields = self::initFields($returnData);
+
+ $row = Database::queryFirst("SELECT $fields FROM machine WHERE machineuuid = :machineuuid", compact('machineuuid'));
if ($row === false)
return false;
$m = new Machine();
@@ -37,4 +43,31 @@ class Statistics
return $m;
}
+ /**
+ * @param string $ip
+ * @param int $returnData What kind of data to return Machine::NO_DATA, Machine::RAW_DATA, ...
+ * @param string $sort something like 'lastseen ASC' - not sanitized, don't pass user input!
+ * @return \Machine[] list of matches
+ */
+ public static function getMachinesByIp($ip, $returnData, $sort = false)
+ {
+ $fields = self::initFields($returnData);
+
+ if ($sort === false) {
+ $sort = '';
+ } else {
+ $sort = "ORDER BY $sort";
+ }
+ $res = Database::simpleQuery("SELECT $fields FROM machine WHERE clientip = :ip $sort", compact('ip'));
+ $list = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $m = new Machine();
+ foreach ($row as $key => $val) {
+ $m->{$key} = $val;
+ }
+ $list[] = $m;
+ }
+ return $list;
+ }
+
}
diff --git a/modules-available/statistics/install.inc.php b/modules-available/statistics/install.inc.php
index bfa342c4..4e2dfcca 100644
--- a/modules-available/statistics/install.inc.php
+++ b/modules-available/statistics/install.inc.php
@@ -36,6 +36,7 @@ $res[] = $machineCreate = tableCreate('machine', "
`logintime` int(10) unsigned NOT NULL,
`position` varchar(200) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`lastboot` int(10) unsigned NOT NULL,
+ `state` enum('OFFLINE', 'IDLE', 'OCCUPIED', 'STANDBY', 'IGNORED') NOT NULL DEFAULT 'OFFLINE',
`realcores` smallint(5) unsigned NOT NULL,
`mbram` int(10) unsigned NOT NULL,
`kvmstate` enum('UNKNOWN','UNSUPPORTED','DISABLED','ENABLED') NOT NULL,
@@ -51,6 +52,7 @@ $res[] = $machineCreate = tableCreate('machine', "
PRIMARY KEY (`machineuuid`),
KEY `macaddr` (`macaddr`),
KEY `clientip` (`clientip`),
+ KEY `state` (`state`),
KEY `realcores` (`realcores`),
KEY `mbram` (`mbram`),
KEY `kvmstate` (`kvmstate`),
@@ -198,6 +200,7 @@ if ($addTrigger) {
finalResponse(UPDATE_RETRY, 'Locations module not installed yet, retry later');
}
}
+ $res[] = UPDATE_DONE;
}
if ($machineHwCreate === UPDATE_DONE) {
@@ -217,12 +220,19 @@ if ($machineHwCreate === UPDATE_DONE) {
if ($ret === false) {
finalResponse(UPDATE_FAILED, 'Adding constraint to statistic_hw_prop failed: ' . Database::lastError());
}
+ $res[] = UPDATE_DONE;
}
-// Create response
-
-if (in_array(UPDATE_DONE, $res)) {
- finalResponse(UPDATE_DONE, 'Tables created successfully');
+// 2017-11-27: Add state column
+if (!tableHasColumn('machine', 'state')) {
+ $ret = Database::exec("ALTER TABLE `machine`
+ ADD COLUMN `state` enum('OFFLINE', 'IDLE', 'OCCUPIED', 'STANDBY', 'IGNORED') NOT NULL DEFAULT 'OFFLINE' AFTER `lastboot`,
+ ADD INDEX `state` (`state`)");
+ if ($ret === false) {
+ finalResponse(UPDATE_FAILED, 'Adding state column to machine table failed: ' . Database::lastError());
+ }
+ $res[] = UPDATE_DONE;
}
-finalResponse(UPDATE_NOOP, 'Everything already up to date');
+// Create response
+responseFromArray($res);
diff --git a/modules-available/statistics/lang/de/messages.json b/modules-available/statistics/lang/de/messages.json
index 8bdf9cfc..e3dff61c 100644
--- a/modules-available/statistics/lang/de/messages.json
+++ b/modules-available/statistics/lang/de/messages.json
@@ -1,6 +1,6 @@
{
- "invalid-filter": "Ung\u00fcltiger Filter",
"invalid-filter-argument": "Das Argument {{1}} ist nicht g\u00fcltig f\u00fcr den Filter {{0}}",
"invalid-filter-key": "{{0}} ist kein g\u00fcltiges Filterkriterium",
- "notes-saved": "Anmerkungen gespeichert"
+ "notes-saved": "Anmerkungen gespeichert",
+ "unknown-machine": "Unbekannte Rechner-ID {{0}}"
} \ No newline at end of file
diff --git a/modules-available/statistics/lang/de/template-tags.json b/modules-available/statistics/lang/de/template-tags.json
index 474d952a..56cf55d7 100644
--- a/modules-available/statistics/lang/de/template-tags.json
+++ b/modules-available/statistics/lang/de/template-tags.json
@@ -37,6 +37,7 @@
"lang_machineOccupied": "Der Rechner ist eingeschaltet und wird benutzt",
"lang_machineOccupiedBy": "In Verwendung durch",
"lang_machineOff": "Der Rechner ist ausgeschaltet, oder hat kein bwLehrpool gebootet",
+ "lang_machineStandby": "Im Standby",
"lang_machineSummary": "Zusammenfassung",
"lang_maximumAbbrev": "Max.",
"lang_memoryStats": "Arbeitsspeicher",
@@ -65,6 +66,8 @@
"lang_ramSlots": "Speicher-Slots",
"lang_realCores": "Kerne",
"lang_reallocatedSectors": "Defekte Sektoren",
+ "lang_runMode": "Betriebsmodus",
+ "lang_runmodeMachines": "Mit besonderem Betriebsmodus",
"lang_screens": "Bildschirme",
"lang_serialNo": "Serien-Nr",
"lang_showList": "Liste",
@@ -83,4 +86,4 @@
"lang_virtualCores": "Virtuelle Kerne",
"lang_when": "Wann",
"lang_withBadSectors": "Clients mit potentiell defekten Festplatten (mehr als 10 defekte Sektoren)"
-}
+} \ No newline at end of file
diff --git a/modules-available/statistics/lang/en/messages.json b/modules-available/statistics/lang/en/messages.json
index 40eac8c9..ae6c47af 100644
--- a/modules-available/statistics/lang/en/messages.json
+++ b/modules-available/statistics/lang/en/messages.json
@@ -1,6 +1,6 @@
{
- "invalid-filter": "Invalid filter",
"invalid-filter-argument": "{{1}} is not a vald argument for filter {{0}}",
"invalid-filter-key": "{{0}} is not a valid filter",
- "notes-saved": "Notes have been saved"
+ "notes-saved": "Notes have been saved",
+ "unknown-machine": "Unknown machine uuid {{0}}"
} \ No newline at end of file
diff --git a/modules-available/statistics/lang/en/template-tags.json b/modules-available/statistics/lang/en/template-tags.json
index f514b894..ab7a7d0a 100644
--- a/modules-available/statistics/lang/en/template-tags.json
+++ b/modules-available/statistics/lang/en/template-tags.json
@@ -37,6 +37,7 @@
"lang_machineOccupied": "Machine is powered on and in use",
"lang_machineOccupiedBy": "In use by",
"lang_machineOff": "Machine is powered down, or is not running bwLehrpool",
+ "lang_machineStandby": "In standby mode",
"lang_machineSummary": "Summary",
"lang_maximumAbbrev": "max.",
"lang_memoryStats": "Memory",
@@ -65,6 +66,8 @@
"lang_ramSlots": "Memory slots",
"lang_realCores": "Cores",
"lang_reallocatedSectors": "Bad sectors",
+ "lang_runMode": "Mode of operation",
+ "lang_runmodeMachines": "With special mode of operation",
"lang_screens": "Screens",
"lang_serialNo": "Serial no",
"lang_showList": "List",
@@ -83,4 +86,4 @@
"lang_virtualCores": "Virtual cores",
"lang_when": "When",
"lang_withBadSectors": "Clients with potentially bad HDDs (more than 10 reallocated sectors)"
-}
+} \ No newline at end of file
diff --git a/modules-available/statistics/page.inc.php b/modules-available/statistics/page.inc.php
index a003a303..d80d4614 100644
--- a/modules-available/statistics/page.inc.php
+++ b/modules-available/statistics/page.inc.php
@@ -106,6 +106,12 @@ class Page_Statistics extends Page
'op' => Page_Statistics::$op_nominal,
'type' => 'string',
'column' => true
+ ],
+ 'state' => [
+ 'op' => Page_Statistics::$op_nominal,
+ 'type' => 'enum',
+ 'column' => true,
+ 'values' => ['occupied', 'on']
]
];
if (Module::isAvailable('locations')) {
@@ -190,8 +196,6 @@ class Page_Statistics extends Page
} elseif ($action === 'addprojector' || $action === 'delprojector') {
$this->handleProjector($action);
}
- // Fix online state of machines that crashed -- TODO: Make cronjob for this
- Database::exec("UPDATE machine SET lastboot = 0 WHERE lastseen < UNIX_TIMESTAMP() - 610");
}
protected function doRender()
@@ -199,7 +203,12 @@ class Page_Statistics extends Page
$uuid = Request::get('uuid', false, 'string');
if ($uuid !== false) {
$this->showMachine($uuid);
+ return;
+ }
+ $show = Request::get('show', 'stat', 'string');
+ if ($show === 'projectors') {
+ $this->showProjectors();
return;
}
@@ -210,23 +219,19 @@ class Page_Statistics extends Page
}
$sortColumn = Request::any('sortColumn');
$sortDirection = Request::any('sortDirection');
- $filters = Filter::parseQuery($this->query);
+ $filters = Filter::parseQuery($this->query);
$filterSet = new FilterSet($filters);
$filterSet->setSort($sortColumn, $sortDirection);
-
- $show = Request::get('show', 'stat', 'string');
if ($show == 'list') {
Render::openTag('div', array('class' => 'row'));
$this->showFilter('list', $filterSet);
Render::closeTag('div');
$this->showMachineList($filterSet);
return;
- } elseif ($show === 'projectors') {
- $this->showProjectors();
- return;
}
+ $filterSet->filterNonClients();
Render::openTag('div', array('class' => 'row'));
$this->showFilter('stat', $filterSet);
$this->showSummary($filterSet);
@@ -328,8 +333,8 @@ class Page_Statistics extends Page
if ($known['val'] == 1) {
$this->redirectFirst($where, $join, $args);
}
- $on = Database::queryFirst("SELECT Count(*) AS val FROM machine $join WHERE lastboot <> 0 AND ($where)", $args);
- $used = Database::queryFirst("SELECT Count(*) AS val FROM machine $join WHERE lastboot <> 0 AND logintime <> 0 AND ($where)", $args);
+ $on = Database::queryFirst("SELECT Count(*) AS val FROM machine $join WHERE state IN ('IDLE', 'OCCUPIED') AND ($where)", $args);
+ $used = Database::queryFirst("SELECT Count(*) AS val FROM machine $join WHERE state = 'OCCUPIED' AND ($where)", $args);
$hdd = Database::queryFirst("SELECT Count(*) AS val FROM machine $join WHERE badsectors >= 10 AND ($where)", $args);
if ($on['val'] != 0) {
$usedpercent = round($used['val'] / $on['val'] * 100);
@@ -367,6 +372,10 @@ class Page_Statistics extends Page
}
$data['json'] = json_encode(array('labels' => $labels, 'datasets' => array($points1, $points2)));
$data['query'] = $this->query;
+ if (Module::get('runmode') !== false) {
+ $res = Database::queryFirst('SELECT Count(*) AS cnt FROM runmode');
+ $data['runmode'] = $res['cnt'];
+ }
// Draw
Render::addTemplate('summary', $data);
}
@@ -570,10 +579,16 @@ class Page_Statistics extends Page
$xtra = '';
if ($filterSet->isNoId44Filter()) {
- $xtra = ', data';
+ $xtra .= ', data';
+ }
+ if (Module::isAvailable('runmode')) {
+ $xtra .= ', runmode.module AS rmmodule';
+ if (strpos($join, 'runmode') === false) {
+ $join .= ' LEFT JOIN runmode USING (machineuuid) ';
+ }
}
- $res = Database::simpleQuery('SELECT machineuuid, macaddr, clientip, firstseen, lastseen,'
- . ' logintime, lastboot, realcores, mbram, kvmstate, cpumodel, id44mb, hostname, notes IS NOT NULL AS hasnotes,'
+ $res = Database::simpleQuery('SELECT machineuuid, macaddr, clientip, lastseen,'
+ . ' logintime, state, realcores, mbram, kvmstate, cpumodel, id44mb, hostname, notes IS NOT NULL AS hasnotes,'
. ' badsectors ' . $xtra . ' FROM machine'
. " $join WHERE $where $sort", $args);
$rows = array();
@@ -585,13 +600,7 @@ class Page_Statistics extends Page
} else {
$singleMachine = false;
}
- if ($row['lastboot'] == 0) {
- $row['state_off'] = true;
- } elseif ($row['logintime'] == 0) {
- $row['state_idle'] = true;
- } else {
- $row['state_occupied'] = true;
- }
+ $row['state_' . $row['state']] = true;
//$row['firstseen'] = date('d.m.Y H:i', $row['firstseen']);
$row['lastseen_int'] = $row['lastseen'];
$row['lastseen'] = date('d.m. H:i', $row['lastseen']);
@@ -725,24 +734,40 @@ class Page_Statistics extends Page
private function showMachine($uuid)
{
- $client = Database::queryFirst('SELECT machineuuid, locationid, macaddr, clientip, firstseen, lastseen, logintime, lastboot,'
+ $client = Database::queryFirst('SELECT machineuuid, locationid, macaddr, clientip, firstseen, lastseen, logintime, lastboot, state,'
. ' mbram, kvmstate, cpumodel, id44mb, data, hostname, currentuser, currentsession, notes FROM machine WHERE machineuuid = :uuid',
array('uuid' => $uuid));
+ if ($client === false) {
+ Message::addError('unknown-machine', $uuid);
+ return;
+ }
// Hack: Get raw collected data
if (Request::get('raw', false)) {
Header('Content-Type: text/plain; charset=utf-8');
die($client['data']);
}
+ // Runmode
+ if (Module::isAvailable('runmode')) {
+ $data = RunMode::getRunMode($uuid, RunMode::DATA_STRINGS);
+ if ($data !== false) {
+ $client += $data;
+ }
+ }
+ if (!isset($client['isclient'])) {
+ $client['isclient'] = true;
+ }
// Mangle fields
$NOW = time();
- if ($client['lastboot'] == 0) {
- $client['state_off'] = true;
- } elseif ($client['logintime'] == 0) {
- $client['state_idle'] = true;
+ if (!$client['isclient']) {
+ if ($client['state'] === 'IDLE') {
+ $client['state'] = 'OCCUPIED';
+ }
} else {
- $client['state_occupied'] = true;
- $this->fillSessionInfo($client);
+ if ($client['state'] === 'OCCUPIED') {
+ $this->fillSessionInfo($client);
+ }
}
+ $client['state_' . $client['state']] = true;
$client['firstseen_s'] = date('d.m.Y H:i', $client['firstseen']);
$client['lastseen_s'] = date('d.m.Y H:i', $client['lastseen']);
if ($client['lastboot'] == 0) {
@@ -750,7 +775,7 @@ class Page_Statistics extends Page
} else {
$uptime = $NOW - $client['lastboot'];
$client['lastboot_s'] = date('d.m.Y H:i', $client['lastboot']);
- if (!isset($client['state_off']) || !$client['state_off']) {
+ if ($client['state'] === 'IDLE' || $client['state'] === 'OCCUPIED') {
$client['lastboot_s'] .= ' (Up ' . floor($uptime / 86400) . 'd ' . gmdate('H:i', $uptime) . ')';
}
}
@@ -833,6 +858,8 @@ class Page_Statistics extends Page
$last = false;
$first = true;
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ if (!$client['isclient'] && $row['typeid'] === '~session-length')
+ continue; // Don't differentiate between session and idle for non-clients
if ($first && $row['dateline'] > $cutoff && $client['lastboot'] > $cutoff) {
// Special case: offline before
$spans['graph'] .= '<div style="background:#444;left:0%;width:' . round((min($row['dateline'], $client['lastboot']) - $cutoff) * $scale, 2) . '%">&nbsp;</div>';
@@ -865,12 +892,17 @@ class Page_Statistics extends Page
$color = '#e77';
}
$spans['graph'] .= '<div style="background:' . $color . ';left:' . round(($row['dateline'] - $cutoff) * $scale, 2) . '%;width:' . round(($row['data']) * $scale, 2) . '%">&nbsp;</div>';
- $spans['rows'][] = $row;
+ if ($client['isclient']) {
+ $spans['rows'][] = $row;
+ }
$last = $row;
}
if ($first && $client['lastboot'] > $cutoff) {
// Special case: offline before
$spans['graph'] .= '<div style="background:#444;left:0%;width:' . round(($client['lastboot'] - $cutoff) * $scale, 2) . '%">&nbsp;</div>';
+ } elseif ($first) {
+ // Not seen in last two weeks
+ $spans['graph'] .= '<div style="background:#444;left:0%;width:100%">&nbsp;</div>';
}
if (isset($client['state_occupied'])) {
$spans['graph'] .= '<div style="background:#e99;left:' . round(($client['logintime'] - $cutoff) * $scale, 2) . '%;width:' . round(($NOW - $client['logintime'] + 900) * $scale, 2) . '%">&nbsp;</div>';
@@ -891,6 +923,7 @@ class Page_Statistics extends Page
$spans['rows2'] = array_slice($spans['rows'], ceil(count($spans['rows']) / 2));
$spans['rows'] = array_slice($spans['rows'], 0, ceil(count($spans['rows']) / 2));
}
+ $spans['isclient'] = $client['isclient'];
Render::addTemplate('machine-usage', $spans);
// Any hdds?
if (!empty($hdds['hdds'])) {
@@ -922,7 +955,7 @@ class Page_Statistics extends Page
}
}
Render::addTemplate('syslog', array(
- 'clientip' => $client['clientip'],
+ 'machineuuid' => $client['machineuuid'],
'list' => $log,
));
}
diff --git a/modules-available/statistics/templates/clientlist.html b/modules-available/statistics/templates/clientlist.html
index ecfe622c..0b6fb37f 100644
--- a/modules-available/statistics/templates/clientlist.html
+++ b/modules-available/statistics/templates/clientlist.html
@@ -46,17 +46,21 @@
<tr>
<td data-sort-value="{{hostname}}" class="text-nowrap">
{{#hasnotes}}<span class="glyphicon glyphicon-exclamation-sign pull-right"></span>{{/hasnotes}}
- {{#state_off}}
+ {{#state_OFFLINE}}
<span class="glyphicon glyphicon-off" title="{{lang_machineOff}}"></span>
- {{/state_off}}
- {{#state_idle}}
+ {{/state_OFFLINE}}
+ {{#state_IDLE}}
<span class="glyphicon glyphicon-ok green" title="{{lang_machineIdle}}"></span>
- {{/state_idle}}
- {{#state_occupied}}
+ {{/state_IDLE}}
+ {{#state_OCCUPIED}}
<span class="glyphicon glyphicon-user red" title="{{lang_machineOccupied}}"></span>
- {{/state_occupied}}
+ {{/state_OCCUPIED}}
+ {{#state_STANDBY}}
+ <span class="glyphicon glyphicon-off green" title="{{lang_machineStandby}}"></span>
+ {{/state_STANDBY}}
<a href="?do=Statistics&amp;uuid={{machineuuid}}"><b>{{hostname}}</b></a>
<div class="small">{{machineuuid}}</div>
+ {{#rmmodule}}<div class="small">{{lang_runMode}}: <a class="slx-bold" href="?do=runmode&amp;module={{rmmodule}}">{{rmmodule}}</a></div>{{/rmmodule}}
</td>
<td data-sort-value="{{clientip}}"><b><a href="?do=Statistics&amp;show=list&amp;filters=subnet={{subnet}}">{{subnet}}</a>{{lastoctet}}</b><br>{{macaddr}}</td>
<td data-sort-value="{{lastseen_int}}" class="text-right">{{lastseen}}</td>
@@ -118,4 +122,4 @@ function toggleButton(v) {
$queryForm.submit();
}
-//--></script> \ No newline at end of file
+//--></script>
diff --git a/modules-available/statistics/templates/machine-main.html b/modules-available/statistics/templates/machine-main.html
index 74df80c4..14d388d3 100644
--- a/modules-available/statistics/templates/machine-main.html
+++ b/modules-available/statistics/templates/machine-main.html
@@ -50,13 +50,13 @@
<tr>
<td class="text-nowrap">{{lang_usageState}}</td>
<td>
- {{#state_off}}
+ {{#state_OFFLINE}}
<span class="glyphicon glyphicon-off"></span> {{lang_machineOff}}
- {{/state_off}}
- {{#state_idle}}
+ {{/state_OFFLINE}}
+ {{#state_IDLE}}
<span class="glyphicon glyphicon-ok green"></span> {{lang_machineIdle}}
- {{/state_idle}}
- {{#state_occupied}}
+ {{/state_IDLE}}
+ {{#state_OCCUPIED}}
{{#username}}
<span class="glyphicon glyphicon-user red"></span> {{lang_machineOccupiedBy}} <b>{{username}}</b>
{{/username}}
@@ -64,7 +64,10 @@
<span class="glyphicon glyphicon-user red"></span> {{lang_machineOccupied}}
{{/username}}
<div>{{logintime_s}}</div>
- {{/state_occupied}}
+ {{/state_OCCUPIED}}
+ {{#state_STANDBY}}
+ <span class="glyphicon glyphicon-off green"></span> {{lang_machineStandby}}
+ {{/state_STANDBY}}
{{#session}}
<div>
{{#lectureid}}
@@ -77,6 +80,14 @@
{{/session}}
</td>
</tr>
+ {{#modeid}}
+ <tr>
+ <td class="text-nowrap">{{lang_runMode}}</td>
+ <td>
+ <a href="?do={{modeid}}">{{moduleName}}</a> – {{modeName}}
+ </td>
+ </tr>
+ {{/modeid}}
</table>
</div>
</div>
diff --git a/modules-available/statistics/templates/machine-usage.html b/modules-available/statistics/templates/machine-usage.html
index ef969fc6..be435ee5 100644
--- a/modules-available/statistics/templates/machine-usage.html
+++ b/modules-available/statistics/templates/machine-usage.html
@@ -5,46 +5,50 @@
{{lang_usageDetails}}
</div>
<div class="panel-body">
- <div class="row">
- <div class="col-sm-6">
- <table class="table table-condensed">
- <tr>
- <th>{{lang_eventType}}</th>
- <th>{{lang_when}}</th>
- <th>{{lang_duration}}</th>
- </tr>
- {{#rows}}
- <tr>
- <td><span class="glyphicon glyphicon-{{glyph}}"></span></td>
- <td>{{from}}</td>
- <td>{{duration}}</td>
- </tr>
- {{/rows}}
- </table>
+ {{#isclient}}
+ <div class="row">
+ <div class="col-sm-6">
+ <table class="table table-condensed">
+ <tr>
+ <th>{{lang_eventType}}</th>
+ <th>{{lang_when}}</th>
+ <th>{{lang_duration}}</th>
+ </tr>
+ {{#rows}}
+ <tr>
+ <td><span class="glyphicon glyphicon-{{glyph}}"></span></td>
+ <td>{{from}}</td>
+ <td>{{duration}}</td>
+ </tr>
+ {{/rows}}
+ </table>
+ </div>
+ <div class="col-sm-6">
+ <table class="table table-condensed">
+ {{#hasrows2}}
+ <tr>
+ <th>{{lang_eventType}}</th>
+ <th>{{lang_when}}</th>
+ <th>{{lang_duration}}</th>
+ </tr>
+ {{/hasrows2}}
+ {{#rows2}}
+ <tr>
+ <td><span class="glyphicon glyphicon-{{glyph}}"></span></td>
+ <td>{{from}}</td>
+ <td>{{duration}}</td>
+ </tr>
+ {{/rows2}}
+ </table>
+ </div>
</div>
- <div class="col-sm-6">
- <table class="table table-condensed">
- {{#hasrows2}}
- <tr>
- <th>{{lang_eventType}}</th>
- <th>{{lang_when}}</th>
- <th>{{lang_duration}}</th>
- </tr>
- {{/hasrows2}}
- {{#rows2}}
- <tr>
- <td><span class="glyphicon glyphicon-{{glyph}}"></span></td>
- <td>{{from}}</td>
- <td>{{duration}}</td>
- </tr>
- {{/rows2}}
- </table>
- </div>
- </div>
+ {{/isclient}}
<div class="timebar">&nbsp;{{{graph}}}</div>
- <div>
- {{lang_timebarDesc}}
- </div>
+ {{#isclient}}
+ <div>
+ {{lang_timebarDesc}}
+ </div>
+ {{/isclient}}
</div>
</div>
</div>
diff --git a/modules-available/statistics/templates/summary.html b/modules-available/statistics/templates/summary.html
index 642c48fc..fe9559ed 100644
--- a/modules-available/statistics/templates/summary.html
+++ b/modules-available/statistics/templates/summary.html
@@ -1,6 +1,11 @@
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-body">
+ {{#runmode}}
+ <div class="pull-right">
+ <a href="?do=runmode">{{lang_runmodeMachines}}</a>: <b>{{runmode}}</b>
+ </div>
+ {{/runmode}}
<div>
{{lang_knownMachines}}: <b>{{known}}</b>&emsp;
<a href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~state=on">{{lang_onlineMachines}}</a>: <b>{{online}}</b>&emsp;
@@ -10,8 +15,9 @@
<div>
<span class="glyphicon glyphicon-exclamation-sign red"></span>
<a href="?do=Statistics&amp;show=list&amp;filters={{query}}~,~badsectors>=10">
- {{lang_withBadSectors}}: <b>{{badhdd}}</b>
+ {{lang_withBadSectors}}:
</a>
+ <b>{{badhdd}}</b>
</div>
{{/badhdd}}
</div>
diff --git a/modules-available/statistics/templates/syslog.html b/modules-available/statistics/templates/syslog.html
index c82cb8ac..968d32ab 100644
--- a/modules-available/statistics/templates/syslog.html
+++ b/modules-available/statistics/templates/syslog.html
@@ -20,7 +20,7 @@
{{/list}}
</tbody>
</table>
-<div class="pull-right"><a class="btn btn-default btn-sm" href="?do=SysLog&amp;ip={{clientip}}">{{lang_more}} &raquo;</a></div>
+<div class="pull-right"><a class="btn btn-default btn-sm" href="?do=SysLog&machineuuid={{machineuuid}}">{{lang_more}} &raquo;</a></div>
<div class="clearfix"></div>
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
diff --git a/modules-available/sysconfig/hooks/cron.inc.php b/modules-available/sysconfig/hooks/cron.inc.php
index d1f91437..b518ca06 100644
--- a/modules-available/sysconfig/hooks/cron.inc.php
+++ b/modules-available/sysconfig/hooks/cron.inc.php
@@ -1,3 +1,8 @@
<?php
-Trigger::ldadp(); \ No newline at end of file
+Trigger::ldadp();
+
+// Cleanup orphaned config<->location where the location has been deleted
+Database::exec("DELETE c FROM configtgz_location c
+ LEFT JOIN location l USING (locationid)
+ WHERE l.locationid IS NULL AND c.locationid <> 0");
diff --git a/modules-available/sysconfig/install.inc.php b/modules-available/sysconfig/install.inc.php
index 91f282dd..3e99b777 100644
--- a/modules-available/sysconfig/install.inc.php
+++ b/modules-available/sysconfig/install.inc.php
@@ -39,21 +39,18 @@ $res[] = tableCreate('configtgz_location', "
// Constraints
if (in_array(UPDATE_DONE, $res)) {
- $ret = Database::exec("ALTER TABLE `configtgz_x_module`
- ADD CONSTRAINT `configtgz_x_module_ibfk_1` FOREIGN KEY (`configid`) REFERENCES `configtgz` (`configid`)
- ON DELETE CASCADE");
- $ret = Database::exec("ALTER TABLE `configtgz_x_module`
- ADD CONSTRAINT `configtgz_x_module_ibfk_2` FOREIGN KEY (`moduleid`) REFERENCES `configtgz_module` (`moduleid`)") || $ret;
- $ret = Database::exec("ALTER TABLE `configtgz_location`
- ADD CONSTRAINT `configtgz_location_fk_configid` FOREIGN KEY ( `configid` ) REFERENCES `openslx`.`configtgz` (`configid`)
- ON DELETE CASCADE ON UPDATE CASCADE") || $ret;
- if ($ret) {
- $res[] = UPDATE_DONE;
- }
+ // To self
+ $res[] = tableAddConstraint('configtgz_x_module', 'configid', 'configtgz', 'configid',
+ '');
+ $res[] = tableAddConstraint('configtgz_x_module', 'moduleid', 'configtgz_module', 'moduleid',
+ '');
+ $res[] = tableAddConstraint('configtgz_location', 'configid', 'configtgz', 'configid',
+ 'ON DELETE CASCADE ON UPDATE CASCADE');
}
// Update path
+// #######################
// ##### 2014-12-12
// Rename config modules
Database::exec("UPDATE configtgz_module SET moduletype = 'Branding' WHERE moduletype = 'BRANDING'");
@@ -98,9 +95,4 @@ if ($list === false) {
}
// Create response for browser
-
-if (in_array(UPDATE_DONE, $res)) {
- finalResponse(UPDATE_DONE, 'Tables created successfully');
-}
-
-finalResponse(UPDATE_NOOP, 'Everything already up to date');
+responseFromArray($res);
diff --git a/modules-available/sysconfig/lang/de/module.json b/modules-available/sysconfig/lang/de/module.json
index 056cd158..4b401437 100644
--- a/modules-available/sysconfig/lang/de/module.json
+++ b/modules-available/sysconfig/lang/de/module.json
@@ -1,4 +1,5 @@
{
+ "config-module": "Konfigurationsmodul",
"lang_clientSshConfig": "SSH-Konfiguration",
"lang_configurationCompilation": "Konfiguration zusammenstellen",
"lang_contentOf": "Inhalt von",
diff --git a/modules-available/sysconfig/lang/en/module.json b/modules-available/sysconfig/lang/en/module.json
index 526f6562..ba2d0591 100644
--- a/modules-available/sysconfig/lang/en/module.json
+++ b/modules-available/sysconfig/lang/en/module.json
@@ -1,4 +1,5 @@
{
+ "config-module": "Config module",
"lang_clientSshConfig": "SSH configuration",
"lang_configurationCompilation": "Compile configuration",
"lang_contentOf": "Content of",
@@ -7,4 +8,4 @@
"lang_unknwonTaskManager": "Unknown Task Manager error",
"module_name": "Localization",
"page_title": "Localize and integrate"
-}
+} \ No newline at end of file
diff --git a/modules-available/syslog/page.inc.php b/modules-available/syslog/page.inc.php
index f2bc4854..927a3adf 100644
--- a/modules-available/syslog/page.inc.php
+++ b/modules-available/syslog/page.inc.php
@@ -21,12 +21,13 @@ class Page_SysLog extends Page
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
$types[$row['logtypeid']] = $row;
}
- if (isset($_GET['filter'])) {
- $filter = $_GET['filter'];
- $not = isset($_GET['not']) ? 'NOT' : '';
- } elseif (isset($_POST['filter'])) {
- $filter = $_POST['filter'];
- $not = isset($_POST['not']) ? 'NOT' : '';
+ if (Request::get('filter') !== false) {
+ $filter = Request::get('filter');
+ $not = Request::get('not') ? 'NOT' : '';
+ } elseif (Request::post('filter') !== false) {
+ $filter = Request::post('filter');
+ $not = Request::post('not') ? 'NOT' : '';
+
Session::set('log_filter', $filter);
Session::set('log_not', $not);
Session::save();
@@ -48,14 +49,14 @@ class Page_SysLog extends Page
if (!empty($whereClause)) $whereClause = ' WHERE logtypeid ' . $not . ' IN (' . implode(', ', $whereClause) . ')';
}
if (!isset($whereClause) || empty($whereClause)) $whereClause = '';
- if (Request::get('ip')) {
+ if (Request::get('machineuuid')) {
if (empty($whereClause))
$whereClause .= ' WHERE ';
else
$whereClause .= ' AND ';
- $whereClause .= "clientip LIKE '" . preg_replace('/[^0-9\.\:]/', '', Request::get('ip')) . "%'";
- }
+ $whereClause .= "machineuuid='" . preg_replace('/[^0-9a-zA-Z\-]/', '', Request::get('machineuuid', '', 'string')) . "'";
+ }
$today = date('d.m.Y');
$yesterday = date('d.m.Y', time() - 86400);
$lines = array();
@@ -78,9 +79,10 @@ class Page_SysLog extends Page
'not' => $not,
'list' => $lines,
'types' => json_encode(array_values($types)),
+ 'machineuuid' => Request::get('machineuuid'),
));
}
-
+
private function eventToIconName($event)
{
switch ($event) {
diff --git a/modules-available/syslog/templates/page-syslog.html b/modules-available/syslog/templates/page-syslog.html
index 2177822f..8b590038 100644
--- a/modules-available/syslog/templates/page-syslog.html
+++ b/modules-available/syslog/templates/page-syslog.html
@@ -4,7 +4,7 @@
max-width: 500px;
}
</style>
-<form method="post" action="?do=SysLog">
+<form method="post" action="?do=SysLog{{#machineuuid}}&machineuuid={{machineuuid}}{{/machineuuid}}">
<input type="hidden" name="token" value="{{token}}">
<div class="pull-left">
<label for="filterstring">{{lang_filter}}</label>
diff --git a/script/collapse.js b/script/collapse.js
deleted file mode 100644
index ca026f87..00000000
--- a/script/collapse.js
+++ /dev/null
@@ -1,3 +0,0 @@
-$('.slx-decollapse').click(function () {
- $(this).siblings('.collapse').removeClass('collapse');
-}); \ No newline at end of file
diff --git a/script/fileselect.js b/script/fileselect.js
deleted file mode 100644
index 13711fd5..00000000
--- a/script/fileselect.js
+++ /dev/null
@@ -1,20 +0,0 @@
-$(document).on('change', '.btn-file :file', function() {
- var input = $(this);
- var numFiles = input.get(0).files ? input.get(0).files.length : 1;
- var label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
- input.trigger('fileselect', [numFiles, label]);
-});
-
-$(document).ready(function() {
- $('.btn-file :file').on('fileselect', function(event, numFiles, label) {
- var input = $(this).parents('.upload-ex').find(':text');
- var log = numFiles > 1 ? numFiles + ' files selected' : label;
- if (input.length) {
- input.val(log);
- }
- });
-});
-
-$('.upload-ex :text').click(function () {
- $(this).parents('.upload-ex').find(':file').click();
-}); \ No newline at end of file
diff --git a/script/slx-fixes.js b/script/slx-fixes.js
new file mode 100644
index 00000000..12db9c79
--- /dev/null
+++ b/script/slx-fixes.js
@@ -0,0 +1,42 @@
+// Give file select dialogs a modern style and feel
+$(document).on('change', '.btn-file :file', function() {
+ var input = $(this);
+ var numFiles = input.get(0).files ? input.get(0).files.length : 1;
+ var label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
+ input.trigger('fileselect', [numFiles, label]);
+});
+$(document).ready(function() {
+ $('.btn-file :file').on('fileselect', function(event, numFiles, label) {
+ var input = $(this).parents('.upload-ex').find(':text');
+ var log = numFiles > 1 ? numFiles + ' files selected' : label;
+ if (input.length) {
+ input.val(log);
+ }
+ });
+});
+$('.upload-ex :text').click(function () {
+ $(this).parents('.upload-ex').find(':file').click();
+});
+
+// Replace message query params in URL, so you won't see them again if you bookmark or share the link
+if (history && history.replaceState && window && window.location && window.location.search && window.location.search.indexOf('message[]=') !== -1) {
+ var str = window.location.search;
+ console.log('TRUE: ' + str);
+ do {
+ var repl = str.match(/([\?&])message\[\]=[^&]+(&|$)/);
+ console.log(repl);
+ if (!repl) break;
+ if (repl[2].length === 0) {
+ str = str.replace(repl[0], '');
+ } else {
+ str = str.replace(repl[0], repl[1]);
+ }
+ console.log('Replace: ' + str);
+ } while (1);
+ history.replaceState(null, null, window.location.pathname + str);
+}
+
+// Simple decollapse functionality for tables
+$('.slx-decollapse').click(function () {
+ $(this).siblings('.collapse').removeClass('collapse');
+}); \ No newline at end of file