summaryrefslogtreecommitdiffstats
path: root/modules-available
diff options
context:
space:
mode:
Diffstat (limited to 'modules-available')
-rw-r--r--modules-available/baseconfig_bwlp/baseconfig/settings.json6
-rw-r--r--modules-available/baseconfig_bwlp/lang/de/config-variables.json1
-rw-r--r--modules-available/baseconfig_bwlp/lang/en/config-variables.json3
-rw-r--r--modules-available/dnbd3/baseconfig/getconfig.inc.php3
-rw-r--r--modules-available/dnbd3/hooks/main-warning.inc.php2
-rw-r--r--modules-available/dnbd3/inc/dnbd3util.inc.php11
-rw-r--r--modules-available/dnbd3/lang/de/template-tags.json2
-rw-r--r--modules-available/dnbd3/lang/en/template-tags.json2
-rw-r--r--modules-available/dnbd3/page.inc.php36
-rw-r--r--modules-available/dnbd3/templates/fragment-server-settings.html14
-rw-r--r--modules-available/dnbd3/templates/page-server-locations.html2
-rw-r--r--modules-available/dozmod/page.inc.php4
-rw-r--r--modules-available/dozmod/templates/runtimeconfig.html8
-rw-r--r--modules-available/locationinfo/api.inc.php6
-rw-r--r--modules-available/locationinfo/frontend/frontendscript.js83
-rw-r--r--modules-available/locationinfo/inc/coursebackend.inc.php74
-rw-r--r--modules-available/locationinfo/inc/coursebackend/coursebackend_davinci.inc.php3
-rw-r--r--modules-available/locationinfo/inc/coursebackend/coursebackend_dummy.inc.php17
-rw-r--r--modules-available/locationinfo/inc/infopanel.inc.php28
-rw-r--r--modules-available/locationinfo/inc/locationinfo.inc.php7
-rw-r--r--modules-available/locationinfo/install.inc.php7
-rw-r--r--modules-available/locationinfo/lang/en/template-tags.json2
-rw-r--r--modules-available/locationinfo/page.inc.php3
-rwxr-xr-xmodules-available/locationinfo/templates/frontend-default.html103
-rw-r--r--modules-available/locationinfo/templates/frontend-summary.html158
-rw-r--r--modules-available/locationinfo/templates/page-locations.html2
-rw-r--r--modules-available/locations/page.inc.php10
-rw-r--r--modules-available/locations/templates/locations.html23
-rw-r--r--modules-available/roomplanner/clientscript.js11
-rw-r--r--modules-available/roomplanner/hooks/runmode/config.json3
-rw-r--r--modules-available/roomplanner/lang/de/module.json4
-rw-r--r--modules-available/runmode/inc/runmode.inc.php5
-rw-r--r--modules-available/runmode/lang/de/messages.json9
-rw-r--r--modules-available/runmode/lang/en/messages.json9
-rw-r--r--modules-available/runmode/page.inc.php45
-rw-r--r--modules-available/runmode/templates/module-machine-list.html12
-rw-r--r--modules-available/statistics/inc/filter.inc.php13
-rw-r--r--modules-available/statistics/lang/de/messages.json1
-rw-r--r--modules-available/statistics/lang/de/template-tags.json1
-rw-r--r--modules-available/statistics/lang/en/messages.json1
-rw-r--r--modules-available/statistics/lang/en/template-tags.json1
-rw-r--r--modules-available/statistics/page.inc.php37
-rw-r--r--modules-available/statistics/templates/clientlist.html47
-rw-r--r--modules-available/statistics/templates/filterbox.html1
-rw-r--r--modules-available/statistics/templates/machine-main.html2
-rw-r--r--modules-available/sysconfig/addmodule_adauth.inc.php29
-rw-r--r--modules-available/sysconfig/addmodule_ldapauth.inc.php21
-rw-r--r--modules-available/sysconfig/inc/configmodule.inc.php7
-rw-r--r--modules-available/sysconfig/inc/configmodulebaseldap.inc.php23
-rw-r--r--modules-available/sysconfig/lang/de/messages.json2
-rw-r--r--modules-available/sysconfig/lang/de/template-tags.json6
-rw-r--r--modules-available/sysconfig/lang/en/messages.json2
-rw-r--r--modules-available/sysconfig/lang/en/template-tags.json4
-rw-r--r--modules-available/sysconfig/templates/ad-selfsearch.html6
-rw-r--r--modules-available/sysconfig/templates/ad-start.html44
-rw-r--r--modules-available/sysconfig/templates/ad_ldap-checkconnection.html7
-rw-r--r--modules-available/sysconfig/templates/ad_ldap-checkcredentials.html6
-rw-r--r--modules-available/sysconfig/templates/ad_ldap-homedir.html3
-rw-r--r--modules-available/sysconfig/templates/ldap-start.html38
-rw-r--r--modules-available/systemstatus/lang/de/template-tags.json4
-rw-r--r--modules-available/systemstatus/lang/en/template-tags.json4
-rw-r--r--modules-available/systemstatus/page.inc.php44
-rw-r--r--modules-available/systemstatus/templates/services.html26
-rw-r--r--modules-available/vmstore/page.inc.php3
64 files changed, 777 insertions, 324 deletions
diff --git a/modules-available/baseconfig_bwlp/baseconfig/settings.json b/modules-available/baseconfig_bwlp/baseconfig/settings.json
index 32d9aa88..18e87ab6 100644
--- a/modules-available/baseconfig_bwlp/baseconfig/settings.json
+++ b/modules-available/baseconfig_bwlp/baseconfig/settings.json
@@ -185,5 +185,11 @@
"defaultvalue": "OFF",
"permissions": "2",
"validator": "list:ON|OFF"
+ },
+ "SLX_PASSTHROUGH_USB_ID": {
+ "catid": "vmchooser",
+ "defaultvalue": "",
+ "permissions": "2",
+ "validator": "regex:\/^(([0-9a-f]{4}:[0-9a-f]{4}\\s*)+|)$\/i"
}
}
diff --git a/modules-available/baseconfig_bwlp/lang/de/config-variables.json b/modules-available/baseconfig_bwlp/lang/de/config-variables.json
index b88fddba..a1ef4bab 100644
--- a/modules-available/baseconfig_bwlp/lang/de/config-variables.json
+++ b/modules-available/baseconfig_bwlp/lang/de/config-variables.json
@@ -9,6 +9,7 @@
"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_PASSTHROUGH_USB_ID": "Geben Sie hier eindeutige IDs von USB-Ger\u00e4ten an, die direkt in die VMs weitergereicht werden sollen. Das erwartete Format ist *vendorID:productID* , als jeweils vierstellige Hexadezimalzahlen, beispielsweise *1234:abcd* .\r\nMehrere IDs k\u00f6nnen als leerzeichengetrennte Liste angegeben werden.",
"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.",
"SLX_PROXY_IP": "Die Adresse des zu verwendenden Proxy Servers.",
diff --git a/modules-available/baseconfig_bwlp/lang/en/config-variables.json b/modules-available/baseconfig_bwlp/lang/en/config-variables.json
index 4778ae67..3e33cbef 100644
--- a/modules-available/baseconfig_bwlp/lang/en/config-variables.json
+++ b/modules-available/baseconfig_bwlp/lang/en/config-variables.json
@@ -9,6 +9,7 @@
"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_PASSTHROUGH_USB_ID": "Specify IDs of USB devices that should be passed through to the VM directly.\r\nThe expected format is *vendorID:productID* , where each ID is a 4-digit hexadecimal number, e.g. *1234:abcd* \r\nMultiple IDs can be given as a space-separated list.",
"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.",
"SLX_PROXY_IP": "The address to use for the proxy server.",
@@ -28,4 +29,4 @@
"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/baseconfig/getconfig.inc.php b/modules-available/dnbd3/baseconfig/getconfig.inc.php
index e0389c71..fe1bef17 100644
--- a/modules-available/dnbd3/baseconfig/getconfig.inc.php
+++ b/modules-available/dnbd3/baseconfig/getconfig.inc.php
@@ -24,6 +24,7 @@ $res = Database::simpleQuery('SELECT s.fixedip, m.clientip, sxl.locationid FROM
LEFT JOIN dnbd3_server_x_location sxl USING (serverid)
WHERE sxl.locationid IS NULL OR sxl.locationid IN (:lids)', array('lids' => $locationIds));
// Lookup of priority - first index (0) will be closest location in chain
+// low value is higher priority
$locationsAssoc = array_flip($locationIds);
$servers = array();
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
@@ -33,7 +34,7 @@ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
} else {
$defPrio = 1000;
}
- $ip = $row['clientip'] ? $row['clientip'] : $row['fixedip'];
+ $ip = $row['fixedip'] ? $row['fixedip'] : $row['clientip'];
if ($defPrio === 1000 && is_null($row['locationid'])) {
$serverLoc = Location::getFromIp($ip);
if ($serverLoc !== false) {
diff --git a/modules-available/dnbd3/hooks/main-warning.inc.php b/modules-available/dnbd3/hooks/main-warning.inc.php
index 258d03d0..e38048e1 100644
--- a/modules-available/dnbd3/hooks/main-warning.inc.php
+++ b/modules-available/dnbd3/hooks/main-warning.inc.php
@@ -8,7 +8,7 @@ if (Dnbd3::isEnabled()) {
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
$error = $row['errormsg'] ? $row['errormsg'] : '<unknown error>';
- $lastSeen = date('d.m.Y H:i', $row['dnbd3lastseen']);
+ $lastSeen = Util::prettyTime($row['dnbd3lastseen']);
if ($row['fixedip'] === '<self>') {
Message::addError('dnbd3.main-dnbd3-unreachable', true, $error, $lastSeen);
continue;
diff --git a/modules-available/dnbd3/inc/dnbd3util.inc.php b/modules-available/dnbd3/inc/dnbd3util.inc.php
index 02ca89c2..95b6ffe2 100644
--- a/modules-available/dnbd3/inc/dnbd3util.inc.php
+++ b/modules-available/dnbd3/inc/dnbd3util.inc.php
@@ -11,10 +11,10 @@ class Dnbd3Util {
FROM dnbd3_server s
LEFT JOIN machine m USING (machineuuid)');
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- if (!is_null($row['clientip'])) {
- $ip = $row['clientip'];
- } elseif (!is_null($row['fixedip'])) {
+ if (!is_null($row['fixedip'])) {
$ip = $row['fixedip'];
+ } elseif (!is_null($row['clientip'])) {
+ $ip = $row['clientip'];
} else {
continue; // Huh?
}
@@ -25,6 +25,9 @@ class Dnbd3Util {
RunMode::setRunMode($row['machineuuid'], 'dnbd3', null, null, null);
continue;
}
+ } elseif ($ip === $satServerIp) {
+ // Manually configured sat server as dnbd3 server - makes no sense
+ continue;
}
$server = array(
'serverid' => $row['serverid'],
@@ -141,7 +144,7 @@ class Dnbd3Util {
$self = Property::getServerIp();
$public[$self] = $self;
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $ip = $row['clientip'] ? $row['clientip'] : $row['fixedip'];
+ $ip = $row['fixedip'] ? $row['fixedip'] : $row['clientip'];
if ($ip === '<self>') {
continue;
}
diff --git a/modules-available/dnbd3/lang/de/template-tags.json b/modules-available/dnbd3/lang/de/template-tags.json
index a178df6d..5406f1b5 100644
--- a/modules-available/dnbd3/lang/de/template-tags.json
+++ b/modules-available/dnbd3/lang/de/template-tags.json
@@ -38,6 +38,8 @@
"lang_managedServerAdd": "Automatisch konfigurierten Proxy hinzuf\u00fcgen",
"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_overrideIp": "Zu verwendende IP-Adresse",
+ "lang_overrideIpInfo": "Normalerweise wird die automatisch per DHCP zugewiesene Adresse auf dem Boot-Interface verwendet. Falls der Proxy mit weiteren Netzwerkkarten ausgestattet ist (die ebenfalls per DHCP konfiguriert werden) kann durch Angabe einer solchen Alternativadresse hier die Verwendung der entsprechenden Karte erzwungen werden.",
"lang_proxyConfig": "Konfiguration",
"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",
diff --git a/modules-available/dnbd3/lang/en/template-tags.json b/modules-available/dnbd3/lang/en/template-tags.json
index 0441521f..81b9d538 100644
--- a/modules-available/dnbd3/lang/en/template-tags.json
+++ b/modules-available/dnbd3/lang/en/template-tags.json
@@ -38,6 +38,8 @@
"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_overrideIp": "IP address to use",
+ "lang_overrideIpInfo": "Usually the address that the DHCP server assigns to the boot interface of the proxy will be used. If the proxy has multiple interfaces (that also get an address assigned via DHCP) you can specify that address here to enforce their usage instead.",
"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",
diff --git a/modules-available/dnbd3/page.inc.php b/modules-available/dnbd3/page.inc.php
index 2836cbf4..39500f83 100644
--- a/modules-available/dnbd3/page.inc.php
+++ b/modules-available/dnbd3/page.inc.php
@@ -39,6 +39,34 @@ class Page_Dnbd3 extends Page
}
$bgr = Request::post('bgr', false, 'bool');
$firewall = Request::post('firewall', false, 'bool');
+ $overrideIp = false;
+ $sip = Request::post('fixedip', null, 'string');
+ if (empty($sip)) {
+ $overrideIp = null;
+ } elseif ($server['fixedip'] !== $overrideIp) {
+ $ip = ip2long(trim($sip));
+ if ($ip !== false) {
+ $ip = long2ip($ip);
+ }
+ if ($ip === false) {
+ Message::addError('invalid-ipv4', $sip);
+ return;
+ }
+ $res = Database::queryFirst('SELECT serverid FROM dnbd3_server s
+ LEFT JOIN machine m USING (machineuuid)
+ WHERE s.fixedip = :ip OR m.clientip = :ip', compact('ip'));
+ if ($res !== false) {
+ Message::addError('server-already-exists', $ip);
+ return;
+ }
+ $overrideIp = $ip;
+ }
+ if ($overrideIp !== false) {
+ Database::exec('UPDATE dnbd3_server SET fixedip = :fixedip WHERE machineuuid = :uuid', array(
+ 'uuid' => $server['machineuuid'],
+ 'fixedip' => $overrideIp,
+ ));
+ }
RunMode::setRunMode($server['machineuuid'], 'dnbd3', 'proxy',
json_encode(compact('bgr', 'firewall')), false);
}
@@ -146,7 +174,7 @@ class Page_Dnbd3 extends Page
if ($server['uptime'] != 0) {
$server['uptime'] += ($NOW - $server['dnbd3lastseen']);
}
- $server['dnbd3lastseen_s'] = $server['dnbd3lastseen'] ? date('d.m.Y H:i', $server['dnbd3lastseen']) : '-';
+ $server['dnbd3lastseen_s'] = $server['dnbd3lastseen'] ? Util::prettyTime($server['dnbd3lastseen']) : '-';
$server['uptime_s'] = $server['uptime'] ? floor($server['uptime'] / 86400) . 'd ' . gmdate('H:i', $server['uptime']) : '-';
$server['totalup_s'] = Util::readableFileSize($server['totalup']);
$server['totaldown_s'] = Util::readableFileSize($server['totaldown']);
@@ -310,10 +338,10 @@ class Page_Dnbd3 extends Page
Message::addError('server-non-existent', 'server');
Util::redirect('?do=dnbd3');
}
- if (!is_null($server['clientip'])) {
- $server['ip'] = $server['clientip'];
- } elseif (!is_null($server['fixedip'])) {
+ if (!is_null($server['fixedip'])) {
$server['ip'] = $server['fixedip'];
+ } elseif (!is_null($server['clientip'])) {
+ $server['ip'] = $server['clientip'];
} else {
$server['ip'] = '127.0.0.1';
}
diff --git a/modules-available/dnbd3/templates/fragment-server-settings.html b/modules-available/dnbd3/templates/fragment-server-settings.html
index 61b0626d..be3e74e2 100644
--- a/modules-available/dnbd3/templates/fragment-server-settings.html
+++ b/modules-available/dnbd3/templates/fragment-server-settings.html
@@ -2,13 +2,21 @@
<div class="checkbox">
<input type="checkbox" name="bgr" id="bgr" {{#bgr}}checked{{/bgr}}>
- <label for="bgr">{{lang_backgroundReplication}}</label>
+ <label for="bgr"><b>{{lang_backgroundReplication}}</b></label>
</div>
<i>{{lang_backgroundReplicationInfo}}</i>
-<br>
+<br><br>
+
<div class="checkbox">
<input type="checkbox" name="firewall" id="firewall" {{#firewall}}checked{{/firewall}}>
- <label for="firewall">{{lang_firewalled}}</label>
+ <label for="firewall"><b>{{lang_firewalled}}</b></label>
</div>
<i>{{lang_firewallInfo}}</i>
+<br><br>
+
+<div>
+ <label for="fixedip">{{lang_overrideIp}}</label>
+ <input class="form-control" type="text" name="fixedip" id="fixedip" value="{{fixedip}}">
+</div>
+<i>{{lang_overrideIpInfo}}</i>
<br>
diff --git a/modules-available/dnbd3/templates/page-server-locations.html b/modules-available/dnbd3/templates/page-server-locations.html
index 20dddaac..7d7ddaf1 100644
--- a/modules-available/dnbd3/templates/page-server-locations.html
+++ b/modules-available/dnbd3/templates/page-server-locations.html
@@ -35,7 +35,7 @@
</div>
<div class="buttonbar text-right">
- <button type="submit" class="btn btn-success" name="action" value="savelocations">
+ <button type="submit" class="btn btn-primary" name="action" value="savelocations">
<span class="glyphicon glyphicon-floppy-disk"></span>
{{lang_save}}
</button>
diff --git a/modules-available/dozmod/page.inc.php b/modules-available/dozmod/page.inc.php
index 7a5ddf23..b58d57aa 100644
--- a/modules-available/dozmod/page.inc.php
+++ b/modules-available/dozmod/page.inc.php
@@ -341,8 +341,8 @@ class Page_DozMod extends Page
$params = [
'int' => [
- 'maxImageValidityDays' => array('min' => 7, 'max' => 999),
- 'maxLectureValidityDays' => array('min' => 7, 'max' => 999),
+ 'maxImageValidityDays' => array('min' => 7, 'max' => 9999),
+ 'maxLectureValidityDays' => array('min' => 7, 'max' => 9999),
'maxLocationsPerLecture' => array('min' => 0, 'max' => 999),
'maxTransfers' => array('min' => 1, 'max' => 10),
],
diff --git a/modules-available/dozmod/templates/runtimeconfig.html b/modules-available/dozmod/templates/runtimeconfig.html
index 4d97ade0..1d4cc6cb 100644
--- a/modules-available/dozmod/templates/runtimeconfig.html
+++ b/modules-available/dozmod/templates/runtimeconfig.html
@@ -73,13 +73,17 @@
<tr class="input-group">
<td class="input-group-addon">{{lang_maxImageValidity}}</td>
<td>
- <input name="maxImageValidityDays" class="form-control" type="number" value="{{maxImageValidityDays}}" min="7" max="999" pattern="^\d+$">
+ <input name="maxImageValidityDays"
+ class="form-control" type="number"
+ value="{{maxImageValidityDays}}" min="7" max="9999" pattern="^\d+$">
</td>
</tr>
<tr class="input-group">
<td class="input-group-addon">{{lang_maxLectureVisibility}}</td>
<td>
- <input name="maxLectureValidityDays" class="form-control" type="number" value="{{maxLectureValidityDays}}" min="1" max="999" pattern="^\d+$">
+ <input name="maxLectureValidityDays"
+ class="form-control" type="number"
+ value="{{maxLectureValidityDays}}" min="1" max="9999" pattern="^\d+$">
</td>
</tr>
<tr class="input-group">
diff --git a/modules-available/locationinfo/api.inc.php b/modules-available/locationinfo/api.inc.php
index a89f16ed..ceaf04c0 100644
--- a/modules-available/locationinfo/api.inc.php
+++ b/modules-available/locationinfo/api.inc.php
@@ -175,10 +175,10 @@ function getCalendar($idList)
foreach ($serverList as $serverid => $server) {
$serverInstance = CourseBackend::getInstance($server['type']);
if ($serverInstance === false) {
- EventLog::warning('Cannot fetch schedule for locationid ' . $server['locationid']
+ EventLog::warning('Cannot fetch schedule for location (' . implode(', ', $server['idlist']) . ')'
. ': Backend type ' . $server['type'] . ' unknown. Disabling location.');
- Database::exec("UPDATE locationinfo_locationconfig SET serverid = 0 WHERE locationid = :lid",
- array('lid' => $server['locationid']));
+ Database::exec("UPDATE locationinfo_locationconfig SET serverid = NULL WHERE locationid IN (:lid)",
+ array('lid' => $server['idlist']));
continue;
}
$credentialsOk = $serverInstance->setCredentials($serverid, $server['credentials']);
diff --git a/modules-available/locationinfo/frontend/frontendscript.js b/modules-available/locationinfo/frontend/frontendscript.js
new file mode 100644
index 00000000..cc5c6827
--- /dev/null
+++ b/modules-available/locationinfo/frontend/frontendscript.js
@@ -0,0 +1,83 @@
+/**
+ * checks if a room is on a given date/time open
+ * @param date Date Object
+ * @param room Room object
+ * @returns {Boolean} for open or not
+ */
+function IsOpen(date, room) {
+ if (!room.openingTimes || room.openingTimes.length === 0) return true;
+ var tmp = room.openingTimes[date.getDay()];
+ if (!tmp) return false;
+ var openDate = new Date(date.getTime());
+ var closeDate = new Date(date.getTime());
+ for (var i = 0; i < tmp.length; i++) {
+ openDate.setHours(tmp[i].HourOpen);
+ openDate.setMinutes(tmp[i].MinutesOpen);
+ closeDate.setHours(tmp[i].HourClose);
+ closeDate.setMinutes(tmp[i].MinutesClose);
+ if (openDate < date && closeDate > date) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Convert passed argument to integer if possible, return NaN otherwise.
+ * The difference to parseInt() is that leading zeros are ignored and not
+ * interpreted as octal representation.
+ *
+ * @param str string or already a number
+ * @return {number} str converted to number, or NaN
+ */
+function toInt(str) {
+ var t = typeof str;
+ if (t === 'number') return str | 0;
+ if (t === 'string') return parseInt(str.replace(/^0+([^0])/, '$1'));
+ return NaN;
+}
+
+/**
+ * used for countdown
+ * computes the time difference between 2 Date objects
+ * @param {Date} a
+ * @param {Date} b
+ * @param {Array} globalConfig
+ * @returns {string} printable time
+ */
+function GetTimeDiferenceAsString(a, b, globalConfig = null) {
+ if (!a || !b) {
+ return "";
+ }
+ var milliseconds = a.getTime() - b.getTime();
+ var days = Math.floor((milliseconds / (1000 * 60 * 60 * 24)) % 31);
+ if (days !== 0) {
+ // don't show?
+ return "";
+ }
+ var seconds = Math.floor((milliseconds / 1000) % 60);
+ milliseconds -= seconds * 1000;
+ var minutes = Math.floor((milliseconds / (1000 * 60)) % 60);
+ milliseconds -= minutes * 1000 * 60;
+ var hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24);
+
+ if (globalConfig && globalConfig.prettytime) {
+ var str = '';
+ if (hours > 0) {
+ str += hours + 'h ';
+ }
+ str += minutes + 'min ';
+ return str;
+ }
+
+ if (minutes < 10) {
+ minutes = "0" + minutes;
+ }
+ if (globalConfig && globalConfig.eco) {
+ return hours + ":" + minutes;
+ }
+ if (seconds < 10) {
+ seconds = "0" + seconds;
+ }
+ return hours + ":" + minutes + ":" + seconds;
+} \ No newline at end of file
diff --git a/modules-available/locationinfo/inc/coursebackend.inc.php b/modules-available/locationinfo/inc/coursebackend.inc.php
index 1fe87202..7162c885 100644
--- a/modules-available/locationinfo/inc/coursebackend.inc.php
+++ b/modules-available/locationinfo/inc/coursebackend.inc.php
@@ -47,17 +47,20 @@ abstract class CourseBackend
foreach (glob(dirname(__FILE__) . '/coursebackend/coursebackend_*.inc.php', GLOB_NOSORT) as $file) {
require_once $file;
preg_match('#coursebackend_([^/\.]+)\.inc\.php$#i', $file, $out);
- if (!class_exists('coursebackend_' . $out[1])) {
- trigger_error("Backend type source unit $file doesn't seem to define class CourseBackend_{$out[1]}", E_USER_ERROR);
+ $className = 'CourseBackend_' . $out[1];
+ if (!class_exists($className)) {
+ trigger_error("Backend type source unit $file doesn't seem to define class $className", E_USER_ERROR);
}
+ if (!CONFIG_DEBUG && defined("$className::DEBUG") && constant("$className::DEBUG"))
+ continue;
self::$backendTypes[$out[1]] = true;
}
}
/**
- * Get all known config module types.
+ * Get all known backend types.
*
- * @return array list of modules
+ * @return array list of backends
*/
public static function getList()
{
@@ -65,24 +68,30 @@ abstract class CourseBackend
return array_keys(self::$backendTypes);
}
+ public static function exists($backendType)
+ {
+ self::loadDb();
+ return isset(self::$backendTypes[$backendType]);
+ }
+
/**
- * Get fresh instance of ConfigModule subclass for given module type.
+ * Get fresh instance of CourseBackend subclass for given backend type.
*
- * @param string $moduleType name of module type
- * @return \CourseBackend module instance
+ * @param string $backendType name of module type
+ * @return \CourseBackend|false module instance
*/
- public static function getInstance($moduleType)
+ public static function getInstance($backendType)
{
self::loadDb();
- if (!isset(self::$backendTypes[$moduleType])) {
- error_log('Unknown module type: ' . $moduleType);
+ if (!isset(self::$backendTypes[$backendType])) {
+ error_log('Unknown module type: ' . $backendType);
return false;
}
- if (!is_object(self::$backendTypes[$moduleType])) {
- $class = "coursebackend_$moduleType";
- self::$backendTypes[$moduleType] = new $class;
+ if (!is_object(self::$backendTypes[$backendType])) {
+ $class = "coursebackend_$backendType";
+ self::$backendTypes[$backendType] = new $class;
}
- return self::$backendTypes[$moduleType];
+ return self::$backendTypes[$backendType];
}
/**
@@ -131,6 +140,19 @@ abstract class CourseBackend
*/
protected abstract function fetchSchedulesInternal($roomId);
+ private static function fixTime(&$start, &$end)
+ {
+ if (!preg_match('/^\d+-\d+-\d+T\d+:\d+:\d+$/', $start) || !preg_match('/^\d+-\d+-\d+T\d+:\d+:\d+$/', $start))
+ return false;
+ $start = strtotime($start);
+ $end = strtotime($end);
+ if ($start >= $end)
+ return false;
+ $start = date('Y-m-d\TH:i:s', $start);
+ $end = date('Y-m-d\TH:i:s', $end);
+ return true;
+ }
+
/**
* Method for fetching the schedule of the given rooms on a server.
*
@@ -184,18 +206,31 @@ abstract class CourseBackend
return false;
}
- if ($this->getCacheTime() > 0) {
- // Caching requested by backend, write to DB
- foreach ($backendResponse as $serverRoomId => $calendar) {
+ foreach ($backendResponse as $serverRoomId => &$calendar) {
+ $calendar = array_values($calendar);
+ for ($i = 0; $i < count($calendar); ++$i) {
+ if (empty($calendar[$i]['title'])) {
+ $calendar[$i]['title'] = '-';
+ }
+ if (!self::fixTime($calendar[$i]['start'], $calendar[$i]['end'])) {
+ error_log("Ignoring calendar entry '{$calendar[$i]['title']}' with bad time format");
+ unset($calendar[$i]);
+ }
+ }
+ $calendar = array_values($calendar);
+ if ($this->getCacheTime() > 0) {
+ // Caching requested by backend, write to DB
$value = json_encode($calendar);
Database::simpleQuery("UPDATE locationinfo_locationconfig SET calendar = :ttable, lastcalendarupdate = :now
- WHERE serverid = :serverid AND serverlocationid = :serverlocationid", array(
+ WHERE serverid = :serverid AND serverlocationid = :serverlocationid", array(
'serverid' => $this->serverId,
'serverlocationid' => $serverRoomId,
'ttable' => $value,
'now' => $NOW
));
}
+
+ unset($calendar);
}
// Add rooms that were requested to the final return value
foreach ($remoteIds as $location => $serverRoomId) {
@@ -302,6 +337,9 @@ abstract class CourseBackend
$xml = new SimpleXMLElement($cleanresponse);
} catch (Exception $e) {
$this->error = 'Could not parse reply as XML, got ' . get_class($e) . ': ' . $e->getMessage();
+ if (CONFIG_DEBUG) {
+ error_log($cleanresponse);
+ }
return false;
}
$array = json_decode(json_encode((array)$xml), true);
diff --git a/modules-available/locationinfo/inc/coursebackend/coursebackend_davinci.inc.php b/modules-available/locationinfo/inc/coursebackend/coursebackend_davinci.inc.php
index fac3f296..8843e372 100644
--- a/modules-available/locationinfo/inc/coursebackend/coursebackend_davinci.inc.php
+++ b/modules-available/locationinfo/inc/coursebackend/coursebackend_davinci.inc.php
@@ -106,6 +106,9 @@ class CourseBackend_Davinci extends CourseBackend
}
$return = $this->xmlStringToArray($return);
if ($return === false) {
+ if (CONFIG_DEBUG) {
+ error_log('Room was ' . $roomId);
+ }
continue;
}
$lessons = $this->getArrayPath($return, '/Lessons/Lesson');
diff --git a/modules-available/locationinfo/inc/coursebackend/coursebackend_dummy.inc.php b/modules-available/locationinfo/inc/coursebackend/coursebackend_dummy.inc.php
index e2577284..adff8b1b 100644
--- a/modules-available/locationinfo/inc/coursebackend/coursebackend_dummy.inc.php
+++ b/modules-available/locationinfo/inc/coursebackend/coursebackend_dummy.inc.php
@@ -4,6 +4,8 @@ class CourseBackend_Dummy extends CourseBackend
{
private $pw;
+ const DEBUG = true;
+
/**
* uses json to setCredentials, the json must follow the form given in
* getCredentials
@@ -87,13 +89,24 @@ class CourseBackend_Dummy extends CourseBackend
*
* @param $roomIds array with local ID as key and serverId as value
* @return array a recursive array that uses the roomID as key
- * and has the schedule array as value. A shedule array contains an array in this format:
- * ["start"=>'JJJJ-MM-DD HH:MM:SS',"end"=>'JJJJ-MM-DD HH:MM:SS',"title"=>string]
+ * and has the schedule array as value. A schedule array contains an array in this format:
+ * ["start"=>'YYYY-MM-DD<T>HH:MM:SS',"end"=>'YYYY-MM-DD<T>HH:MM:SS',"title"=>string]
*/
public function fetchSchedulesInternal($roomId)
{
$a = array();
foreach ($roomId as $id) {
+ if ($id == 1) {
+ $now = time();
+ return array($id => array(
+ array(
+ 'title' => 'Lange',
+ 'start' => date('Y-m-d', $now) . 'T0:00:00',
+ 'end' => date('Y-m-d', $now + 86400 * 3) . 'T0:00:00',
+ )
+ ));
+ }
+ // Normal
$x = array();
$time = strtotime('today');
$end = strtotime('+7 days', $time);
diff --git a/modules-available/locationinfo/inc/infopanel.inc.php b/modules-available/locationinfo/inc/infopanel.inc.php
index 94f264bb..12b6aec7 100644
--- a/modules-available/locationinfo/inc/infopanel.inc.php
+++ b/modules-available/locationinfo/inc/infopanel.inc.php
@@ -45,17 +45,27 @@ class InfoPanel
}
$config['locations'] = array();
$lids = array_map('intval', explode(',', $panel['locationids']));
- foreach ($lids as $lid) {
- $config['locations'][$lid] = array(
- '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];
+ // Locations -
+ if ($panel['paneltype'] === 'SUMMARY') {
+ $lids = Location::getRecursiveFlat($lids);
+ $lids = array_keys($lids);
+ foreach ($lids as $lid) {
+ $config['locations'][$lid] = array('id' => $lid);
+ }
+ }
+ if ($panel['paneltype'] === 'DEFAULT') {
+ foreach ($lids as $lid) {
+ $config['locations'][$lid] = array(
+ '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];
+ }
}
+ self::appendMachineData($config['locations'], $lids, true);
}
- self::appendMachineData($config['locations'], $lids, true);
self::appendOpeningTimes($config['locations'], $lids);
$config['ts'] = (int)$panel['lastchange'];
diff --git a/modules-available/locationinfo/inc/locationinfo.inc.php b/modules-available/locationinfo/inc/locationinfo.inc.php
index 64070cd4..2ed3622d 100644
--- a/modules-available/locationinfo/inc/locationinfo.inc.php
+++ b/modules-available/locationinfo/inc/locationinfo.inc.php
@@ -36,11 +36,7 @@ class LocationInfo
$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;
- }
+ $idArray = array_keys($idList);
}
return $idArray;
}
@@ -143,6 +139,7 @@ class LocationInfo
ConfigHolder::add('SLX_SCREEN_STANDBY_TIMEOUT', '', 1000);
ConfigHolder::add('SLX_SYSTEM_STANDBY_TIMEOUT', '', 1000);
ConfigHolder::add('SLX_AUTOLOGIN', '1', 1000);
+ ConfigHolder::add('SLX_BROWSER_INSECURE', '1'); // TODO: Sat server might redirect to HTTPS, which in turn could have a self-signed cert - push to client
}
}
diff --git a/modules-available/locationinfo/install.inc.php b/modules-available/locationinfo/install.inc.php
index 7c47ac90..bbba3741 100644
--- a/modules-available/locationinfo/install.inc.php
+++ b/modules-available/locationinfo/install.inc.php
@@ -26,7 +26,7 @@ $t2 = $res[] = tableCreate('locationinfo_coursebackend', '
$t3 = $res[] = tableCreate('locationinfo_panel', "
`paneluuid` char(36) CHARACTER SET ascii NOT NULL,
`panelname` varchar(30) NOT NULL,
- `locationids` varchar(20) CHARACTER SET ascii NOT NULL,
+ `locationids` varchar(100) CHARACTER SET ascii NOT NULL,
`paneltype` enum('DEFAULT','SUMMARY', 'URL') NOT NULL,
`panelconfig` blob NOT NULL,
`lastchange` int(10) UNSIGNED NOT NULL DEFAULT 0,
@@ -52,7 +52,7 @@ if ($t1 === UPDATE_NOOP) {
if ($ret === false) {
finalResponse(UPDATE_FAILED, 'Could not add lastchange field');
} elseif ($ret > 0) {
- $ret[] = UPDATE_DONE;
+ $res[] = UPDATE_DONE;
}
}
}
@@ -71,6 +71,9 @@ if ($t1 === UPDATE_DONE) {
if ($t3 === UPDATE_NOOP) {
Database::exec("ALTER TABLE `locationinfo_panel` CHANGE `paneltype`
`paneltype` ENUM('DEFAULT', 'SUMMARY', 'URL') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL");
+ // 2017-12-02 expand locationids column
+ Database::exec("ALTER TABLE `locationinfo_panel` CHANGE `locationids`
+ `locationids` varchar(100) CHARACTER SET ascii NOT NULL");
}
// 2017-07-26 Add servername key
diff --git a/modules-available/locationinfo/lang/en/template-tags.json b/modules-available/locationinfo/lang/en/template-tags.json
index 0d2b42a3..be927ee4 100644
--- a/modules-available/locationinfo/lang/en/template-tags.json
+++ b/modules-available/locationinfo/lang/en/template-tags.json
@@ -39,7 +39,7 @@
"lang_language": "Language",
"lang_languageTooltip": "The language the frontend uses",
"lang_locationName": "Name",
- "lang_locationSettings": "Settings",
+ "lang_locationSettings": "Location specific 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.",
diff --git a/modules-available/locationinfo/page.inc.php b/modules-available/locationinfo/page.inc.php
index d6035869..c6aa0860 100644
--- a/modules-available/locationinfo/page.inc.php
+++ b/modules-available/locationinfo/page.inc.php
@@ -449,7 +449,7 @@ class Page_LocationInfo extends Page
$locations = Location::getLocations(0, 0, false, true);
// Get hidden state of all locations
- $dbquery = Database::simpleQuery("SELECT li.locationid, li.serverid, li.serverlocationid, li.openingtime, li.lastcalendarupdate, cb.servername
+ $dbquery = Database::simpleQuery("SELECT li.locationid, li.serverid, li.serverlocationid, li.openingtime, li.lastcalendarupdate, cb.servertype, cb.servername
FROM `locationinfo_locationconfig` AS li
LEFT JOIN `locationinfo_coursebackend` AS cb USING (serverid)");
@@ -466,6 +466,7 @@ class Page_LocationInfo extends Page
'openingGlyph' => $glyph,
'backend' => $backend,
'lastCalendarUpdate' => $row['lastcalendarupdate'], // TODO
+ 'backendMissing' => !CourseBackend::exists($row['servertype']),
);
}
diff --git a/modules-available/locationinfo/templates/frontend-default.html b/modules-available/locationinfo/templates/frontend-default.html
index 4147e4e2..92cad055 100755
--- a/modules-available/locationinfo/templates/frontend-default.html
+++ b/modules-available/locationinfo/templates/frontend-default.html
@@ -343,6 +343,7 @@ optional:
<script type='text/javascript' src='{{dirprefix}}script/jquery.js'></script>
<script type='text/javascript' src='{{dirprefix}}modules/js_jqueryui/clientscript.js'></script>
<script type='text/javascript' src="{{dirprefix}}modules/js_weekcalendar/clientscript.js"></script>
+ <script type='text/javascript' src='{{dirprefix}}modules/locationinfo/frontend/frontendscript.js'></script>
</head>
<body>
@@ -980,10 +981,11 @@ optional:
var columnWidth = $cal.find(".wc-day-1").width();
if (room.config.scaledaysauto) {
- var result = ($cal.weekCalendar("option", "daysToShow") * columnWidth) / 100;
+ var result = ($cal.weekCalendar("option", "daysToShow") * columnWidth) / 130;
result = parseInt(Math.min(Math.max(Math.abs(result), 1), 7));
if (result !== $cal.weekCalendar("option", "daysToShow")) {
$cal.weekCalendar("option", "daysToShow", result);
+ columnWidth = $cal.find(".wc-day-1").width();
}
}
if (((!room.config.scaledaysauto) || $cal.weekCalendar("option", "daysToShow") === 1) && columnWidth < 85) {
@@ -1007,21 +1009,21 @@ optional:
clientHeight -= 6;
var height = clientHeight / (room.openTimes * $cal.weekCalendar("option", "timeslotsPerHour"));
-
if (height < 30) {
height = 30;
}
+ var fontHeight = Math.min(height, columnWidth / 2.1);
// Scale calendar font
- if (height > 120) {
+ if (fontHeight > 120) {
$cal.weekCalendar("option", "textSize", 28);
}
- else if (height > 100) {
+ else if (fontHeight > 100) {
$cal.weekCalendar("option", "textSize", 24);
- } else if (height > 80) {
+ } else if (fontHeight > 80) {
$cal.weekCalendar("option", "textSize", 22);
- } else if (height > 70) {
+ } else if (fontHeight > 70) {
$cal.weekCalendar("option", "textSize", 20);
- } else if (height > 60) {
+ } else if (fontHeight > 60) {
$cal.weekCalendar("option", "textSize", 14);
} else {
$cal.weekCalendar("option", "textSize", 13);
@@ -1040,50 +1042,6 @@ optional:
}
/**
- * used for countdown
- * computes the time difference between 2 Date objects
- * @param {Date} a
- * @param {Date} b
- * @returns {string} printable time
- */
- function GetTimeDiferenceAsString(a, b) {
- if (!a || !b) {
- return "";
- }
- var milliseconds = a.getTime() - b.getTime();
- var days = Math.floor((milliseconds / (1000 * 60 * 60 * 24)) % 31);
- if (days !== 0) {
- // don't show?
- return "";
- }
- var seconds = Math.floor((milliseconds / 1000) % 60);
- milliseconds -= seconds * 1000;
- var minutes = Math.floor((milliseconds / (1000 * 60)) % 60);
- milliseconds -= minutes * 1000 * 60;
- var hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24);
-
- if (globalConfig.prettytime) {
- var str = '';
- if (hours > 0) {
- str += hours + 'h ';
- }
- str += minutes + 'min ';
- return str;
- }
-
- if (minutes < 10) {
- minutes = "0" + minutes;
- }
- if (globalConfig.eco) {
- return hours + ":" + minutes;
- }
- if (seconds < 10) {
- seconds = "0" + seconds;
- }
- return hours + ":" + minutes + ":" + seconds;
- }
-
- /**
* returns next closing time of a given room
* @param room
* @returns {Date} Object of next closing
@@ -1114,32 +1072,6 @@ optional:
return null;
}
-
- /**
- * checks if a room is on a given date/time open
- * @param date Date Object
- * @param room Room object
- * @returns {Boolean} for open or not
- */
- function IsOpen(date, room) {
- if (!room.openingTimes || room.openingTimes.length === 0) return true;
- var tmp = room.openingTimes[date.getDay()];
- if (!tmp) return false;
- var openDate = new Date(date.getTime());
- var closeDate = new Date(date.getTime());
- for (var i = 0; i < tmp.length; i++) {
- openDate.setHours(tmp[i].HourOpen);
- openDate.setMinutes(tmp[i].MinutesOpen);
- closeDate.setHours(tmp[i].HourClose);
- closeDate.setMinutes(tmp[i].MinutesClose);
- if (openDate < date && closeDate > date) {
- return true;
- }
- }
- return false;
- }
-
-
/**
* Returns next Opening
* @param room Room Object
@@ -1200,7 +1132,7 @@ optional:
var newText = false, newTime = false;
var seats = room.freePcs;
if (tmp.state === 'closed' || tmp.state === 'CalendarEvent' || tmp.state === 'Free') {
- newTime = GetTimeDiferenceAsString(tmp.end, MyDate());
+ newTime = GetTimeDiferenceAsString(tmp.end, MyDate(), globalConfig);
} else if (!same) {
newTime = '';
}
@@ -1726,20 +1658,5 @@ optional:
}, interval);
}
- /**
- * Convert passed argument to integer if possible, return NaN otherwise.
- * The difference to parseInt() is that leading zeros are ignored and not
- * interpreted as octal representation.
- *
- * @param str string or already a number
- * @return {number} str converted to number, or NaN
- */
- function toInt(str) {
- var t = typeof str;
- if (t === 'number') return str | 0;
- if (t === 'string') return parseInt(str.replace(/^0+([^0])/, '$1'));
- return NaN;
- }
-
</script>
</html>
diff --git a/modules-available/locationinfo/templates/frontend-summary.html b/modules-available/locationinfo/templates/frontend-summary.html
index 7faa01e5..ec5d8aab 100644
--- a/modules-available/locationinfo/templates/frontend-summary.html
+++ b/modules-available/locationinfo/templates/frontend-summary.html
@@ -3,6 +3,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8">
<head>
<script type='text/javascript' src='{{dirprefix}}script/jquery.js'></script>
+ <script type='text/javascript' src='{{dirprefix}}modules/locationinfo/frontend/frontendscript.js'></script>
<style type='text/css'>
@@ -33,6 +34,7 @@
.parent .parent, .parent .child {
min-height: 5em;
+ min-width: 90px;
}
.border {
@@ -101,7 +103,8 @@
.paperEffect {
margin: 0 auto;
background-color: #fff;
- box-shadow: 0 0 0.2vmin rgba(0, 0, 0, 0.4), inset 0 0 1vmin rgba(0, 0, 0, 0.1);
+ box-shadow: 0 0 3px rgba(0, 0, 0, 0.4), 0 0 10px rgba(0, 0, 0, 0.1) inset;
+ box-shadow: 0 0 0.2vmin rgba(0, 0, 0, 0.4), 0 0 1vmin rgba(0, 0, 0, 0.1) inset;
border-radius: 1px;
}
@@ -115,21 +118,25 @@
var config = {{{config}}};
$(document).ready(function () {
- //temp
- SetUpDate(new Date());
init();
});
function init() {
- // var ids = getUrlParameter("id");
-
- /*
- $.getJSON("../../../api.php?do=locationinfo&action=locationtree&id=" + ids, function (result) {
- generateLayout(result);
-
- setTimeout(update, 1000);
- });
- */
+ var time = false;
+ if (config.time) {
+ var p = config.time.split('-');
+ if (p.length === 6) {
+ 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.getFullYear() < 2010) {
+ time = new Date(config.time);
+ }
+ if (!time || isNaN(time.getTime()) || time.getFullYear() < 2010) {
+ time = new Date();
+ }
+ }
+ SetUpDate(time);
generateLayout(config.tree);
update();
}
@@ -187,14 +194,16 @@
const ROOMUPDATE_MS = 2*60*1000;
const CALUPDATE_MS = 20*60*1000;
+ var timeout = null;
function update() {
var calendarUpdateIds = "";
var rommUpdateIds = "";
var count = 0;
var nextUpdate = 15000;
+ var property;
// TODO: Only query a few rooms is not possible with the new api stuff ...
- for (var property in rooms) {
+ for (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);
@@ -207,7 +216,7 @@
count++;
rooms[property].lastRoomUpdate = MyDate().getTime();
}
- if (count > 7) break;
+ // TODO if (count > 7) break;
}
if (calendarUpdateIds !== "") {
queryCalendars();
@@ -217,12 +226,33 @@
queryRooms();
nextUpdate = 1000;
}
- for (var property in rooms) {
+ for (property in rooms) {
upDateRoomState(rooms[property]);
}
+ clearTimeout(timeout);
setTimeout(update, nextUpdate);
}
+ function cleanDate(d) {
+ if (typeof d === 'string') {
+ // if is numeric
+ if (!isNaN(Number(d))) {
+ return cleanDate(parseInt(d, 10));
+ }
+
+ // this is a human readable date
+ if (d[d.length - 1] !== 'Z') d += 'Z';
+ var o = new Date(d);
+ o.setTime(o.getTime() + (o.getTimezoneOffset() * 60 * 1000));
+ return o;
+ }
+
+ if (typeof d === 'number') {
+ return new Date(d);
+ }
+
+ return d;
+ }
function UpdateTimeTables(json) {
var l = json.length;
@@ -232,11 +262,12 @@
}
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);
- rooms[json[i].id].timetable[property].end = new Date(rooms[json[i].id].timetable[property].end);
+ rooms[json[i].id].timetable[property].start = cleanDate(rooms[json[i].id].timetable[property].start);
+ rooms[json[i].id].timetable[property].end = cleanDate(rooms[json[i].id].timetable[property].end);
}
ComputeCurrentState(rooms[json[i].id]);
}
+ update();
}
/**
@@ -289,7 +320,6 @@
updateCourseText(room.id, "Geschlossen");
updateCoursTimer(room.id, "");
}
-
}
/**
@@ -303,6 +333,10 @@
}
}
+
+ const OT_DAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
+ const OT_KEYS = ['HourOpen', 'HourClose', 'MinutesOpen', 'MinutesClose'];
+
/**
* Generates a room Object and adds it to the rooms array
* @param id ID of the room
@@ -310,6 +344,22 @@
* @param config Config Json of the room
*/
function addRoom(id, name) {
+ var ot = [];
+ if (config && config.locations) {
+ for (var i = 0; i < config.locations.length; ++i) {
+ if (config.locations[i].id == id) {
+ // TODO: Messed up transformation from default panel
+ if (config.locations[i].openingtime) {
+ var raw_ot = config.locations[i].openingtime;
+
+ for (var j = 0; j < OT_DAYS.length; ++j) {
+ ot.push(filterOpeningTimesDay(raw_ot[OT_DAYS[j]]));
+ }
+ }
+ }
+ }
+ }
+
var room = {
id: id,
name: name,
@@ -318,7 +368,7 @@
nextEventEnd: null,
timeTilFree: null,
state: null,
- openingTimes: null,
+ openingTimes: ot,
lastCalendarUpdate: null,
lastRoomUpdate: null,
getState: function () {
@@ -346,6 +396,33 @@
}
}
+ /**
+ * Filter out invalid opening time entries from given array,
+ * also make sure all the values are of type number (int)
+ *
+ * @param {Array} arr
+ * @return {Array} list of valid opening times
+ */
+ function filterOpeningTimesDay(arr) {
+ if (!arr || arr.constructor !== Array) return [];
+ return arr.map(function (el) {
+ if (!el || typeof el !== 'object') return null;
+ for (var i = 0; i < OT_KEYS.length; ++i) {
+ el[OT_KEYS[i]] = toInt(el[OT_KEYS[i]]);
+ if (isNaN(el[OT_KEYS[i]])) return null;
+ }
+ return el;
+ }).filter(function (el) {
+ if (!el) return false;
+ if (el.HourOpen < 0 || el.HourOpen > 23) return false;
+ if (el.HourClose < 0 || el.HourClose > 23) return false;
+ if (el.HourClose < el.HourOpen) return false;
+ if (el.MinutesOpen < 0 || el.MinutesOpen > 59) return false;
+ if (el.MinutesClose < 0 || el.MinutesClose > 59) return false;
+ if (el.HourOpen === el.HourClose && el.MinutesClose < el.MinutesOpen) return false;
+ return true;
+ });
+ }
/**
* computes state of a room, states are:
@@ -396,6 +473,7 @@
room.state = {state: "Free", end: closing, titel: "", next: "closing"};
}
}
+
/**
* checks if a room is open
* @param room Room object
@@ -408,6 +486,7 @@
// changes from falls needs testing
return true;
}
+
var tmp = room.openingTimes[now.getDay()];
if (tmp == null) {
return false;
@@ -477,7 +556,7 @@
openDate.setHours(tmp[i].HourOpen);
openDate.setMinutes(tmp[i].MinutesOpen);
if (openDate > now) {
- if (!IsOpen(new Date(openDate.getTime() - 60000))) {
+ if (!IsOpen(new Date(openDate.getTime() - 60000), room)) {
if (bestdate == null || bestdate > openDate) {
bestdate = openDate;
}
@@ -517,7 +596,7 @@
closeDate.setHours(tmp[i].HourClose);
closeDate.setMinutes(tmp[i].MinutesClose);
if (closeDate > now) {
- if (!IsOpen(new Date(closeDate.getTime() + 60000))) {
+ if (!IsOpen(new Date(closeDate.getTime() + 60000), room)) {
if (bestdate == null || bestdate > closeDate) {
bestdate = closeDate;
}
@@ -541,13 +620,16 @@
* @param occupied PC's used
* @param offline PC's that are off
* @param broken PC's that are broken
+ * @param standby PCs in standby mode
*/
function updateRoomUsage(id, idle, occupied, offline, broken, standby) {
- if (idle == 0 && occupied == 0 && offline == 0 && broken == 0 && standby == 0) {
+ /* TODO Broken
+ 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(offline) + parseInt(broken) + parseInt(standby);
$("#pc_Idle_" + id).text(idle).width((idle / total) * 100 + '%');
$("#pc_Occupied_" + id).text(occupied).width((occupied / total) * 100 + '%');
@@ -683,38 +765,6 @@
});
}
-
- /**
- * used for countdown
- * computes the time difference between 2 Date objects
- * @param a Date Object
- * @param b Date Object
- * @returns time string
- */
- function GetTimeDiferenceAsString(a, b) {
- if (a == null || b == null) {
- return "";
- }
- var milliseconds = a.getTime() - b.getTime();
- var seconds = Math.floor((milliseconds / 1000) % 60);
- milliseconds -= seconds * 1000;
- var minutes = Math.floor((milliseconds / (1000 * 60)) % 60);
- milliseconds -= minutes * 1000 * 60;
- var hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24);
-
- var days = Math.floor((milliseconds / (1000 * 60 * 60 * 24)) % 31);
- if (seconds < 10) {
- seconds = "0" + seconds;
- }
- if (minutes < 10) {
- minutes = "0" + minutes;
- }
- if (days != 0) {
- // dont show?
- return "";
- }
- return hours + ":" + minutes + ":" + seconds;
- }
</script>
</head>
<body>
diff --git a/modules-available/locationinfo/templates/page-locations.html b/modules-available/locationinfo/templates/page-locations.html
index 3eafa7bf..de8dab7e 100644
--- a/modules-available/locationinfo/templates/page-locations.html
+++ b/modules-available/locationinfo/templates/page-locations.html
@@ -21,7 +21,7 @@
<span class="glyphicon glyphicon-edit"></span>
</a>
</td>
- <td>
+ <td {{#backendMissing}}class="text-danger"{{/backendMissing}}>
{{backend}}
</td>
<td>
diff --git a/modules-available/locations/page.inc.php b/modules-available/locations/page.inc.php
index d9bc7130..0cfa5b90 100644
--- a/modules-available/locations/page.inc.php
+++ b/modules-available/locations/page.inc.php
@@ -69,7 +69,7 @@ class Page_Locations extends Page
private function addLocations()
{
$names = Request::post('newlocation', false);
- $parents = Request::post('newparent', false);
+ $parents = Request::post('newparent', []);
if (!is_array($names) || !is_array($parents)) {
Message::addError('main.empty-field');
Util::redirect('?do=Locations');
@@ -198,6 +198,7 @@ class Page_Locations extends Page
}
}
}
+ // TODO: Check permissions for new parent (only if changed)
$ret = Database::exec('UPDATE location SET parentlocationid = :parent, locationname = :name'
. ' WHERE locationid = :lid', array(
'lid' => $locationId,
@@ -318,13 +319,13 @@ class Page_Locations extends Page
$res = Database::simpleQuery("SELECT subnetid, startaddr, endaddr, locationid FROM subnet
WHERE locationid IN (:locations) ORDER BY startaddr ASC",
array("locations" => User::getAllowedLocations("location.view")));
+ $allowedLocs = User::getAllowedLocations("subnet.add");
$rows = array();
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
$row['startaddr'] = long2ip($row['startaddr']);
$row['endaddr'] = long2ip($row['endaddr']);
$row['locations'] = Location::getLocations($row['locationid']);
- $allowedLocs = User::getAllowedLocations("subnet.add");
foreach ($row['locations'] as &$loc) {
if (!(in_array($loc["locationid"], $allowedLocs) || $loc["locationid"] == $row['locationid'])) {
$loc["disabled"] = "disabled";
@@ -376,6 +377,7 @@ class Page_Locations extends Page
unset($loc);
foreach ($locs as $loc) {
foreach ($loc['parents'] as $pid) {
+ $locs[(int)$pid]['hasChild'] = true;
$locs[(int)$pid]['clientCountSum'] += $loc['clientCount'];
}
}
@@ -444,7 +446,8 @@ class Page_Locations extends Page
}
$addAllowedLocs = User::getAllowedLocations("location.add");
- $addAllowedList = Location::getLocations(0, 0, True);
+ $addAllowedLocs[] = 0;
+ $addAllowedList = Location::getLocations(0, 0, true);
foreach ($addAllowedList as &$loc) {
if (!in_array($loc["locationid"], $addAllowedLocs)) {
$loc["disabled"] = "disabled";
@@ -514,6 +517,7 @@ class Page_Locations extends Page
);
$allowedLocs = User::getAllowedLocations("location.edit");
+ $allowedLocs[] = 0;
foreach ($data['parents'] as &$parent) {
if (!(in_array($parent["locationid"], $allowedLocs) || $parent["locationid"] == $loc['parentlocationid'])) {
$parent["disabled"] = "disabled";
diff --git a/modules-available/locations/templates/locations.html b/modules-available/locations/templates/locations.html
index 02428323..be3d5115 100644
--- a/modules-available/locations/templates/locations.html
+++ b/modules-available/locations/templates/locations.html
@@ -41,12 +41,12 @@
<td class="text-nowrap" align="right">
{{^linkClass}}
{{#havestatistics}}
- {{clientCount}}
- <span class="text-muted">
- /
- <span style="display:inline-block;width:3ex">{{clientCountSum}}</span>
+ <a href="?do=Statistics&amp;show=list&amp;filters=location={{locationid}}">&nbsp;{{clientCount}}&nbsp;</a>
+ <span style="display:inline-block;width:5ex">
+ {{#hasChild}}
+ (<a href="?do=Statistics&amp;show=list&amp;filters=location~{{locationid}}">&downarrow;{{clientCountSum}}</a>)
+ {{/hasChild}}
</span>
- <a class="btn btn-default btn-xs" href="?do=Statistics&amp;show=list&amp;filters=location={{locationid}}"><span class="glyphicon glyphicon-eye-open"></span></a>
{{/havestatistics}}
{{/linkClass}}
</td>
@@ -87,9 +87,8 @@
<tr>
<td>{{lang_unassignedMachines}}</td>
<td class="text-nowrap" align="right">
- {{unassignedCount}}
- <a class="btn btn-default btn-xs" href="?do=Statistics&amp;show=list&amp;filters=location=0">
- <span class="glyphicon glyphicon-eye-open"></span>
+ <a href="?do=Statistics&amp;show=list&amp;filters=location=0">
+ &nbsp;{{unassignedCount}}&nbsp;
</a>
</td>
<td class="text-nowrap" align="right">
@@ -103,16 +102,16 @@
<form method="post" action="?do=Locations">
<input type="hidden" name="token" value="{{token}}">
<input type="hidden" name="action" value="addlocations">
- <table class="table table-condensed table-hover">
+ <table class="table table-condensed">
<tr id="lasttr">
<td width="60%">&emsp;</td>
<td class="text-right" colspan="2">
- <button id="saveLocationRows" type="submit" class="btn btn-primary" style="display: none">
- <span class="glyphicon glyphicon-floppy-disk"></span> {{lang_save}}
- </button>
<button {{^addAllowed}}disabled{{/addAllowed}} class="btn btn-success" type="button" onclick="slxAddLocationRow()">
<span class="glyphicon glyphicon-plus"></span> {{lang_location}}
</button>
+ <button id="saveLocationRows" type="submit" class="btn btn-primary collapse">
+ <span class="glyphicon glyphicon-floppy-disk"></span> {{lang_save}}
+ </button>
</td>
</tr>
</table>
diff --git a/modules-available/roomplanner/clientscript.js b/modules-available/roomplanner/clientscript.js
index 722e3909..5c9776b2 100644
--- a/modules-available/roomplanner/clientscript.js
+++ b/modules-available/roomplanner/clientscript.js
@@ -174,9 +174,14 @@ function onBtnSelect() {
}
function onPcDelete(muuid) {
- var bySubnet = machineCache[muuid];
- var bySearch = machineCache[muuid];
- var value = !bySubnet ? bySearch : bySubnet;
+ var value = machineCache[muuid];
+ if (!value) {
+ subnetMachines.forEach(function (v, i, a) {
+ if (subnetMachines[i] && subnetMachines[i].machineuuid === muuid) {
+ value = subnetMachines[i];
+ }
+ });
+ }
value.fixedlocationid = null;
makeCombinedFieldSingle(value);
}
diff --git a/modules-available/roomplanner/hooks/runmode/config.json b/modules-available/roomplanner/hooks/runmode/config.json
index 0f2ce493..27c601fd 100644
--- a/modules-available/roomplanner/hooks/runmode/config.json
+++ b/modules-available/roomplanner/hooks/runmode/config.json
@@ -2,5 +2,6 @@
"getModeName": "PvsGenerator::getManagerName",
"isClient": false,
"configHook": "PvsGenerator::runmodeConfigHook",
- "allowGenericEditor": false
+ "allowGenericEditor": false,
+ "deleteUrlSnippet": "locationid="
} \ No newline at end of file
diff --git a/modules-available/roomplanner/lang/de/module.json b/modules-available/roomplanner/lang/de/module.json
new file mode 100644
index 00000000..58dac918
--- /dev/null
+++ b/modules-available/roomplanner/lang/de/module.json
@@ -0,0 +1,4 @@
+{
+ "module_name": "PVS-Manager",
+ "page_title": "PVS-Manager"
+} \ No newline at end of file
diff --git a/modules-available/runmode/inc/runmode.inc.php b/modules-available/runmode/inc/runmode.inc.php
index d333af58..ad1f52bf 100644
--- a/modules-available/runmode/inc/runmode.inc.php
+++ b/modules-available/runmode/inc/runmode.inc.php
@@ -279,6 +279,10 @@ class RunModeModuleConfig
* @var bool Allow adding and removing machines to this mode via the generic form
*/
public $allowGenericEditor = true;
+ /**
+ * @var string Snippet to construct URL for delete
+ */
+ public $deleteUrlSnippet = false;
public function __construct($file)
{
@@ -293,6 +297,7 @@ class RunModeModuleConfig
$this->loadType($data, 'isClient', 'boolean');
$this->loadType($data, 'noSysconfig', 'boolean');
$this->loadType($data, 'allowGenericEditor', 'boolean');
+ $this->loadType($data, 'deleteUrlSnippet', 'string');
}
private function loadType($data, $key, $type)
diff --git a/modules-available/runmode/lang/de/messages.json b/modules-available/runmode/lang/de/messages.json
new file mode 100644
index 00000000..21b4b6ae
--- /dev/null
+++ b/modules-available/runmode/lang/de/messages.json
@@ -0,0 +1,9 @@
+{
+ "cannot-edit-module": "Modul {{0}} kann nicht direkt editiert werden",
+ "enabled-removed-save": "{{0}} Rechner gespeichert, {{1}} entfernt",
+ "invalid-modeid": "{{1}} ist kein g\u00fcltiger Betriebsmodus f\u00fcr Modul {{0}}",
+ "machine-not-found": "Rechner {{0}} nicht gefunden",
+ "machine-not-runmode": "Rechner {{0}} hatte keinen speziellen Betriebsmodus aktiviert",
+ "machine-removed": "Rechner {{0}} entfernt",
+ "module-hasnt-runmode": "Modul {{0}} bietet keine speziellen Betriebsmodi"
+} \ No newline at end of file
diff --git a/modules-available/runmode/lang/en/messages.json b/modules-available/runmode/lang/en/messages.json
new file mode 100644
index 00000000..1985ca66
--- /dev/null
+++ b/modules-available/runmode/lang/en/messages.json
@@ -0,0 +1,9 @@
+{
+ "cannot-edit-module": "Module {{0}} cannot be edited directly",
+ "enabled-removed-save": "Saved {{0}} clients, deleted {{1}}",
+ "invalid-modeid": "Module {{0}} doesn't provide mode {{1}}",
+ "machine-not-found": "Client {{0}} not found",
+ "machine-not-runmode": "No special mode of operation configured for client {{0}}",
+ "machine-removed": "Removed client {{0}}",
+ "module-hasnt-runmode": "Module {{0}} doesn't supply any special mode of operation"
+} \ No newline at end of file
diff --git a/modules-available/runmode/page.inc.php b/modules-available/runmode/page.inc.php
index ef42e7be..e26950d0 100644
--- a/modules-available/runmode/page.inc.php
+++ b/modules-available/runmode/page.inc.php
@@ -28,16 +28,16 @@ class Page_RunMode extends Page
$modeId = Request::post('modeid', false, 'string');
$modConfig = RunMode::getModuleConfig($module);
if ($modConfig === false) {
- Message::addError('module-hasnt-runmode', $module);
+ Message::addError('runmode.module-hasnt-runmode', $module);
return;
}
if (!$modConfig->allowGenericEditor) {
- Message::addError('cannot-edit-module', $module);
+ Message::addError('runmode.cannot-edit-module', $module);
return;
}
$test = RunMode::getModeName($module, $modeId);
if ($test === false) {
- Message::addError('invalid-modeid', $module, $modeId);
+ Message::addError('runmode.invalid-modeid', $module, $modeId);
return;
}
$active = 0;
@@ -53,21 +53,31 @@ class Page_RunMode extends Page
WHERE module = :module AND modeid = :modeId AND machineuuid NOT IN (:machines)',
compact('module', 'modeId', 'machines'));
Message::addSuccess('runmode.enabled-removed-save', $active, $deleted);
- $redirect = Request::post('redirect', false, 'string');
- if ($redirect !== false) {
- Util::redirect($redirect);
- }
- Util::redirect('?do=runmode&module=' . $module . '&modeid=' . $modeId);
+ Util::redirect('?do=runmode&module=' . $module . '&modeid=' . $modeId, true);
} elseif ($action === 'delete-machine') {
$machineuuid = Request::post('machineuuid', false, 'string');
if ($machineuuid === false) {
- Message::addError('machine-not-found', $machineuuid);
+ Message::addError('main.parameter-missing', 'machineuuid');
+ return;
+ }
+ $mode = RunMode::getRunMode($machineuuid);
+ if ($mode === false) {
+ Message::addError('runmode.machine-not-found', $machineuuid);
+ return;
+ }
+ $modConfig = RunMode::getModuleConfig($mode['module']);
+ if ($modConfig === false) {
+ Message::addError('module-hasnt-runmode', $mode['moduleName']);
+ return;
+ }
+ if (!$modConfig->allowGenericEditor) {
+ Message::addError('runmode.cannot-edit-module', $mode['moduleName']);
+ return;
+ }
+ if (RunMode::setRunMode($machineuuid, null, null)) {
+ Message::addSuccess('machine-removed', $machineuuid);
} else {
- if (RunMode::setRunMode($machineuuid, null, null)) {
- Message::addSuccess('machine-removed', $machineuuid);
- } else {
- Message::addWarning('machine-not-runmode', $machineuuid);
- }
+ Message::addWarning('machine-not-runmode', $machineuuid);
}
}
}
@@ -95,6 +105,10 @@ class Page_RunMode extends Page
Message::addError('module-hasnt-runmode', $moduleId);
Util::redirect('?do=runmode');
}
+ if (!$config->allowGenericEditor) {
+ Message::addError('runmode.cannot-edit-module', $moduleId);
+ return;
+ }
// Given modeId?
$modeId = Request::get('modeid', false, 'string');
if ($modeId !== false) {
@@ -137,10 +151,13 @@ class Page_RunMode extends Page
$module = Module::get($moduleId);
if ($module === false)
continue;
+ $config = RunMode::getModuleConfig($moduleId);
Render::addTemplate('module-machine-list', array(
'list' => $rows['list'],
'modulename' => $module->getDisplayName(),
'module' => $moduleId,
+ 'canedit' => $config !== false && $config->allowGenericEditor && $config->deleteUrlSnippet === false,
+ 'deleteUrl' => $config->deleteUrlSnippet
));
}
}
diff --git a/modules-available/runmode/templates/module-machine-list.html b/modules-available/runmode/templates/module-machine-list.html
index 45f574ef..61bbbad9 100644
--- a/modules-available/runmode/templates/module-machine-list.html
+++ b/modules-available/runmode/templates/module-machine-list.html
@@ -35,8 +35,16 @@
{{/isclient}}
</td>
<td class="text-center">
- <button class="btn btn-danger btn-sm" name="machineuuid" value="{{machineuuid}}"><span
- class="glyphicon glyphicon-remove"></span></button>
+ {{#canedit}}
+ <button type="submit" class="btn btn-danger btn-sm" name="machineuuid" value="{{machineuuid}}">
+ <span class="glyphicon glyphicon-trash"></span>
+ </button>
+ {{/canedit}}
+ {{#deleteUrl}}
+ <a class="btn btn-danger btn-sm" href="?do={{module}}&amp;{{deleteUrl}}{{modeid}}">
+ <span class="glyphicon glyphicon-trash"></span>
+ </a>
+ {{/deleteUrl}}
</td>
</tr>
{{/list}}
diff --git a/modules-available/statistics/inc/filter.inc.php b/modules-available/statistics/inc/filter.inc.php
index 6e437a71..be6df752 100644
--- a/modules-available/statistics/inc/filter.inc.php
+++ b/modules-available/statistics/inc/filter.inc.php
@@ -208,15 +208,22 @@ class LocationFilter extends Filter
public function whereClause(&$args, &$joins)
{
+ $recursive = (substr($this->operator, -1) === '~');
+ $this->operator = str_replace('~', '=', $this->operator);
+
settype($this->argument, 'int');
+ $neg = $this->operator === '=' ? '' : 'NOT';
if ($this->argument === 0) {
- $neg = $this->operator === '=' ? '' : 'NOT';
return "machine.locationid IS $neg NULL";
} else {
global $unique_key;
$key = $this->column . '_arg' . ($unique_key++);
- $args[$key] = $this->argument;
- return "machine.locationid {$this->operator} :$key";
+ if ($recursive) {
+ $args[$key] = array_keys(Location::getRecursiveFlat($this->argument));
+ } else {
+ $args[$key] = $this->argument;
+ }
+ return "machine.locationid $neg IN (:$key)";
}
}
}
diff --git a/modules-available/statistics/lang/de/messages.json b/modules-available/statistics/lang/de/messages.json
index e3dff61c..c9667f7b 100644
--- a/modules-available/statistics/lang/de/messages.json
+++ b/modules-available/statistics/lang/de/messages.json
@@ -1,4 +1,5 @@
{
+ "deleted-n-machines": "{{0}} Clients gel\u00f6scht",
"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",
diff --git a/modules-available/statistics/lang/de/template-tags.json b/modules-available/statistics/lang/de/template-tags.json
index 56cf55d7..3cdde813 100644
--- a/modules-available/statistics/lang/de/template-tags.json
+++ b/modules-available/statistics/lang/de/template-tags.json
@@ -74,6 +74,7 @@
"lang_showVisualization": "Visualisierung",
"lang_sockets": "Sockel",
"lang_subnet": "Subnetz",
+ "lang_sureDeletePermanent": "M\u00f6chten Sie diese(n) Rechner wirklich unwiderruflich aus der Datenbank entfernen?\r\n\r\nWichtig: L\u00f6schen verhindert nicht, dass ein Rechner nach erneutem Starten von bwLehrpool wieder in die Datenbank aufgenommen wird.",
"lang_tempPart": "Temp. Partition",
"lang_tempPartStats": "Tempor\u00e4re Partition",
"lang_thoseAreProjectors": "Diese Modellnamen werden als Beamer behandelt, auch wenn die EDID-Informationen des Ger\u00e4tes anderes berichten.",
diff --git a/modules-available/statistics/lang/en/messages.json b/modules-available/statistics/lang/en/messages.json
index ae6c47af..3471c472 100644
--- a/modules-available/statistics/lang/en/messages.json
+++ b/modules-available/statistics/lang/en/messages.json
@@ -1,4 +1,5 @@
{
+ "deleted-n-machines": "Deleted {{0}} clients",
"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",
diff --git a/modules-available/statistics/lang/en/template-tags.json b/modules-available/statistics/lang/en/template-tags.json
index ab7a7d0a..35c4e68a 100644
--- a/modules-available/statistics/lang/en/template-tags.json
+++ b/modules-available/statistics/lang/en/template-tags.json
@@ -74,6 +74,7 @@
"lang_showVisualization": "Visualization",
"lang_sockets": "Sockets",
"lang_subnet": "Subnet",
+ "lang_sureDeletePermanent": "Are your sure you want to delete the selected machine(s) from the database? This cannot be undone.\r\n\r\nNote: Deleting machines from the database does not prevent booting up bwLehrpool again, which would recreate their respective database entries.",
"lang_tempPart": "Temp. partition",
"lang_tempPartStats": "Temporary partition",
"lang_thoseAreProjectors": "These model names will always be treated as beamers, even if the device's EDID data says otherwise.",
diff --git a/modules-available/statistics/page.inc.php b/modules-available/statistics/page.inc.php
index fafa7cd2..c3ecf52b 100644
--- a/modules-available/statistics/page.inc.php
+++ b/modules-available/statistics/page.inc.php
@@ -111,12 +111,12 @@ class Page_Statistics extends Page
'op' => Page_Statistics::$op_nominal,
'type' => 'enum',
'column' => true,
- 'values' => ['occupied', 'on']
+ 'values' => ['occupied', 'on', 'off', 'idle', 'standby']
]
];
if (Module::isAvailable('locations')) {
Page_Statistics::$columns['location'] = [
- 'op' => Page_Statistics::$op_nominal,
+ 'op' => Page_Statistics::$op_stringcmp,
'type' => 'enum',
'column' => false,
'values' => array_keys(Location::getLocationsAssoc()),
@@ -195,6 +195,35 @@ class Page_Statistics extends Page
Util::redirect('?do=Statistics&uuid=' . $uuid);
} elseif ($action === 'addprojector' || $action === 'delprojector') {
$this->handleProjector($action);
+ } elseif ($action === 'delmachines') {
+ $this->deleteMachines();
+ Util::redirect('?do=statistics', true);
+ }
+ }
+
+ private function deleteMachines()
+ {
+ $ids = Request::post('uuid', [], 'array');
+ $ids = array_values($ids);
+ if (empty($ids)) {
+ Message::addError('main.parameter-empty', 'uuid');
+ return;
+ }
+ $res = Database::simpleQuery('SELECT machineuuid, locationid FROM machine WHERE machineuuid IN (:ids)', compact('ids'));
+ $ids = array_flip($ids);
+ $delete = [];
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ // TODO: Check locationid permissions
+ unset($ids[$row['machineuuid']]);
+ $delete[] = $row['machineuuid'];
+ }
+ if (!empty($delete)) {
+ Database::exec('DELETE FROM machine WHERE machineuuid IN (:delete)', compact('delete'));
+ Message::addSuccess('deleted-n-machines', count($delete));
+ }
+ if (!empty($ids)) {
+ // TODO: Warn permissions
+ Message::addWarning('unknown-machine', implode(', ', array_keys($ids)));
}
}
@@ -575,6 +604,7 @@ class Page_Statistics extends Page
*/
private function showMachineList($filterSet)
{
+ Module::isAvailable('js_stupidtable');
$filterSet->makeFragments($where, $join, $sort, $args);
$xtra = '';
@@ -638,7 +668,8 @@ class Page_Statistics extends Page
'sortColumn' => $filterSet->getSortColumn(),
'columns' => json_encode(Page_Statistics::$columns),
'showList' => 1,
- 'show' => 'list'
+ 'show' => 'list',
+ 'redirect' => $_SERVER['QUERY_STRING']
));
}
diff --git a/modules-available/statistics/templates/clientlist.html b/modules-available/statistics/templates/clientlist.html
index 01ca82f1..13e148fa 100644
--- a/modules-available/statistics/templates/clientlist.html
+++ b/modules-available/statistics/templates/clientlist.html
@@ -1,6 +1,10 @@
+<h2>{{lang_clientList}} ({{rowCount}})</h2>
-<table class="stupidtable table table-condensed table-striped">
+<form method="post" action="?do=statistics">
+<input type="hidden" name="token" value="{{token}}">
+<input type="hidden" name="redirect" value="?{{redirect}}">
+<table class="stupidtable table table-condensed table-striped">
<thead>
<tr>
<td></td>
@@ -45,6 +49,10 @@
{{#rows}}
<tr>
<td data-sort-value="{{hostname}}" class="text-nowrap">
+ <div class="checkbox checkbox-inline">
+ <input type="checkbox" name="uuid[]" value="{{machineuuid}}">
+ <label></label>
+ </div>
{{#hasnotes}}<span class="glyphicon glyphicon-exclamation-sign pull-right"></span>{{/hasnotes}}
{{#state_OFFLINE}}
<span class="glyphicon glyphicon-off" title="{{lang_machineOff}}"></span>
@@ -68,10 +76,10 @@
<td data-sort-value="{{gbram}}" class="text-right {{ramclass}}">{{gbram}}&thinsp;GiB</td>
<td data-sort-value="{{gbtmp}}" class="text-right {{hddclass}}">
{{gbtmp}}&thinsp;GiB
- {{#badsectors}}<div>
- <span class="glyphicon glyphicon-exclamation-sign" data-toggle="tooltip" title="{{lang_reallocatedSectors}}" data-placement="left"></span>
+ {{#badsectors}}<div><span data-toggle="tooltip" title="{{lang_reallocatedSectors}}" data-placement="left">
+ <span class="glyphicon glyphicon-exclamation-sign"></span>
{{badsectors}}
- </div>{{/badsectors}}
+ </span></div>{{/badsectors}}
{{#nohdd}}<div>
<span class="glyphicon glyphicon-hdd red"></span>
</div>{{/nohdd}}
@@ -81,6 +89,37 @@
{{/rows}}
</tbody>
</table>
+ <div class="text-right buttonbar">
+ <button type="reset" class="btn btn-default">
+ <span class="glyphicon glyphicon-remove"></span>
+ {{lang_reset}}
+ </button>
+ <button type="button" class="btn btn-danger" onclick="$('#del-confirm').modal()">
+ <span class="glyphicon glyphicon-trash"></span>
+ {{lang_delete}}
+ </button>
+ </div>
+ <div class="modal fade" id="del-confirm" tabindex="-1" role="dialog">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal">&times;</button>
+ <b>{{lang_delete}}</b>
+ </div>
+ <div class="modal-body">
+ {{lang_sureDeletePermanent}}
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">{{lang_cancel}}</button>
+ <button type="submit" class="btn btn-danger" name="action" value="delmachines">
+ <span class="glyphicon glyphicon-trash"></span>
+ {{lang_delete}}
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+</form>
<script type="application/javascript"><!--
document.addEventListener("DOMContentLoaded", function () {
diff --git a/modules-available/statistics/templates/filterbox.html b/modules-available/statistics/templates/filterbox.html
index c2630ed9..31daabc6 100644
--- a/modules-available/statistics/templates/filterbox.html
+++ b/modules-available/statistics/templates/filterbox.html
@@ -170,6 +170,7 @@ document.addEventListener("DOMContentLoaded", function () {
$('#argumentInput').datepicker({format : 'yyyy-mm-dd'});
$('#argumentSelect').hide();
} else if(columns[col]['type'] == 'enum') {
+ $('#argumentSelect').empty();
$('#argumentInput').hide();
$('#argumentSelect').show();
columns[col]['values'].forEach(function (v) {
diff --git a/modules-available/statistics/templates/machine-main.html b/modules-available/statistics/templates/machine-main.html
index 14d388d3..19deb8b3 100644
--- a/modules-available/statistics/templates/machine-main.html
+++ b/modules-available/statistics/templates/machine-main.html
@@ -84,7 +84,7 @@
<tr>
<td class="text-nowrap">{{lang_runMode}}</td>
<td>
- <a href="?do={{modeid}}">{{moduleName}}</a> – {{modeName}}
+ <a href="?do=runmode&amp;module={{module}}">{{moduleName}}</a> – {{modeName}}
</td>
</tr>
{{/modeid}}
diff --git a/modules-available/sysconfig/addmodule_adauth.inc.php b/modules-available/sysconfig/addmodule_adauth.inc.php
index 6e4463ae..07806061 100644
--- a/modules-available/sysconfig/addmodule_adauth.inc.php
+++ b/modules-available/sysconfig/addmodule_adauth.inc.php
@@ -13,7 +13,7 @@ class AdAuth_Start extends AddModule_Base
protected function renderInternal()
{
- $ADAUTH_COMMON_FIELDS = array('title', 'server', 'searchbase', 'binddn', 'bindpw', 'home', 'homeattr', 'ssl', 'fixnumeric', 'certificate');
+ $ADAUTH_COMMON_FIELDS = array('title', 'server', 'searchbase', 'binddn', 'bindpw', 'home', 'homeattr', 'ssl', 'fixnumeric', 'certificate', 'mapping');
$data = array();
if ($this->edit !== false) {
moduleToArray($this->edit, $data, $ADAUTH_COMMON_FIELDS);
@@ -31,7 +31,12 @@ class AdAuth_Start extends AddModule_Base
if (isset($data['server']) && preg_match('/^(.*)\:(636|3269|389|3268)$/', $data['server'], $out)) {
$data['server'] = $out[1];
}
+ if (isset($data['homeattr']) && !isset($data['mapping']['homemount'])) {
+ $data['mapping']['homemount'] = $data['homeattr'];
+ }
$data['step'] = 'AdAuth_CheckConnection';
+ $data['map_empty'] = true;
+ $data['mapping'] = ConfigModuleBaseLdap::getMapping(isset($data['mapping']) ? $data['mapping'] : false, $data['map_empty']);
Render::addDialog(Dictionary::translateFile('config-module', 'adAuth_title'), false, 'ad-start', $data);
}
@@ -67,10 +72,11 @@ class AdAuth_CheckConnection extends AddModule_Base
if (preg_match('/^([^\:]+)\:(\d+)$/', $this->server, $out)) {
$ports = array($out[2]);
$this->server = $out[1];
+ // Test the default ports twice since the other one might not return all required data (home directory)
} elseif ($ssl) {
- $ports = array(636, 3269);
+ $ports = array(636, 3269, 636);
} else {
- $ports = array(389, 3268);
+ $ports = array(389, 3268, 389);
}
$this->scanTask = Taskmanager::submit('PortScan', array(
'host' => $this->server,
@@ -97,7 +103,8 @@ class AdAuth_CheckConnection extends AddModule_Base
'ssl' => Request::post('ssl'),
'fixnumeric' => Request::post('fixnumeric'),
'certificate' => Request::post('certificate', ''),
- 'taskid' => $this->scanTask['id']
+ 'taskid' => $this->scanTask['id'],
+ 'mapping' => ConfigModuleBaseLdap::getMapping(Request::post('mapping', false, 'array')),
);
$data['prev'] = 'AdAuth_Start';
if ((preg_match(AD_BOTH_REGEX, $this->bindDn) > 0) || (strlen($this->searchBase) < 2)) {
@@ -157,8 +164,8 @@ class AdAuth_SelfSearch extends AddModule_Base
$taskData['filter'] = 'sAMAccountName=' . $out[2];
} elseif (preg_match(AD_AT_REGEX, $binddn, $out) && !empty($out[1])) {
$this->originalBindDn = $binddn;
- $taskData['filter'] = 'sAMAccountName=' . $out[1];
- } elseif (preg_match('/^cn\=([^\=]+),.*?,dc\=([^\=]+),/i', Ldap::normalizeDn($binddn), $out)) {
+ $taskData['filter'] = 'userPrincipalName=' . $binddn;
+ } elseif (preg_match('/^cn\=([^\=]+),.*?dc\=([^\=]+),/i', Ldap::normalizeDn($binddn), $out)) {
if (empty($selfSearchBase)) {
$this->originalBindDn = $out[2] . '\\' . $out[1];
$taskData['filter'] = 'sAMAccountName=' . $out[1];
@@ -198,6 +205,7 @@ class AdAuth_SelfSearch extends AddModule_Base
'fingerprint' => Request::post('fingerprint'),
'certificate' => Request::post('certificate', ''),
'originalbinddn' => $this->originalBindDn,
+ 'mapping' => ConfigModuleBaseLdap::getMapping(Request::post('mapping', false, 'array')),
'prev' => 'AdAuth_Start'
);
if (empty($data['homeattr'])) {
@@ -275,6 +283,7 @@ class AdAuth_HomeAttrCheck extends AddModule_Base
'certificate' => Request::post('certificate', ''),
'originalbinddn' => Request::post('originalbinddn'),
'tryHomeAttr' => true,
+ 'mapping' => ConfigModuleBaseLdap::getMapping(Request::post('mapping', false, 'array')),
'prev' => 'AdAuth_Start',
'next' => 'AdAuth_CheckCredentials'
))
@@ -316,7 +325,8 @@ class AdAuth_CheckCredentials extends AddModule_Base
'server' => $uri,
'searchbase' => $searchbase,
'binddn' => $binddn,
- 'bindpw' => $bindpw
+ 'bindpw' => $bindpw,
+ 'mapping' => Request::post('mapping', false, 'array'),
));
if (!isset($ldapSearch['id'])) {
AddModule_Base::setStep('AdAuth_Start'); // Continues with AdAuth_Start for render()
@@ -325,8 +335,6 @@ class AdAuth_CheckCredentials extends AddModule_Base
$this->taskIds = array(
'tm-search' => $ldapSearch['id']
);
- if (isset($selfSearch['id']))
- $this->taskIds['self-search'] = $selfSearch['id'];
}
protected function renderInternal()
@@ -345,6 +353,7 @@ class AdAuth_CheckCredentials extends AddModule_Base
'fingerprint' => Request::post('fingerprint'),
'certificate' => Request::post('certificate', ''),
'originalbinddn' => Request::post('originalbinddn'),
+ 'mapping' => ConfigModuleBaseLdap::getMapping(Request::post('mapping', false, 'array')),
'prev' => 'AdAuth_Start',
'next' => 'AdAuth_HomeDir'
))
@@ -408,6 +417,7 @@ class AdAuth_HomeDir extends AddModule_Base
'fingerprint' => Request::post('fingerprint'),
'certificate' => Request::post('certificate', ''),
'originalbinddn' => Request::post('originalbinddn'),
+ 'mapping' => ConfigModuleBaseLdap::getMapping(Request::post('mapping', false, 'array')),
'prev' => 'AdAuth_Start',
'next' => 'AdAuth_Finish'
);
@@ -466,6 +476,7 @@ class AdAuth_Finish extends AddModule_Base
$module->setData('homeattr', Request::post('homeattr'));
$module->setData('certificate', Request::post('certificate'));
$module->setData('ssl', $ssl);
+ $module->setData('mapping', Request::post('mapping', false, 'array'));
$module->setData('fixnumeric', Request::post('fixnumeric', '', 'string'));
foreach (AdAuth_HomeDir::getAttributes() as $key) {
$value = Request::post($key);
diff --git a/modules-available/sysconfig/addmodule_ldapauth.inc.php b/modules-available/sysconfig/addmodule_ldapauth.inc.php
index 62120b48..a193f779 100644
--- a/modules-available/sysconfig/addmodule_ldapauth.inc.php
+++ b/modules-available/sysconfig/addmodule_ldapauth.inc.php
@@ -9,7 +9,7 @@ class LdapAuth_Start extends AddModule_Base
protected function renderInternal()
{
- $LDAPAUTH_COMMON_FIELDS = array('title', 'server', 'searchbase', 'binddn', 'bindpw', 'home', 'ssl', 'fixnumeric', 'certificate');
+ $LDAPAUTH_COMMON_FIELDS = array('title', 'server', 'searchbase', 'binddn', 'bindpw', 'home', 'homeattr', 'ssl', 'fixnumeric', 'certificate', 'mapping');
$data = array();
if ($this->edit !== false) {
moduleToArray($this->edit, $data, $LDAPAUTH_COMMON_FIELDS);
@@ -23,7 +23,12 @@ class LdapAuth_Start extends AddModule_Base
if (isset($data['server']) && preg_match('/^(.*)\:(636|389)$/', $data['server'], $out)) {
$data['server'] = $out[1];
}
+ if (isset($data['homeattr']) && !isset($data['mapping']['homemount'])) {
+ $data['mapping']['homemount'] = $data['homeattr'];
+ }
$data['step'] = 'LdapAuth_CheckConnection';
+ $data['map_empty'] = true;
+ $data['mapping'] = ConfigModuleBaseLdap::getMapping(isset($data['mapping']) ? $data['mapping'] : false, $data['map_empty']);
Render::addDialog(Dictionary::translateFile('config-module', 'ldapAuth_title'), false, 'ldap-start', $data);
}
@@ -77,7 +82,8 @@ class LdapAuth_CheckConnection extends AddModule_Base
'ssl' => Request::post('ssl'),
'fixnumeric' => Request::post('fixnumeric'),
'certificate' => Request::post('certificate', ''),
- 'taskid' => $this->scanTask['id']
+ 'taskid' => $this->scanTask['id'],
+ 'mapping' => ConfigModuleBaseLdap::getMapping(Request::post('mapping', false, 'array')),
);
$data['prev'] = 'LdapAuth_Start';
$data['next'] = 'LdapAuth_CheckCredentials';
@@ -123,6 +129,7 @@ class LdapAuth_CheckCredentials extends AddModule_Base
'binddn' => $binddn,
'bindpw' => $bindpw,
'plainldap' => true,
+ 'mapping' => Request::post('mapping', false, 'array'),
));
if (!isset($ldapSearch['id'])) {
AddModule_Base::setStep('LdapAuth_Start'); // Continues with LdapAuth_Start for render()
@@ -131,8 +138,6 @@ class LdapAuth_CheckCredentials extends AddModule_Base
$this->taskIds = array(
'tm-search' => $ldapSearch['id']
);
- if (isset($selfSearch['id']))
- $this->taskIds['self-search'] = $selfSearch['id'];
}
protected function renderInternal()
@@ -149,8 +154,9 @@ class LdapAuth_CheckCredentials extends AddModule_Base
'fixnumeric' => Request::post('fixnumeric'),
'fingerprint' => Request::post('fingerprint'),
'certificate' => Request::post('certificate', ''),
+ 'mapping' => ConfigModuleBaseLdap::getMapping(Request::post('mapping', false, 'array')),
'prev' => 'LdapAuth_Start',
- 'next' => 'LdapAuth_HomeDir'
+ 'next' => 'LdapAuth_HomeDir',
))
);
}
@@ -184,14 +190,14 @@ class LdapAuth_HomeDir extends AddModule_Base
'binddn' => Request::post('binddn'),
'bindpw' => Request::post('bindpw'),
'home' => Request::post('home'),
- 'homeattr' => Request::post('homeattr'),
'ssl' => Request::post('ssl') === 'on',
'fixnumeric' => Request::post('fixnumeric'),
'fingerprint' => Request::post('fingerprint'),
'certificate' => Request::post('certificate', ''),
'originalbinddn' => Request::post('originalbinddn'),
+ 'mapping' => ConfigModuleBaseLdap::getMapping(Request::post('mapping', false, 'array')),
'prev' => 'LdapAuth_Start',
- 'next' => 'LdapAuth_Finish'
+ 'next' => 'LdapAuth_Finish',
);
if ($this->edit !== false) {
foreach (self::getAttributes() as $key) {
@@ -249,6 +255,7 @@ class LdapAuth_Finish extends AddModule_Base
$module->setData('home', Request::post('home'));
$module->setData('certificate', Request::post('certificate'));
$module->setData('ssl', $ssl);
+ $module->setData('mapping', Request::post('mapping', false, 'array'));
$module->setData('fixnumeric', Request::post('fixnumeric', '', 'string'));
foreach (LdapAuth_HomeDir::getAttributes() as $key) {
$value = Request::post($key);
diff --git a/modules-available/sysconfig/inc/configmodule.inc.php b/modules-available/sysconfig/inc/configmodule.inc.php
index ca40094a..54d06afe 100644
--- a/modules-available/sysconfig/inc/configmodule.inc.php
+++ b/modules-available/sysconfig/inc/configmodule.inc.php
@@ -16,6 +16,9 @@ abstract class ConfigModule
private $moduleTitle = false;
private $moduleStatus = false;
private $currentVersion = 0;
+ /**
+ * @var false|array Data of module, false if not initialized
+ */
protected $moduleData = false;
/**
@@ -86,7 +89,7 @@ abstract class ConfigModule
* Get fresh instance of ConfigModule subclass for given module type.
*
* @param string $moduleType name of module type
- * @return \ConfigModule module instance
+ * @return false|\ConfigModule module instance
*/
public static function getInstance($moduleType)
{
@@ -117,7 +120,7 @@ abstract class ConfigModule
* Get module instance from id.
*
* @param int $moduleId module id to get
- * @return ConfigModule The requested module from DB, or false on error
+ * @return false|\ConfigModule The requested module from DB, or false on error
*/
public static function get($moduleId)
{
diff --git a/modules-available/sysconfig/inc/configmodulebaseldap.inc.php b/modules-available/sysconfig/inc/configmodulebaseldap.inc.php
index 686bcbc0..d8a41a8b 100644
--- a/modules-available/sysconfig/inc/configmodulebaseldap.inc.php
+++ b/modules-available/sysconfig/inc/configmodulebaseldap.inc.php
@@ -8,7 +8,28 @@ abstract class ConfigModuleBaseLdap extends ConfigModule
private static $REQUIRED_FIELDS = array('server', 'searchbase');
private static $OPTIONAL_FIELDS = array('binddn', 'bindpw', 'home', 'ssl', 'fixnumeric', 'fingerprint', 'certificate', 'homeattr',
'shareRemapMode', 'shareRemapCreate', 'shareDocuments', 'shareDownloads', 'shareDesktop', 'shareMedia',
- 'shareOther', 'shareHomeDrive', 'shareDomain', 'credentialPassthrough');
+ 'shareOther', 'shareHomeDrive', 'shareDomain', 'credentialPassthrough', 'mapping');
+
+ public static function getMapping($config = false, &$empty = true)
+ {
+ $list = array(
+ ['name' => 'uid', 'field' => 'uid', 'ad' => 'sAMAccountName'],
+ ['name' => 'uidnumber', 'field' => 'uidnumber', 'ad' => false],
+ ['name' => 'uncHomePath', 'field' => 'homemount', 'ad' => 'homeDirectory'],
+ ['name' => 'homeDirectory', 'field' => 'localhome', 'ad' => false],
+ ['name' => 'posixAccount', 'field' => 'posixAccount', 'ad' => 'user'],
+ //['name' => 'shadowAccount', 'field' => 'shadowAccount'],
+ );
+ if (is_array($config)) {
+ foreach ($list as &$item) {
+ if (!empty($config[$item['field']])) {
+ $item['value'] = $config[$item['field']];
+ $empty = false;
+ }
+ }
+ }
+ return $list;
+ }
protected function generateInternal($tgz, $parent)
{
diff --git a/modules-available/sysconfig/lang/de/messages.json b/modules-available/sysconfig/lang/de/messages.json
index 0a1f6de3..5bceb2f0 100644
--- a/modules-available/sysconfig/lang/de/messages.json
+++ b/modules-available/sysconfig/lang/de/messages.json
@@ -2,7 +2,7 @@
"config-activated": "Konfiguration {{0}} wurde aktiviert",
"config-deleted": "Konfiguration {{0}} wurde gel\u00f6scht",
"config-invalid": "Konfiguration mit ID {{0}} existiert nicht",
- "could-not-determine-binddn": "Konnte Bind-DN nicht ermitteln",
+ "could-not-determine-binddn": "Konnte Bind-DN nicht ermitteln ({{0}})",
"invalid-action": "Ung\u00fcltige Aktion: {{0}}",
"missing-file": "Es wurde keine Datei ausgew\u00e4hlt!",
"missing-title": "Kein Titel eingegeben",
diff --git a/modules-available/sysconfig/lang/de/template-tags.json b/modules-available/sysconfig/lang/de/template-tags.json
index 900b67a8..9ad29e90 100644
--- a/modules-available/sysconfig/lang/de/template-tags.json
+++ b/modules-available/sysconfig/lang/de/template-tags.json
@@ -33,6 +33,9 @@
"lang_customCertificate": "Zur Validierung zus\u00e4tzlich erforderliche (Intermediate-)Zertifikate",
"lang_customModuleInfo1": "\u00dcber ein benutzerdefiniertes Modul ist es m\u00f6glich, beliebige Dateien zum Linux-Grundsystem, das auf den Clients gebootet wird, hinzuzuf\u00fcgen. Dazu kann ein Archiv mit einer Dateisystemstruktur hochgeladen werden, die in dieser Form 1:1 in das gebootete Linux extrahiert wird.",
"lang_customModuleInfo2": "Beispiel: Enth\u00e4lt das hochgeladene Archiv eine Datei etc\/beispiel.conf, so wird auf einem gebooteten Client diese Datei als \/etc\/beispiel.conf zu finden sein.",
+ "lang_customizeAttrDesc": "Hier k\u00f6nnen Sie die Standardwerte f\u00fcr bestimmte Attribute und deren Werte \u00fcberschreiben, wenn ihr LDAP-Schema nicht dem \u00fcblichen Unix-Schema entspricht.",
+ "lang_customizeAttrDescAd": "Hier k\u00f6nnen Sie die Standardwerte f\u00fcr bestimmte Attribute und deren Werte \u00fcberschreiben, wenn Sie z.B. den cn oder userPrincipalName f\u00fcr den Login verwenden wollen, anstelle des sAMAccountNames, oder der Pfad zum Netzlaufwerk des Benutzers nicht im Attribut homeDirectory zu finden ist.",
+ "lang_customizeAttributes": "Attribute anpassen",
"lang_deleteLong": "Modul oder Konfiguration l\u00f6schen.",
"lang_determiningHomeDirectory": "Versuche Attribut f\u00fcr das Home-Verzeichnis zu ermitteln...",
"lang_dnLookup": "Ermitteln der Bind-DN",
@@ -41,7 +44,7 @@
"lang_driveLetterNote": "WICHTIG: Bitte w\u00e4hlen Sie einen Laufwerksbuchstaben, der in den eingesetzten VMs verf\u00fcgbar ist, da ansonsten auf einen anderen Buchstaben ausgewichen werden muss.",
"lang_editLong": "Modul oder Konfiguration bearbeiten.",
"lang_editingLocationInfo": "Sie setzen die Konfiguration eines bestimmten Raums\/Orts, nicht die globale Konfiguration",
- "lang_fixNumeric": "Nummerischen Account-Namen muss ein 's' vorangestellt werden",
+ "lang_fixNumeric": "Numerischen Account-Namen muss ein 's' vorangestellt werden",
"lang_fixNumericDescription": "Wenn Sie diese Option aktivieren, m\u00fcssen Benutzer, deren Account-Name nur aus Ziffern besteht, diesem ein 's' voranstellen beim Login. Diese Option ist beim alten Login-Manager (KDM) zwingend erforderlich, da sonst der Loginvorgang fehlschl\u00e4gt. Mit dem neuen lightdm-basierten Login-Screen lassen sich numerische Account-Namen jedoch direkt verwenden. Wenn Sie an Ihrer Einrichtung keine numerischen Account-Namen verwenden, hat diese Option keine Auswirkung.",
"lang_folderRedirection": "Folder Redirection",
"lang_generateModule": "Modul erzeugen",
@@ -52,6 +55,7 @@
"lang_helpSystemConfiguration": "\u00dcber eine Systemkonfiguration wird die grundlegende Lokalisierung des bwLehrpool-Systems durchgef\u00fchrt. Dazu geh\u00f6ren Aspekte wie das Authentifizierungsverfahren f\u00fcr Benutzer (z.B. Active Directory, LDAP), Druckerkonfiguration, Home-Verzeichnisse, etc. Eine Systemkonfiguration setzt sich aus einem oder mehreren Konfigurationsmodulen zusammen, welche im angrenzenden Panel verwaltet werden k\u00f6nnen.",
"lang_homeAttr": "Home-Attribut",
"lang_homeAttributeExplanation": "Bitte w\u00e4hlen Sie das Attribut, welches das Home-Verzeichnis der User enth\u00e4lt.",
+ "lang_homeFallback": "Home-Fallback",
"lang_homedirHandling": "(Home-)Verzeichnis Einbindung",
"lang_inheritFromParentLoc": "Von \u00fcbergeordnetem Ort erben",
"lang_ldapStarted": "Der LDAP-Proxy wurde gestartet",
diff --git a/modules-available/sysconfig/lang/en/messages.json b/modules-available/sysconfig/lang/en/messages.json
index 83f47903..6e50b80c 100644
--- a/modules-available/sysconfig/lang/en/messages.json
+++ b/modules-available/sysconfig/lang/en/messages.json
@@ -2,7 +2,7 @@
"config-activated": "Configuration {{0}} has been activated",
"config-deleted": "Deleted configuration {{0}}",
"config-invalid": "Configuration with id {{0}} does not exist",
- "could-not-determine-binddn": "Could not determine bind dn",
+ "could-not-determine-binddn": "Could not determine bind dn ({{0}})",
"invalid-action": "Invalid action: {{0}}",
"missing-file": "There was no file selected!",
"missing-title": "No title given",
diff --git a/modules-available/sysconfig/lang/en/template-tags.json b/modules-available/sysconfig/lang/en/template-tags.json
index 6a482772..e2cfd114 100644
--- a/modules-available/sysconfig/lang/en/template-tags.json
+++ b/modules-available/sysconfig/lang/en/template-tags.json
@@ -33,6 +33,9 @@
"lang_customCertificate": "Additional (intermediate) certificates required for certificate validation",
"lang_customModuleInfo1": "About a custom module, it is possible to add arbitrary files to a Linux system that is booted clients. For this purpose, an archive can be uploaded using a file system structure that is extracted in this form 1:1 in the booted Linux.",
"lang_customModuleInfo2": "Example: If the uploaded archive is the file etc\/example.conf, this file will be located as \/etc\/example.conf to a booted client.",
+ "lang_customizeAttrDesc": "Here you can override attribute names and values if your LDAP scheme doesn't adhere to the usual Unix scheme.",
+ "lang_customizeAttrDescAd": "Here you can override attribute names and values, for example if you don't want to use the sAMAccountName for identification, but something like cn oder userPrincipalName, or maybe the user's network drive isn't to be found in the homeDirectory attribute.",
+ "lang_customizeAttributes": "Customize attributes",
"lang_deleteLong": "Delete module or configuration.",
"lang_determiningHomeDirectory": "Trying to determine home directory attribute...",
"lang_dnLookup": "Looking up bind dn",
@@ -52,6 +55,7 @@
"lang_helpSystemConfiguration": "The fundamental localization of the bwLehrpool system is done through a system configuration. These include aspects such as the authentication method for users (eg Active Directory, LDAP), printer configuration, home directories, etc. A system configuration is composed of one or more configuration modules, which can be managed in the panel next to this one.",
"lang_homeAttr": "Home attribute",
"lang_homeAttributeExplanation": "Please select the attribute which holds the user's home directory.",
+ "lang_homeFallback": "Home fallback",
"lang_homedirHandling": "(Home) directory handling",
"lang_inheritFromParentLoc": "Inherit from parent location",
"lang_ldapStarted": "The LDAP proxy has been launched",
diff --git a/modules-available/sysconfig/templates/ad-selfsearch.html b/modules-available/sysconfig/templates/ad-selfsearch.html
index 6c5bcb8c..6b85b9ed 100644
--- a/modules-available/sysconfig/templates/ad-selfsearch.html
+++ b/modules-available/sysconfig/templates/ad-selfsearch.html
@@ -39,6 +39,9 @@
<input name="ssl" value="on" type="hidden">
<input type="hidden" name="certificate" value="{{certificate}}">
{{/ssl}}
+ {{#mapping}}
+ <input type="hidden" name="mapping[{{field}}]" value="{{value}}">
+ {{/mapping}}
<input name="fixnumeric" value="{{fixnumeric}}" type="hidden">
<button type="submit" class="btn btn-primary">&laquo; {{lang_back}}</button>
</form>
@@ -60,6 +63,9 @@
<input name="ssl" value="on" type="hidden">
<input type="hidden" name="certificate" value="{{certificate}}">
{{/ssl}}
+ {{#mapping}}
+ <input type="hidden" name="mapping[{{field}}]" value="{{value}}">
+ {{/mapping}}
<input name="fixnumeric" value="{{fixnumeric}}" type="hidden">
<input name="fingerprint" value="{{fingerprint}}" type="hidden">
<button id="nextbutton" type="submit" class="btn btn-primary" style="display:none">{{lang_skip}} &raquo;</button>
diff --git a/modules-available/sysconfig/templates/ad-start.html b/modules-available/sysconfig/templates/ad-start.html
index 4a183dfc..7f211343 100644
--- a/modules-available/sysconfig/templates/ad-start.html
+++ b/modules-available/sysconfig/templates/ad-start.html
@@ -20,45 +20,55 @@
<input type="hidden" name="token" value="{{token}}">
<input type="hidden" name="edit" value="{{edit}}">
<div class="input-group">
- <span style="min-width:150px;" class="input-group-addon slx-ga">{{lang_moduleTitle}}</span>
+ <span class="input-group-addon slx-ga2">{{lang_moduleTitle}}</span>
<input tabindex="1" name="title" value="{{title}}" type="text" class="form-control" autofocus>
</div>
<div class="input-group">
- <span style="min-width:150px;" class="input-group-addon slx-ga">Server *</span>
+ <span class="input-group-addon slx-ga2">Server *</span>
<input tabindex="2" name="server" value="{{server}}" type="text" class="form-control" placeholder="dc0.institution.example.com">
</div>
<div class="input-group">
- <span style="min-width:150px;" class="input-group-addon slx-ga">{{lang_bindDN}} *</span>
+ <span class="input-group-addon slx-ga2">{{lang_bindDN}} *</span>
<input tabindex="3" name="binddn" value="{{binddn}}" type="text" class="form-control" placeholder="domain\bwlp *ODER* CN=bwlp,OU=Benutzer,DC=domain,DC=hs-beispiel,DC=de">
</div>
<div class="input-group">
- <span style="min-width:150px;" class="input-group-addon slx-ga">{{lang_password}} *</span>
+ <span class="input-group-addon slx-ga2">{{lang_password}} *</span>
<input tabindex="4" name="bindpw" value="{{bindpw}}" type="{{password_type}}" class="form-control" placeholder="{{lang_password}}">
</div>
<div class="input-group">
- <span style="min-width:150px;" class="input-group-addon slx-ga">{{lang_searchBase}}</span>
+ <span class="input-group-addon slx-ga2">{{lang_searchBase}}</span>
<input tabindex="5" name="searchbase" value="{{searchbase}}" type="text" class="form-control" placeholder="dc=windows,dc=hs-beispiel,dc=de">
</div>
- <br>
<div class="input-group">
- <span style="min-width:150px;" class="input-group-addon slx-ga">Home</span>
+ <span class="input-group-addon slx-ga2">Home</span>
<input tabindex="6" name="home" value="{{home}}" type="text" class="form-control" placeholder="\\server.example.com\%s">
<span class="input-group-btn">
<a class="btn btn-default" data-toggle="modal" data-target="#help-home"><span class="glyphicon glyphicon-question-sign"></span></a>
</span>
</div>
- <div class="input-group">
- <span style="min-width:150px;" class="input-group-addon slx-ga">{{lang_homeAttr}}</span>
- <input tabindex="6" name="homeattr" value="{{homeattr}}" type="text" class="form-control" placeholder="homeDirectory">
- <span class="input-group-btn">
- <a class="btn btn-default" data-toggle="modal" data-target="#help-homeattr"><span class="glyphicon glyphicon-question-sign"></span></a>
- </span>
+ <br>
+ <div class="{{#map_empty}}collapse{{/map_empty}}" id="attrbox">
+ <p>{{lang_customizeAttrDescAd}}</p>
+ {{#mapping}}
+ {{#ad}}
+ <div class="input-group">
+ <span class="input-group-addon slx-ga2">{{name}}</span>
+ <input name="mapping[{{field}}]" value="{{value}}" type="text" class="form-control" placeholder="{{ad}}">
+ </div>
+ {{/ad}}
+ {{/mapping}}
</div>
+ {{#map_empty}}
+ <div class="btn btn-default center-block" onclick="$('#attrbox').show();$(this).hide()">
+ {{lang_customizeAttributes}}
+ <span class="glyphicon glyphicon-menu-down"></span>
+ </div>
+ {{/map_empty}}
<br>
<div>
<div class="checkbox">
- <input type="checkbox" name="fixnumeric" {{#fixnumeric}}checked{{/fixnumeric}}>
- <label><b>{{lang_fixNumeric}}</b></label>
+ <input id="num-cb" type="checkbox" name="fixnumeric" {{#fixnumeric}}checked{{/fixnumeric}}>
+ <label for="num-cb"><b>{{lang_fixNumeric}}</b></label>
</div>
<div>
<i>{{lang_fixNumericDescription}}</i>
@@ -67,8 +77,8 @@
<br>
<div>
<div class="checkbox">
- <input type="checkbox" name="ssl" onchange="$('#cert-box').css('display', this.checked ? '' : 'none')" {{#ssl}}checked{{/ssl}}>
- <label><b>{{lang_ssl}}</b></label>
+ <input if="ssl-cb" type="checkbox" name="ssl" onchange="$('#cert-box').css('display', this.checked ? '' : 'none')" {{#ssl}}checked{{/ssl}}>
+ <label for="ssl-cb"><b>{{lang_ssl}}</b></label>
</div>
<div>
<i>{{lang_sslDescription}}</i>
diff --git a/modules-available/sysconfig/templates/ad_ldap-checkconnection.html b/modules-available/sysconfig/templates/ad_ldap-checkconnection.html
index 35c8f1ee..630da398 100644
--- a/modules-available/sysconfig/templates/ad_ldap-checkconnection.html
+++ b/modules-available/sysconfig/templates/ad_ldap-checkconnection.html
@@ -26,6 +26,10 @@
<input name="ssl" value="on" type="hidden">
<input type="hidden" name="certificate" value="{{certificate}}">
{{/ssl}}
+ {{#mapping}}
+ <input type="hidden" name="mapping[{{field}}]" value="{{value}}">
+ {{/mapping}}
+
<input name="fixnumeric" value="{{fixnumeric}}" type="hidden">
<button type="submit" class="btn btn-primary">&laquo; {{lang_back}}</button>
</form>
@@ -47,6 +51,9 @@
<input id="fingerprint" name="fingerprint" value="" type="hidden">
<input id="certificate" type="hidden" name="certificate" value="{{certificate}}">
{{/ssl}}
+ {{#mapping}}
+ <input type="hidden" name="mapping[{{field}}]" value="{{value}}">
+ {{/mapping}}
<input name="fixnumeric" value="{{fixnumeric}}" type="hidden">
<input name="originalbinddn" value="{{binddn}}" type="hidden">
<button id="nextbutton" type="submit" class="btn btn-primary" style="display:none">{{lang_next}} &raquo;</button>
diff --git a/modules-available/sysconfig/templates/ad_ldap-checkcredentials.html b/modules-available/sysconfig/templates/ad_ldap-checkcredentials.html
index bf151da3..4f822a9b 100644
--- a/modules-available/sysconfig/templates/ad_ldap-checkcredentials.html
+++ b/modules-available/sysconfig/templates/ad_ldap-checkcredentials.html
@@ -22,6 +22,9 @@
<input name="ssl" value="on" type="hidden">
<input type="hidden" name="certificate" value="{{certificate}}">
{{/ssl}}
+ {{#mapping}}
+ <input type="hidden" name="mapping[{{field}}]" value="{{value}}">
+ {{/mapping}}
<input name="fixnumeric" value="{{fixnumeric}}" type="hidden">
<button type="submit" class="btn btn-primary">&laquo; {{lang_back}}</button>
</form>
@@ -42,6 +45,9 @@
<input name="ssl" value="on" type="hidden">
<input type="hidden" name="certificate" value="{{certificate}}">
{{/ssl}}
+ {{#mapping}}
+ <input type="hidden" name="mapping[{{field}}]" value="{{value}}">
+ {{/mapping}}
<input name="fixnumeric" value="{{fixnumeric}}" type="hidden">
<input name="fingerprint" value="{{fingerprint}}" type="hidden">
<input name="originalbinddn" value="{{binddn}}" type="hidden">
diff --git a/modules-available/sysconfig/templates/ad_ldap-homedir.html b/modules-available/sysconfig/templates/ad_ldap-homedir.html
index 10a43030..ad543594 100644
--- a/modules-available/sysconfig/templates/ad_ldap-homedir.html
+++ b/modules-available/sysconfig/templates/ad_ldap-homedir.html
@@ -14,6 +14,9 @@
<input name="ssl" value="on" type="hidden">
<input type="hidden" name="certificate" value="{{certificate}}">
{{/ssl}}
+ {{#mapping}}
+ <input type="hidden" name="mapping[{{field}}]" value="{{value}}">
+ {{/mapping}}
<input name="fixnumeric" value="{{fixnumeric}}" type="hidden">
<input name="fingerprint" value="{{fingerprint}}" type="hidden">
diff --git a/modules-available/sysconfig/templates/ldap-start.html b/modules-available/sysconfig/templates/ldap-start.html
index a457ecd3..940316b9 100644
--- a/modules-available/sysconfig/templates/ldap-start.html
+++ b/modules-available/sysconfig/templates/ldap-start.html
@@ -10,32 +10,32 @@
<input type="hidden" name="token" value="{{token}}">
<input type="hidden" name="edit" value="{{edit}}">
<div class="input-group">
- <span style="min-width:150px;" class="input-group-addon slx-ga">{{lang_moduleTitle}}</span>
+ <span class="input-group-addon slx-ga2">{{lang_moduleTitle}}</span>
<input tabindex="1" name="title" value="{{title}}" type="text" class="form-control">
</div>
<div class="input-group">
- <span style="min-width:150px;" class="input-group-addon slx-ga">Server *</span>
+ <span class="input-group-addon slx-ga2">Server *</span>
<input tabindex="2" name="server" value="{{server}}" type="text" class="form-control" placeholder="dc0.institution.example.com">
<!--span class="input-group-btn">
<a class="btn btn-default"><span class="glyphicon glyphicon-question-sign"></span></a>
</span-->
</div>
<div class="input-group">
- <span style="min-width:150px;" class="input-group-addon slx-ga">{{lang_bindDN}}</span>
+ <span class="input-group-addon slx-ga2">{{lang_bindDN}}</span>
<input tabindex="3" name="binddn" value="{{binddn}}" type="text" class="form-control" placeholder="CN=bwlp,OU=Benutzer,DC=domain,DC=hs-beispiel,DC=de">
<!--span class="input-group-btn">
<a class="btn btn-default"><span class="glyphicon glyphicon-question-sign"></span></a>
</span-->
</div>
<div class="input-group">
- <span style="min-width:150px;" class="input-group-addon slx-ga">{{lang_password}}</span>
+ <span class="input-group-addon slx-ga2">{{lang_password}}</span>
<input tabindex="4" name="bindpw" value="{{bindpw}}" type="{{password_type}}" class="form-control" placeholder="{{lang_password}}">
<!--span class="input-group-btn">
<a class="btn btn-default"><span class="glyphicon glyphicon-question-sign"></span></a>
</span-->
</div>
- <div style="min-width:150px;" class="input-group">
- <span style="min-width:150px;" class="input-group-addon slx-ga">{{lang_searchBase}} *</span>
+ <div class="input-group">
+ <span class="input-group-addon slx-ga2">{{lang_searchBase}} *</span>
<input tabindex="5" name="searchbase" value="{{searchbase}}" type="text" class="form-control" placeholder="ou=users,dc=hochschule,dc=de">
<!--span class="input-group-btn">
<a class="btn btn-default"><span class="glyphicon glyphicon-question-sign"></span></a>
@@ -43,17 +43,33 @@
</div>
<br>
<div class="input-group">
- <span style="min-width:150px;" class="input-group-addon slx-ga">Home</span>
+ <span class="input-group-addon slx-ga2">{{lang_homeFallback}}</span>
<input tabindex="6" name="home" value="{{home}}" type="text" class="form-control" placeholder="\\server.example.com\%s">
<span class="input-group-btn">
<a class="btn btn-default" data-toggle="modal" data-target="#help-home"><span class="glyphicon glyphicon-question-sign"></span></a>
</span>
</div>
<br>
+ <div class="{{#map_empty}}collapse{{/map_empty}}" id="attrbox">
+ <p>{{lang_customizeAttrDesc}}</p>
+ {{#mapping}}
+ <div class="input-group">
+ <span class="input-group-addon slx-ga2">{{name}}</span>
+ <input name="mapping[{{field}}]" value="{{value}}" type="text" class="form-control" placeholder="{{name}}">
+ </div>
+ {{/mapping}}
+ </div>
+ {{#map_empty}}
+ <div class="btn btn-default center-block" onclick="$('#attrbox').show();$(this).hide()">
+ {{lang_customizeAttributes}}
+ <span class="glyphicon glyphicon-menu-down"></span>
+ </div>
+ {{/map_empty}}
+ <br>
<div>
<div class="checkbox">
- <input type="checkbox" name="fixnumeric" {{#fixnumeric}}checked{{/fixnumeric}}>
- <label><b>{{lang_fixNumeric}}</b></label>
+ <input id="num-cb" type="checkbox" name="fixnumeric" {{#fixnumeric}}checked{{/fixnumeric}}>
+ <label for="num-cb"><b>{{lang_fixNumeric}}</b></label>
</div>
<div>
<i>{{lang_fixNumericDescription}}</i>
@@ -62,8 +78,8 @@
<br>
<div>
<div class="checkbox">
- <input type="checkbox" name="ssl" onchange="$('#cert-box').css('display', this.checked ? '' : 'none')" {{#ssl}}checked{{/ssl}}>
- <label><b>{{lang_ssl}}</b></label>
+ <input id="ssl-cb" type="checkbox" name="ssl" onchange="$('#cert-box').css('display', this.checked ? '' : 'none')" {{#ssl}}checked{{/ssl}}>
+ <label for="ssl-cb"><b>{{lang_ssl}}</b></label>
</div>
<div>
<i>{{lang_sslDescription}}</i>
diff --git a/modules-available/systemstatus/lang/de/template-tags.json b/modules-available/systemstatus/lang/de/template-tags.json
index 94488d89..fcf836ec 100644
--- a/modules-available/systemstatus/lang/de/template-tags.json
+++ b/modules-available/systemstatus/lang/de/template-tags.json
@@ -1,4 +1,5 @@
{
+ "lang_OK": "OK",
"lang_addressConfiguration": "Adresskonfiguration",
"lang_areYouSureNoUndo": "Sind Sie sicher? Diese Aktion kann nicht r\u00fcckg\u00e4ngig gemacht werden.",
"lang_attention": "Achtung!",
@@ -7,10 +8,10 @@
"lang_cpuLoad": "CPU-Last",
"lang_dmsdUnreachable": "dmsd nicht erreichbar",
"lang_downloads": "Downloads",
+ "lang_failure": "Fehler",
"lang_foundStore": "Vorgefunden:",
"lang_free": "Frei",
"lang_goToStoreConf": "Zur VM-Store-Konfiguration wechseln",
- "lang_iAmSure": "Ja, ich bin sicher",
"lang_logicCPUs": "Logische CPUs",
"lang_maintenance": "Maintenance",
"lang_moduleHeading": "System-Status",
@@ -30,6 +31,7 @@
"lang_systemPartition": "Systempartition",
"lang_systemStoreError": "Fehler beim Ermitteln des verf\u00fcgbaren Systemspeichers",
"lang_total": "Gesamt",
+ "lang_unknownState": "Unbekannter Status",
"lang_updatedPackages": "Ausstehende Updates",
"lang_uploads": "Uploads",
"lang_uptimeOS": "OS Uptime",
diff --git a/modules-available/systemstatus/lang/en/template-tags.json b/modules-available/systemstatus/lang/en/template-tags.json
index 3e5a4433..159ae59d 100644
--- a/modules-available/systemstatus/lang/en/template-tags.json
+++ b/modules-available/systemstatus/lang/en/template-tags.json
@@ -1,4 +1,5 @@
{
+ "lang_OK": "OK",
"lang_addressConfiguration": "Address Configuration",
"lang_areYouSureNoUndo": "Are you sure? This cannot be undone!",
"lang_attention": "Attention!",
@@ -7,10 +8,10 @@
"lang_cpuLoad": "CPU Load",
"lang_dmsdUnreachable": "dmsd not reachable",
"lang_downloads": "Downloads",
+ "lang_failure": "Failure",
"lang_foundStore": "Found:",
"lang_free": "Free",
"lang_goToStoreConf": "Go to VM store configuration",
- "lang_iAmSure": "Yes, I am sure",
"lang_logicCPUs": "Logic CPUs",
"lang_maintenance": "Maintenance",
"lang_moduleHeading": "System Status",
@@ -30,6 +31,7 @@
"lang_systemPartition": "System Partition",
"lang_systemStoreError": "Error querying available system storage",
"lang_total": "Total",
+ "lang_unknownState": "Unknown status",
"lang_updatedPackages": "Pending updates",
"lang_uploads": "Uploads",
"lang_uptimeOS": "OS Uptime",
diff --git a/modules-available/systemstatus/page.inc.php b/modules-available/systemstatus/page.inc.php
index 7ee9e5a4..c1c52af0 100644
--- a/modules-available/systemstatus/page.inc.php
+++ b/modules-available/systemstatus/page.inc.php
@@ -228,20 +228,42 @@ class Page_SystemStatus extends Page
protected function ajaxServices()
{
- $data = array();
+ $data = array('services' => array());
+ $tasks = array();
- $taskId = Trigger::ldadp();
- if ($taskId === false)
- return;
- $status = Taskmanager::waitComplete($taskId, 10000);
+ foreach (['dmsd', 'dnbd3-server', 'atftpd'] as $svc) {
+ $tasks[] = array(
+ 'name' => $svc,
+ 'task' => Taskmanager::submit('Systemctl', ['service' => $svc, 'operation' => 'is-active'])
+ );
+ }
+ $tasks[] = array(
+ 'name' => 'LDAP/AD-Proxy',
+ 'task' => Trigger::ldadp()
+ );
+ $deadline = time() + 10;
+ do {
+ $done = true;
+ foreach ($tasks as &$task) {
+ if (!is_string($task['task']) && (Taskmanager::isFailed($task['task']) || Taskmanager::isFinished($task['task'])))
+ continue;
+ $task['task'] = Taskmanager::waitComplete($task['task'], 100);
+ if (!Taskmanager::isFailed($task['task']) && !Taskmanager::isFinished($task['task'])) {
+ $done = false;
+ }
+ }
+ unset($task);
+ } while (!$done && time() < $deadline);
- if (Taskmanager::isFailed($status)) {
- if (isset($status['data']['messages']))
- $data['ldadpError'] = $status['data']['messages'];
- else
- $data['ldadpError'] = 'Taskmanager error';
+ foreach ($tasks as $task) {
+ $fail = Taskmanager::isFailed($task['task']);
+ $data['services'][] = array(
+ 'name' => $task['name'],
+ 'fail' => $fail,
+ 'data' => isset($task['data']) ? $task['data'] : null,
+ 'unknown' => $task['task'] === false
+ );
}
- // TODO: Dozentenmodul, tftp, ...
echo Render::parse('services', $data);
}
diff --git a/modules-available/systemstatus/templates/services.html b/modules-available/systemstatus/templates/services.html
index 6c4f0b93..29b33687 100644
--- a/modules-available/systemstatus/templates/services.html
+++ b/modules-available/systemstatus/templates/services.html
@@ -1,6 +1,20 @@
-{{#ldadpError}}
-<pre>{{ldadpError}}</pre>
-{{/ldadpError}}
-{{^ldadpError}}
-<div class="alert alert-success">LDAP-AD-Proxy: OK</div>
-{{/ldadpError}}
+{{#services}}
+ {{#unknown}}
+ <div class="alert alert-warning">
+ {{name}}: {{lang_unknownState}}
+ </div>
+ {{/unknown}}
+ {{^unknown}}
+ {{#fail}}
+ <div class="alert alert-danger">
+ {{name}}: <b>{{lang_failure}}</b>
+ {{#data.messages}}<pre>{{data.messages}}</pre>{{/data.messages}}
+ </div>
+ {{/fail}}
+ {{^fail}}
+ <div class="alert alert-success">
+ {{name}}: {{lang_OK}}
+ </div>
+ {{/fail}}
+ {{/unknown}}
+{{/services}}
diff --git a/modules-available/vmstore/page.inc.php b/modules-available/vmstore/page.inc.php
index 6ef04669..3314bfe2 100644
--- a/modules-available/vmstore/page.inc.php
+++ b/modules-available/vmstore/page.inc.php
@@ -38,8 +38,9 @@ class Page_VmStore extends Page
private function setStore()
{
+ $vmstore = array();
foreach (array('storetype', 'nfsaddr', 'cifsaddr', 'cifsuser', 'cifspasswd', 'cifsuserro', 'cifspasswdro') as $key) {
- $vmstore[$key] = trim(Request::post($key, ''));
+ $vmstore[$key] = trim(Request::post($key, '', 'string'));
}
$storetype = $vmstore['storetype'];
if (!in_array($storetype, array('internal', 'nfs', 'cifs'))) {