summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2018-12-12 14:00:35 +0100
committerSimon Rettberg2018-12-12 14:00:35 +0100
commitf14c04f3af8936f07d0efaa292ce0ea92981b1f6 (patch)
treee1275d80fdced4f0b3d652277863150f756785a3
parent[serversetup-bwlp] Simplify permissions (diff)
parent[dozmod] ldapfilters: Fix list (attr/value) (diff)
downloadslx-admin-f14c04f3af8936f07d0efaa292ce0ea92981b1f6.tar.gz
slx-admin-f14c04f3af8936f07d0efaa292ce0ea92981b1f6.tar.xz
slx-admin-f14c04f3af8936f07d0efaa292ce0ea92981b1f6.zip
Merge branch 'master' into ipxe
-rw-r--r--inc/user.inc.php11
-rw-r--r--lang/pt/flag.pngbin1115 -> 0 bytes
-rw-r--r--lang/pt/name.txt1
-rw-r--r--modules-available/adduser/lang/en/template-tags.json4
-rw-r--r--modules-available/baseconfig_bwlp/baseconfig/settings.json6
-rw-r--r--modules-available/dozmod/lang/de/messages.json14
-rw-r--r--modules-available/dozmod/lang/de/module.json3
-rw-r--r--modules-available/dozmod/lang/de/permissions.json6
-rw-r--r--modules-available/dozmod/lang/de/template-tags.json47
-rw-r--r--modules-available/dozmod/lang/en/messages.json12
-rw-r--r--modules-available/dozmod/lang/en/module.json2
-rw-r--r--modules-available/dozmod/lang/en/permissions.json4
-rw-r--r--modules-available/dozmod/lang/en/template-tags.json23
-rw-r--r--modules-available/dozmod/page.inc.php5
-rw-r--r--modules-available/dozmod/pages/ldapfilters.inc.php119
-rw-r--r--modules-available/dozmod/pages/networkshares.inc.php108
-rw-r--r--modules-available/dozmod/pages/runscripts.inc.php133
-rw-r--r--modules-available/dozmod/permissions/permissions.json18
-rw-r--r--modules-available/dozmod/style.css20
-rw-r--r--modules-available/dozmod/templates/ldapfilter-add.html39
-rw-r--r--modules-available/dozmod/templates/ldapfilters.html77
-rw-r--r--modules-available/dozmod/templates/networkshares-edit.html84
-rw-r--r--modules-available/dozmod/templates/networkshares.html113
-rw-r--r--modules-available/dozmod/templates/runscripts-edit.html89
-rw-r--r--modules-available/dozmod/templates/runscripts-list.html89
-rw-r--r--modules-available/exams/lang/de/template-tags.json2
-rw-r--r--modules-available/exams/lang/en/template-tags.json2
-rw-r--r--modules-available/locationinfo/api.inc.php23
-rw-r--r--modules-available/locationinfo/frontend/frontendscript.js2
-rw-r--r--modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php50
-rw-r--r--modules-available/locationinfo/inc/infopanel.inc.php6
-rw-r--r--modules-available/locationinfo/inc/locationinfo.inc.php19
-rw-r--r--modules-available/locationinfo/lang/de/template-tags.json7
-rw-r--r--modules-available/locationinfo/lang/en/template-tags.json7
-rw-r--r--modules-available/locationinfo/page.inc.php18
-rwxr-xr-xmodules-available/locationinfo/templates/frontend-default.html75
-rw-r--r--modules-available/locationinfo/templates/frontend-summary.html230
-rw-r--r--modules-available/locationinfo/templates/page-config-panel-default.html41
-rw-r--r--modules-available/locationinfo/templates/page-config-panel-summary.html57
-rw-r--r--modules-available/locationinfo/templates/page-config-panel-url.html7
-rw-r--r--modules-available/locationinfo/templates/page-locations.html6
-rw-r--r--modules-available/locationinfo/templates/page-panels.html49
-rw-r--r--modules-available/locationinfo/templates/page-servers.html101
-rw-r--r--modules-available/roomplanner/page.inc.php1
-rw-r--r--modules-available/runmode/templates/machine-selector.html1
-rw-r--r--modules-available/statistics/api.inc.php4
-rw-r--r--modules-available/sysconfig/addmodule_adauth.inc.php9
-rw-r--r--modules-available/sysconfig/addmodule_ldapauth.inc.php7
-rw-r--r--modules-available/sysconfig/inc/configmodulebaseldap.inc.php3
-rw-r--r--modules-available/sysconfig/lang/de/template-tags.json2
-rw-r--r--modules-available/sysconfig/templates/ad-selfsearch.html2
-rw-r--r--modules-available/sysconfig/templates/ad-start.html10
-rw-r--r--modules-available/sysconfig/templates/ad_ldap-checkconnection.html2
-rw-r--r--modules-available/sysconfig/templates/ad_ldap-checkcredentials.html2
-rw-r--r--modules-available/sysconfig/templates/ad_ldap-homedir.html1
-rw-r--r--modules-available/sysconfig/templates/ldap-start.html10
-rw-r--r--modules-available/syslog/api.inc.php39
-rw-r--r--modules-available/syslog/lang/de/template-tags.json4
-rw-r--r--modules-available/syslog/lang/en/template-tags.json4
-rw-r--r--modules-available/syslog/page.inc.php2
-rw-r--r--modules-available/syslog/permissions/permissions.json3
-rw-r--r--modules-available/syslog/templates/heading.html39
62 files changed, 1604 insertions, 270 deletions
diff --git a/inc/user.inc.php b/inc/user.inc.php
index 2571c61c..20e8cd3d 100644
--- a/inc/user.inc.php
+++ b/inc/user.inc.php
@@ -34,8 +34,15 @@ class User
if ($permission{0} === '.') {
$permission = substr($permission, 1);
} else {
- $module = Page::getModule();
- $permission = $module ? $module->getIdentifier() . "." . $permission : $permission;
+ if (class_exists('Page')) {
+ $module = Page::getModule();
+ if ($module !== false) {
+ $module = $module->getIdentifier();
+ }
+ } else {
+ $module = strtolower(Request::any('do'));
+ }
+ $permission = $module ? $module . "." . $permission : $permission;
}
return PermissionUtil::userHasPermission(self::$user['userid'], $permission, $locationid);
}
diff --git a/lang/pt/flag.png b/lang/pt/flag.png
deleted file mode 100644
index 78c57dea..00000000
--- a/lang/pt/flag.png
+++ /dev/null
Binary files differ
diff --git a/lang/pt/name.txt b/lang/pt/name.txt
deleted file mode 100644
index 811b10b4..00000000
--- a/lang/pt/name.txt
+++ /dev/null
@@ -1 +0,0 @@
-Português \ No newline at end of file
diff --git a/modules-available/adduser/lang/en/template-tags.json b/modules-available/adduser/lang/en/template-tags.json
index b86f6cfb..d927964e 100644
--- a/modules-available/adduser/lang/en/template-tags.json
+++ b/modules-available/adduser/lang/en/template-tags.json
@@ -15,11 +15,11 @@
"lang_loginTooShort": "Login too short",
"lang_name": "Name",
"lang_passwordTooShort": "Password too short",
- "lang_passwordsDontMatch": "Passwords don't match",
+ "lang_passwordsDontMatch": "Passwords do not match",
"lang_phone": "Phone",
"lang_role": "Role",
"lang_userDeleteConfirm": "Do you want to delete this user?",
"lang_userIdCol": "ID",
"lang_userManagement": "User management",
"lang_userlist": "User list"
-} \ No newline at end of file
+}
diff --git a/modules-available/baseconfig_bwlp/baseconfig/settings.json b/modules-available/baseconfig_bwlp/baseconfig/settings.json
index 0d4db4d4..8145854d 100644
--- a/modules-available/baseconfig_bwlp/baseconfig/settings.json
+++ b/modules-available/baseconfig_bwlp/baseconfig/settings.json
@@ -186,6 +186,12 @@
]
}
},
+ "SLX_PREFERRED_SOUND_OUTPUT": {
+ "catid": "sysconfig",
+ "defaultvalue": "SOUNDCARD",
+ "permissions": "2",
+ "validator": "list:HDMI|SOUNDCARD"
+ },
"SLX_PVS_DEFAULT": {
"catid": "vmchooser",
"defaultvalue": "OFF",
diff --git a/modules-available/dozmod/lang/de/messages.json b/modules-available/dozmod/lang/de/messages.json
index 60c37927..4a4be923 100644
--- a/modules-available/dozmod/lang/de/messages.json
+++ b/modules-available/dozmod/lang/de/messages.json
@@ -3,9 +3,23 @@
"delete-images": "L\u00f6schung: {{0}}",
"dozmod-error": "Fehler bei der Kommunikation mit dem bwLehrpool-Suite server: {{0}}",
"images-pending-delete-exist": "Zur L\u00f6schung markierte VM-Versionen: {{0}}",
+ "ldap-filter-created": "LDAP Filter wurde erfolgreich erstellt",
+ "ldap-filter-deleted": "LDAP Filter wurde erfolgreich gel\u00f6scht",
+ "ldap-filter-id-missing": "Fehlende LDAP Filter ID",
+ "ldap-filter-insert-failed": "LDAP filter konnte der Datenbank nicht hinzugef\u00fcgt werden",
+ "ldap-filter-save-missing-information": "Es fehlen LDAP Filter Informationen",
+ "ldap-filter-saved": "LDAP Filter wurde erfolgreich gespeichert",
+ "ldap-invalid-filter-id": "Ung\u00fcltige LDAP Filter ID",
"mail-config-saved": "Mail-Konfiguration gespeichert",
+ "networkshare-deleted": "Netzlaufwerk gel\u00f6scht",
+ "networkshare-invalid-auth-type": "Ung\u00fcltiger Authentifizierungs-Typ: {{0}}",
+ "networkshare-invalid-shareid": "Nicht-existierender Share: {{0}}",
+ "networkshare-missing-path": "Fehlende Pfadangabe",
+ "networkshare-saved": "Netzlaufwerk gespeichert",
"no-expired-images": "Keine Abgelaufenen VM-Versionen",
"nothing-submitted": "Es wurde nichts \u00fcbermittelt",
+ "runscript-invalid-id": "Ung\u00fcltige Script-ID: {{0}}",
+ "runscript-saved": "Script gespeichert",
"runtimelimits-config-saved": "Einstellungen gespeichert",
"templates-saved": "Templates wurden gespeichert",
"timeout": "Zeit\u00fcberschreitung",
diff --git a/modules-available/dozmod/lang/de/module.json b/modules-available/dozmod/lang/de/module.json
index c9acd4ee..8902852a 100644
--- a/modules-available/dozmod/lang/de/module.json
+++ b/modules-available/dozmod/lang/de/module.json
@@ -3,7 +3,10 @@
"page_title": "Verwalten der bwLehrpool-Suite",
"submenu_actionlog": "Aktions-Log",
"submenu_expiredimages": "Abgelaufene VM-Versionen",
+ "submenu_ldapfilters": "LDAP-Filter",
"submenu_mailconfig": "Email-Konfiguration",
+ "submenu_networkshares": "Netzlaufwerke",
+ "submenu_runscripts": "Startskripte",
"submenu_runtimeconfig": "Limits und Standardwerte",
"submenu_templates": "Textbausteine f\u00fcr E-Mails",
"submenu_users": "Benutzer und Berechtigungen"
diff --git a/modules-available/dozmod/lang/de/permissions.json b/modules-available/dozmod/lang/de/permissions.json
index df913265..a1675148 100644
--- a/modules-available/dozmod/lang/de/permissions.json
+++ b/modules-available/dozmod/lang/de/permissions.json
@@ -1,7 +1,13 @@
{
"actionlog.view": "Aktions-Log einsehen.",
"expiredimages.delete": "Zur L\u00f6schung vorgemerkete Abbilder l\u00f6schen.",
+ "ldapfilters.save": "LDAP Filter speichern.",
+ "ldapfilters.view": "LDAP Filter einsehen.",
"mailconfig.save": "\u00c4nderungen an der SMTP-Konfiguration zum Versenden von Mails speichern.",
+ "networkshares.save": "Netzlaufwerke einsehen.",
+ "networkshares.view": "\u00c4nderungen an den Netzlaufwerken speichern.",
+ "runscripts.save": "Startkripte erstellen\/bearbeiten",
+ "runscripts.view": "Startscripte auflisten",
"runtimeconfig.save": "\u00c4nderungen an der Laufzeit-Konfiguration speichern.",
"templates.reset": "E-Mail Templates zur\u00fccksetzen.",
"templates.save": "E-Mail Templates speichern.",
diff --git a/modules-available/dozmod/lang/de/template-tags.json b/modules-available/dozmod/lang/de/template-tags.json
index 84f6e0de..0a719057 100644
--- a/modules-available/dozmod/lang/de/template-tags.json
+++ b/modules-available/dozmod/lang/de/template-tags.json
@@ -1,8 +1,11 @@
{
"lang_actionTarget": "Aktionsziel",
+ "lang_active": "Aktiv",
+ "lang_addShare": "Netzlaufwerk hinzuf\u00fcgen",
"lang_allowLoginByDefault": "Login standardm\u00e4\u00dfig erlauben",
"lang_allowLoginDescription": "Wenn diese Option aktiviert ist, k\u00f6nnen sich alle Mitarbeiter der Einrichtung \u00fcber die bwLehrpool-Suite anmelden und VMs\/Veranstaltungen verwalten. Wenn Sie diese Option deaktivieren, m\u00fcssen Sie in der Untersektion \"Benutzer und Berechtigungen\" jeden Benutzer nach dem ersten Loginversuch manuell freischalten.",
"lang_asteriskRequired": "Felder mit (*) sind erforderlich",
+ "lang_authMethod": "Authentifizierung",
"lang_blockCount": "Anzahl Bl\u00f6cke",
"lang_bwlehrpoolsuite": "bwLehrpool-Suite",
"lang_canLoginOrganization": "Nutzer dieser Einrichtung k\u00f6nnen sich am Satelliten anmelden",
@@ -21,6 +24,8 @@
"lang_descriptionRuntimeLimits": "Hier k\u00f6nnen Sie verschiedene Limits festlegen, z.B. wie lange eine VM nach dem Hochladen g\u00fcltig ist. Nach Ablauf dieses Zeitraums ist der Verantwortliche gezwungen, eine neue Version der VM hochzuladen. Damit k\u00f6nnen Sie das Ansammeln nicht mehr ben\u00f6tigter VMs eind\u00e4mmen. Weiterhin k\u00f6nnen Sie die maximale Anzahl gleichzeitiger Transfers pro Benutzer einschr\u00e4nken.\r\n\r\nVer\u00e4nderte Einstellungen wirken sich nicht auf bereits bestehende VMs aus.",
"lang_description_delete_images": "Diese Liste zeigt VMs, die entweder abgelaufen sind, oder deren Datei besch\u00e4digt, verschoben oder gel\u00f6scht wurde. Diese Images sind zur Zeit im Lehrpool nicht verf\u00fcgbar, ihre endg\u00fcltige L\u00f6schung muss aber manuell best\u00e4tigt werden, um gr\u00f6\u00dfere Katastrophen durch Softwarefehler, verstellte Systemuhren etc. zu vermeiden.",
"lang_dozmodLogHeading": "bwLehrpool-Suite Aktionslog",
+ "lang_editNetworkshare": "Netzlaufwerk bearbeiten",
+ "lang_editScript": "Startscript bearbeiten",
"lang_email": "EMail",
"lang_emailNotifications": "EMail-Benachrichtigungen aktiviert",
"lang_error": "Fehler",
@@ -30,15 +35,26 @@
"lang_hasNewer": "Neuere Version existiert",
"lang_hash": "Hash",
"lang_heading": "Zu l\u00f6schende VM-Versionen",
+ "lang_hidden": "Versteckt",
"lang_host": "Host",
"lang_image": "VM",
"lang_lastEditor": "Zuletzt bearbeitet von",
"lang_lastLogin": "Letzte Anmeldung",
"lang_latestVersion": "Neuste Version",
+ "lang_ldapFilterAdd": "LDAP-Filter hinzuf\u00fcgen",
+ "lang_ldapFilterAttribute": "Attribut",
+ "lang_ldapFilterDeleteConfirmation": "Wollen Sie diesen LDAP-Filter wirklich l\u00f6schen?",
+ "lang_ldapFilterDescription": "Dies sind die Filter, die ein Benutzer in der bwLehrpool-Suite Veranstaltungen hinzuf\u00fcgen kann.",
+ "lang_ldapFilterEdit": "LDAP-Filter bearbeiten",
+ "lang_ldapFilterName": "Filtername",
+ "lang_ldapFilterValue": "Wert",
+ "lang_ldapfilters": "LDAP-Filter",
+ "lang_ldapfiltersIntro": "Hier k\u00f6nnen Sie Vorgaben f\u00fcr veranstaltungsspezifische LDAP-Filter machen. LDAP-Filter sind einfache Attributsfilter, die clientseitig Anwendung finden. Die hier definierten Filter werden in der bwLehrpool-Suite als Vorschl\u00e4ge aufgelistet und k\u00f6nnen mittels Checkbox aktiviert werden. Alternativ k\u00f6nnen in der bwLehrpool-Suite auch eigene Filter angelegt werden.",
"lang_lecture": "Veranstaltung",
"lang_lecturePermissionAdmin": "Administration",
"lang_lecturePermissionEdit": "Bearbeiten",
"lang_loadDefaults": "Alle Texte auf Auslieferungszustand zur\u00fccksetzen",
+ "lang_loggedInUser": "Angemeldeter Nutzer",
"lang_mailConfig": "SMTP-Konfiguration zum Versenden von Mails",
"lang_mailDescription": "F\u00fcllen Sie die folgenden Felder aus, wenn sie m\u00f6chten, dass Dozierende per Mail benachrichtigt werden, falls eine von ihnen genutzte oder erstellte VM oder Veranstaltung abl\u00e4uft. Um diese Funktion zu deaktivieren, lassen Sie eines der mit (*) gekennzeichneten Felder leer. Wenn das hier angegebene E-Mail-Konto nur zum Versenden von Mails genutzt wird, sollten Sie einen Auto-Responder einrichten f\u00fcr den Fall, dass ein Dozierender auf eine der automatisch generierten Mails antwortet (bzw. eine explizit angegebene Reply-To Adresse ignoriert).",
"lang_mailTemplates": "E-Mail Templates",
@@ -46,30 +62,55 @@
"lang_maxLectureVisibility": "Sp\u00e4testes Enddatum einer Veranstaltung (Tage in der Zukunft)",
"lang_maxLocationsPerLecture": "Max. explizite Orte pro Veranstaltung",
"lang_maxTransfers": "Maximale Zahl gleichzeitiger Up-\/Downloads pro Benutzer",
+ "lang_minimized": "Minimiert",
"lang_miscOptions": "Verschiedene Einstellungen",
"lang_modified": "Modifiziert",
+ "lang_name": "Name",
+ "lang_networkshares": "Netzlaufwerke",
+ "lang_networksharesIntro": "Hier k\u00f6nnen Sie vordefinierte Netzlaufwerke anlegen, die den Nutzern der bwLehrpool-Suite zur Auswahl gestellt werden. Es ist den Nutzern der bwLehrpool-Suite weiterhin m\u00f6glich, komplett eigene Netzwerkfreigaben zu definieren. Die Angaben hier sollen lediglich das Hinzuf\u00fcgen h\u00e4ufig genutzter Laufwerke vereinfachen, bzw. das \u00c4ndern eines Netzwerkpfades vereinfachen, da in diesem Fall nur der Zentrale Eintrag hier angepasst werden muss, und nicht mehr wie zuvor jede Veranstaltung einzeln.",
+ "lang_none": "(Keiner)",
+ "lang_normal": "Normal",
"lang_organization": "Einrichtung",
"lang_organizationListHeader": "Nutzungsrechte f\u00fcr den Satelliten festlegen",
"lang_os": "Betriebssystem",
"lang_owner": "Besitzer",
- "lang_passwordplaceholder": "SMTP Passwort",
+ "lang_passwordplaceholder": "SMTP-Passwort",
+ "lang_path": "Pfad",
"lang_placeholders": "Platzhalter",
"lang_port": "Port",
+ "lang_printer": "Drucker",
"lang_reallyResetTemplates": "Sind Sie sicher, dass Sie alle Texte l\u00f6schen und auf die Standardwerte zur\u00fccksetzen wollen?",
"lang_replaceWithOriginal": "Originaltext in Textbox laden",
"lang_replyTo": "Reply-To Adresse",
+ "lang_runScriptAdd": "Skript hinzuf\u00fcgen",
+ "lang_runScriptDeleteConfirmation": "Skript wirklich l\u00f6schen?",
"lang_runtimeConfig": "Laufzeit-Konfiguration",
"lang_runtimeConfigLimits": "Beschr\u00e4nkungen",
+ "lang_scriptContent": "Skriptinhalt",
+ "lang_scriptExtension": "Dateinamenerweiterung",
+ "lang_scriptExtensionHead": "Erweiterung",
+ "lang_scriptIsGlobal": "Skript ist global, wird in jeder Veranstaltung ausgef\u00fchrt",
+ "lang_scriptIsGlobalHead": "Global",
+ "lang_scriptIsPredefined": "Skript ist ein vordefiniertes Skript, das in der bwLehrpool-Suite zur Auswahl steht",
+ "lang_scriptPassCredentials": "Benutzername\/Passwort an dieses Skript \u00fcbergeben",
+ "lang_scriptPassCredentialsHead": "User\/Pass",
+ "lang_scriptVisibility": "Anzeigemodus",
+ "lang_scriptVisibilityHead": "Anzeige",
+ "lang_scriptsHead": "Startskripte f\u00fcr Virtuelle Umgebungen",
+ "lang_scriptsIntro": "Hier k\u00f6nnen Sie Startskripte definieren, die entweder global bei jedem Start einer Veranstaltung ausgef\u00fchrt werden, oder den Nutzerinnen der bwLehrpool-Suite zur Vorauswahl zur Verf\u00fcgung gestellt werden.",
"lang_senderAddress": "Absenderadresse",
"lang_senderName": "Absender Anzeigename",
+ "lang_shareDeleteConfirm": "Wollen Sie dieses Netzlaufwerk wirklich l\u00f6schen?",
"lang_size": "Gr\u00f6\u00dfe",
"lang_spaceWastedDuplication": "Potentiell durch mehrfach vorkommende Bl\u00f6cke belegter Speicherplatz",
+ "lang_specificUser": "Spezifischer Nutzer",
"lang_ssl": "SSL-Modus",
"lang_sslExplicit": "Explizites SSL (\"STARTTLS\")",
"lang_sslImplicit": "Implizites SSL",
"lang_sslNone": "Kein SSL",
"lang_superUser": "Ist SuperUser (darf alle Veranstaltungen und VMs bearbeiten\/l\u00f6schen)",
"lang_system": "System",
+ "lang_target": "Ziel",
"lang_template": "Template",
"lang_templatePageDescription": "Hier k\u00f6nnen Sie die Textbausteine bearbeiten, aus denen die Mails generiert werden, die der bwLehrpool-Server bez\u00fcglich Virtueller Maschinen und Veranstaltungen versendet. Diese Funktionalit\u00e4t unterst\u00fctzt zur Zeit keine Internationalisierung.",
"lang_test": "Test-Mail senden",
@@ -81,7 +122,7 @@
"lang_userId": "Benutzer-ID",
"lang_userListDescription": "Hier k\u00f6nnen Sie individuelle Nutzer zu \"Super-Usern\" machen. Diese haben in der bwLehrpool-Suite auf alle Veranstaltungen und VMs Vollzugriff, unabh\u00e4ngig von den gesetzten Berechtigungen. Au\u00dferdem k\u00f6nnen Sie hier Benutzer vom Zugriff mittels der bwLehrpool-Suite ausschlie\u00dfen.",
"lang_userListHeader": "Dem Satelliten bekannte Benutzer",
- "lang_usernameplaceholder": "SMTP Benutzername",
+ "lang_usernameplaceholder": "SMTP-Benutzername",
"lang_version": "Version vom",
"lang_when": "Wann"
-} \ No newline at end of file
+}
diff --git a/modules-available/dozmod/lang/en/messages.json b/modules-available/dozmod/lang/en/messages.json
index 6d7ea0ac..d09ff279 100644
--- a/modules-available/dozmod/lang/en/messages.json
+++ b/modules-available/dozmod/lang/en/messages.json
@@ -3,7 +3,19 @@
"delete-images": "Delete: {{0}}",
"dozmod-error": "Error communicating with the bwLehrpool-Suite server: {{0}}",
"images-pending-delete-exist": "VMs marked for deletion: {{0}}",
+ "ldap-filter-created": "LDAP filter was successfully created",
+ "ldap-filter-deleted": "LDAP filter successfully deleted",
+ "ldap-filter-id-missing": "LDAP filter id was missing",
+ "ldap-filter-insert-failed": "LDAP filter could not be inserted into the database",
+ "ldap-filter-save-missing-information": "LDAP filter information is missing",
+ "ldap-filter-saved": "Successfully modified LDAP filter",
+ "ldap-invalid-filter-id": "Invalid LDAP filter id",
"mail-config-saved": "Mail config saved",
+ "networkshare-deleted": "Network share deleted",
+ "networkshare-invalid-auth-type": "Invalid auth type: {{0}}",
+ "networkshare-invalid-shareid": "Invalid share id: {{0}}",
+ "networkshare-missing-path": "Missing network path",
+ "networkshare-saved": "Network share saved",
"no-expired-images": "No expired VMs",
"nothing-submitted": "There was nothing submitted",
"runtimelimits-config-saved": "Configuration saved successfully",
diff --git a/modules-available/dozmod/lang/en/module.json b/modules-available/dozmod/lang/en/module.json
index b7a04762..4e3969ff 100644
--- a/modules-available/dozmod/lang/en/module.json
+++ b/modules-available/dozmod/lang/en/module.json
@@ -3,7 +3,9 @@
"page_title": "Manage the bwLehrpool-Suite",
"submenu_actionlog": "action log",
"submenu_expiredimages": "Expired VM versions",
+ "submenu_ldapfilters": "LDAP filters",
"submenu_mailconfig": "email configuration",
+ "submenu_networkshares": "Network Shares",
"submenu_runtimeconfig": "limits and defaults",
"submenu_templates": "templates",
"submenu_users": "users and permissions"
diff --git a/modules-available/dozmod/lang/en/permissions.json b/modules-available/dozmod/lang/en/permissions.json
index 0827c8d2..d45e5207 100644
--- a/modules-available/dozmod/lang/en/permissions.json
+++ b/modules-available/dozmod/lang/en/permissions.json
@@ -1,7 +1,11 @@
{
"actionlog.view": "View action log.",
"expiredimages.delete": "Delete images marked for deletion.",
+ "ldapfilters.save": "Save LDAP filter.",
+ "ldapfilters.view": "View LDAP filters. ",
"mailconfig.save": "Save SMTP configuration for sending mails.",
+ "networkshares.save": "View network drives.",
+ "networkshares.view": "Save network drives.",
"runtimeconfig.save": "Save limits and defaults of a runtime configuration.",
"templates.reset": "Reset email templates.",
"templates.save": "Save email templates.",
diff --git a/modules-available/dozmod/lang/en/template-tags.json b/modules-available/dozmod/lang/en/template-tags.json
index 3ee95e8c..ddc89284 100644
--- a/modules-available/dozmod/lang/en/template-tags.json
+++ b/modules-available/dozmod/lang/en/template-tags.json
@@ -1,8 +1,11 @@
{
"lang_actionTarget": "Action target",
+ "lang_active": "Active",
+ "lang_addShare": "Add Network Share",
"lang_allowLoginByDefault": "Allow all staff members to login and use the bwLehrpool-Suite",
"lang_allowLoginDescription": "If this option is enabled, all members of the organization marked as staff or employee are allowed to login to this server and manage VMs\/courses. Otherwise, new users need to be individually allowed access after their first login attempt by visiting the sub page \"users and permissions\" in this web interface.",
"lang_asteriskRequired": "Fields marked with (*) are required",
+ "lang_authMethod": "Authentication",
"lang_blockCount": "Block count",
"lang_bwlehrpoolsuite": "bwLehrpool-Suite",
"lang_canLoginOrganization": "Users from this organization can login",
@@ -21,6 +24,7 @@
"lang_descriptionRuntimeLimits": "Here you can define some limits, e.g. how long a newly uploaded VM will be valid. This should make sure that you don't end up with a lot of old, unused VMs over time.\r\n\r\nModified settings won't apply for already existing VMs.",
"lang_description_delete_images": "This is a list of VMs that either expired, or where the disk image is damaged or missing. These VMs are not available in bwLehrpool currently, but you have to manually confirm the deletion of the disk images for safety reasons (clock skew etc.)",
"lang_dozmodLogHeading": "bwLehrpool-Suite action log",
+ "lang_editNetworkshare": "Edit Network Share",
"lang_email": "E-Mail",
"lang_emailNotifications": "E-Mail notifications enabled",
"lang_error": "Error",
@@ -35,10 +39,20 @@
"lang_lastEditor": "Edited by",
"lang_lastLogin": "Last login",
"lang_latestVersion": "latest version",
+ "lang_ldapFilterAdd": "Add LDAP filter",
+ "lang_ldapFilterAttribute": "Attribute",
+ "lang_ldapFilterDeleteConfirmation": "Do you really want to delete this LDAP filter.",
+ "lang_ldapFilterDescription": "These are the LDAP filters that can be applied to lectures in the bwLehrpool-Suite.",
+ "lang_ldapFilterEdit": "Edit LDAP filter",
+ "lang_ldapFilterName": "Filter name",
+ "lang_ldapFilterValue": "Value",
+ "lang_ldapfilters": "LDAP filters",
+ "lang_ldapfiltersIntro": "This is the list of predefined lecture filters, based on LDAP attributes. These filters can be applied client side to only show lectures to specific user groups. These predefined filters are shown in the bwLehrpool-Suite when editing a lecture, and can be activated by a simple check box. Users of the bwLehrpool-Suite can either make use of these predefined filters, or add custom ones if desired.",
"lang_lecture": "Lecture",
"lang_lecturePermissionAdmin": "Administrate",
"lang_lecturePermissionEdit": "Edit",
"lang_loadDefaults": "Reset all templates to their defaults",
+ "lang_loggedInUser": "Logged in User",
"lang_mailConfig": "SMTP configuration for sending mails",
"lang_mailDescription": "Fill in the following fields if you want to notify tutors\/professors\/lecturers about expiring VMs and lectures. If you leave one of the required fields blank, the feature will be disabled.",
"lang_mailTemplates": "E-Mail Templates",
@@ -48,13 +62,19 @@
"lang_maxTransfers": "Max concurrent transfers per user",
"lang_miscOptions": "Misc options",
"lang_modified": "modified",
+ "lang_name": "Name",
+ "lang_networkshares": "Network Shares",
+ "lang_networksharesIntro": "This is the list of predefined network shares. bwLehrpool-Suite users can still add custom network shares to their lectures, however having commonly used network shares as predefined entries should be much more convenient. Another advantage is that changing the path of a network share centrally avoids having to edit a dozen lectures' configuration manually.",
+ "lang_none": "(none)",
"lang_organization": "Organization",
"lang_organizationListHeader": "Set access permissions for organizations",
"lang_os": "Operating System",
"lang_owner": "Owner",
"lang_passwordplaceholder": "SMTP Password",
+ "lang_path": "Path",
"lang_placeholders": "Placeholders",
"lang_port": "Port",
+ "lang_printer": "Printer",
"lang_reallyResetTemplates": "Are you sure you want to reset all texts to their default values?",
"lang_replaceWithOriginal": "load original text into text box",
"lang_replyTo": "Reply-To address",
@@ -62,14 +82,17 @@
"lang_runtimeConfigLimits": "Limitations",
"lang_senderAddress": "Sender address",
"lang_senderName": "Sender's display name",
+ "lang_shareDeleteConfirm": "Do you really want to delete this network share?",
"lang_size": "Size",
"lang_spaceWastedDuplication": "Potentially wasted space by duplicate blocks",
+ "lang_specificUser": "Specific User",
"lang_ssl": "SSL mode",
"lang_sslExplicit": "Explicit SSL (\"STARTTLS\")",
"lang_sslImplicit": "Implicit SSL",
"lang_sslNone": "No SSL",
"lang_superUser": "Is super user (can edit\/delete all lectures and VMs)",
"lang_system": "System",
+ "lang_target": "Target",
"lang_template": "Template",
"lang_templatePageDescription": "Here you can edit text fragments that are used to compose the information and reminder mails sent by the bwLehrpool server, e.g. when a lecture or VM is about to expire.",
"lang_test": "Send test mail",
diff --git a/modules-available/dozmod/page.inc.php b/modules-available/dozmod/page.inc.php
index a8e0da0e..776109cf 100644
--- a/modules-available/dozmod/page.inc.php
+++ b/modules-available/dozmod/page.inc.php
@@ -5,7 +5,7 @@ class Page_DozMod extends Page
/** @var bool true if we have a proper subpage */
private $haveSubPage = false;
- private $validSections = ['expiredimages', 'mailconfig', 'templates', 'runtimeconfig', 'users', 'actionlog'];
+ private $validSections = ['expiredimages', 'mailconfig', 'templates', 'runtimeconfig', 'users', 'actionlog', 'networkshares', 'ldapfilters', 'runscripts'];
private $section;
@@ -60,6 +60,9 @@ class Page_DozMod extends Page
Dictionary::translate('submenu_runtimeconfig', true);
Dictionary::translate('submenu_users', true);
Dictionary::translate('submenu_actionlog', true);
+ Dictionary::translate('submenu_networkshares', true);
+ Dictionary::translate('submenu_ldapfilters', true);
+ Dictionary::translate('submenu_runscripts', true);
*/
/* add sub-menus */
diff --git a/modules-available/dozmod/pages/ldapfilters.inc.php b/modules-available/dozmod/pages/ldapfilters.inc.php
new file mode 100644
index 00000000..d0ae41b8
--- /dev/null
+++ b/modules-available/dozmod/pages/ldapfilters.inc.php
@@ -0,0 +1,119 @@
+<?php
+
+class SubPage
+{
+ private static $show;
+
+ public static function doPreprocess()
+ {
+ self::$show = Request::any('show', false, 'string');
+ $action = Request::post('action');
+
+ if ($action === 'deleteFilter') {
+ User::assertPermission("ldapfilters.save");
+ self::deleteLdapFilter();
+ } else if ($action === 'saveFilter') {
+ User::assertPermission("ldapfilters.save");
+ self::saveLdapFilter();
+ }
+ User::assertPermission("ldapfilters.view");
+ }
+
+ public static function doRender()
+ {
+ if (self::$show === false) {
+ // Get all ldapfilters from the sat db.
+ $ldapfilters = Database::queryAll("SELECT filterid, filtername, filterkey, filtervalue FROM sat.presetlecturefilter
+ WHERE filtertype ='LDAP' ORDER BY filtername ASC");
+
+ $data = array(
+ 'ldapfilters' => $ldapfilters,
+ 'hasEditPermission' => User::hasPermission('ldapfilters.save')
+ );
+
+ Render::addTemplate('ldapfilters', $data);
+ } else if (self::$show === 'edit') {
+ $filterid = Request::get('filterid', false, 'int');
+
+ if ($filterid === false) {
+ Render::addTemplate('ldapfilter-add', array(
+ 'filterid' => 0
+ ));
+ } else {
+ $ldapfilter = Database::queryFirst("SELECT filterid, filtername, filterkey, filtervalue FROM sat.presetlecturefilter
+ WHERE filterid = :id AND filtertype = 'LDAP'", array( 'id' => $filterid));
+ // TODO: Show error if not exists
+
+ Render::addTemplate('ldapfilter-add', $ldapfilter);
+ }
+ }
+ }
+
+ private static function deleteLdapFilter() {
+ User::assertPermission('ldapfilters.save');
+ $filterid = Request::post('filterid', false, 'int');
+ if ($filterid === false) {
+ Message::addError('ldap-filter-id-missing');
+ return;
+ }
+ $res = Database::exec("DELETE FROM sat.presetlecturefilter WHERE filterid = :id AND filtertype = 'LDAP'", array('id' => $filterid));
+ if ($res !== 1) {
+ Message::addWarning('ldap-invalid-filter-id', $filterid);
+ } else {
+ Message::addSuccess('ldap-filter-deleted');
+ }
+ }
+
+ private static function saveLdapFilter() {
+ $filterid = Request::post('filterid', '', 'int');
+ $filtername = Request::post('filtername', false, 'string');
+ $filterattribute = Request::post('attribute', false, 'string');
+ $filtervalue = Request::post('value', false, 'string');
+
+ if ($filtername === false || $filterattribute === false || $filtervalue === false) {
+ Message::addError('ldap-filter-save-missing-information');
+ return;
+ }
+
+ if ($filterid === 0) {
+ // Insert filter in the db.
+ $res = Database::exec("INSERT INTO sat.presetlecturefilter (filtertype, filtername, filterkey, filtervalue)
+ VALUES ('LDAP', :filtername, :attribute, :value)", array(
+ 'filtername' => $filtername,
+ 'attribute' => $filterattribute,
+ 'value' => $filtervalue
+ ));
+
+ if ($res !== 1) {
+ Message::addError('ldap-filter-insert-failed');
+ } else {
+ Message::addSuccess('ldap-filter-created');
+ }
+
+ } else {
+ // Update filter in the db.
+ $res = Database::exec("UPDATE sat.presetlecturefilter SET
+ filtername = :filtername, filterkey = :attribute, filtervalue = :value
+ WHERE filterid = :filterid AND filtertype = 'LDAP'", array(
+ 'filterid' => $filterid,
+ 'filtername' => $filtername,
+ 'attribute' => $filterattribute,
+ 'value' => $filtervalue
+ ));
+
+ if ($res !== 1) {
+ Message::addError('ldap-filter-insert-failed');
+ } else {
+ Message::addSuccess('ldap-filter-saved');
+ }
+
+ }
+ Util::redirect("?do=dozmod&section=ldapfilters");
+ }
+
+ public static function doAjax()
+ {
+
+ }
+
+} \ No newline at end of file
diff --git a/modules-available/dozmod/pages/networkshares.inc.php b/modules-available/dozmod/pages/networkshares.inc.php
new file mode 100644
index 00000000..659321b4
--- /dev/null
+++ b/modules-available/dozmod/pages/networkshares.inc.php
@@ -0,0 +1,108 @@
+<?php
+
+class SubPage
+{
+
+ public static function doPreprocess()
+ {
+ $action = Request::post('action', '', 'string');
+
+ if ($action === 'delete') {
+ User::assertPermission('networkshares.save');
+ $shareid = Request::post('shareid', false, 'int');
+ if ($shareid !== false) {
+ $res = Database::exec('DELETE FROM sat.presetnetworkshare WHERE shareid = :shareid', ['shareid' => $shareid]);
+ if ($res !== false) {
+ Message::addSuccess('networkshare-deleted');
+ }
+ }
+ } else if ($action === 'save') {
+ User::assertPermission('networkshares.save');
+ $shareid = Request::post('shareid', 0, 'int');
+ $sharename = Request::post('sharename', '', 'string');
+ $path = Request::post('path', false, 'string');
+ $target = Request::post('target', '', 'string');
+ $authType = Request::post('auth', '', 'string');
+ $username = Request::post('username', '', 'string');
+ $password = Request::post('password', '', 'string');
+ if (!in_array($authType, ['LOGIN_USER', 'OTHER_USER'], true)) {
+ Message::addError('networkshare-invalid-auth-type', $authType);
+ } elseif (empty($path)) {
+ Message::addError('networkshare-missing-path');
+ } else {
+ $data = json_encode([
+ 'auth' => $authType,
+ 'path' => $path,
+ 'displayname' => $sharename,
+ 'mountpoint' => $target,
+ 'username' => $username,
+ 'password' => $password,
+ ]);
+ if ($shareid !== 0) {
+ Database::exec('UPDATE sat.presetnetworkshare SET sharename = :sharename, sharedata = :data'
+ .' WHERE shareid = :shareid', compact('shareid', 'sharename', 'data'));
+ } else {
+ Database::exec('INSERT INTO sat.presetnetworkshare (sharename, sharedata, active)'
+ .' VALUES (:sharename, :data, 1)', compact('sharename', 'data'));
+ }
+ Message::addSuccess('networkshare-saved');
+ }
+ } else if ($action === 'activate' || $action === 'deactivate') {
+ User::assertPermission('networkshares.save');
+ $shareid = Request::post('shareid', false, 'int');
+ $active = ($action === 'activate' ? 1 : 0);
+ Database::exec('UPDATE sat.presetnetworkshare SET active = :active WHERE shareid = :shareid', compact('active', 'shareid'));
+ }
+ if (Request::isPost()) {
+ Util::redirect('?do=dozmod&section=networkshares');
+ }
+ User::assertPermission('networkshares.view');
+ }
+
+ public static function doRender()
+ {
+ $show = Request::get('show', 'list', 'string');
+ if ($show === 'list') {
+ $res = Database::simpleQuery('SELECT shareid, sharename, sharedata, active
+ FROM sat.presetnetworkshare ORDER BY sharename ASC');
+ $rows = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $dec = json_decode($row['sharedata'], true);
+ if (!is_array($dec)) {
+ $dec = [];
+ }
+ if ($dec['auth'] === 'LOGIN_USER') {
+ $row['loginAsUser'] = true;
+ }
+ $rows[] = $row + $dec;
+ }
+ Render::addTemplate('networkshares', [
+ 'networkshares' => $rows,
+ 'hasEditPermissions' => User::hasPermission('networkshares.save')
+ ]);
+ } else if ($show === 'edit') {
+ $shareid = Request::get('shareid', 0, 'int');
+ if ($shareid === 0) {
+ $data = [];
+ } else {
+ $data = Database::queryFirst('SELECT shareid, sharename, sharedata
+ FROM sat.presetnetworkshare WHERE shareid = :shareid', ['shareid' => $shareid]);
+ if ($data === false) {
+ Message::addError('networkshare-invalid-shareid', $shareid);
+ Util::redirect('?do=dozmod&section=networkshares');
+ }
+ $dec = json_decode($data['sharedata'], true);
+ if (is_array($dec)) {
+ $data += $dec;
+ }
+ if ($data['auth'] === 'LOGIN_USER') {
+ $data['loggedInUser_selected'] = 'selected';
+ } else {
+ $data['specificUser_selected'] = 'selected';
+ }
+ }
+ Render::addTemplate('networkshares-edit', $data);
+ }
+ }
+
+}
diff --git a/modules-available/dozmod/pages/runscripts.inc.php b/modules-available/dozmod/pages/runscripts.inc.php
new file mode 100644
index 00000000..c6566c13
--- /dev/null
+++ b/modules-available/dozmod/pages/runscripts.inc.php
@@ -0,0 +1,133 @@
+<?php
+
+class SubPage
+{
+
+ public static function doPreprocess()
+ {
+ /* execute actions */
+ $action = Request::post('action', false, 'string');
+
+ if ($action === 'save') {
+ User::assertPermission("runscripts.save");
+ self::saveScript();
+ }
+
+ if (Request::isPost()) {
+ Util::redirect('?do=dozmod&section=runscripts');
+ }
+ User::assertPermission('runscripts.view');
+ }
+
+ private static function saveScript()
+ {
+ $id = Request::post('runscriptid', false, 'int');
+ $scriptname = Request::post('scriptname', '', 'string');
+ if ($id === false) {
+ Message::addError('main.parameter-missing', 'runscriptid');
+ return;
+ }
+ $data = [
+ 'scriptname' => $scriptname,
+ 'content' => Request::post('content', '', 'string'),
+ 'visibility' => Request::post('visibility', 1, 'int'),
+ 'extension' => preg_replace('/[^a-z0-9_\-~\!\$\=]/i', '', Request::post('extension', '', 'string')),
+ 'passcreds' => Request::post('passcreds', 0, 'int') !== 0,
+ 'isglobal' => Request::post('isglobal', 0, 'int') !== 0,
+ ];
+ if ($id === 0) {
+ // New entry
+ $ret = Database::exec('INSERT INTO sat.presetrunscript
+ (scriptname, content, extension, visibility, passcreds, isglobal) VALUES
+ (:scriptname, :content, :extension, :visibility, :passcreds, :isglobal)', $data);
+ $id = Database::lastInsertId();
+ } else {
+ // Edit entry
+ $data['id'] = $id;
+ Database::exec('UPDATE sat.presetrunscript SET
+ scriptname = :scriptname, content = :content, extension = :extension, visibility = :visibility,
+ passcreds = :passcreds, isglobal = :isglobal
+ WHERE runscriptid = :id', $data);
+ }
+ $oslist = Request::post('osid', false, 'array');
+ if (is_array($oslist)) {
+ $oslist = array_filter($oslist, 'is_numeric');
+ $query = Database::prepare('INSERT INTO sat.presetrunscript_x_operatingsystem
+ (runscriptid, osid) VALUES (:id, :osid)');
+ foreach ($oslist as $osid) {
+ $query->execute(['id' => $id, 'osid' => $osid]);
+ }
+ $query->closeCursor();
+ Database::exec('DELETE FROM sat.presetrunscript_x_operatingsystem
+ WHERE runscriptid = :id AND osid NOT IN (:oslist)', ['id' => $id, 'oslist' => $oslist]);
+ }
+ Message::addSuccess('runscript-saved');
+ }
+
+ public static function doRender()
+ {
+ $show = Request::get('show', 'list', 'string');
+ if ($show === 'list') {
+ $res = Database::simpleQuery('SELECT runscriptid, scriptname, extension, visibility, passcreds, isglobal
+ FROM sat.presetrunscript
+ ORDER BY scriptname ASC');
+ $rows = [];
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ if ($row['visibility'] == 0) {
+ $row['visibility'] = 'eye-close';
+ } elseif ($row['visibility'] == 1) {
+ $row['visibility'] = 'eye-open';
+ } else {
+ $row['visibility'] = 'arrow-down';
+ }
+ $rows[] = $row;
+ }
+ Render::addTemplate('runscripts-list', ['list' => $rows, 'hasEditPermission' => User::hasPermission('runscripts.save')]);
+ } elseif ($show === 'edit') {
+ // Edit
+ $id = Request::get('runscriptid', false, 'int');
+ if ($id === false) {
+ Message::addError('main.parameter-missing', 'runscriptid');
+ Util::redirect('?do=dozmod&section=runscripts');
+ }
+ if ($id === 0) {
+ $row = [
+ 'runscriptid' => 0,
+ 'visibility_1_checked' => 'checked',
+ 'isglobal_1_checked' => 'checked',
+ ];
+ } else {
+ $row = Database::queryFirst('SELECT runscriptid, scriptname, content, extension, visibility, passcreds, isglobal
+ FROM sat.presetrunscript
+ WHERE runscriptid = :runscriptid', ['runscriptid' => $id]);
+ $row['visibility_' . $row['visibility'] . '_selected'] = 'selected';
+ $row['passcreds_checked'] = $row['passcreds'] ? 'checked' : '';
+ $row['isglobal_' . $row['isglobal'] . '_checked'] = 'checked';
+ if ($row === false) {
+ Message::addError('runscript-invalid-id', $id);
+ Util::redirect('?do=dozmod&section=runscripts');
+ }
+ }
+ // Get OS
+ $row['oslist'] = [];
+ $res = Database::simpleQuery('SELECT o.osid, o.displayname, pxo.osid AS isvalid FROM sat.operatingsystem o
+ LEFT JOIN sat.presetrunscript_x_operatingsystem pxo ON (o.osid = pxo.osid AND pxo.runscriptid = :runscriptid)
+ ORDER BY o.displayname ASC', ['runscriptid' => $id]);
+ while ($osrow = $res->fetch(PDO::FETCH_ASSOC)) {
+ $row['oslist'][] = [
+ 'osid' => $osrow['osid'],
+ 'displayname' => $osrow['displayname'],
+ 'checked' => $osrow['isvalid'] ? 'checked' : '',
+ ];
+ }
+ // Output
+ Render::addTemplate('runscripts-edit', $row);
+ }
+ }
+
+ public static function doAjax()
+ {
+
+ }
+
+}
diff --git a/modules-available/dozmod/permissions/permissions.json b/modules-available/dozmod/permissions/permissions.json
index b9c82107..3f9cd604 100644
--- a/modules-available/dozmod/permissions/permissions.json
+++ b/modules-available/dozmod/permissions/permissions.json
@@ -5,12 +5,30 @@
"actionlog.view": {
"location-aware": false
},
+ "ldapfilters.view": {
+ "location-aware": false
+ },
+ "ldapfilters.save": {
+ "location-aware": false
+ },
"mailconfig.save": {
"location-aware": false
},
+ "networkshares.view": {
+ "location-aware": false
+ },
+ "networkshares.save": {
+ "location-aware": false
+ },
"runtimeconfig.save": {
"location-aware": false
},
+ "runscripts.view": {
+ "location-aware": false
+ },
+ "runscripts.save": {
+ "location-aware": false
+ },
"templates.reset": {
"location-aware": false
},
diff --git a/modules-available/dozmod/style.css b/modules-available/dozmod/style.css
index 23dd121e..4cec715a 100644
--- a/modules-available/dozmod/style.css
+++ b/modules-available/dozmod/style.css
@@ -48,4 +48,24 @@
.table > tbody > tr > td > div {
display: inline-block;
+}
+
+/* number of columns in tree view depending on screen size */
+.column-list {
+ column-gap: 20px;
+ column-count: 2;
+ padding-left: 20px;
+ padding-right: 20px;
+}
+
+@media (min-width: 768px) {
+ .column-list {
+ column-count: 3;
+ }
+}
+
+@media (min-width: 992px) {
+ .column-list {
+ column-count: 4;
+ }
} \ No newline at end of file
diff --git a/modules-available/dozmod/templates/ldapfilter-add.html b/modules-available/dozmod/templates/ldapfilter-add.html
new file mode 100644
index 00000000..f66972d1
--- /dev/null
+++ b/modules-available/dozmod/templates/ldapfilter-add.html
@@ -0,0 +1,39 @@
+<h1>{{lang_ldapfilters}}</h1>
+
+<div class="panel panel-default">
+ <div class="panel-heading">
+ {{lang_ldapFilterEdit}}
+ </div>
+ <div class="panel-body">
+
+ <p>{{lang_ldapFilterDescription}}</p>
+ <form method="post" action="?do=dozmod&section=ldapfilters">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" id="filterid" name="filterid" value="{{filterid}}">
+
+ <div class="input-group">
+ <label class="input-group-addon" for="filtername">{{lang_ldapFilterName}}</label>
+ <input type="text" name="filtername" id ="filtername" class="form-control" placeholder="" value="{{filtername}}" required>
+ </div>
+ <div class="input-group">
+ <label class="input-group-addon" for="attribute">{{lang_ldapFilterAttribute}}</label>
+ <input type="text" name="attribute" id ="attribute" class="form-control" placeholder="" value="{{filterkey}}" required>
+ </div>
+ <div class="input-group">
+ <label class="input-group-addon" for="value">{{lang_ldapFilterValue}}</label>
+ <input type="text" name="value" id ="value" class="form-control" placeholder="" value="{{filtervalue}}" required>
+ </div>
+
+ <div class="text-right" style="margin-top: 20px;">
+ <a type="button" class="btn btn-default" href="?do=dozmod&section=ldapfilters">{{lang_cancel}}</a>
+ <button class="btn btn-primary" type="submit" name="action" value="saveFilter">
+ <span class="glyphicon glyphicon-floppy-disk"></span>
+ {{lang_save}}
+ </button>
+ </div>
+ </form>
+ </div>
+</div>
+
+<script type="text/javascript">
+</script>
diff --git a/modules-available/dozmod/templates/ldapfilters.html b/modules-available/dozmod/templates/ldapfilters.html
new file mode 100644
index 00000000..824ec70b
--- /dev/null
+++ b/modules-available/dozmod/templates/ldapfilters.html
@@ -0,0 +1,77 @@
+<h1>{{lang_ldapfilters}}</h1>
+
+<p>
+ {{lang_ldapfiltersIntro}}
+</p>
+
+<table class="table">
+ <thead>
+ <tr>
+ <th class="">{{lang_ldapFilterName}}</th>
+ <th class="">{{lang_ldapFilterAttribute}}</th>
+ <th class="">{{lang_ldapFilterValue}}</th>
+ {{#hasEditPermission}}
+ <th class="slx-smallcol">{{lang_edit}}</th>
+ <th class="slx-smallcol">{{lang_delete}}</th>
+ {{/hasEditPermission}}
+ </tr>
+ </thead>
+ <tbody>
+ {{#ldapfilters}}
+ <tr>
+ <td>{{filtername}}</td>
+ <td>{{filterkey}}</td>
+ <td>{{filtervalue}}</td>
+ {{#hasEditPermission}}
+ <td class="text-center">
+ <a class="btn btn-xs btn-primary" href="?do=dozmod&section=ldapfilters&show=edit&filterid={{filterid}}">
+ <span class="glyphicon glyphicon-edit"></span>
+ </a>
+ </td>
+ <td class="text-center">
+ <button type="button" class="btn btn-xs btn-danger" data-toggle="modal" data-target="#deleteModal" onclick="setDeleteId('{{filterid}}')">
+ <span class="glyphicon glyphicon-trash"></span>
+ </button>
+ </td>
+ {{/hasEditPermission}}
+ </tr>
+ {{/ldapfilters}}
+ </tbody>
+</table>
+
+{{#hasEditPermission}}
+<div class="text-right">
+ <a class="btn btn-sm btn-success" href="?do=dozmod&section=ldapfilters&show=edit">
+ <span class="glyphicon glyphicon-plus"></span>
+ {{lang_ldapFilterAdd}}
+ </a>
+</div>
+{{/hasEditPermission}}
+
+<form method="post" action="?do=dozmod&section=ldapfilters">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" id="deleteAction" name="filterid" value="">
+ <div class ="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ <h4 class="modal-title" id="myModalLabel">{{lang_delete}}</h4>
+ </div>
+ <div class="modal-body">
+ <p>{{lang_ldapFilterDeleteConfirmation}}</p>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">{{lang_cancel}}</button>
+ <button type="submit" name="action" value="deleteFilter" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> {{lang_delete}}</button>
+ </div>
+ </div>
+ </div>
+ </div>
+</form>
+
+<script type="text/javascript">
+function setDeleteId(id) {
+ $('#deleteAction').val(id)
+}
+</script>
diff --git a/modules-available/dozmod/templates/networkshares-edit.html b/modules-available/dozmod/templates/networkshares-edit.html
new file mode 100644
index 00000000..f0b43837
--- /dev/null
+++ b/modules-available/dozmod/templates/networkshares-edit.html
@@ -0,0 +1,84 @@
+<h1>{{lang_networkshares}}</h1>
+
+<div class="panel panel-default">
+ <div class="panel-heading">
+ {{lang_editNetworkshare}}
+ </div>
+ <div class="panel-body">
+ <form method="post" action="?do=dozmod">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="section" value="networkshares">
+ <input type="hidden" name="shareid" value="{{shareid}}">
+
+ <div class="input-group">
+ <label class="input-group-addon" for="name">{{lang_name}}</label>
+ <input required type="text" name="sharename" id="name" class="form-control" value="{{sharename}}">
+ </div>
+ <div class="input-group">
+ <label class="input-group-addon" for="path">{{lang_path}}</label>
+ <input required type="text" name="path" id="path" class="form-control" value="{{path}}">
+ </div>
+ <div class="input-group">
+ <label class="input-group-addon" for="target">{{lang_target}}</label>
+ <select class="form-control" name="target" id="target">
+ <option value="-">{{lang_none}}</option>
+ <option value="D">D:</option><option value="E">E:</option><option value="F">F:</option>
+ <option value="G">G:</option><option value="H">H:</option><option value="I">I:</option>
+ <option value="J">J:</option><option value="K">K:</option><option value="L">L:</option>
+ <option value="M">M:</option><option value="N">N:</option><option value="O">O:</option>
+ <option value="P">P:</option><option value="Q">Q:</option><option value="R">R:</option>
+ <option value="S">S:</option><option value="T">T:</option><option value="U">U:</option>
+ <option value="V">V:</option><option value="W">W:</option><option value="X">X:</option>
+ <option value="Y">Y:</option><option value="Z">Z:</option>
+ <option value="PRINTER">{{lang_printer}}</option>
+ </select>
+ </div>
+ <div class="input-group">
+ <label class="input-group-addon" for="auth">{{lang_authMethod}}</label>
+ <select class="form-control" name="auth" id="auth">
+ <option value="LOGIN_USER" {{loggedInUser_selected}}>{{lang_loggedInUser}}</option>
+ <option value="OTHER_USER" {{specificUser_selected}}>{{lang_specificUser}}</option>
+ </select>
+ </div>
+ <div class="input-group">
+ <label class="input-group-addon" for="username">{{lang_username}}</label>
+ <input required type="text" name="username" id="username" class="form-control" value="{{username}}" {{#loggedInUser_selected}}disabled{{/loggedInUser_selected}}>
+ </div>
+ <div class="input-group">
+ <label class="input-group-addon" for="password">{{lang_password}}</label>
+ <input required type="{{password_type}}" name="password" id="password" class="form-control" value="{{password}}" {{#loggedInUser_selected}}disabled{{/loggedInUser_selected}}>
+ </div>
+ <div class="text-right" style="margin-top: 20px">
+ <a href="?do=dozmod&amp;section=networkshares" class="btn btn-default">
+ {{lang_cancel}}
+ </a>
+ <button type="submit" class="btn btn-primary" name="action" value="save">
+ <span class="glyphicon glyphicon-floppy-disk"></span>
+ {{lang_save}}
+ </button>
+ </div>
+ </form>
+ </div>
+</div>
+
+<script type="text/javascript">
+
+ document.addEventListener("DOMContentLoaded", function () {
+
+ $('#target').val('{{mountpoint}}');
+
+ $('#auth').change(function () {
+ var username = $('#username');
+ var password = $('#password');
+ if ($(this).val() === 'OTHER_USER') {
+ username.prop('disabled', false);
+ password.prop('disabled', false);
+ } else {
+ username.prop('disabled', true);
+ password.prop('disabled', true);
+ }
+ }).change();
+
+ })
+
+</script> \ No newline at end of file
diff --git a/modules-available/dozmod/templates/networkshares.html b/modules-available/dozmod/templates/networkshares.html
new file mode 100644
index 00000000..aaafa256
--- /dev/null
+++ b/modules-available/dozmod/templates/networkshares.html
@@ -0,0 +1,113 @@
+<h1>{{lang_networkshares}}</h1>
+
+<p>
+ {{lang_networksharesIntro}}
+</p>
+
+<table class="table">
+ <thead>
+ <tr>
+ <th>{{lang_name}}</th>
+ <th>{{lang_path}}</th>
+ <th>{{lang_target}}</th>
+ <th>{{lang_authMethod}}</th>
+ <th>{{lang_username}}</th>
+ {{#hasEditPermissions}}
+ <th class="slx-smallcol">{{lang_active}}</th>
+ <th class="slx-smallcol">{{lang_edit}}</th>
+ <th class="slx-smallcol">{{lang_delete}}</th>
+ {{/hasEditPermissions}}
+ </tr>
+ </thead>
+ <tbody>
+ {{#networkshares}}
+ <tr>
+ <td>{{sharename}}</td>
+ <td>{{path}}</td>
+ <td>{{mountpoint}}</td>
+ <td>
+ {{#loginAsUser}}{{lang_loggedInUser}}{{/loginAsUser}}
+ {{^loginAsUser}}{{lang_specificUser}}{{/loginAsUser}}
+ </td>
+ <td>
+ {{^loginAsUser}}{{username}}{{/loginAsUser}}
+ </td>
+ {{#hasEditPermissions}}
+ <td class="text-nowrap">
+ <form method="post" action="?do=dozmod">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="section" value="networkshares">
+ <input type="hidden" name="shareid" value="{{shareid}}">
+ {{#active}}
+ <span class="glyphicon glyphicon-ok text-success"></span>
+ <button type="submit" name="action" value="deactivate" class="btn btn-xs btn-default">
+ <span class="glyphicon glyphicon-remove"></span>
+ </button>
+ {{/active}}
+ {{^active}}
+ <span class="glyphicon glyphicon-remove text-danger"></span>
+ <button type="submit" name="action" value="activate" class="btn btn-xs btn-default">
+ <span class="glyphicon glyphicon-ok"></span>
+ </button>
+ {{/active}}
+ </form>
+ </td>
+ <td align="center">
+ <a href="?do=dozmod&amp;section=networkshares&amp;show=edit&amp;shareid={{shareid}}" class="btn btn-xs btn-primary">
+ <span class="glyphicon glyphicon-edit"></span>
+ </a>
+ </td>
+ <td align="center">
+ <button type="button" class="btn btn-xs btn-danger" data-toggle="modal" data-target="#deleteModal" onclick="deleteShare('{{shareid}}')">
+ <span class="glyphicon glyphicon-trash"></span>
+ </button>
+ </td>
+ {{/hasEditPermissions}}
+ </tr>
+ {{/networkshares}}
+ </tbody>
+</table>
+{{#hasEditPermissions}}
+<div class="text-right">
+ <a href="?do=dozmod&amp;section=networkshares&amp;show=edit" class="btn btn-success {{allowAddShare}}">
+ <span class="glyphicon glyphicon-plus"></span>
+ {{lang_addShare}}
+ </a>
+</div>
+{{/hasEditPermissions}}
+
+<!-- Modals -->
+<form method="post" action="?do=dozmod">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="section" value="networkshares">
+ <div class ="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ <h4 class="modal-title" id="myModalLabel">{{lang_delete}}</h4>
+ </div>
+ <div class="modal-body">
+ <p>{{lang_shareDeleteConfirm}}</p>
+ </div>
+ <div class="modal-footer">
+ <input type="hidden" id="delete-share-id" name="shareid" value="">
+ <button type="button" class="btn btn-default" data-dismiss="modal">{{lang_cancel}}</button>
+ <button type="submit" name="action" value="delete" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> {{lang_delete}}</button>
+ </div>
+ </div>
+ </div>
+ </div>
+</form>
+
+<script type="text/javascript">
+
+ function deleteShare(shareid) {
+ $("#delete-share-id").val(shareid);
+ }
+
+ document.addEventListener("DOMContentLoaded", function () {
+
+ })
+
+</script> \ No newline at end of file
diff --git a/modules-available/dozmod/templates/runscripts-edit.html b/modules-available/dozmod/templates/runscripts-edit.html
new file mode 100644
index 00000000..8d81b33c
--- /dev/null
+++ b/modules-available/dozmod/templates/runscripts-edit.html
@@ -0,0 +1,89 @@
+<h1>{{lang_scriptsHead}}</h1>
+
+<form method="post" action="?do=dozmod&section=runscripts">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" id="runscriptid" name="runscriptid" value="{{runscriptid}}">
+
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ {{lang_editScript}}
+ </div>
+ <div class="panel-body">
+ <div class="input-group">
+ <label class="input-group-addon" for="scriptname">{{lang_name}}</label>
+ <input type="text" name="scriptname" id="scriptname" class="form-control"
+ value="{{scriptname}}" required>
+ </div>
+ <div class="input-group">
+ <label class="input-group-addon" for="extension">{{lang_scriptExtension}}</label>
+ <input type="text" name="extension" id="extension" class="form-control" value="{{extension}}"
+ list="default-exts">
+ <datalist id="default-exts">
+ <option value="bat">
+ <option value="ps">
+ <option value="py">
+ <option value="sh">
+ </datalist>
+ </div>
+ <div class="input-group">
+ <label class="input-group-addon" for="visibility">{{lang_scriptVisibility}}</label>
+ <select type="text" name="visibility" id="visibility" class="form-control" required>
+ <option value="0" {{visibility_0_selected}}>{{lang_hidden}}</option>
+ <option value="1" {{visibility_1_selected}}>{{lang_normal}}</option>
+ <option value="2" {{visibility_2_selected}}>{{lang_minimized}}</option>
+ </select>
+ </div>
+ <div class="form-group">
+ <div class="checkbox">
+ <input type="checkbox" name="passcreds" value="1" {{passcreds_checked}} id="passcreds"
+ class="form-control">
+ <label for="passcreds">
+ {{lang_scriptPassCredentials}}
+ </label>
+ </div>
+ </div>
+ <div class="form-group">
+ <div class="radio">
+ <input type="radio" name="isglobal" value="1" {{isglobal_1_checked}} id="isglobal1" class="form-control">
+ <label for="isglobal1">
+ {{lang_scriptIsGlobal}}
+ </label>
+ </div>
+ <div class="radio">
+ <input type="radio" name="isglobal" value="0" {{isglobal_0_checked}} id="isglobal0" class="form-control">
+ <label for="isglobal0">
+ {{lang_scriptIsPredefined}}
+ </label>
+ </div>
+ </div>
+ <div>
+ <label for="content">{{lang_scriptContent}}</label>
+ <textarea name="content" id="content" rows="20" cols="80" class="form-control">{{content}}</textarea>
+ </div>
+
+ <div class="panel panel-body column-list">
+ {{#oslist}}
+ <div class="checkbox">
+ <input type="checkbox" name="osid[]" value="{{osid}}" {{checked}} id="osid{{osid}}" class="form-control">
+ <label for="osid{{osid}}">
+ {{displayname}}
+ </label>
+ </div>
+ {{/oslist}}
+ </div>
+
+ <div class="text-right" style="margin-top: 20px;">
+ <a type="button" class="btn btn-default" href="?do=dozmod&section=runscripts">{{lang_cancel}}</a>
+ <button class="btn btn-primary" type="submit" name="action" value="save">
+ <span class="glyphicon glyphicon-floppy-disk"></span>
+ {{lang_save}}
+ </button>
+ </div>
+
+ </div>
+ </div>
+
+</form>
+
+<script type="text/javascript">
+</script>
diff --git a/modules-available/dozmod/templates/runscripts-list.html b/modules-available/dozmod/templates/runscripts-list.html
new file mode 100644
index 00000000..4d519afb
--- /dev/null
+++ b/modules-available/dozmod/templates/runscripts-list.html
@@ -0,0 +1,89 @@
+<h1>{{lang_scriptsHead}}</h1>
+
+<p>
+ {{lang_scriptsIntro}}
+</p>
+
+<table class="table">
+ <thead>
+ <tr>
+ <th>{{lang_name}}</th>
+ <th class="slx-smallcol">{{lang_scriptExtensionHead}}</th>
+ <th class="slx-smallcol">{{lang_scriptVisibilityHead}}</th>
+ <th class="slx-smallcol">{{lang_scriptPassCredentialsHead}}</th>
+ <th class="slx-smallcol">{{lang_scriptIsGlobalHead}}</th>
+ {{#hasEditPermission}}
+ <th class="slx-smallcol">{{lang_edit}}</th>
+ <th class="slx-smallcol">{{lang_delete}}</th>
+ {{/hasEditPermission}}
+ </tr>
+ </thead>
+ <tbody>
+ {{#list}}
+ <tr>
+ <td>{{scriptname}}</td>
+ <td>.{{extension}}</td>
+ <td><span class="glyphicon glyphicon-{{visibility}}"></span></td>
+ <td>
+ {{#passcreds}}
+ <span class="glyphicon glyphicon-ok"></span>
+ {{/passcreds}}
+ </td>
+ <td>
+ {{#isglobal}}
+ <span class="glyphicon glyphicon-ok"></span>
+ {{/isglobal}}
+ </td>
+ {{#hasEditPermission}}
+ <td class="text-center">
+ <a class="btn btn-xs btn-primary" href="?do=dozmod&amp;section=runscripts&amp;show=edit&amp;runscriptid={{runscriptid}}">
+ <span class="glyphicon glyphicon-edit"></span>
+ </a>
+ </td>
+ <td class="text-center">
+ <button type="button" class="btn btn-xs btn-danger" data-toggle="modal" data-target="#deleteModal" onclick="setDeleteId('{{runscriptid}}')">
+ <span class="glyphicon glyphicon-trash"></span>
+ </button>
+ </td>
+ {{/hasEditPermission}}
+ </tr>
+ {{/list}}
+ </tbody>
+</table>
+
+{{#hasEditPermission}}
+ <div class="text-right">
+ <a class="btn btn-sm btn-success" href="?do=dozmod&amp;section=runscripts&amp;show=edit&amp;runscriptid=0">
+ <span class="glyphicon glyphicon-plus"></span>
+ {{lang_runScriptAdd}}
+ </a>
+ </div>
+{{/hasEditPermission}}
+
+<form method="post" action="?do=dozmod&amp;section=runscripts">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" id="deleteAction" name="runscriptid" value="">
+ <div class ="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ <h4 class="modal-title" id="myModalLabel">{{lang_delete}}</h4>
+ </div>
+ <div class="modal-body">
+ <p>{{lang_runScriptDeleteConfirmation}}</p>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">{{lang_cancel}}</button>
+ <button type="submit" name="action" value="delete" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> {{lang_delete}}</button>
+ </div>
+ </div>
+ </div>
+ </div>
+</form>
+
+<script type="text/javascript">
+ function setDeleteId(id) {
+ $('#deleteAction').val(id)
+ }
+</script>
diff --git a/modules-available/exams/lang/de/template-tags.json b/modules-available/exams/lang/de/template-tags.json
index 8bf37143..1dd51374 100644
--- a/modules-available/exams/lang/de/template-tags.json
+++ b/modules-available/exams/lang/de/template-tags.json
@@ -29,7 +29,7 @@
"lang_headingMain": "bwLehrpool Pr\u00fcfungsmodus",
"lang_id": "ID",
"lang_lectureName": "Veranstaltungsname",
- "lang_lectureOutOfRange": "Achtung: Start- bzw. Endzeitpunkt der Veranstaltung liegen au\u00dferhalb des oben angegebenen Zeitraums",
+ "lang_lectureOutOfRange": "Achtung: Der oben angegebene Zeitraum ist k\u00fcrzer als die Dauer der Veranstaltung",
"lang_location": "Raum\/Ort",
"lang_locationInfo": "W\u00e4hlen Sie hier die R\u00e4ume und Orte aus, die w\u00e4hrend des unten ausgew\u00e4hlten Zeitraums in den Pr\u00fcfungsmodus versetzt werden. Wenn sie hier keine Auswahl treffen, werden alle R\u00e4ume in den Pr\u00fcfungsmodus versetzt.",
"lang_locations": "R\u00e4ume\/Orte",
diff --git a/modules-available/exams/lang/en/template-tags.json b/modules-available/exams/lang/en/template-tags.json
index af87bb01..23266154 100644
--- a/modules-available/exams/lang/en/template-tags.json
+++ b/modules-available/exams/lang/en/template-tags.json
@@ -29,7 +29,7 @@
"lang_headingMain": "bwLehrpool Exam Mode",
"lang_id": "ID",
"lang_lectureName": "Lecture name",
- "lang_lectureOutOfRange": "Hint: Start or end date of given lecture lies outside of exam period given above",
+ "lang_lectureOutOfRange": "Hint: The exam period given above is shorter than the duration of the given lecture",
"lang_location": "Room\/Location",
"lang_locationInfo": "Select the rooms and locations you want to enable the exam mode in. Selecting nothing at all means that all clients will boot into exam mode during the given time period.",
"lang_locations": "Rooms\/Locations",
diff --git a/modules-available/locationinfo/api.inc.php b/modules-available/locationinfo/api.inc.php
index ad71de8b..9ebbcea6 100644
--- a/modules-available/locationinfo/api.inc.php
+++ b/modules-available/locationinfo/api.inc.php
@@ -7,7 +7,7 @@
HandleParameters();
/**
- * Handles the API paramenters.
+ * Handles the API parameters.
*/
function HandleParameters()
{
@@ -20,7 +20,7 @@ function HandleParameters()
} elseif ($get === "machines") {
$locationIds = LocationInfo::getLocationsOr404($uuid);
$output = array();
- InfoPanel::appendMachineData($output, $locationIds, false);
+ InfoPanel::appendMachineData($output, $locationIds, true);
$output = array_values($output);
} elseif ($get === "config") {
$type = InfoPanel::getConfig($uuid, $output);
@@ -30,7 +30,7 @@ function HandleParameters()
}
} elseif ($get === "pcstates") {
$locationIds = LocationInfo::getLocationsOr404($uuid);
- $output = getPcStates($locationIds);
+ $output = getPcStates($locationIds, $uuid);
} elseif ($get === "locationtree") {
$locationIds = LocationInfo::getLocationsOr404($uuid);
$output = getLocationTree($locationIds);
@@ -84,7 +84,7 @@ function getLastChangeTs($paneluuid)
* @param int[] $idList list of the location ids.
* @return array aggregated PC states
*/
-function getPcStates($idList)
+function getPcStates($idList, $paneluuid)
{
$pcStates = array();
foreach ($idList as $id) {
@@ -99,13 +99,24 @@ function getPcStates($idList)
}
$locationInfoList = array();
- InfoPanel::appendMachineData($locationInfoList, $idList);
+ InfoPanel::appendMachineData($locationInfoList, $idList, true);
+
+ $panel = Database::queryFirst('SELECT paneluuid, panelconfig FROM locationinfo_panel WHERE paneluuid = :paneluuid',
+ compact('paneluuid'));
+ $config = json_decode($panel['panelconfig'], true);
+
foreach ($locationInfoList as $locationInfo) {
$id = $locationInfo['id'];
foreach ($locationInfo['machines'] as $pc) {
$key = strtolower($pc['pcState']);
if (isset($pcStates[$id][$key])) {
- $pcStates[$id][$key]++;
+ if ($config['roomplanner']) {
+ if (isset($pc['x']) && isset($pc['y'])) {
+ $pcStates[$id][$key]++;
+ }
+ } else {
+ $pcStates[$id][$key]++;
+ }
}
}
}
diff --git a/modules-available/locationinfo/frontend/frontendscript.js b/modules-available/locationinfo/frontend/frontendscript.js
index b5f59792..efe4d5b6 100644
--- a/modules-available/locationinfo/frontend/frontendscript.js
+++ b/modules-available/locationinfo/frontend/frontendscript.js
@@ -15,7 +15,7 @@ function IsOpen(date, room) {
openDate.setMinutes(tmp[i].MinutesOpen);
closeDate.setHours(tmp[i].HourClose);
closeDate.setMinutes(tmp[i].MinutesClose);
- if (openDate < date && closeDate > date) {
+ if (openDate <= date && closeDate > date) {
return true;
}
}
diff --git a/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php b/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php
index 558f5cd0..22b1d8fb 100644
--- a/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php
+++ b/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php
@@ -274,8 +274,7 @@ class CourseBackend_HisInOne extends CourseBackend
foreach ($eventDetails as $event) {
foreach (array('/hisdefaulttext',
'/hisshorttext',
- '/hisshortcomment',
- '/hisplanelements/hisplanelement/hisdefaulttext') as $path) {
+ '/hisshortcomment') as $path) {
$name = $this->getArrayPath($event, $path);
if (!empty($name) && !empty($name[0]))
break;
@@ -284,25 +283,40 @@ class CourseBackend_HisInOne extends CourseBackend
if ($name === false) {
$name = ['???'];
}
- $unitPlannedDates = $this->getArrayPath($event,
- '/hisplanelements/hisplanelement/hisplannedDates/hisplannedDate/hisindividualDates/hisindividualDate');
- if ($unitPlannedDates === false) {
- $this->error = 'Cannot find ./hisplanelements/hisplanelement/hisplannedDates/hisplannedDate/hisindividualDates/hisindividualDate';
- error_log('Cannot find ./hisplanelements/hisplanelement/hisplannedDates/hisplannedDate/hisindividualDates/hisindividualDate');
+ $planElements = $this->getArrayPath($event, '/hisplanelements/hisplanelement');
+ if ($planElements === false) {
+ $this->error = 'Cannot find ./hisplanelements/hisplanelement';
+ error_log('Cannot find ./hisplanelements/hisplanelement');
error_log(print_r($event, true));
continue;
}
- foreach ($unitPlannedDates as $plannedDate) {
- $eventRoomId = $this->getArrayPath($plannedDate, '/hisroomId')[0];
- $eventDate = $this->getArrayPath($plannedDate, '/hisexecutiondate')[0];
- if (in_array($eventRoomId, $requestedRoomIds) && in_array($eventDate, $currentWeek)) {
- $startTime = $this->getArrayPath($plannedDate, '/hisstarttime')[0];
- $endTime = $this->getArrayPath($plannedDate, '/hisendtime')[0];
- $tTables[$eventRoomId][] = array(
- 'title' => $name[0],
- 'start' => $eventDate . "T" . $startTime,
- 'end' => $eventDate . "T" . $endTime
- );
+ foreach ($planElements as $planElement) {
+ if (empty($planElement['hisplannedDates']))
+ continue;
+ $unitPlannedDates = $this->getArrayPath($planElement,
+ '/hisplannedDates/hisplannedDate/hisindividualDates/hisindividualDate');
+ if ($unitPlannedDates === false) {
+ $this->error = 'Cannot find ./hisplannedDates/hisplannedDate/hisindividualDates/hisindividualDate';
+ error_log('Cannot find ./hisplannedDates/hisplannedDate/hisindividualDates/hisindividualDate');
+ error_log(print_r($planElement, true));
+ continue;
+ }
+ $localName = $this->getArrayPath($planElement, '/hisdefaulttext');
+ if ($localName === false || empty($localName[0])) {
+ $localName = $name;
+ }
+ foreach ($unitPlannedDates as $plannedDate) {
+ $eventRoomId = $this->getArrayPath($plannedDate, '/hisroomId')[0];
+ $eventDate = $this->getArrayPath($plannedDate, '/hisexecutiondate')[0];
+ if (in_array($eventRoomId, $requestedRoomIds) && in_array($eventDate, $currentWeek)) {
+ $startTime = $this->getArrayPath($plannedDate, '/hisstarttime')[0];
+ $endTime = $this->getArrayPath($plannedDate, '/hisendtime')[0];
+ $tTables[$eventRoomId][] = array(
+ 'title' => $localName[0],
+ 'start' => $eventDate . "T" . $startTime,
+ 'end' => $eventDate . "T" . $endTime
+ );
+ }
}
}
}
diff --git a/modules-available/locationinfo/inc/infopanel.inc.php b/modules-available/locationinfo/inc/infopanel.inc.php
index edeb9ccf..918030f0 100644
--- a/modules-available/locationinfo/inc/infopanel.inc.php
+++ b/modules-available/locationinfo/inc/infopanel.inc.php
@@ -75,6 +75,8 @@ class InfoPanel
}
/**
+ * {"language":"de","mode":1,"vertical":true,"eco":false,"scaledaysauto":true,"daystoshow":7,"rotation":0,"scale":56,"switchtime":10,"calupdate":120,"roomupdate":20,
+ * "overrides": { "12" : { "mode":4} }
* Gets the location info of the given locations.
* Append to passed array which is expected to
* map location ids to properties of that location.
@@ -157,6 +159,8 @@ class InfoPanel
// Iterate over the locations we're actually interested in
$locations = Location::getLocationsAssoc();
foreach ($idList as $locationId) {
+ if (empty($locationId))
+ continue;
// Start checking at actual location...
$currentId = $locationId;
while ($currentId !== 0) {
@@ -175,6 +179,8 @@ class InfoPanel
}
}
// Keep trying with parent
+ if (!isset($locations[$currentId]))
+ break;
$currentId = $locations[$currentId]['parentlocationid'];
}
}
diff --git a/modules-available/locationinfo/inc/locationinfo.inc.php b/modules-available/locationinfo/inc/locationinfo.inc.php
index c51be666..377e960b 100644
--- a/modules-available/locationinfo/inc/locationinfo.inc.php
+++ b/modules-available/locationinfo/inc/locationinfo.inc.php
@@ -80,6 +80,7 @@ class LocationInfo
'vertical' => false,
'eco' => false,
'prettytime' => true,
+ 'roomplanner' => true,
'scaledaysauto' => true,
'daystoshow' => 7,
'rotation' => 0,
@@ -93,9 +94,9 @@ class LocationInfo
if ($type === 'SUMMARY') {
return array(
'language' => defined('LANG') ? LANG : 'en',
- 'calupdate' => 30,
- 'roomupdate' => 15,
- 'configupdate' => 180,
+ 'roomplanner' => true,
+ 'eco' => false,
+ 'panelupdate' => 60,
);
}
return array();
@@ -127,11 +128,13 @@ class LocationInfo
} elseif ($row['paneltype'] === 'URL') {
// Check if we should set the insecure SSL mode (accept invalid/self signed certs etc.)
$data = json_decode($row['panelconfig'], true);
- if ($data && $data['insecure-ssl']) {
- ConfigHolder::add('SLX_BROWSER_INSECURE', '1');
- }
- if ($data && $data['reload-minutes']) {
- ConfigHolder::add('SLX_BROWSER_RELOAD_SECS', $data['reload-minutes'] * 60);
+ if (is_array($data)) {
+ if (isset($data['insecure-ssl']) && $data['insecure-ssl']) {
+ ConfigHolder::add('SLX_BROWSER_INSECURE', '1');
+ }
+ if (isset($data['reload-minutes']) && $data['reload-minutes']) {
+ ConfigHolder::add('SLX_BROWSER_RELOAD_SECS', $data['reload-minutes'] * 60);
+ }
}
}
ConfigHolder::add('SLX_BROWSER_URL', 'http://' . $_SERVER['SERVER_ADDR'] . '/panel/' . $panelUuid);
diff --git a/modules-available/locationinfo/lang/de/template-tags.json b/modules-available/locationinfo/lang/de/template-tags.json
index bcdf7148..03eb63a5 100644
--- a/modules-available/locationinfo/lang/de/template-tags.json
+++ b/modules-available/locationinfo/lang/de/template-tags.json
@@ -42,6 +42,7 @@
"lang_locations": "Orte",
"lang_locationsTable": "R\u00e4ume \/ Orte",
"lang_locationsTableHints": "Hier k\u00f6nnen Sie f\u00fcr die R\u00e4ume und Orte Ihrer Einrichtung \u00d6ffnungszeiten hinterlegen, sowie die Verkn\u00fcpfung mit Raum-IDs aus konfigurierten Backends (z.B. HISinOne) vornehmen, damit Belegungspl\u00e4ne abgerufen werden k\u00f6nnen.",
+ "lang_locsHint": "Hier k\u00f6nnen Sie die Orte ausw\u00e4hlen, die in diesem Panel angezeigt werden.",
"lang_longFri": "Freitag",
"lang_longMon": "Montag",
"lang_longSat": "Samstag",
@@ -57,7 +58,7 @@
"lang_modeTooltip": "Die Anzeigemodi, welche das Frontend unterst\u00fctzt",
"lang_monTilFr": "Montag - Freitag",
"lang_nameTooltip": "Legt den Namen des Servers fest",
- "lang_noLocationsWarning": "Sie haben keine Orte f\u00fcr dieses Panel ausgew\u00e4hlt",
+ "lang_noLocationsWarning": "Bitte w\u00e4hlen Sie mindestens einen Ort aus, der vom Panel angezeigt werden soll.",
"lang_noServer": "<Kein Server>",
"lang_openingTime": "\u00d6ffnungszeit",
"lang_openingtimes": "\u00d6ffnungszeiten",
@@ -76,7 +77,8 @@
"lang_remoteSchedule": "Abruf Belegungsplan",
"lang_room": "Raum",
"lang_roomId": "Raum ID",
- "lang_roomIdTooltip": "Die Raum ID, die der Server ben\u00f6tigt, um Kalenderdaten abzurufen",
+ "lang_roomIdTooltip": "Die Raum ID, die der Server ben\u00f6tigt, um Kalenderdaten abzurufen (bei Exchange die Postfachadresse)",
+ "lang_roomplannerTooltip": "Nur PCs berücksichtigen, die im Raumplaner gesetzt wurden",
"lang_roomupdateTooltip": "Zeit nach der die PCs aktualisiert werden (in Sekunden)",
"lang_rotation": "Rotation",
"lang_rotation0": "0\u00b0",
@@ -118,6 +120,7 @@
"lang_url": "URL",
"lang_urlPanel": "URL-Panel",
"lang_urlTooltip": "URL die aufgerufen wird",
+ "lang_useRoomplanner": "Raumplaner benutzen",
"lang_vertical": "Vertikaler Modus",
"lang_verticalTooltip": "Legt fest, ob Kalender und Raum \u00fcbereinander angezeigt werden sollen"
} \ No newline at end of file
diff --git a/modules-available/locationinfo/lang/en/template-tags.json b/modules-available/locationinfo/lang/en/template-tags.json
index 558ddff0..1b5ab0fd 100644
--- a/modules-available/locationinfo/lang/en/template-tags.json
+++ b/modules-available/locationinfo/lang/en/template-tags.json
@@ -42,6 +42,7 @@
"lang_locations": "Locations",
"lang_locationsTable": "Rooms \/ Locations",
"lang_locationsTableHints": "Here you can define opening times for your locations and link the location ID to a configured backend (e.g. HISinOne) to show calendar events.",
+ "lang_locsHint": "You can pick up the locations that will be shown in this panel.",
"lang_longFri": "Friday",
"lang_longMon": "Monday",
"lang_longSat": "Saturday",
@@ -57,7 +58,7 @@
"lang_modeTooltip": "The display modes the frontend supports",
"lang_monTilFr": "Monday - Friday",
"lang_nameTooltip": "Defines the name of the server",
- "lang_noLocationsWarning": "Please select at least one location this panel should display",
+ "lang_noLocationsWarning": "Please select at least one location this panel should display.",
"lang_noServer": "<no server>",
"lang_openingTime": "Opening time",
"lang_openingtimes": "Opening times",
@@ -76,7 +77,8 @@
"lang_remoteSchedule": "Time table retrieval",
"lang_room": "Room",
"lang_roomId": "Room ID",
- "lang_roomIdTooltip": "The ID of the room the server needs, for querying the calendar data",
+ "lang_roomIdTooltip": "The ID of the room the server needs, for querying the calendar data (when using exchange the room mailbox)",
+ "lang_roomplannerTooltip": "Only consider PCs which were set in the roomplanner",
"lang_roomupdateTooltip": "Time the PCs in the room gets updated (in seconds)",
"lang_rotation": "Rotation",
"lang_rotation0": "0\u00b0",
@@ -118,6 +120,7 @@
"lang_url": "URL",
"lang_urlPanel": "URL panel",
"lang_urlTooltip": "URL which is shown by the panel",
+ "lang_useRoomplanner": "Use roomplans",
"lang_vertical": "Vertical mode",
"lang_verticalTooltip": "Defines whether the room and calendar are shown above each other"
} \ No newline at end of file
diff --git a/modules-available/locationinfo/page.inc.php b/modules-available/locationinfo/page.inc.php
index 3ff80927..13382438 100644
--- a/modules-available/locationinfo/page.inc.php
+++ b/modules-available/locationinfo/page.inc.php
@@ -359,6 +359,7 @@ class Page_LocationInfo extends Page
'vertical' => Request::post('vertical', false, 'bool'),
'eco' => Request::post('eco', false, 'bool'),
'prettytime' => Request::post('prettytime', false, 'bool'),
+ 'roomplanner' => Request::post('roomplanner', false, 'bool'),
'scaledaysauto' => Request::post('scaledaysauto', false, 'bool'),
'daystoshow' => Request::post('daystoshow', 7, 'int'),
'rotation' => Request::post('rotation', 0, 'int'),
@@ -388,9 +389,19 @@ class Page_LocationInfo extends Page
private function preparePanelConfigSummary()
{
+ // Build json structure
+ $conf = array(
+ 'language' => Request::post('language', 'en', 'string'),
+ 'eco' => Request::post('eco', false, 'bool'),
+ 'roomplanner' => Request::post('roomplanner', false, 'bool'),
+ 'panelupdate' => Request::post('panelupdate', 30, 'int')
+ );
+ if ($conf['panelupdate'] < 15) {
+ $conf['panelupdate'] = 15;
+ }
// Check locations
$locationids = self::getLocationIdsFromRequest(true);
- return array('locationids' => $locationids);
+ return array('config' => $conf, 'locationids' => $locationids);
}
/**
@@ -922,6 +933,7 @@ class Page_LocationInfo extends Page
'vertical_checked' => $config['vertical'] ? 'checked' : '',
'eco_checked' => $config['eco'] ? 'checked' : '',
'prettytime_checked' => $config['prettytime'] ? 'checked' : '',
+ 'roomplanner_checked' => $config['roomplanner'] ? 'checked' : '',
'scaledaysauto_checked' => $config['scaledaysauto'] ? 'checked' : '',
'daystoshow' => $config['daystoshow'],
'rotation' => $config['rotation'],
@@ -947,9 +959,11 @@ class Page_LocationInfo extends Page
'uuid' => $id,
'panelname' => $panel['panelname'],
'languages' => $langs,
- 'roomupdate' => $config['roomupdate'],
+ 'panelupdate' => $config['panelupdate'],
+ 'roomplanner_checked' => $config['roomplanner'] ? 'checked' : '',
'locations' => Location::getLocations(),
'locationids' => $panel['locationids'],
+ 'eco_checked' => $config['eco'] ? 'checked' : '',
));
}
}
diff --git a/modules-available/locationinfo/templates/frontend-default.html b/modules-available/locationinfo/templates/frontend-default.html
index 006d2661..4dee8ef7 100755
--- a/modules-available/locationinfo/templates/frontend-default.html
+++ b/modules-available/locationinfo/templates/frontend-default.html
@@ -327,6 +327,7 @@ optional:
.wc-scrollable-grid {
transition: height 500ms;
background: rgba(0, 0, 0, 0);
+ overflow-y: hidden !important;
}
.wc-grid-timeslot-header,
@@ -618,6 +619,15 @@ optional:
generateProgressBar();
}
+ // Manually initialize mode 2, as initRoomLayout isn't called for this mode
+ if (room.config.mode === 2) {
+ var date = MyDate();
+ var now = date.getTime();
+ queryCalendars();
+ queryRooms();
+ lastCalendarUpdate = now;
+ lastRoomUpdate = now;
+ }
mainUpdateLoop();
setInterval(mainUpdateLoop, 10000);
setInterval(updateHeaders, globalConfig.eco ? 10000 : 1000);
@@ -735,7 +745,7 @@ optional:
timeslotHeight: 30,
daysToShow: daysToShow,
height: function () {
- if (room.config.mode === 1 && room.config.vertical && (!room.timetable || !room.timetable.length)) return 20;
+ // if (room.config.mode === 1 && room.config.vertical && (!room.timetable || !room.timetable.length)) return 20;
var height = $(window).height();
if (roomIds.length === 4) {
height /= 2;
@@ -770,7 +780,9 @@ optional:
timeSeparator: " - ",
startOnFirstDayOfWeek: false,
displayFreeBusys: true,
- defaultFreeBusy: {free: false}
+ defaultFreeBusy: {free: false},
+ allowCalEventOverlap: true,
+ overlapEventsSeparate: true
});
}
@@ -918,7 +930,7 @@ optional:
const SEVEN_DAYS = 7 * 86400 * 1000;
/**
- * applays new calendar data to the calendar plugin and also saves it to the room object
+ * applies new calendar data to the calendar plugin and also saves it to the room object
* @param {Array} json Calendar data
* @param room Room Object
*/
@@ -945,9 +957,18 @@ optional:
console.log('Notice: Calendar has no current events for ' + room.name);
}
try {
+ for (var i = json.length - 1; i > 0; i--) {
+ // if title, start and end are the same, "merge" two events by removing one of them
+ if (json[i].title === json[i-1].title && json[i].start === json[i-1].start && json[i].end === json[i-1].end) {
+ json.splice(i, 1);
+ }
+ }
room.timetable = json;
+ for (var property in room.timetable) {
+ room.timetable[property].start = cleanDate(room.timetable[property].start);
+ room.timetable[property].end = cleanDate(room.timetable[property].end);
+ }
if (room.config.mode !== 3) {
- // TODO: Check if they're the same
var cal = room.$.calendar;
cal.weekCalendar('option', 'data', {events: json});
cal.weekCalendar("refresh");
@@ -967,6 +988,27 @@ optional:
}
}
+ 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;
+ }
+
/**
* scales calendar, called once on create and on window resize
* @param room Room Object
@@ -1108,12 +1150,11 @@ optional:
* @param room Room
*/
function SetFreeSeats(room) {
- room.$.seatsCounter.text(room.freePcs >= 0 ? room.freePcs : '');
+ // if room has no allowed value, set text in the box to -
+ room.$.seatsCounter.text(room.freePcs >= 0 ? room.freePcs : '-');
room.$.seatsCounter.data('state', JSON.stringify(room.state));
if (room.freePcs > 0 && room.state && room.state.free) {
- room.$.seatsBackground.css('background-color', '#250');
- } else if (room.freePcs === -1) {
- room.$.seatsBackground.css('background-color', 'red');
+ room.$.seatsBackground.css('background-color', '#250');
} else {
room.$.seatsBackground.css('background-color', 'red');
}
@@ -1140,7 +1181,8 @@ optional:
if (!same) newText = t("closed");
} else if (tmp.state === "CalendarEvent") {
if (!same) newText = tmp.title;
- seats = -1;
+ // whilst event is running set freePcs to -, hopefully not breaking anything else with this
+ room.freePcs = "-";
} else if (tmp.state === "Free") {
if (!same) newText = t("free");
} else if (tmp.state === "FreeNoEnd") {
@@ -1166,11 +1208,10 @@ optional:
function ComputeCurrentState(room) {
if (!IsOpen(MyDate(), room)) {
room.state = {state: "closed", end: GetNextOpening(room), title: "", next: ""};
-
return;
}
- var closing = GetNextClosing(room);
+ var closing = GetNextClosing(room);
var event = getNextEvent(room.timetable);
// no event and no closing
@@ -1488,8 +1529,14 @@ optional:
for (var i = 0; i < update.length; i++) {
var $div = $("#pc_" + room.id + "_" + update[i].id);
// Pc free
- if (update[i].pcState === "IDLE" || update[i].pcState === "OFFLINE" || update[i].pcState === "STANDBY") {
- freePcs++;
+ if (room.config.roomplanner === true) {
+ if ((update[i].pcState === "IDLE" || update[i].pcState === "OFFLINE" || update[i].pcState === "STANDBY") && !isNaN(update[i].x) && !isNaN(update[i].y)) {
+ freePcs++;
+ }
+ } else {
+ if ((update[i].pcState === "IDLE" || update[i].pcState === "OFFLINE" || update[i].pcState === "STANDBY")) {
+ freePcs++;
+ }
}
$div.removeClass('BROKEN OFFLINE IDLE OCCUPIED STANDBY'.replace(update[i].pcState, '')).addClass(update[i].pcState);
@@ -1585,7 +1632,7 @@ optional:
/**
* Function for translation
- * @param toTranslate key which we wan't to translate
+ * @param toTranslate key which we want to translate
* @returns r translated string
*/
function t(toTranslate) {
diff --git a/modules-available/locationinfo/templates/frontend-summary.html b/modules-available/locationinfo/templates/frontend-summary.html
index 4105dd16..ecb41467 100644
--- a/modules-available/locationinfo/templates/frontend-summary.html
+++ b/modules-available/locationinfo/templates/frontend-summary.html
@@ -1,5 +1,5 @@
<!DOCTYPE html>
-<html lang="de">
+<html lang="{{language}}">
<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>
@@ -108,6 +108,10 @@
border-radius: 1px;
}
+ #i18n {
+ display: none;
+ }
+
</style>
<script type='text/javascript'>
@@ -116,6 +120,7 @@
var startdate;
var roomidsString = "";
var config = {{{config}}};
+ var lastPanelUpdate = 0;
$(document).ready(function () {
init();
@@ -139,6 +144,7 @@
SetUpDate(time);
generateLayout(config.tree);
update();
+ setInterval(update, 10000);
}
function SetUpDate(d) {
@@ -165,7 +171,7 @@
*/
function generateObject(json, myParent, outermost) {
var obj;
- if (!json.children || json.children.length == 0) {
+ if (!json.children || json.children.length === 0) {
obj = generateChild(myParent, json.locationid, json.locationname, outermost);
} else {
obj = generateParent(myParent, json.locationid, json.locationname, outermost);
@@ -178,59 +184,24 @@
}
/**
- * Helper function to generate id string used in query functions
- * @param list A string, wicht contains ids or not(for now)
- * @param id An ID which should be added to the list
+ * Main Update loop, this loop runs every 10 seconds
*/
- function addIdToUpdateList(list, id) {
- if (list == "") {
- list += id;
- } else {
- list += ("," + id);
- }
- return list;
- }
-
-
- 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 (property in rooms) {
- if (rooms[property].lastCalendarUpdate === null || rooms[property].lastCalendarUpdate + CALUPDATE_MS < MyDate().getTime()) {
- // TODO: NOT NECESSARY ANYMORE?!
- calendarUpdateIds = addIdToUpdateList(calendarUpdateIds, rooms[property].id);
- count++;
- rooms[property].lastCalendarUpdate = MyDate().getTime();
- }
- if (rooms[property].lastRoomUpdate === null || rooms[property].lastRoomUpdate + ROOMUPDATE_MS < MyDate().getTime()) {
- // TODO: NOT NECESSARY ANYMORE?!
- rommUpdateIds = addIdToUpdateList(rommUpdateIds, rooms[property].id);
- count++;
- rooms[property].lastRoomUpdate = MyDate().getTime();
- }
- // TODO if (count > 7) break;
- }
- if (calendarUpdateIds !== "") {
- queryCalendars();
- nextUpdate = 1000;
- }
- if (rommUpdateIds !== "") {
+ var date = MyDate();
+ var now = date.getTime();
+ if (lastPanelUpdate + (config.panelupdate * 1000) < now) {
+ // Set Roomupdate Interval has passed, update.
queryRooms();
- nextUpdate = 1000;
- }
- if (nextUpdate !== 1000) {
+ queryCalendars();
+ lastPanelUpdate = now;
+ for (var property in rooms) {
+ rooms[property].lastCalendarUpdate = now;
+ rooms[property].lastRoomUpdate = now;
+ }
+ } else {
+ // Set Roomupdate Interval has NOT passed, check if panel was changed since last call and reload if true.
queryPanelChange();
}
- clearTimeout(timeout);
- setTimeout(update, nextUpdate);
}
function cleanDate(d) {
@@ -283,7 +254,6 @@
cache: false,
timeout: 30000,
success: function (result) {
- var l = result.length;
if (result[0] == null) {
console.log("Error: Backend reported null back for RoomUpdate, this might happend if the room isn't" +
"configurated.");
@@ -309,17 +279,16 @@
var state = room.getState();
if (state.state == "CalendarEvent") {
- updateCourseText(room.id, state.titel);
+ updateCourseText(room.id, state.title);
updateCoursTimer(room.id, GetTimeDiferenceAsString(state.end, MyDate()));
} else if (state.state == "Free") {
- updateCourseText(room.id, "Frei");
+ updateCourseText(room.id, t("free"));
updateCoursTimer(room.id, GetTimeDiferenceAsString(state.end, MyDate()));
} else if (state.state == "FreeNoEnd") {
- updateCourseText(room.id, "Frei");
+ updateCourseText(room.id, t("free"));
updateCoursTimer(room.id, "");
- }
- else if (state.state == "closed") {
- updateCourseText(room.id, "Geschlossen");
+ } else if (state.state == "closed") {
+ updateCourseText(room.id, t("closed"));
updateCoursTimer(room.id, "");
}
}
@@ -331,7 +300,7 @@
function updatePcStates(json) {
var l = json.length;
for (var i = 0; i < l; i++) {
- updateRoomUsage(json[i].id, json[i].idle, json[i].occupied, json[i].offline, json[i].broken, json[i].standby)
+ updateRoomUsage(json[i].id, json[i].idle, json[i].occupied, json[i].offline, json[i].broken, json[i].standby);
}
}
@@ -428,109 +397,86 @@
/**
* computes state of a room, states are:
- * closed, FreeNoEnd, Free, ClaendarEvent.
- * @param Room Object
+ * closed, FreeNoEnd, Free, CalendarEvent.
+ * @param room Object
*/
function ComputeCurrentState(room) {
- if (room.lastRoomUpdate === null) {
- room.state = {state: 'unknown'};
+ if (!IsOpen(MyDate(), room)) {
+ room.state = {state: "closed", end: GetNextOpening(room), title: "", next: ""};
return;
}
- if (!IsOpenNow(room)) {
- room.state = {state: "closed", end: GetNextOpening(room), titel: "", next: ""};
- return;
- }
- var closing = GetNextClosing(room);
+ console.log("Timetable", room.id, room.timetable);
+ var closing = GetNextClosing(room);
var event = getNextEvent(room.timetable);
+
+ console.log("Event", room.id, event);
+
// no event and no closing
- if (closing == null && event == null) {
- room.state = {state: "FreeNoEnd", end: "", titel: "", next: ""};
+ if (!closing && !event) {
+ room.state = {state: "FreeNoEnd", end: "", title: "", next: "", free: true};
return;
}
// no event so closing is next
- if (event == null) {
- room.state = {state: "Free", end: closing, titel: "", next: "closing"};
+ if (!event) {
+ room.state = {state: "Free", end: closing, title: "", next: "closing", free: true};
return;
}
// event is at the moment
- if ((closing == null || event.start.getTime() < closing.getTime()) && event.start.getTime() < new MyDate()) {
- room.state = {state: "CalendarEvent", end: event.end, titel: event.title, next: ""};
+ if ((!closing || event.start.getTime() < closing.getTime()) && event.start.getTime() < MyDate()) {
+ room.state = {
+ state: "CalendarEvent",
+ end: event.end,
+ title: event.title,
+ next: ""
+ };
+ console.log("CalendarEvent", room.id, room.state);
return;
}
// no closing so event is next
- if (closing == null) {
- room.state = {state: "Free", end: event.start, titel: "", next: "event"};
+ if (!closing) {
+ room.state = {state: "Free", end: event.start, title: "", next: "event", free: true};
return;
}
// event sooner then closing
if (event.start.getTime() < closing) {
- room.state = {state: "Free", end: event.start, titel: "", next: "event"};
- } else if (event.start.getTime() > closing) {
- room.state = {state: "Free", end: closing, titel: "", next: "closing"};
- }
- }
-
- /**
- * checks if a room is open
- * @param room Room object
- * @returns bool for open or not
- */
- function IsOpenNow(room) {
- var now = new MyDate();
- if (room.openingTimes == null) {
-
- // changes from falls needs testing
- return true;
+ room.state = {state: "Free", end: event.start, title: "", next: "event", free: true};
+ } else {
+ room.state = {state: "Free", end: closing, title: "", next: "closing", free: true};
}
- var tmp = room.openingTimes[now.getDay()];
- if (tmp == null) {
- return false;
- }
- for (var i = 0; i < tmp.length; i++) {
- var openDate = new MyDate();
- openDate.setHours(tmp[i].HourOpen);
- openDate.setMinutes(tmp[i].MinutesOpen);
- var closeDate = new MyDate();
- closeDate.setHours(tmp[i].HourClose);
- closeDate.setMinutes(tmp[i].MinutesClose);
- if (openDate < now && closeDate > now) {
- return true;
- }
- }
- return false;
}
/**
* returns next event from a given json of events
- * @param json Json which contains the calendar data.
- * @returns event next Carlendar Event
+ * @param calEvents Json which contains the calendar data.
+ * @returns event next Calendar Event
*/
- function getNextEvent(json) {
- if (json == null) {
- return;
- }
- var event;
- var now = new MyDate();
- for (var i = 0; i < json.length; i++) {
+ function getNextEvent(calEvents) {
+ if (!calEvents) return null;
+ if (calEvents.constructor !== Array) {
+ console.log('getNextEvent called with something not array: ' + typeof(calEvents));
+ return null;
+ }
+ var event = null;
+ var now = MyDate();
+ for (var i = 0; i < calEvents.length; i++) {
//event is now active
- if (json[i].start.getTime() < now.getTime() && json[i].end.getTime() > now.getTime()) {
- return json[i];
+ if (calEvents[i].start.getTime() < now.getTime() && calEvents[i].end.getTime() > now.getTime()) {
+ return calEvents[i];
}
//first element to consider
- if (event == null) {
- if (json[i].start.getTime() > now.getTime()) {
- event = json[i];
+ if (!event) {
+ if (calEvents[i].start.getTime() > now.getTime()) {
+ event = calEvents[i];
}
- }
- if (json[i].start.getTime() > now.getTime() && event.start.getTime() > json[i].start.getTime()) {
- event = json[i];
+ } else if (calEvents[i].start.getTime() > now.getTime() && event.start.getTime() > calEvents[i].start.getTime()) {
+ event = calEvents[i];
}
}
return event;
@@ -791,9 +737,43 @@
})
}
+ /**
+ * Function for translation
+ * @param toTranslate key which we want to translate
+ * @returns r translated string
+ */
+ function t(toTranslate) {
+ if (tCache[toTranslate])
+ return tCache[toTranslate];
+ var r = $('#i18n').find('[data-tag="' + toTranslate + '"]');
+ return tCache[toTranslate] = (r.length === 0 ? toTranslate : r.text());
+ }
+ var tCache = {};
+
</script>
</head>
<body>
<div id="main"></div>
+
+<div id="i18n">
+ <span data-tag="room">{{lang_room}}</span>
+ <span data-tag="closed">{{lang_closed}}</span>
+ <span data-tag="free">{{lang_free}}</span>
+ <span data-tag="shortSun">{{lang_shortSun}}</span>
+ <span data-tag="shortMon">{{lang_shortMon}}</span>
+ <span data-tag="shortTue">{{lang_shortTue}}</span>
+ <span data-tag="shortWed">{{lang_shortWed}}</span>
+ <span data-tag="shortThu">{{lang_shortThu}}</span>
+ <span data-tag="shortFri">{{lang_shortFri}}</span>
+ <span data-tag="shortSat">{{lang_shortSat}}</span>
+ <span data-tag="longSun">{{lang_longSun}}</span>
+ <span data-tag="longMon">{{lang_longMon}}</span>
+ <span data-tag="longTue">{{lang_longTue}}</span>
+ <span data-tag="longWed">{{lang_longWed}}</span>
+ <span data-tag="longThu">{{lang_longThu}}</span>
+ <span data-tag="longFri">{{lang_longFri}}</span>
+ <span data-tag="longSat">{{lang_longSat}}</span>
+ <span data-tag="to">{{lang_to}}</span>
+</div>
</body>
</html>
diff --git a/modules-available/locationinfo/templates/page-config-panel-default.html b/modules-available/locationinfo/templates/page-config-panel-default.html
index ba493579..116c2e11 100644
--- a/modules-available/locationinfo/templates/page-config-panel-default.html
+++ b/modules-available/locationinfo/templates/page-config-panel-default.html
@@ -107,6 +107,22 @@
</div>
</div>
</div>
+
+ <div class="list-group-item">
+ <div class="row">
+ <div class="col-sm-3">
+ <label for="input-roomplanner">{{lang_useRoomplanner}}</label>
+ </div>
+ <div class="col-sm-7">
+ <input id="input-roomplanner" type="checkbox" name="roomplanner" {{roomplanner_checked}}>
+ </div>
+ <div class="col-sm-2">
+ <p class="btn btn-static helptext" title="{{lang_roomplannerTooltip}}">
+ <span class="glyphicon glyphicon-question-sign"></span>
+ </p>
+ </div>
+ </div>
+ </div>
</div>
</div>
</div>
@@ -293,7 +309,7 @@
</ul>
<div class="dropdown pull-right">
- <button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown">
+ <button id="addLocDdBtn" type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown">
<span class="glyphicon glyphicon-plus"></span>
</button>
<ul class="dropdown-menu" id="location-list">
@@ -309,8 +325,10 @@
</div>
</div>
- <a href="?do=locationinfo&amp;show=panels" class="btn btn-default">{{lang_cancel}}</a>
- <button type="submit" class="btn btn-primary">{{lang_save}}</button>
+ <div class="text-right">
+ <a href="?do=locationinfo&amp;show=panels" class="btn btn-default">{{lang_cancel}}</a>
+ <button type="submit" class="btn btn-primary">{{lang_save}}</button>
+ </div>
</form>
<div class="modal fade" id="no-locations-message" tabindex="-1" role="dialog">
@@ -336,6 +354,7 @@ document.addEventListener("DOMContentLoaded", function () {
var $selLocs = $('#selected-locations');
var $locList = $('#location-list');
var $locInput = $('#locationids');
+ var $addLocDbBtn = $('#addLocDdBtn');
// Initialize fancy tooltips
$('p.helptext').tooltip();
@@ -372,6 +391,7 @@ document.addEventListener("DOMContentLoaded", function () {
// Adding/removing locations
$locList.find('a').click(function(ev) {
ev.preventDefault();
+ ev.stopPropagation();
var $this = $(this);
var name = $this.find('.name').text();
var id = $this.data('lid');
@@ -388,13 +408,24 @@ document.addEventListener("DOMContentLoaded", function () {
function addLocation(id, name) {
$selLocs.find('li[data-lid="' + id + '"]').remove();
- var delButton = $('<button class="btn btn-danger btn-xs" type="button">').append($('<span class="glyphicon glyphicon-remove">')).click(delParent);
- $selLocs.append($('<li>').attr('data-lid', id).text(name).prepend(delButton));
+ var delButton = $('<button style="margin-right: 5px;" class="btn btn-danger btn-xs" type="button">').append($('<span class="glyphicon glyphicon-remove">')).click(delParent);
+ $selLocs.append($('<li style="margin-top: 5px;">').attr('data-lid', id).text(name).prepend(delButton));
+
+ if ($selLocs.find('li').length >= 4) {
+ $addLocDbBtn.prop("disabled",true);
+ var $addLocBtnParent = $addLocDbBtn.parent();
+ if ($addLocBtnParent.hasClass('open')) {
+ $addLocBtnParent.removeClass('open');
+ }
+ }
}
function delParent() {
$(this).parent().remove();
serializeLocs();
+ if ($selLocs.find('li').length < 4) {
+ $addLocDbBtn.prop("disabled",false);
+ }
}
function serializeLocs() {
diff --git a/modules-available/locationinfo/templates/page-config-panel-summary.html b/modules-available/locationinfo/templates/page-config-panel-summary.html
index 7f145916..a77719b8 100644
--- a/modules-available/locationinfo/templates/page-config-panel-summary.html
+++ b/modules-available/locationinfo/templates/page-config-panel-summary.html
@@ -70,6 +70,22 @@
</div>
</div>
</div>
+
+ <div class="list-group-item">
+ <div class="row">
+ <div class="col-sm-3">
+ <label for="input-roomplanner">{{lang_useRoomplanner}}</label>
+ </div>
+ <div class="col-sm-7">
+ <input id="input-roomplanner" type="checkbox" name="roomplanner" {{roomplanner_checked}}>
+ </div>
+ <div class="col-sm-2">
+ <p class="btn btn-static helptext" title="{{lang_roomplannerTooltip}}">
+ <span class="glyphicon glyphicon-question-sign"></span>
+ </p>
+ </div>
+ </div>
+ </div>
<!--
<div class="list-group-item">
<div class="row">
@@ -91,6 +107,34 @@
</div>
</div>
</div>
+
+ <div class="col-md-6">
+ <div class="panel panel-default">
+ <div class="panel-heading">{{lang_updateRates}}</div>
+ <div class="panel-body">
+ <div class="list-group">
+
+ <div class="list-group-item">
+ <div class="row">
+ <div class="col-sm-3">
+ <label for="form-panelupdate">{{lang_panel}}</label>
+ </div>
+ <div class="col-sm-7">
+ <input class="form-control" name="panelupdate" type="number" min="15" id="form-panelupdate"
+ max="86400" value="{{panelupdate}}" required>
+ </div>
+ <div class="col-sm-2">
+ <p class="btn btn-static helptext" title="{{lang_panelupdateTooltip}}">
+ <span class="glyphicon glyphicon-question-sign"></span>
+ </p>
+ </div>
+ </div>
+ </div>
+
+ </div>
+ </div>
+ </div>
+ </div>
</div>
<div class="modify-inputs">
@@ -101,7 +145,7 @@
<div class="panel-heading">{{lang_locations}}</div>
<div class="panel-body">
<input type="hidden" name="locationids" value="{{locationids}}" id="locationids">
- <p>{{lang_fourLocsHint}}</p>
+ <p>{{lang_locsHint}}</p>
<ul id="selected-locations" class="list-unstyled">
</ul>
@@ -122,8 +166,10 @@
</div>
</div>
- <a href="?do=locationinfo&amp;show=panels" class="btn btn-default">{{lang_cancel}}</a>
- <button type="submit" class="btn btn-primary">{{lang_save}}</button>
+ <div class="text-right">
+ <a href="?do=locationinfo&amp;show=panels" class="btn btn-default">{{lang_cancel}}</a>
+ <button type="submit" class="btn btn-primary">{{lang_save}}</button>
+ </div>
</form>
<div class="modal fade" id="no-locations-message" tabindex="-1" role="dialog">
@@ -171,6 +217,7 @@ document.addEventListener("DOMContentLoaded", function () {
// Adding/removing locations
$locList.find('a').click(function(ev) {
ev.preventDefault();
+ ev.stopPropagation();
var $this = $(this);
var name = $this.find('.name').text();
var id = $this.data('lid');
@@ -187,8 +234,8 @@ document.addEventListener("DOMContentLoaded", function () {
function addLocation(id, name) {
$selLocs.find('li[data-lid="' + id + '"]').remove();
- var delButton = $('<button class="btn btn-danger btn-xs" type="button">').append($('<span class="glyphicon glyphicon-remove">')).click(delParent);
- $selLocs.append($('<li>').attr('data-lid', id).text(name).prepend(delButton));
+ var delButton = $('<button style="margin-right: 5px;" class="btn btn-danger btn-xs" type="button">').append($('<span class="glyphicon glyphicon-remove">')).click(delParent);
+ $selLocs.append($('<li style="margin-top: 5px;">').attr('data-lid', id).text(name).prepend(delButton));
}
function delParent() {
diff --git a/modules-available/locationinfo/templates/page-config-panel-url.html b/modules-available/locationinfo/templates/page-config-panel-url.html
index efcafb77..1187c19f 100644
--- a/modules-available/locationinfo/templates/page-config-panel-url.html
+++ b/modules-available/locationinfo/templates/page-config-panel-url.html
@@ -87,9 +87,10 @@
</div>
</div>
</div>
-
- <a href="?do=locationinfo&amp;show=panels" class="btn btn-default">{{lang_cancel}}</a>
- <button type="submit" class="btn btn-primary">{{lang_save}}</button>
+ <div class="text-right">
+ <a href="?do=locationinfo&amp;show=panels" class="btn btn-default">{{lang_cancel}}</a>
+ <button type="submit" class="btn btn-primary">{{lang_save}}</button>
+ </div>
</form>
<script type="text/javascript"><!--
diff --git a/modules-available/locationinfo/templates/page-locations.html b/modules-available/locationinfo/templates/page-locations.html
index 67da2c2e..c79cdfe8 100644
--- a/modules-available/locationinfo/templates/page-locations.html
+++ b/modules-available/locationinfo/templates/page-locations.html
@@ -8,7 +8,7 @@
<th>{{lang_locationName}}</th>
<th>{{lang_backend}}</th>
<th>{{lang_lastCalendarUpdate}}</th>
- <th>{{lang_openingtimes}}</th>
+ <th class="text-center slx-smallcol">{{lang_openingtimes}}</th>
</tr>
</thead>
@@ -29,12 +29,12 @@
<td {{#backendMissing}}class="text-danger"{{/backendMissing}}>
{{backend}}
</td>
- <td>
+ <td class="text-center">
{{#backend}}
{{lastCalendarUpdate}}
{{/backend}}
</td>
- <td>
+ <td class="text-center">
<span class="glyphicon glyphicon-{{openingGlyph}}"></span>
</td>
</tr>
diff --git a/modules-available/locationinfo/templates/page-panels.html b/modules-available/locationinfo/templates/page-panels.html
index 2bccc796..b30e7c87 100644
--- a/modules-available/locationinfo/templates/page-panels.html
+++ b/modules-available/locationinfo/templates/page-panels.html
@@ -9,16 +9,13 @@
<th>{{lang_panelType}}</th>
<th>{{lang_locations}}</th>
{{#hasRunmode}}
- <th class="slx-smallcol">{{lang_runmodeTHead}}</th>
+ <th class="text-center slx-smallcol">{{lang_runmodeTHead}}</th>
{{/hasRunmode}}
- <th class="slx-smallcol">{{lang_edit}}</th>
- <th class="slx-smallcol">{{lang_delete}}</th>
+ <th class="text-center slx-smallcol">{{lang_edit}}</th>
+ <th class="text-center slx-smallcol">{{lang_delete}}</th>
</tr>
</thead>
<tbody>
- <form method="post" action="?do=locationinfo" onsubmit="return confirm('{{lang_areYouSure}}')">
- <input type="hidden" name="token" value="{{token}}">
- <input type="hidden" name="action" value="deletePanel">
{{#panels}}
<tr>
<td>
@@ -33,7 +30,7 @@
{{#locationurl}}</a>{{/locationurl}}
</td>
{{#hasRunmode}}
- <td>
+ <td class="text-center" style="vertical-align: middle">
<a class="btn btn-default btn-xs {{runmode_disabled}}"
href="?do=runmode&amp;module=locationinfo&amp;modeid={{paneluuid}}&amp;redirect=?do=locationinfo">
<span class="glyphicon glyphicon-edit"></span>
@@ -41,20 +38,19 @@
{{assignedMachineCount}}
</td>
{{/hasRunmode}}
- <td>
+ <td class="text-center" style="vertical-align: middle">
<a class="btn btn-default btn-xs {{edit_disabled}}"
href="?do=locationinfo&amp;show=edit-panel&amp;uuid={{paneluuid}}">
<span class="glyphicon glyphicon-cog"></span>
</a>
</td>
- <td>
- <button type="submit" name="uuid" value="{{paneluuid}}" class="btn btn-danger btn-xs" {{edit_disabled}}>
+ <td class="text-center" style="vertical-align: middle">
+ <button type="button" class="btn btn-xs btn-danger" data-toggle="modal" data-target="#deleteModal" onclick="deletePanel('{{paneluuid}}')" {{edit_disabled}}>
<span class="glyphicon glyphicon-trash"></span>
</button>
</td>
</tr>
{{/panels}}
- </form>
</tbody>
</table>
@@ -71,4 +67,33 @@
<span class="glyphicon glyphicon-plus"></span>
{{lang_urlPanel}}
</a>
-</div> \ No newline at end of file
+</div>
+
+<form method="post" action="?do=locationinfo">
+<input type="hidden" name="token" value="{{token}}">
+
+<div class ="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ <h4 class="modal-title" id="myModalLabel">{{lang_delete}}</h4>
+ </div>
+ <div class="modal-body">
+ <p>{{lang_deleteConfirmation}}</p>
+ </div>
+ <div class="modal-footer">
+ <input type="hidden" id="delete-panel-id" name="uuid" value="">
+ <button type="button" class="btn btn-default" data-dismiss="modal">{{lang_cancel}}</button>
+ <button type="submit" name="action" value="deletePanel" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> {{lang_delete}}</button>
+ </div>
+ </div>
+ </div>
+</div>
+</form>
+
+<script>
+ function deletePanel(panelid) {
+ $("#delete-panel-id").val(panelid);
+ }
+</script> \ No newline at end of file
diff --git a/modules-available/locationinfo/templates/page-servers.html b/modules-available/locationinfo/templates/page-servers.html
index 6b02826e..46a148ce 100644
--- a/modules-available/locationinfo/templates/page-servers.html
+++ b/modules-available/locationinfo/templates/page-servers.html
@@ -2,50 +2,67 @@
<p>{{lang_serverTableHints}}</p>
-<table class="table table-hover">
- <thead>
- <tr>
- <th width="1">{{lang_serverType}}</th>
- <th>{{lang_locationName}}</th>
- <th width="1"></th>
- <th width="1"></th>
- </tr>
- </thead>
- <tbody>
- {{#serverlist}}
- <form method="post" action="?do=locationinfo">
- <input type="hidden" name="token" value="{{token}}">
- <input type="hidden" name="serverid" value="{{serverid}}">
+<form method="post" action="?do=locationinfo">
+ <table class="table table-hover">
+ <thead>
<tr>
- <td nowrap>{{typename}}</td>
- <td nowrap>{{servername}}</td>
-
- <td align="center" nowrap>
- <button class="btn btn-xs {{^autherror}}btn-default{{/autherror}}{{#autherror}}btn-danger{{/autherror}}"
- data-server-edit="{{serverid}}" {{disabled}} {{perms.backend.edit.disabled}} type="button">
- <span class="glyphicon glyphicon-cog"></span>
- {{lang_edit}}
- </button>
- <button class="btn btn-xs btn-primary server-check" {{disabled}} {{perms.backend.check.disabled}}
- name="action" value="checkConnection" type="submit">
- <span class="glyphicon glyphicon-refresh"></span>
- {{lang_checkConnection}}
- </button>
- </td>
- <td align="center" nowrap>
- <button class="btn btn-xs btn-danger server-delete" type="submit" name="action" value="deleteServer"
- {{perms.backend.edit.disabled}}>
- <span class="glyphicon glyphicon-trash"></span>
- {{lang_delete}}
- </button>
- </td>
+ <th class="text-center slx-smallcol">{{lang_serverType}}</th>
+ <th>{{lang_locationName}}</th>
+ <th class="text-center slx-smallcol">{{lang_edit}}</th>
+ <th class="text-center slx-smallcol">{{lang_checkConnection}}</th>
+ <th class="text-center slx-smallcol">{{lang_delete}}</th>
</tr>
- </form>
- {{/serverlist}}
- </tbody>
-</table>
+ </thead>
+ <tbody>
+ {{#serverlist}}
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="serverid" value="{{serverid}}">
+ <tr>
+ <td nowrap>{{typename}}</td>
+ <td nowrap>{{servername}}</td>
+
+ <td class="text-center">
+ <button class="btn btn-xs {{^autherror}}btn-default{{/autherror}}{{#autherror}}btn-danger{{/autherror}}"
+ data-server-edit="{{serverid}}" {{disabled}} {{perms.backend.edit.disabled}} type="button">
+ <span class="glyphicon glyphicon-cog"></span>
+ </button>
+ </td>
+ <td class="text-center">
+ <button class="btn btn-xs btn-primary server-check" {{disabled}} {{perms.backend.check.disabled}}
+ name="action" value="checkConnection" type="submit">
+ <span class="glyphicon glyphicon-refresh"></span>
+ </button>
+ </td>
+ <td class="text-center">
+ <button type="button" class="btn btn-xs btn-danger" data-toggle="modal" data-target="#deleteModal" {{perms.backend.edit.disabled}}>
+ <span class="glyphicon glyphicon-trash"></span>
+ </button>
+ </td>
+ </tr>
+ {{/serverlist}}
+ </tbody>
+ </table>
+
+ <div class ="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ <h4 class="modal-title" id="myModalLabel">{{lang_delete}}</h4>
+ </div>
+ <div class="modal-body">
+ <p>{{lang_deleteConfirmation}}</p>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">{{lang_cancel}}</button>
+ <button type="submit" name="action" value="deleteServer" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> {{lang_delete}}</button>
+ </div>
+ </div>
+ </div>
+ </div>
+</form>
-<div>
+<div class="text-right">
<button class="btn btn-sm btn-success" id="addServerButton" onclick="addServer()">
<span class="glyphicon glyphicon-plus"></span>
{{lang_addServer}}
@@ -54,7 +71,6 @@
<div class="modal fade" id="myModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
-
<div class="modal-content">
<div class="modal-header" id="myModalHeader"></div>
<div class="modal-body" id="myModalBody"></div>
@@ -63,7 +79,6 @@
<button type="submit" id="myModalSubmitButton" class="btn btn-primary" form="">{{lang_save}}</button>
</div>
</div>
-
</div>
</div>
diff --git a/modules-available/roomplanner/page.inc.php b/modules-available/roomplanner/page.inc.php
index 023cef73..8b75499b 100644
--- a/modules-available/roomplanner/page.inc.php
+++ b/modules-available/roomplanner/page.inc.php
@@ -108,6 +108,7 @@ class Page_Roomplanner extends Page
if ($this->action === 'getmachines') {
+ User::load();
$locations = User::getAllowedLocations('edit');
if (empty($locations)) {
die('{"machines":[]}');
diff --git a/modules-available/runmode/templates/machine-selector.html b/modules-available/runmode/templates/machine-selector.html
index 8b608f7e..665c56f3 100644
--- a/modules-available/runmode/templates/machine-selector.html
+++ b/modules-available/runmode/templates/machine-selector.html
@@ -13,6 +13,7 @@
<select id="machine-sel" name="machines[]" multiple {{disabled}}>
</select>
<div class="buttonbar">
+ <button type="button" class="btn btn-default" onClick="javascript:history.back()">{{lang_cancel}}</button>
<button type="submit" class="btn btn-primary" {{disabled}}>{{lang_save}}</button>
</div>
</form>
diff --git a/modules-available/statistics/api.inc.php b/modules-available/statistics/api.inc.php
index a7a636b3..8f5e9fd0 100644
--- a/modules-available/statistics/api.inc.php
+++ b/modules-available/statistics/api.inc.php
@@ -267,6 +267,10 @@ if ($type{0} === '~') {
foreach ($screens as $port => $screen) {
if (!array_key_exists('name', $screen))
continue;
+ // Filter bogus data
+ $screen['name'] = iconv('UTF-8', 'UTF-8//IGNORE', $screen['name']);
+ if (empty($screen['name']))
+ continue;
if (array_key_exists($screen['name'], $hwids)) {
$hwid = $hwids[$screen['name']];
} else {
diff --git a/modules-available/sysconfig/addmodule_adauth.inc.php b/modules-available/sysconfig/addmodule_adauth.inc.php
index 7d30e15b..fa2f5258 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', 'mapping');
+ $ADAUTH_COMMON_FIELDS = array('title', 'server', 'searchbase', 'binddn', 'bindpw', 'home', 'homeattr', 'ssl', 'fixnumeric', 'genuid', 'certificate', 'mapping');
$data = array();
if ($this->edit !== false) {
moduleToArray($this->edit, $data, $ADAUTH_COMMON_FIELDS);
@@ -107,6 +107,7 @@ class AdAuth_CheckConnection extends AddModule_Base
'home' => Request::post('home'),
'ssl' => Request::post('ssl'),
'fixnumeric' => Request::post('fixnumeric'),
+ 'genuid' => Request::post('genuid'),
'certificate' => Request::post('certificate', ''),
'taskid' => $this->scanTask['id'],
'mapping' => ConfigModuleBaseLdap::getMapping($mapping),
@@ -208,6 +209,7 @@ class AdAuth_SelfSearch extends AddModule_Base
'home' => Request::post('home'),
'ssl' => Request::post('ssl') === 'on',
'fixnumeric' => Request::post('fixnumeric'),
+ 'genuid' => Request::post('genuid'),
'fingerprint' => Request::post('fingerprint'),
'certificate' => Request::post('certificate', ''),
'originalbinddn' => $this->originalBindDn,
@@ -285,6 +287,7 @@ class AdAuth_HomeAttrCheck extends AddModule_Base
'home' => Request::post('home'),
'ssl' => Request::post('ssl') === 'on',
'fixnumeric' => Request::post('fixnumeric'),
+ 'genuid' => Request::post('genuid'),
'fingerprint' => Request::post('fingerprint'),
'certificate' => Request::post('certificate', ''),
'originalbinddn' => Request::post('originalbinddn'),
@@ -357,6 +360,7 @@ class AdAuth_CheckCredentials extends AddModule_Base
'homeattr' => Request::post('homeattr'),
'ssl' => Request::post('ssl') === 'on',
'fixnumeric' => Request::post('fixnumeric'),
+ 'genuid' => Request::post('genuid'),
'fingerprint' => Request::post('fingerprint'),
'certificate' => Request::post('certificate', ''),
'originalbinddn' => Request::post('originalbinddn'),
@@ -421,6 +425,7 @@ class AdAuth_HomeDir extends AddModule_Base
'homeattr' => Request::post('homeattr'),
'ssl' => Request::post('ssl') === 'on',
'fixnumeric' => Request::post('fixnumeric'),
+ 'genuid' => Request::post('genuid'),
'fingerprint' => Request::post('fingerprint'),
'certificate' => Request::post('certificate', ''),
'originalbinddn' => Request::post('originalbinddn'),
@@ -477,7 +482,7 @@ class AdAuth_Finish extends AddModule_Base
else
$module = $this->edit;
$ssl = Request::post('ssl', 'off') === 'on';
- foreach (['searchbase', 'binddn', 'server', 'bindpw', 'home', 'homeattr', 'certificate', 'fixnumeric',
+ foreach (['searchbase', 'binddn', 'server', 'bindpw', 'home', 'homeattr', 'certificate', 'fixnumeric', 'genuid',
'ldapAttrMountOpts', 'shareHomeMountOpts'] as $key) {
$module->setData($key, Request::post($key, '', 'string'));
}
diff --git a/modules-available/sysconfig/addmodule_ldapauth.inc.php b/modules-available/sysconfig/addmodule_ldapauth.inc.php
index 6490ff20..126a6549 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', 'homeattr', 'ssl', 'fixnumeric', 'certificate', 'mapping');
+ $LDAPAUTH_COMMON_FIELDS = array('title', 'server', 'searchbase', 'binddn', 'bindpw', 'home', 'homeattr', 'ssl', 'fixnumeric', 'genuid', 'certificate', 'mapping');
$data = array();
if ($this->edit !== false) {
moduleToArray($this->edit, $data, $LDAPAUTH_COMMON_FIELDS);
@@ -81,6 +81,7 @@ class LdapAuth_CheckConnection extends AddModule_Base
'home' => Request::post('home'),
'ssl' => Request::post('ssl'),
'fixnumeric' => Request::post('fixnumeric'),
+ 'genuid' => Request::post('genuid'),
'certificate' => Request::post('certificate', ''),
'taskid' => $this->scanTask['id'],
'mapping' => ConfigModuleBaseLdap::getMapping(Request::post('mapping', false, 'array')),
@@ -152,6 +153,7 @@ class LdapAuth_CheckCredentials extends AddModule_Base
'home' => Request::post('home'),
'ssl' => Request::post('ssl') === 'on',
'fixnumeric' => Request::post('fixnumeric'),
+ 'genuid' => Request::post('genuid'),
'fingerprint' => Request::post('fingerprint'),
'certificate' => Request::post('certificate', ''),
'mapping' => ConfigModuleBaseLdap::getMapping(Request::post('mapping', false, 'array')),
@@ -192,6 +194,7 @@ class LdapAuth_HomeDir extends AddModule_Base
'home' => Request::post('home'),
'ssl' => Request::post('ssl') === 'on',
'fixnumeric' => Request::post('fixnumeric'),
+ 'genuid' => Request::post('genuid'),
'fingerprint' => Request::post('fingerprint'),
'certificate' => Request::post('certificate', ''),
'originalbinddn' => Request::post('originalbinddn'),
@@ -248,7 +251,7 @@ class LdapAuth_Finish extends AddModule_Base
else
$module = $this->edit;
$ssl = Request::post('ssl', 'off') === 'on';
- foreach (['searchbase', 'binddn', 'server', 'bindpw', 'home', 'certificate', 'fixnumeric',
+ foreach (['searchbase', 'binddn', 'server', 'bindpw', 'home', 'certificate', 'fixnumeric', 'genuid',
'ldapAttrMountOpts', 'shareHomeMountOpts'] as $key) {
$module->setData($key, Request::post($key, '', 'string'));
}
diff --git a/modules-available/sysconfig/inc/configmodulebaseldap.inc.php b/modules-available/sysconfig/inc/configmodulebaseldap.inc.php
index c5df8697..9364c2e3 100644
--- a/modules-available/sysconfig/inc/configmodulebaseldap.inc.php
+++ b/modules-available/sysconfig/inc/configmodulebaseldap.inc.php
@@ -8,7 +8,7 @@ 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', 'mapping',
+ 'shareOther', 'shareHomeDrive', 'shareDomain', 'credentialPassthrough', 'mapping', 'genuid',
'ldapAttrMountOpts', 'shareHomeMountOpts');
public static function getMapping($config = false, &$empty = true)
@@ -67,6 +67,7 @@ abstract class ConfigModuleBaseLdap extends ConfigModule
if (!isset($config['fixnumeric'])) {
$config['fixnumeric'] = 's';
}
+ $config['genuid'] = isset($config['genuid']) && !empty($config['genuid']);
$this->preTaskmanagerHook($config);
$task = Taskmanager::submit('CreateLdapConfig', $config);
if (is_array($task) && isset($task['id'])) {
diff --git a/modules-available/sysconfig/lang/de/template-tags.json b/modules-available/sysconfig/lang/de/template-tags.json
index cea8e282..1a09abbd 100644
--- a/modules-available/sysconfig/lang/de/template-tags.json
+++ b/modules-available/sysconfig/lang/de/template-tags.json
@@ -72,7 +72,7 @@
"lang_moduleConfiguration": "Konfigurationsmodule",
"lang_moduleName": "Modulname",
"lang_moduleTitle": "Titel",
- "lang_mountOptionsNote": "Diese Einstellungen beziehen sich nur auf Linux und \u00e4hnliche Systeme (sowohl das MiiLinux als auch laufende VMs) und beeinflussen die Optionen, die beim Mounten des Verzeichnisses verwendet werden sollen. Sofern es im LDAP\/AD ein Nutzerattribut gibt, welches die passenden Optionen enth\u00e4lt, k\u00f6nnen Sie dieses hier angeben. Das Attribut wird dann vorrangig behandelt. Ist das Attribut leer oder nicht vorhanden, werden die Optionen verwendet, die Sie im Feld \"feste Mount-Optionen\" eingetragen haben. Sind beide Felder leer, werden verschiedene Optionen automatisch durchprobiert.",
+ "lang_mountOptionsNote": "Diese Einstellungen beziehen sich nur auf Linux und \u00e4hnliche Systeme (sowohl das MiniLinux als auch laufende VMs) und beeinflussen die Optionen, die beim Mounten des Verzeichnisses verwendet werden sollen. Sofern es im LDAP\/AD ein Nutzerattribut gibt, welches die passenden Optionen enth\u00e4lt, k\u00f6nnen Sie dieses hier angeben. Das Attribut wird dann vorrangig behandelt. Ist das Attribut leer oder nicht vorhanden, werden die Optionen verwendet, die Sie im Feld \"feste Mount-Optionen\" eingetragen haben. Sind beide Felder leer, werden verschiedene Optionen automatisch durchprobiert.",
"lang_name": "Name",
"lang_newConfiguration": "Neue Konfiguration",
"lang_newModule": "Neues Modul",
diff --git a/modules-available/sysconfig/templates/ad-selfsearch.html b/modules-available/sysconfig/templates/ad-selfsearch.html
index 64e181b2..e6a19468 100644
--- a/modules-available/sysconfig/templates/ad-selfsearch.html
+++ b/modules-available/sysconfig/templates/ad-selfsearch.html
@@ -43,6 +43,7 @@
<input type="hidden" name="mapping[{{field}}]" value="{{value}}">
{{/mapping}}
<input name="fixnumeric" value="{{fixnumeric}}" type="hidden">
+ <input name="genuid" value="{{genuid}}" type="hidden">
<button type="submit" class="btn btn-primary">&laquo; {{lang_back}}</button>
</form>
</div>
@@ -67,6 +68,7 @@
<input type="hidden" name="mapping[{{field}}]" value="{{value}}">
{{/mapping}}
<input name="fixnumeric" value="{{fixnumeric}}" type="hidden">
+ <input name="genuid" value="{{genuid}}" 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>
</form>
diff --git a/modules-available/sysconfig/templates/ad-start.html b/modules-available/sysconfig/templates/ad-start.html
index 7f211343..274473ff 100644
--- a/modules-available/sysconfig/templates/ad-start.html
+++ b/modules-available/sysconfig/templates/ad-start.html
@@ -67,6 +67,16 @@
<br>
<div>
<div class="checkbox">
+ <input id="num-cb" type="checkbox" name="genuid" {{#genuid}}checked{{/genuid}}>
+ <label for="num-cb"><b>{{lang_genUid}}</b></label>
+ </div>
+ <div>
+ <i>{{lang_genUidDescription}}</i>
+ </div>
+ </div>
+ <br>
+ <div>
+ <div class="checkbox">
<input id="num-cb" type="checkbox" name="fixnumeric" {{#fixnumeric}}checked{{/fixnumeric}}>
<label for="num-cb"><b>{{lang_fixNumeric}}</b></label>
</div>
diff --git a/modules-available/sysconfig/templates/ad_ldap-checkconnection.html b/modules-available/sysconfig/templates/ad_ldap-checkconnection.html
index 630da398..e686c29f 100644
--- a/modules-available/sysconfig/templates/ad_ldap-checkconnection.html
+++ b/modules-available/sysconfig/templates/ad_ldap-checkconnection.html
@@ -31,6 +31,7 @@
{{/mapping}}
<input name="fixnumeric" value="{{fixnumeric}}" type="hidden">
+ <input name="genuid" value="{{genuid}}" type="hidden">
<button type="submit" class="btn btn-primary">&laquo; {{lang_back}}</button>
</form>
</div>
@@ -55,6 +56,7 @@
<input type="hidden" name="mapping[{{field}}]" value="{{value}}">
{{/mapping}}
<input name="fixnumeric" value="{{fixnumeric}}" type="hidden">
+ <input name="genuid" value="{{genuid}}" 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>
</form>
diff --git a/modules-available/sysconfig/templates/ad_ldap-checkcredentials.html b/modules-available/sysconfig/templates/ad_ldap-checkcredentials.html
index 4f822a9b..d698d994 100644
--- a/modules-available/sysconfig/templates/ad_ldap-checkcredentials.html
+++ b/modules-available/sysconfig/templates/ad_ldap-checkcredentials.html
@@ -26,6 +26,7 @@
<input type="hidden" name="mapping[{{field}}]" value="{{value}}">
{{/mapping}}
<input name="fixnumeric" value="{{fixnumeric}}" type="hidden">
+ <input name="genuid" value="{{genuid}}" type="hidden">
<button type="submit" class="btn btn-primary">&laquo; {{lang_back}}</button>
</form>
</div>
@@ -49,6 +50,7 @@
<input type="hidden" name="mapping[{{field}}]" value="{{value}}">
{{/mapping}}
<input name="fixnumeric" value="{{fixnumeric}}" type="hidden">
+ <input name="genuid" value="{{genuid}}" type="hidden">
<input name="fingerprint" value="{{fingerprint}}" type="hidden">
<input name="originalbinddn" value="{{binddn}}" 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_ldap-homedir.html b/modules-available/sysconfig/templates/ad_ldap-homedir.html
index a8c9441a..e4fbf380 100644
--- a/modules-available/sysconfig/templates/ad_ldap-homedir.html
+++ b/modules-available/sysconfig/templates/ad_ldap-homedir.html
@@ -18,6 +18,7 @@
<input type="hidden" name="mapping[{{field}}]" value="{{value}}">
{{/mapping}}
<input name="fixnumeric" value="{{fixnumeric}}" type="hidden">
+ <input name="genuid" value="{{genuid}}" type="hidden">
<input name="fingerprint" value="{{fingerprint}}" type="hidden">
<div class="slx-bold">{{lang_credentialPassing}}</div>
diff --git a/modules-available/sysconfig/templates/ldap-start.html b/modules-available/sysconfig/templates/ldap-start.html
index 940316b9..b3495741 100644
--- a/modules-available/sysconfig/templates/ldap-start.html
+++ b/modules-available/sysconfig/templates/ldap-start.html
@@ -68,6 +68,16 @@
<br>
<div>
<div class="checkbox">
+ <input id="num-cb" type="checkbox" name="genuid" {{#genuid}}checked{{/genuid}}>
+ <label for="num-cb"><b>{{lang_genUid}}</b></label>
+ </div>
+ <div>
+ <i>{{lang_genUidDescription}}</i>
+ </div>
+ </div>
+ <br>
+ <div>
+ <div class="checkbox">
<input id="num-cb" type="checkbox" name="fixnumeric" {{#fixnumeric}}checked{{/fixnumeric}}>
<label for="num-cb"><b>{{lang_fixNumeric}}</b></label>
</div>
diff --git a/modules-available/syslog/api.inc.php b/modules-available/syslog/api.inc.php
index 18c42c31..945f9d09 100644
--- a/modules-available/syslog/api.inc.php
+++ b/modules-available/syslog/api.inc.php
@@ -1,5 +1,44 @@
<?php
+// Check for user data export
+if (($user = Request::post('export-user', false, 'string')) !== false) {
+ User::load();
+ User::assertPermission('export-user-data', null, '?do=syslog');
+ if (!Util::verifyToken()) {
+ die('Invalid Token');
+ }
+ $puser = preg_quote($user);
+ $exp = "$puser logged|^\[$puser\]";
+ Header('Content-Type: text/plain; charset=utf-8');
+ Header('Content-Disposition: attachment; filename=bwlehrpool-export-' .Util::sanitizeFilename($user) . '-' . date('Y-m-d') . '.txt');
+ $srcs = [];
+ $srcs[] = ['res' => Database::simpleQuery("SELECT dateline, logtypeid AS typeid, clientip, description FROM clientlog
+ WHERE description REGEXP :exp
+ ORDER BY dateline ASC", ['exp' => $exp])];
+ if (Module::get('statistics') !== false) {
+ $srcs[] = ['res' => Database::simpleQuery("SELECT dateline, typeid, clientip, data AS description FROM statistic
+ WHERE username = :user
+ ORDER BY dateline ASC", ['user' => $user])];
+ }
+ echo "# Begin log\n";
+ for (;;) {
+ unset($best);
+ foreach ($srcs as &$src) {
+ if (!isset($src['row'])) {
+ $src['row'] = $src['res']->fetch(PDO::FETCH_ASSOC);
+ }
+ if ($src['row'] !== false && (!isset($best) || $src['row']['dateline'] < $best['dateline'])) {
+ $best =& $src['row'];
+ }
+ }
+ if (!isset($best))
+ break;
+ echo date('Y-m-d H:i:s', $best['dateline']), "\t", $best['typeid'], "\t", $best['clientip'], "\t", $best['description'], "\n";
+ $best = null; // so we repopulate on next iteration
+ }
+ die("# End log\n");
+}
+
if (empty($_POST['type'])) die('Missing options.');
$type = mb_strtolower($_POST['type']);
diff --git a/modules-available/syslog/lang/de/template-tags.json b/modules-available/syslog/lang/de/template-tags.json
index b5c6f8c7..c00d619a 100644
--- a/modules-available/syslog/lang/de/template-tags.json
+++ b/modules-available/syslog/lang/de/template-tags.json
@@ -5,8 +5,12 @@
"lang_clientLog": "Client Log",
"lang_details": "Details",
"lang_event": "Ereignis",
+ "lang_export": "Exportieren",
+ "lang_exportUserDesc": "Mit dieser Funktion k\u00f6nnen Sie alle in der Datenbank vorhandenen Datens\u00e4tze zu einem bestimmten Benutzer exportieren. Bitte geben Sie den Benutzernamen genau so ein, wie ihn der Nutzer beim Login am Client angeben muss.",
"lang_filter": "Filter",
"lang_not": "not",
"lang_settings": "Einstellungen",
+ "lang_userExport": "Nutzer-Export",
+ "lang_userLogin": "Benutzer-Login",
"lang_when": "Wann"
} \ No newline at end of file
diff --git a/modules-available/syslog/lang/en/template-tags.json b/modules-available/syslog/lang/en/template-tags.json
index 1aae1fe9..24e9aaa1 100644
--- a/modules-available/syslog/lang/en/template-tags.json
+++ b/modules-available/syslog/lang/en/template-tags.json
@@ -5,8 +5,12 @@
"lang_clientLog": "Client Log",
"lang_details": "Details",
"lang_event": "Event",
+ "lang_export": "Export",
+ "lang_exportUserDesc": "This exports all data from the database relating to the given user login. Please specify the user name exactly the way they would provide it when logging in on a client.",
"lang_filter": "Filter",
"lang_not": "not",
"lang_settings": "Settings",
+ "lang_userExport": "User export",
+ "lang_userLogin": "User login",
"lang_when": "When"
} \ No newline at end of file
diff --git a/modules-available/syslog/page.inc.php b/modules-available/syslog/page.inc.php
index 00c55a3f..6c1a0a16 100644
--- a/modules-available/syslog/page.inc.php
+++ b/modules-available/syslog/page.inc.php
@@ -31,7 +31,7 @@ class Page_SysLog extends Page
protected function doRender()
{
$data = ['anondays' => Property::get(self::PROP_ANON_DAYS, 0)];
- Permission::addGlobalTags($data['perms'], NULL, ['configure-anonymization']);
+ Permission::addGlobalTags($data['perms'], NULL, ['configure-anonymization', 'export-user-data']);
Render::addTemplate("heading", $data);
if (!User::hasPermission("view")) {
diff --git a/modules-available/syslog/permissions/permissions.json b/modules-available/syslog/permissions/permissions.json
index cabf82f9..1f2373d4 100644
--- a/modules-available/syslog/permissions/permissions.json
+++ b/modules-available/syslog/permissions/permissions.json
@@ -4,5 +4,8 @@
},
"configure-anonymization": {
"location-aware": false
+ },
+ "export-user-data": {
+ "location-aware": false
}
} \ No newline at end of file
diff --git a/modules-available/syslog/templates/heading.html b/modules-available/syslog/templates/heading.html
index 2ab1a848..8dd3d440 100644
--- a/modules-available/syslog/templates/heading.html
+++ b/modules-available/syslog/templates/heading.html
@@ -1,7 +1,13 @@
<div class="page-header">
- <button type="button" class="btn btn-default pull-right" data-toggle="modal" data-target="#modal-settings">
- <span class="glyphicon glyphicon-cog"></span> {{lang_settings}}
- </button>
+ <span class="buttonbar pull-right">
+ <button type="button" class="btn btn-default" data-toggle="modal" data-target="#modal-export" {{perms.export-user-data.disabled}}>
+ <span class="glyphicon glyphicon-export"></span>
+ {{lang_userExport}}
+ </button>
+ <button type="button" class="btn btn-default" data-toggle="modal" data-target="#modal-settings">
+ <span class="glyphicon glyphicon-cog"></span> {{lang_settings}}
+ </button>
+ </span>
<div id="modal-settings" class="modal fade" role="dialog">
<div class="modal-dialog">
@@ -31,5 +37,32 @@
</div>
</div>
+ <div id="modal-export" class="modal fade" role="dialog">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <form method="post" action="api.php?do=syslog">
+ <input type="hidden" name="token" value="{{token}}">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal">&times;</button>
+ <h4 class="modal-title"><b>{{lang_userExport}}</b></h4>
+ </div>
+ <div class="modal-body">
+ <p>{{lang_exportUserDesc}}</p>
+ <label for="export-user-name">{{lang_userLogin}}</label>
+ <input id="export-user-name" type="text" name="export-user" class="form-control" style="width:100%">
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">{{lang_cancel}}</button>
+ <button type="submit" class="btn btn-primary">
+ <span class="glyphicon glyphicon-export"></span>
+ {{lang_export}}
+ </button>
+ </div>
+ </form>
+ </div>
+
+ </div>
+ </div>
+
<h1>{{lang_clientLog}}</h1>
</div> \ No newline at end of file