diff options
author | Simon Rettberg | 2018-02-09 12:12:31 +0100 |
---|---|---|
committer | Simon Rettberg | 2018-02-09 12:12:31 +0100 |
commit | d57ee1cfdd99fb4e44f5ce6d92d6712c30ff0aa4 (patch) | |
tree | c1030efbdb837f53651272e015a3ab5562f145c8 /modules-available | |
parent | [systemstatus] lang-descriptions for permissions (diff) | |
parent | [inc/User] Add locationid 0 to allowed locations in fallback mode (diff) | |
download | slx-admin-d57ee1cfdd99fb4e44f5ce6d92d6712c30ff0aa4.tar.gz slx-admin-d57ee1cfdd99fb4e44f5ce6d92d6712c30ff0aa4.tar.xz slx-admin-d57ee1cfdd99fb4e44f5ce6d92d6712c30ff0aa4.zip |
Merge branch 'master' into permission-manager
Diffstat (limited to 'modules-available')
90 files changed, 1111 insertions, 629 deletions
diff --git a/modules-available/adduser/lang/de/template-tags.json b/modules-available/adduser/lang/de/template-tags.json index 0f446025..99d17947 100644 --- a/modules-available/adduser/lang/de/template-tags.json +++ b/modules-available/adduser/lang/de/template-tags.json @@ -2,7 +2,5 @@ "lang_confirmation": "Wiederholen", "lang_createUser": "Benutzer anlegen", "lang_fullName": "Vollst\u00e4ndiger Name", - "lang_password": "Passwort", - "lang_telephone": "Telefon", - "lang_username": "Benutzerkennung" -}
\ No newline at end of file + "lang_telephone": "Telefon" +} diff --git a/modules-available/adduser/lang/en/template-tags.json b/modules-available/adduser/lang/en/template-tags.json index 42bae6dc..24f8cd42 100644 --- a/modules-available/adduser/lang/en/template-tags.json +++ b/modules-available/adduser/lang/en/template-tags.json @@ -2,7 +2,5 @@ "lang_confirmation": "Confirm Password", "lang_createUser": "Create User", "lang_fullName": "Full Name", - "lang_password": "Password", - "lang_telephone": "Telephone", - "lang_username": "Username" -}
\ No newline at end of file + "lang_telephone": "Telephone" +} diff --git a/modules-available/adduser/lang/pt/template-tags.json b/modules-available/adduser/lang/pt/template-tags.json index 524f3dd5..ba204937 100644 --- a/modules-available/adduser/lang/pt/template-tags.json +++ b/modules-available/adduser/lang/pt/template-tags.json @@ -2,7 +2,5 @@ "lang_confirmation": "Confirmar Senha", "lang_createUser": "Criar Usu\u00e1rio", "lang_fullName": "Nome Completo", - "lang_password": "Senha", - "lang_telephone": "Telefone", - "lang_username": "Nome de Usu\u00e1rio" -}
\ No newline at end of file + "lang_telephone": "Telefone" +} diff --git a/modules-available/baseconfig/api.inc.php b/modules-available/baseconfig/api.inc.php index a4024c5e..853c6f51 100644 --- a/modules-available/baseconfig/api.inc.php +++ b/modules-available/baseconfig/api.inc.php @@ -16,6 +16,8 @@ class ConfigHolder private static $context = ''; + private static $postHooks = []; + public static function setContext($name) { self::$context = $name; @@ -54,8 +56,26 @@ class ConfigHolder return self::$config[$key][0]['value']; } + /** + * @param callable $func + */ + public static function addPostHook($func) + { + self::$postHooks[] = array('context' => self::$context, 'function' => $func); + } + + public static function applyPostHooks() + { + foreach (self::$postHooks as $hook) { + self::$context = $hook['context'] . ':post'; + $hook['function'](); + } + self::$postHooks = []; + } + public static function getConfig() { + self::applyPostHooks(); $ret = []; foreach (self::$config as $key => $list) { if ($list[0]['value'] === false) @@ -67,6 +87,7 @@ class ConfigHolder public static function outputConfig() { + self::applyPostHooks(); foreach (self::$config as $key => $list) { echo '##', $key, "\n"; foreach ($list as $pos => $item) { diff --git a/modules-available/dnbd3/hooks/runmode/config.json b/modules-available/dnbd3/hooks/runmode/config.json index 095cb42f..a3f6d01f 100644 --- a/modules-available/dnbd3/hooks/runmode/config.json +++ b/modules-available/dnbd3/hooks/runmode/config.json @@ -2,5 +2,7 @@ "isClient": false, "configHook": "Dnbd3Util::runmodeConfigHook", "noSysconfig": true, - "systemdDefaultTarget": "dnbd3-proxy" + "systemdDefaultTarget": "dnbd3-proxy", + "allowGenericEditor": true, + "deleteUrlSnippet": "dummyparam=" }
\ No newline at end of file diff --git a/modules-available/dnbd3/hooks/statistics/machine-replace.inc.php b/modules-available/dnbd3/hooks/statistics/machine-replace.inc.php new file mode 100644 index 00000000..5e4c4e75 --- /dev/null +++ b/modules-available/dnbd3/hooks/statistics/machine-replace.inc.php @@ -0,0 +1,6 @@ +<?php + +foreach ($list as $entry) { + unset($entry['datelimit']); + Database::exec('UPDATE IGNORE dnbd3_server SET machineuuid = :new WHERE machineuuid = :old', $entry); +} diff --git a/modules-available/dozmod/lang/de/messages.json b/modules-available/dozmod/lang/de/messages.json index 47580bcb..60c37927 100644 --- a/modules-available/dozmod/lang/de/messages.json +++ b/modules-available/dozmod/lang/de/messages.json @@ -8,5 +8,7 @@ "nothing-submitted": "Es wurde nichts \u00fcbermittelt", "runtimelimits-config-saved": "Einstellungen gespeichert", "templates-saved": "Templates wurden gespeichert", - "timeout": "Zeit\u00fcberschreitung" + "timeout": "Zeit\u00fcberschreitung", + "unknown-targetid": "Target {{0}} nicht bekannt", + "unknown-userid": "Unbekannter Nutzer, {{0}}" }
\ No newline at end of file diff --git a/modules-available/dozmod/lang/de/template-tags.json b/modules-available/dozmod/lang/de/template-tags.json index 4b49579a..a1c23d2e 100644 --- a/modules-available/dozmod/lang/de/template-tags.json +++ b/modules-available/dozmod/lang/de/template-tags.json @@ -54,7 +54,6 @@ "lang_organizationListHeader": "Nutzungsrechte f\u00fcr den Satelliten festlegen", "lang_os": "Betriebssystem", "lang_owner": "Besitzer", - "lang_password": "Passwort", "lang_passwordplaceholder": "SMTP Passwort", "lang_placeholders": "Platzhalter", "lang_port": "Port", @@ -87,8 +86,7 @@ "lang_userList": "Benutzerliste", "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_username": "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 a17eae37..6d7ea0ac 100644 --- a/modules-available/dozmod/lang/en/messages.json +++ b/modules-available/dozmod/lang/en/messages.json @@ -8,5 +8,7 @@ "nothing-submitted": "There was nothing submitted", "runtimelimits-config-saved": "Configuration saved successfully", "templates-saved": "Templates saved successfully", - "timeout": "Timeout" + "timeout": "Timeout", + "unknown-targetid": "Unknown target id {{0}}", + "unknown-userid": "Unknown user id {{0}}" }
\ No newline at end of file diff --git a/modules-available/dozmod/lang/en/template-tags.json b/modules-available/dozmod/lang/en/template-tags.json index ed8f3465..f12e4ab8 100644 --- a/modules-available/dozmod/lang/en/template-tags.json +++ b/modules-available/dozmod/lang/en/template-tags.json @@ -54,7 +54,6 @@ "lang_organizationListHeader": "Set access permissions for organizations", "lang_os": "Operating System", "lang_owner": "Owner", - "lang_password": "Password", "lang_passwordplaceholder": "SMTP Password", "lang_placeholders": "Placeholders", "lang_port": "Port", @@ -87,8 +86,7 @@ "lang_userList": "User List", "lang_userListDescription": "Here you can promote \"super users\", which will have all permissions in the bwLehrpool-Suite. You can also ban users from accessing this server via the bwLehrpool-Suite.", "lang_userListHeader": "Users known to this satellite", - "lang_username": "Username", "lang_usernameplaceholder": "SMTP Username", "lang_version": "Version timestamp", "lang_when": "When" -}
\ No newline at end of file +} diff --git a/modules-available/exams/baseconfig/getconfig.inc.php b/modules-available/exams/baseconfig/getconfig.inc.php index 37a2caf4..e5dd01c9 100644 --- a/modules-available/exams/baseconfig/getconfig.inc.php +++ b/modules-available/exams/baseconfig/getconfig.inc.php @@ -15,5 +15,4 @@ if (Exams::isInExamMode($locationIds, $lectureId, $autoLogin)) { ConfigHolder::add('SLX_AUTOLOGIN', $autoLogin, 10000); } ConfigHolder::add('SLX_SYSTEMD_TARGET', 'exam-mode', 10000); - ConfigHolder::add("SLX_PVS_HYBRID", false, 10000); } diff --git a/modules-available/exams/templates/page-exams.html b/modules-available/exams/templates/page-exams.html index bb6cbd0a..201a5733 100644 --- a/modules-available/exams/templates/page-exams.html +++ b/modules-available/exams/templates/page-exams.html @@ -5,7 +5,7 @@ </div> <div class="panel-body"> <div class="slx-space"> - <table class="table table-bordered stupidtable"> + <table class="table stupidtable"> <thead> <tr> <th data-sort="int">{{lang_id}}</th> diff --git a/modules-available/locationinfo/inc/locationinfo.inc.php b/modules-available/locationinfo/inc/locationinfo.inc.php index e3d7db3d..38e271fe 100644 --- a/modules-available/locationinfo/inc/locationinfo.inc.php +++ b/modules-available/locationinfo/inc/locationinfo.inc.php @@ -130,6 +130,9 @@ class LocationInfo 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); + } } ConfigHolder::add('SLX_BROWSER_URL', 'http://' . $_SERVER['SERVER_ADDR'] . '/panel/' . $panelUuid); ConfigHolder::add('SLX_ADDONS', '', 1000); diff --git a/modules-available/locationinfo/lang/de/template-tags.json b/modules-available/locationinfo/lang/de/template-tags.json index b305f800..bcdf7148 100644 --- a/modules-available/locationinfo/lang/de/template-tags.json +++ b/modules-available/locationinfo/lang/de/template-tags.json @@ -71,6 +71,8 @@ "lang_prettytimeTooltip": "Verwende ein anderes Anzeigeformat f\u00fcr die Uhrzeit", "lang_recursiveServerSet": "Auch f\u00fcr alle untergeordneten R\u00e4ume setzen", "lang_recursiveSetTooltip": "Wenn aktiviert, wird der Backend-Server auch f\u00fcr alle untergeordneten R\u00e4ume auf den hier gew\u00e4hlten Wert gesetzt", + "lang_reloadIntervalMins": "Neuladen alle X Minuten", + "lang_reloadIntervalTooltip": "Setzen Sie dieses Feld auf einen Wert > 0 (in Minuten), um die Seite auf den Clients regelm\u00e4\u00dfig neu zu laden. Feld auf 0 setzen oder leer lassen deaktiviert diese Funktion.", "lang_remoteSchedule": "Abruf Belegungsplan", "lang_room": "Raum", "lang_roomId": "Raum ID", diff --git a/modules-available/locationinfo/lang/en/template-tags.json b/modules-available/locationinfo/lang/en/template-tags.json index 5679e8b8..558ddff0 100644 --- a/modules-available/locationinfo/lang/en/template-tags.json +++ b/modules-available/locationinfo/lang/en/template-tags.json @@ -71,6 +71,8 @@ "lang_prettytimeTooltip": "Use a different display format for the time", "lang_recursiveServerSet": "Also set for all child locations", "lang_recursiveSetTooltip": "If checked, all direct and indirect child locations will be configured to use the backend server selected above", + "lang_reloadIntervalMins": "Reload every X minutes", + "lang_reloadIntervalTooltip": "Set this field to a value > 0 (in minutes) to reload the page periodically. Set to 0 or leave blank to disable.", "lang_remoteSchedule": "Time table retrieval", "lang_room": "Room", "lang_roomId": "Room ID", diff --git a/modules-available/locationinfo/page.inc.php b/modules-available/locationinfo/page.inc.php index 22a21951..777b84db 100644 --- a/modules-available/locationinfo/page.inc.php +++ b/modules-available/locationinfo/page.inc.php @@ -362,6 +362,7 @@ class Page_LocationInfo extends Page $conf = array( 'url' => Request::post('url', 'https://www.bwlehrpool.de/', 'string'), 'insecure-ssl' => Request::post('insecure-ssl', 0, 'int'), + 'reload-minutes' => max(0, Request::post('reloadminutes', 0, 'int')), ); return array('config' => $conf, 'locationids' => []); } @@ -887,6 +888,7 @@ class Page_LocationInfo extends Page 'panelname' => $panel['panelname'], 'url' => $config['url'], 'ssl_checked' => $config['insecure-ssl'] ? 'checked' : '', + 'reloadminutes' => (int)$config['reload-minutes'], )); } else { Render::addTemplate('page-config-panel-summary', array( diff --git a/modules-available/locationinfo/style.css b/modules-available/locationinfo/style.css new file mode 100644 index 00000000..b5fffe75 --- /dev/null +++ b/modules-available/locationinfo/style.css @@ -0,0 +1,10 @@ +.btn-static { + background-color: white; + border: 1px solid lightgrey; + cursor: default; +} +.btn-static:active{ + -moz-box-shadow: inset 0 0 0px white; + -webkit-box-shadow: inset 0 0 0px white; + box-shadow: inset 0 0 0px white; +} diff --git a/modules-available/locationinfo/templates/ajax-config-location.html b/modules-available/locationinfo/templates/ajax-config-location.html index b42ff98d..102a6ea4 100644 --- a/modules-available/locationinfo/templates/ajax-config-location.html +++ b/modules-available/locationinfo/templates/ajax-config-location.html @@ -110,9 +110,9 @@ </select> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_serverTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_serverTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> <div class="row"> @@ -124,9 +124,9 @@ </div> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_recursiveSetTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_recursiveSetTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> <div class="row"> @@ -137,9 +137,9 @@ <input class="form-control" name="serverlocationid" id="serverlocationid" value="{{serverlocationid}}"> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_roomIdTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_roomIdTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> @@ -170,7 +170,7 @@ setTimepicker($('#settings-outer').find('.timepicker2')); - $('a.helptext').tooltip(); + $('p.helptext').tooltip(); $('#new-openingtime').click(function (e) { e.preventDefault(); diff --git a/modules-available/locationinfo/templates/ajax-config-server.html b/modules-available/locationinfo/templates/ajax-config-server.html index 940bc55a..8c2cb3ba 100644 --- a/modules-available/locationinfo/templates/ajax-config-server.html +++ b/modules-available/locationinfo/templates/ajax-config-server.html @@ -12,9 +12,9 @@ form="form-{{currentbackend}}"> </div> <div class="col-md-2"> - <a class="btn btn-default" title="{{lang_nameTooltip}}"> + <p class="btn btn-static" title="{{lang_nameTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -34,9 +34,9 @@ </select> </div> <div class="col-md-2"> - <a class="btn btn-default" id="help-type" title="{{lang_typeTooltip}}"> + <p class="btn btn-static" id="help-type" title="{{lang_typeTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -92,7 +92,7 @@ currentBackend = value; } - $('a.btn[title]').tooltip(); + $('p.btn[title]').tooltip(); $('#myModalSubmitButton').attr('form', 'form-' + currentBackend); $('.settings-bs-switch').bootstrapSwitch({size:'small'}); diff --git a/modules-available/locationinfo/templates/page-config-panel-default.html b/modules-available/locationinfo/templates/page-config-panel-default.html index b55e3d4d..41a8fd00 100644 --- a/modules-available/locationinfo/templates/page-config-panel-default.html +++ b/modules-available/locationinfo/templates/page-config-panel-default.html @@ -28,9 +28,9 @@ <input class="form-control" name="name" id="panel-title" type="text" value="{{panelname}}"> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_displayNameTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_displayNameTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -48,9 +48,9 @@ </select> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_languageTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_languageTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -69,9 +69,9 @@ </select> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_modeTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_modeTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -85,9 +85,9 @@ <input id="input-eco" type="checkbox" name="eco" {{eco_checked}}> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_ecoTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_ecoTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -101,9 +101,9 @@ <input id="input-prettytime" type="checkbox" name="prettytime" {{prettytime_checked}}> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_prettytimeTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_prettytimeTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -128,9 +128,9 @@ max="1440" value="{{calupdate}}" required> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_calupdateTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_calupdateTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -145,9 +145,9 @@ max="86400" value="{{roomupdate}}" required> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_roomupdateTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_roomupdateTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -177,9 +177,9 @@ <input id="input-vertical" type="checkbox" name="vertical" {{vertical_checked}}> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_verticalTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_verticalTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -193,9 +193,9 @@ <input id="scaledaysauto" type="checkbox" name="scaledaysauto" {{scaledaysauto_checked}}> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_autoscaleTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_autoscaleTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -217,9 +217,9 @@ </select> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_daysToShowTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_daysToShowTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -238,9 +238,9 @@ </select> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_rotationTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_rotationTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -255,9 +255,9 @@ <input id="input-scale" name="scale" type="range" step="1" min="10" max="90" value="{{scale}}"> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_scaleTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_scaleTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -272,9 +272,9 @@ <input id="input-switchtime" name="switchtime" type="range" step="1" min="1" max="120" value="{{switchtime}}"> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_switchTimeTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_switchTimeTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -338,7 +338,7 @@ document.addEventListener("DOMContentLoaded", function () { var $locInput = $('#locationids'); // Initialize fancy tooltips - $('a.helptext').tooltip(); + $('p.helptext').tooltip(); // Add listener to range sliders so their label can be updated $('input[type="range"]').change(function () { $(this).siblings().find('.range-display').text($(this).val()); diff --git a/modules-available/locationinfo/templates/page-config-panel-summary.html b/modules-available/locationinfo/templates/page-config-panel-summary.html index 2a968fc2..3dc93680 100644 --- a/modules-available/locationinfo/templates/page-config-panel-summary.html +++ b/modules-available/locationinfo/templates/page-config-panel-summary.html @@ -28,9 +28,9 @@ <input class="form-control" name="name" id="panel-title" type="text" value="{{panelname}}"> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_displayNameTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_displayNameTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -48,9 +48,9 @@ </select> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_languageTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_languageTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -64,9 +64,9 @@ <input id="input-eco" type="checkbox" name="eco" {{eco_checked}}> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_ecoTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_ecoTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -151,7 +151,7 @@ document.addEventListener("DOMContentLoaded", function () { var $locInput = $('#locationids'); // Initialize fancy tooltips - $('a.helptext').tooltip(); + $('p.helptext').tooltip(); // Add listener to range sliders so their label can be updated $('input[type="range"]').change(function () { $(this).siblings().find('.range-display').text($(this).val()); diff --git a/modules-available/locationinfo/templates/page-config-panel-url.html b/modules-available/locationinfo/templates/page-config-panel-url.html index 401214bd..798ff55d 100644 --- a/modules-available/locationinfo/templates/page-config-panel-url.html +++ b/modules-available/locationinfo/templates/page-config-panel-url.html @@ -25,9 +25,9 @@ <input class="form-control" name="name" id="panel-title" type="text" value="{{panelname}}"> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_displayNameTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_displayNameTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -42,9 +42,9 @@ placeholder="http://www.bwlehrpool.de/" pattern=".*://.*" required> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_urlTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_urlTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> @@ -55,15 +55,35 @@ <label for="input-ssl">{{lang_insecureSsl}}</label> </div> <div class="col-sm-7"> - <input id="input-ssl" type="checkbox" name="insecure-ssl" {{ssl_checked}} value="1"> + <div class="checkbox"> + <input id="input-ssl" type="checkbox" name="insecure-ssl" {{ssl_checked}} value="1"> + <label></label> + </div> </div> <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_ignoreSslTooltip}}"> + <p class="btn btn-static helptext" title="{{lang_ignoreSslTooltip}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> </div> </div> </div> + + <div class="list-group-item"> + <div class="row"> + <div class="col-sm-3"> + <label for="input-reload">{{lang_reloadIntervalMins}}</label> + </div> + <div class="col-sm-7"> + <input class="form-control" id="input-reload" type="number" min="0" max="999" name="reloadminutes" pattern="\d*" value="{{reloadminutes}}"> + </div> + <div class="col-sm-2"> + <p class="btn btn-static helptext" title="{{lang_reloadIntervalTooltip}}"> + <span class="glyphicon glyphicon-question-sign"></span> + </p> + </div> + </div> + </div> + </div> </div> </div> @@ -75,6 +95,8 @@ <script type="text/javascript"><!-- document.addEventListener("DOMContentLoaded", function () { + // Initialize fancy tooltips + $('p.helptext').tooltip(); }); diff --git a/modules-available/locationinfo/templates/page-locations.html b/modules-available/locationinfo/templates/page-locations.html index b30ef2ed..37d8dd96 100644 --- a/modules-available/locationinfo/templates/page-locations.html +++ b/modules-available/locationinfo/templates/page-locations.html @@ -50,7 +50,7 @@ <div class="modal-body"></div> <div class="modal-footer"> <button type="submit" class="btn btn-primary">{{lang_save}}</button> - <a class="btn btn-primary" data-dismiss="modal">{{lang_close}}</a> + <a class="btn btn-default" data-dismiss="modal">{{lang_close}}</a> </div> </form> </div> @@ -103,4 +103,4 @@ document.addEventListener("DOMContentLoaded", function () { $('#settings-form').submit(submitLocationSettings); }); -//--></script>
\ No newline at end of file +//--></script> diff --git a/modules-available/locationinfo/templates/page-servers.html b/modules-available/locationinfo/templates/page-servers.html index eefb1ed5..dbf18b5f 100644 --- a/modules-available/locationinfo/templates/page-servers.html +++ b/modules-available/locationinfo/templates/page-servers.html @@ -60,7 +60,7 @@ <div class="modal-footer"> <button type="submit" id="myModalSubmitButton" class="btn btn-primary" form="">{{lang_save}}</button> - <a class="btn btn-primary" data-dismiss="modal">{{lang_close}}</a> + <a class="btn btn-default" data-dismiss="modal">{{lang_close}}</a> </div> </div> diff --git a/modules-available/locationinfo/templates/server-prop-bool.html b/modules-available/locationinfo/templates/server-prop-bool.html index f430d02c..bd9dcc64 100644 --- a/modules-available/locationinfo/templates/server-prop-bool.html +++ b/modules-available/locationinfo/templates/server-prop-bool.html @@ -7,10 +7,10 @@ </div> <div class="col-md-2"> {{#helptext}} - <a class="btn btn-default" title="{{helptext}}"> + <p class="btn btn-static" title="{{helptext}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> {{/helptext}} </div> </div> -</div>
\ No newline at end of file +</div> diff --git a/modules-available/locationinfo/templates/server-prop-dropdown.html b/modules-available/locationinfo/templates/server-prop-dropdown.html index 80667766..d1351551 100644 --- a/modules-available/locationinfo/templates/server-prop-dropdown.html +++ b/modules-available/locationinfo/templates/server-prop-dropdown.html @@ -10,10 +10,10 @@ </div> <div class="col-md-2"> {{#helptext}} - <a class="btn btn-default" title="{{helptext}}"> + <p class="btn btn-static" title="{{helptext}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> {{/helptext}} </div> </div> -</div>
\ No newline at end of file +</div> diff --git a/modules-available/locationinfo/templates/server-prop-generic.html b/modules-available/locationinfo/templates/server-prop-generic.html index 9e94c23d..23ff1e4e 100644 --- a/modules-available/locationinfo/templates/server-prop-generic.html +++ b/modules-available/locationinfo/templates/server-prop-generic.html @@ -7,10 +7,10 @@ </div> <div class="col-md-2"> {{#helptext}} - <a class="btn btn-default" title="{{helptext}}"> + <p class="btn btn-static" title="{{helptext}}"> <span class="glyphicon glyphicon-question-sign"></span> - </a> + </p> {{/helptext}} </div> </div> -</div>
\ No newline at end of file +</div> diff --git a/modules-available/main/lang/de/global-tags.json b/modules-available/main/lang/de/global-tags.json index 451e016e..de6ee798 100644 --- a/modules-available/main/lang/de/global-tags.json +++ b/modules-available/main/lang/de/global-tags.json @@ -9,9 +9,12 @@ "lang_hours": "Stunde(n)", "lang_next": "Weiter", "lang_no": "Nein", + "lang_password": "Passwort", "lang_reset": "Zur\u00fccksetzen", "lang_save": "Speichern", "lang_today": "Heute", + "lang_user": "Benutzer", + "lang_username": "Benutzername", "lang_yes": "Ja", "lang_yesterday": "Gestern" }
\ No newline at end of file diff --git a/modules-available/main/lang/en/global-tags.json b/modules-available/main/lang/en/global-tags.json index bfadf2f2..a915a625 100644 --- a/modules-available/main/lang/en/global-tags.json +++ b/modules-available/main/lang/en/global-tags.json @@ -9,9 +9,12 @@ "lang_hours": "hour(s)", "lang_next": "Next", "lang_no": "No", + "lang_password": "Password", "lang_reset": "Reset", "lang_save": "Save", "lang_today": "Today", + "lang_user": "User", + "lang_username": "Username", "lang_yes": "Yes", "lang_yesterday": "Yesterday" }
\ No newline at end of file diff --git a/modules-available/main/lang/pt/global-tags.json b/modules-available/main/lang/pt/global-tags.json index 11e032e2..9029d936 100644 --- a/modules-available/main/lang/pt/global-tags.json +++ b/modules-available/main/lang/pt/global-tags.json @@ -7,8 +7,11 @@ "lang_edit": "Editar", "lang_hours": "Hora(s)", "lang_next": "Pr\u00f3ximo", + "lang_password": "Senha", "lang_reset": "Limpar", "lang_save": "Salvar", "lang_today": "Hoje", + "lang_user": "User", + "lang_username": "Nome do Usu\u00e1rio", "lang_yesterday": "Ontem" }
\ No newline at end of file diff --git a/modules-available/minilinux/page.inc.php b/modules-available/minilinux/page.inc.php index df4f14a3..710ffd15 100644 --- a/modules-available/minilinux/page.inc.php +++ b/modules-available/minilinux/page.inc.php @@ -66,7 +66,8 @@ class Page_MiniLinux extends Page foreach ($selected['files'] as &$file) { $file['uid'] = 'dlid' . $count++; $local = CONFIG_HTTP_DIR . '/' . $system['id'] . '/' . $file['name']; - if (!file_exists($local) || filesize($local) !== $file['size'] || filemtime($local) < $file['mtime']) { + if (!file_exists($local) || filesize($local) !== $file['size'] || filemtime($local) < $file['mtime'] + || md5_file($local) !== $file['md5']) { $file['fileChanged'] = true; $system['systemChanged'] = true; } diff --git a/modules-available/permissionmanager/lang/de/template-tags.json b/modules-available/permissionmanager/lang/de/template-tags.json index 88652152..71bd4075 100644 --- a/modules-available/permissionmanager/lang/de/template-tags.json +++ b/modules-available/permissionmanager/lang/de/template-tags.json @@ -1,22 +1,22 @@ { - "lang_Roles": "Rollen", - "lang_Users": "Nutzer", - "lang_Locations": "Räume", + "lang_roles": "Rollen", + "lang_users": "Nutzer", + "lang_locations": "Räume", "lang_addRole": "Rollen erteilen", "lang_removeRole": "Rollen entziehen", "lang_newRole": "Rolle anlegen", - "lang_Selected": "Ausgewählt", - "lang_Edit": "Bearbeiten", - "lang_Delete": "Löschen", + "lang_selected": "Ausgewählt", + "lang_edit": "Bearbeiten", + "lang_delete": "Löschen", "lang_removeCheck": "Sind Sie sich sicher, dass Sie diese Rolle entfernen wollen?", "lang_deleteCheck": "Sind Sie sich sicher, dass Sie diese Rolle löschen wollen?", "lang_emptyNameWarning": "Der Name der Rolle darf nicht leer sein!", - "lang_Name": "Name", - "lang_Cancel": "Abbrechen", - "lang_Save": "Speichern", + "lang_name": "Name", + "lang_cancel": "Abbrechen", + "lang_save": "Speichern", "lang_all": "alle", "lang_selected": "ausgewählte", - "lang_Permissions": "Rechte", + "lang_permissions": "Rechte", "lang_selectizePlaceholder": "Nach Rollen filtern...", "lang_searchPlaceholder": "Nach Rollen suchen...", "lang_moduleName": "Rechtemanager", diff --git a/modules-available/permissionmanager/lang/en/template-tags.json b/modules-available/permissionmanager/lang/en/template-tags.json index 08769f27..2d31b294 100644 --- a/modules-available/permissionmanager/lang/en/template-tags.json +++ b/modules-available/permissionmanager/lang/en/template-tags.json @@ -1,22 +1,22 @@ { - "lang_Roles": "Roles", - "lang_Users": "Users", - "lang_Locations": "Locations", + "lang_roles": "Roles", + "lang_users": "Users", + "lang_locations": "Locations", "lang_addRole": "Grant Roles", "lang_removeRole": "Revoke Roles", "lang_newRole": "New Role", - "lang_Selected": "Selected", - "lang_Edit": "Edit", - "lang_Delete": "Delete", + "lang_selected": "Selected", + "lang_edit": "Edit", + "lang_delete": "Delete", "lang_removeCheck": "Are you sure you want to remove this role?", "lang_deleteCheck": "Are you sure you want to delete this role?", "lang_emptyNameWarning": "Role name can not be empty!", - "lang_Name": "Name", - "lang_Cancel": "Cancel", - "lang_Save": "Save", + "lang_name": "Name", + "lang_cancel": "Cancel", + "lang_save": "Save", "lang_all": "all", "lang_selected": "selected", - "lang_Permissions": "Permissions", + "lang_permissions": "Permissions", "lang_selectizePlaceholder": "Filter for roles...", "lang_searchPlaceholder": "Search for roles...", "lang_moduleName": "Permission Manager", diff --git a/modules-available/permissionmanager/page.inc.php b/modules-available/permissionmanager/page.inc.php index eeed9c02..13d81c6a 100644 --- a/modules-available/permissionmanager/page.inc.php +++ b/modules-available/permissionmanager/page.inc.php @@ -116,7 +116,7 @@ class Page_PermissionManager extends Page if ($toplevel) { $res = Render::parse("treepanel", array("id" => "*", - "name" => Dictionary::translateFile("template-tags", "lang_Permissions"), + "name" => Dictionary::translateFile("template-tags", "lang_permissions"), "checkboxname" => "permissions", "selected" => $selectAll, "HTML" => $res)); @@ -151,7 +151,7 @@ class Page_PermissionManager extends Page if ($toplevel) { $res = Render::parse("treepanel", array("id" => 0, - "name" => Dictionary::translateFile("template-tags", "lang_Locations"), + "name" => Dictionary::translateFile("template-tags", "lang_locations"), "checkboxname" => "locations", "selected" => $selectAll, "HTML" => $res)); diff --git a/modules-available/permissionmanager/templates/_page.html b/modules-available/permissionmanager/templates/_page.html index 405462f7..4140ce78 100644 --- a/modules-available/permissionmanager/templates/_page.html +++ b/modules-available/permissionmanager/templates/_page.html @@ -8,17 +8,17 @@ <div class="btn-group"> <button class="btn btn-default {{rolesButtonClass}}" type="submit" name="show" value="roles"> <span class="glyphicon glyphicon-education"></span> - {{lang_Roles}} + {{lang_roles}} </button> <button class="btn btn-default {{usersButtonClass}}" type="submit" name="show" value="users"> <span class="glyphicon glyphicon-user"></span> - {{lang_Users}} + {{lang_users}} </button> <button class="btn btn-default {{locationsButtonClass}}" type="submit" name="show" value="locations"> <span class="glyphicon glyphicon-home"></span> - {{lang_Locations}} + {{lang_locations}} </button> </div> </form> diff --git a/modules-available/permissionmanager/templates/locationstable.html b/modules-available/permissionmanager/templates/locationstable.html index b910e3f3..153258fe 100644 --- a/modules-available/permissionmanager/templates/locationstable.html +++ b/modules-available/permissionmanager/templates/locationstable.html @@ -15,8 +15,8 @@ <table id="locationsTable" class="table table-condensed table-hover stupidtable dataTable"> <thead> <tr> - <th data-sort="string">{{lang_Locations}}</th> - <th>{{lang_Roles}}</th> + <th data-sort="string">{{lang_locations}}</th> + <th>{{lang_roles}}</th> </tr> </thead> diff --git a/modules-available/permissionmanager/templates/roleeditor.html b/modules-available/permissionmanager/templates/roleeditor.html index ddf6ace9..871fd0cc 100644 --- a/modules-available/permissionmanager/templates/roleeditor.html +++ b/modules-available/permissionmanager/templates/roleeditor.html @@ -7,15 +7,15 @@ <div class="row"> <div class="col-md-12" style="margin-bottom: 20px;"> <ul class="nav nav-tabs text-center" role="tablist"> - <li role="presentation" class="active"><a href="#permissions" role="tab" data-toggle="tab">{{lang_Permissions}}</a></li> - <li role="presentation"><a href="#locations" role="tab" data-toggle="tab">{{lang_Locations}}</a></li> + <li role="presentation" class="active"><a href="#permissions" role="tab" data-toggle="tab">{{lang_permissions}}</a></li> + <li role="presentation"><a href="#locations" role="tab" data-toggle="tab">{{lang_locations}}</a></li> <li style="float: none; display: inline-block"> - <label for="rolename">{{lang_Name}}:</label> + <label for="rolename">{{lang_name}}:</label> <input id="rolename" name="rolename" value="{{rolename}}" type="text" class="form-control"> </li> <li style="float: right;"> - <span><a href="?do=permissionmanager&show={{cancelShow}}" id="cancelButton" class="btn btn-default">{{lang_Cancel}}</a></span> - <button type="submit" id="saveButton" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-disk"></span> {{lang_Save}}</button> + <span><a href="?do=permissionmanager&show={{cancelShow}}" id="cancelButton" class="btn btn-default">{{lang_cancel}}</a></span> + <button type="submit" id="saveButton" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-disk"></span> {{lang_save}}</button> </li> </ul> </div> diff --git a/modules-available/permissionmanager/templates/rolestable.html b/modules-available/permissionmanager/templates/rolestable.html index 7152a1dd..b121a9e0 100644 --- a/modules-available/permissionmanager/templates/rolestable.html +++ b/modules-available/permissionmanager/templates/rolestable.html @@ -17,9 +17,9 @@ <table class="table table-condensed table-hover stupidtable"> <thead> <tr> - <th data-sort="string">{{lang_Roles}}</th> - <th class="text-center">{{lang_Edit}}</th> - <th class="text-center">{{lang_Delete}}</th> + <th data-sort="string">{{lang_roles}}</th> + <th class="text-center">{{lang_edit}}</th> + <th class="text-center">{{lang_delete}}</th> </tr> </thead> @@ -47,7 +47,7 @@ <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> - <h4 class="modal-title" id="myModalLabel">{{lang_Delete}}</h4> + <h4 class="modal-title" id="myModalLabel">{{lang_delete}}</h4> </div> <div class="modal-body"> {{lang_deleteCheck}} @@ -55,7 +55,7 @@ <div class="modal-footer"> <input type="hidden" id="deleteId" name="deleteId" value=""/> <button type="button" class="btn btn-default" data-dismiss="modal">{{lang_cancel}}</button> - <button type="submit" name="action" value="deleteRole" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> {{lang_Delete}}</button> + <button type="submit" name="action" value="deleteRole" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> {{lang_delete}}</button> </div> </div> </div> diff --git a/modules-available/permissionmanager/templates/userstable.html b/modules-available/permissionmanager/templates/userstable.html index 749ae03d..bb0e228e 100644 --- a/modules-available/permissionmanager/templates/userstable.html +++ b/modules-available/permissionmanager/templates/userstable.html @@ -23,9 +23,9 @@ <table id="usersTable" class="table table-condensed table-hover stupidtable dataTable"> <thead> <tr> - <th data-sort="string">{{lang_Users}}</th> - <th>{{lang_Roles}}</th> - <th data-sort="int" data-sort-default="desc">{{lang_Selected}}</th> + <th data-sort="string">{{lang_users}}</th> + <th>{{lang_roles}}</th> + <th data-sort="int" data-sort-default="desc">{{lang_selected}}</th> </tr> </thead> @@ -65,8 +65,8 @@ <table id="addRoleToUserTable" class="table table-condensed table-hover stupidtable"> <thead> <tr> - <th data-sort="string">{{lang_Roles}}</th> - <th data-sort="int" data-sort-default="desc">{{lang_Selected}}</th> + <th data-sort="string">{{lang_roles}}</th> + <th data-sort="int" data-sort-default="desc">{{lang_selected}}</th> </tr> </thead> @@ -108,8 +108,8 @@ <table id="removeRoleFromUserTable" class="table table-condensed table-hover stupidtable"> <thead> <tr> - <th data-sort="string">{{lang_Roles}}</th> - <th data-sort="int" data-sort-default="desc">{{lang_Selected}}</th> + <th data-sort="string">{{lang_roles}}</th> + <th data-sort="int" data-sort-default="desc">{{lang_selected}}</th> </tr> </thead> diff --git a/modules-available/roomplanner/baseconfig/getconfig.inc.php b/modules-available/roomplanner/baseconfig/getconfig.inc.php index f4708547..79af974b 100644 --- a/modules-available/roomplanner/baseconfig/getconfig.inc.php +++ b/modules-available/roomplanner/baseconfig/getconfig.inc.php @@ -1,3 +1,24 @@ <?php ConfigHolder::add("SLX_PVS_CONFIG_URL", 'http://' . $_SERVER['SERVER_ADDR'] . $_SERVER['SCRIPT_NAME'] . '?do=roomplanner'); + +/** + * Make sure we switch to dedicated mode if this is a hybrid mode + * manager and we're in exam mode. + * Also disable exam mode for any kind of manager. + */ +ConfigHolder::addPostHook(function() { + $exam = (bool)ConfigHolder::get('SLX_EXAM'); + $hybrid = ConfigHolder::get('SLX_PVS_HYBRID') === 'yes'; + $dedi = (bool)ConfigHolder::get('SLX_PVS_DEDICATED'); + if ($exam) { + if ($dedi || $hybrid) { + ConfigHolder::add('SLX_EXAM', false, 100000); + ConfigHolder::add('SLX_SYSTEMD_TARGET', false, 100000); + } + if ($hybrid) { + ConfigHolder::add('SLX_PVS_HYBRID', false, 100000); + ConfigHolder::add('SLX_PVS_DEDICATED', 'yes', 100000); + } + } +});
\ No newline at end of file diff --git a/modules-available/roomplanner/hooks/statistics/machine-replace.inc.php b/modules-available/roomplanner/hooks/statistics/machine-replace.inc.php new file mode 100644 index 00000000..651c4c25 --- /dev/null +++ b/modules-available/roomplanner/hooks/statistics/machine-replace.inc.php @@ -0,0 +1,7 @@ +<?php + + +foreach ($list as $entry) { + unset($entry['datelimit']); + Database::exec('UPDATE IGNORE location_roomplan SET tutoruuid = :new WHERE tutoruuid = :old', $entry); +}
\ No newline at end of file diff --git a/modules-available/roomplanner/inc/pvsgenerator.inc.php b/modules-available/roomplanner/inc/pvsgenerator.inc.php index cda32fce..5a7de9f0 100644 --- a/modules-available/roomplanner/inc/pvsgenerator.inc.php +++ b/modules-available/roomplanner/inc/pvsgenerator.inc.php @@ -222,7 +222,6 @@ class PvsGenerator if (isset($data['dedicatedmgr']) && $data['dedicatedmgr']) { ConfigHolder::add("SLX_ADDONS", false, 100000); ConfigHolder::add("SLX_PVS_DEDICATED", 'yes'); - ConfigHolder::add("SLX_EXAM", false, 100000); ConfigHolder::add("SLX_AUTOLOGIN", 'ON', 100000); } else { ConfigHolder::add("SLX_PVS_HYBRID", 'yes'); diff --git a/modules-available/runmode/hooks/statistics/machine-replace.inc.php b/modules-available/runmode/hooks/statistics/machine-replace.inc.php new file mode 100644 index 00000000..cd23e552 --- /dev/null +++ b/modules-available/runmode/hooks/statistics/machine-replace.inc.php @@ -0,0 +1,6 @@ +<?php + +foreach ($list as $entry) { + unset($entry['datelimit']); + Database::exec('UPDATE IGNORE runmode SET machineuuid = :new WHERE machineuuid = :old', $entry); +} diff --git a/modules-available/runmode/inc/runmode.inc.php b/modules-available/runmode/inc/runmode.inc.php index ad1f52bf..2c8083ca 100644 --- a/modules-available/runmode/inc/runmode.inc.php +++ b/modules-available/runmode/inc/runmode.inc.php @@ -72,7 +72,7 @@ class RunMode * @param string $machineuuid * @param int $returnData bitfield of data to return * @return false|array {'machineuuid', 'isclient', 'module', 'modeid', 'modedata', - * <'hostname', 'clientip', 'macaddr', 'locationid', 'lastseen'>} + * ('hostname', 'clientip', 'macaddr', 'locationid', 'lastseen'), ('moduleName', 'modeName')} */ public static function getRunMode($machineuuid, $returnData = self::DATA_MACHINE_DATA) { diff --git a/modules-available/runmode/lang/de/messages.json b/modules-available/runmode/lang/de/messages.json index 21b4b6ae..911d48d4 100644 --- a/modules-available/runmode/lang/de/messages.json +++ b/modules-available/runmode/lang/de/messages.json @@ -5,5 +5,6 @@ "machine-not-found": "Rechner {{0}} nicht gefunden", "machine-not-runmode": "Rechner {{0}} hatte keinen speziellen Betriebsmodus aktiviert", "machine-removed": "Rechner {{0}} entfernt", + "machine-still-assigned": "Rechner {{0}} ist noch im Betriebsmodus {{1}}; ignoriert.", "module-hasnt-runmode": "Modul {{0}} bietet keine speziellen Betriebsmodi" }
\ No newline at end of file diff --git a/modules-available/runmode/lang/en/messages.json b/modules-available/runmode/lang/en/messages.json index 1985ca66..6d890428 100644 --- a/modules-available/runmode/lang/en/messages.json +++ b/modules-available/runmode/lang/en/messages.json @@ -5,5 +5,6 @@ "machine-not-found": "Client {{0}} not found", "machine-not-runmode": "No special mode of operation configured for client {{0}}", "machine-removed": "Removed client {{0}}", + "machine-still-assigned": "Client {{0}} still set to mode {{1}}, not assigning new mode.", "module-hasnt-runmode": "Module {{0}} doesn't supply any special mode of operation" }
\ No newline at end of file diff --git a/modules-available/runmode/page.inc.php b/modules-available/runmode/page.inc.php index e26950d0..8feb097e 100644 --- a/modules-available/runmode/page.inc.php +++ b/modules-available/runmode/page.inc.php @@ -42,6 +42,14 @@ class Page_RunMode extends Page } $active = 0; foreach ($machines as $machine) { + $oldMode = RunMode::getRunMode($machine, 0); + if ($oldMode !== false) { + $oldModule = RunMode::getModuleConfig($oldMode['module']); + if ($oldModule !== false && (!$oldModule->allowGenericEditor || $oldModule->deleteUrlSnippet !== false)) { + Message::addError('runmode.machine-still-assigned', $machine, $oldMode['module']); + continue; + } + } $ret = RunMode::setRunMode($machine, $module, $modeId, null, null); if ($ret) { $active++; @@ -105,10 +113,6 @@ class Page_RunMode extends Page Message::addError('module-hasnt-runmode', $moduleId); Util::redirect('?do=runmode'); } - if (!$config->allowGenericEditor) { - Message::addError('runmode.cannot-edit-module', $moduleId); - return; - } // Given modeId? $modeId = Request::get('modeid', false, 'string'); if ($modeId !== false) { @@ -170,9 +174,17 @@ class Page_RunMode extends Page { $moduleId = $module->getIdentifier(); $modeName = RunMode::getModeName($moduleId, $modeId); + $redirect = Request::get('redirect', '', 'string'); + if (empty($redirect)) { + $redirect = '?do=runmode'; + } if ($modeName === false) { Message::addError('invalid-modeid', $moduleId, $modeId); - Util::redirect('?do=runmode'); + Util::redirect($redirect); + } + if (!RunMode::getModuleConfig($moduleId)->allowGenericEditor) { + Message::addError('runmode.cannot-edit-module', $moduleId); + Util::redirect($redirect); } Render::addTemplate('machine-selector', [ 'module' => $moduleId, @@ -180,7 +192,7 @@ class Page_RunMode extends Page 'moduleName' => $module->getDisplayName(), 'modeName' => $modeName, 'machines' => json_encode(RunMode::getForMode($module, $modeId, true)), - 'redirect' => Request::get('redirect', '', 'string'), + 'redirect' => $redirect, ]); } diff --git a/modules-available/runmode/templates/module-machine-list.html b/modules-available/runmode/templates/module-machine-list.html index 61bbbad9..283fb393 100644 --- a/modules-available/runmode/templates/module-machine-list.html +++ b/modules-available/runmode/templates/module-machine-list.html @@ -41,8 +41,8 @@ </button> {{/canedit}} {{#deleteUrl}} - <a class="btn btn-danger btn-sm" href="?do={{module}}&{{deleteUrl}}{{modeid}}"> - <span class="glyphicon glyphicon-trash"></span> + <a class="btn btn-default btn-sm" href="?do={{module}}&{{deleteUrl}}{{modeid}}"> + <span class="glyphicon glyphicon-edit"></span> </a> {{/deleteUrl}} </td> diff --git a/modules-available/session/lang/de/template-tags.json b/modules-available/session/lang/de/template-tags.json index 20f44be0..c7b6d881 100644 --- a/modules-available/session/lang/de/template-tags.json +++ b/modules-available/session/lang/de/template-tags.json @@ -4,9 +4,7 @@ "lang_enter": "Anmeldung", "lang_login": "Anmelden", "lang_newPassword": "Neues Passwort", - "lang_password": "Passwort", "lang_register": "Registrieren", "lang_rememberID": "Angemeldet bleiben", - "lang_repeatPassword": "Passwort wiederholen", - "lang_username": "Benutzerkennung" -}
\ No newline at end of file + "lang_repeatPassword": "Passwort wiederholen" +} diff --git a/modules-available/session/lang/en/template-tags.json b/modules-available/session/lang/en/template-tags.json index 663354a9..f9e0b393 100644 --- a/modules-available/session/lang/en/template-tags.json +++ b/modules-available/session/lang/en/template-tags.json @@ -4,9 +4,7 @@ "lang_enter": "Enter", "lang_login": "Login", "lang_newPassword": "New password", - "lang_password": "Password", "lang_register": "Register", "lang_rememberID": "Remember ID", - "lang_repeatPassword": "Repeat password", - "lang_username": "Username" -}
\ No newline at end of file + "lang_repeatPassword": "Repeat password" +} diff --git a/modules-available/session/lang/pt/template-tags.json b/modules-available/session/lang/pt/template-tags.json index 3d1e19eb..3a4b9478 100644 --- a/modules-available/session/lang/pt/template-tags.json +++ b/modules-available/session/lang/pt/template-tags.json @@ -1,8 +1,6 @@ { "lang_enter": "Entrar", "lang_login": "Entrar", - "lang_password": "Senha", "lang_register": "Registrar", - "lang_rememberID": "Lembrar Usu\u00e1rio", - "lang_username": "Nome de Usu\u00e1rio" -}
\ No newline at end of file + "lang_rememberID": "Lembrar Usu\u00e1rio" +} diff --git a/modules-available/statistics/inc/filter.inc.php b/modules-available/statistics/inc/filter.inc.php index be6df752..f6765059 100644 --- a/modules-available/statistics/inc/filter.inc.php +++ b/modules-available/statistics/inc/filter.inc.php @@ -88,15 +88,17 @@ class Filter $lhs = trim(substr($q, 0, $pos)); $rhs = trim(substr($q, $pos + strlen($operator))); - if ($lhs == 'gbram') { + if ($lhs === 'gbram') { $filters[] = new RamGbFilter($operator, $rhs); - } elseif ($lhs == 'state') { + } elseif ($lhs === 'runtime') { + $filters[] = new RuntimeFilter($operator, $rhs); + } elseif ($lhs === 'state') { $filters[] = new StateFilter($operator, $rhs); - } elseif ($lhs == 'hddgb') { + } elseif ($lhs === 'hddgb') { $filters[] = new Id44Filter($operator, $rhs); - } elseif ($lhs == 'location') { + } elseif ($lhs === 'location') { $filters[] = new LocationFilter($operator, $rhs); - } elseif ($lhs == 'subnet') { + } elseif ($lhs === 'subnet') { $filters[] = new SubnetFilter($operator, $rhs); } else { if (array_key_exists($lhs, Page_Statistics::$columns) && Page_Statistics::$columns[$lhs]['column']) { @@ -143,6 +145,38 @@ class RamGbFilter extends Filter } } +class RuntimeFilter extends Filter +{ + public function __construct($operator, $argument) + { + parent::__construct('lastboot', $operator, $argument); + } + + public function whereClause(&$args, &$joins) + { + global $SIZE_RAM; + $upper = time() - (int)$this->argument * 3600; + $lower = $upper - 3600; + $common = "state IN ('OCCUPIED', 'IDLE', 'STANDBY') AND"; + if ($this->operator == '=') { + return "$common ({$this->column} BETWEEN $lower AND $upper)"; + } elseif ($this->operator == '<') { + return "$common {$this->column} > $upper"; + } elseif ($this->operator == '<=') { + return "$common {$this->column} > $lower"; + } elseif ($this->operator == '>') { + return "$common {$this->column} < $lower"; + } elseif ($this->operator == '>=') { + return "$common {$this->column} < $upper"; + } elseif ($this->operator == '!=') { + return "$common ({$this->column} < $lower OR {$this->column} > $upper)"; + } else { + error_log("unimplemented operator in RuntimeFilter: $this->operator"); + return ' 1'; + } + } +} + class Id44Filter extends Filter { public function __construct($operator, $argument) diff --git a/modules-available/statistics/lang/de/messages.json b/modules-available/statistics/lang/de/messages.json index c9667f7b..a9256d5a 100644 --- a/modules-available/statistics/lang/de/messages.json +++ b/modules-available/statistics/lang/de/messages.json @@ -1,7 +1,11 @@ { "deleted-n-machines": "{{0}} Clients gel\u00f6scht", + "ignored-both-in-use": "Rechnerpaar ignoriert, da beide noch in Betrieb zu sein scheinen. ({{0}} und {{1}})", "invalid-filter-argument": "Das Argument {{1}} ist nicht g\u00fcltig f\u00fcr den Filter {{0}}", "invalid-filter-key": "{{0}} ist kein g\u00fcltiges Filterkriterium", + "invalid-replace-format": "Ung\u00fcltiges Parameterformat ({{0}})", + "no-replacement-matches": "Keine Rechner gefunden, die den oben genannten Kriterien entsprechen", "notes-saved": "Anmerkungen gespeichert", - "unknown-machine": "Unbekannte Rechner-ID {{0}}" + "unknown-machine": "Unbekannte Rechner-ID {{0}}", + "x-machines-replaced": "{{0}} Rechner ersetzt" }
\ No newline at end of file diff --git a/modules-available/statistics/lang/de/template-tags.json b/modules-available/statistics/lang/de/template-tags.json index 3cdde813..84c4690c 100644 --- a/modules-available/statistics/lang/de/template-tags.json +++ b/modules-available/statistics/lang/de/template-tags.json @@ -66,8 +66,14 @@ "lang_ramSlots": "Speicher-Slots", "lang_realCores": "Kerne", "lang_reallocatedSectors": "Defekte Sektoren", + "lang_replace": "Ersetzen", + "lang_replaceInstructions": "Hier k\u00f6nnen Sie Metadaten automatisch \u00fcbertragen, wenn in einem Raum die Rechner ausgetauscht wurden. Dies setzt voraus, dass alle neuen Rechner die gleiche IP Adresse erhalten haben wie der Rechner, der zuvor am entsprechenden Platz stand, und die neuen Rechner alle einmal gestartet wurden. In der Liste unten sehen Sie alle Rechnerpaare, auf die folgendes zutrifft: 1) Die IP-Adressen sind identisch 2) Der letzte Boot des einen Rechners liegt vor dem ersten Boot des anderen Rechners. W\u00e4hlen Sie alle Rechnerpaare aus, f\u00fcr die eine Ersetzung stattfinden soll. Bei der Ersetzung werden alle Logeintr\u00e4ge, Sitzungslogs, Position im Raumplan und evtl. spezielle Betriebsmodi vom alten Rechner auf den neuen \u00dcbertragen.", + "lang_replaceMachinesHeading": "Rechner ersetzen", + "lang_replaceNew": "Alter Rechner", + "lang_replaceOld": "Neuer Rechner", "lang_runMode": "Betriebsmodus", "lang_runmodeMachines": "Mit besonderem Betriebsmodus", + "lang_runtimeHours": "Laufzeit (Stunden)", "lang_screens": "Bildschirme", "lang_serialNo": "Serien-Nr", "lang_showList": "Liste", @@ -75,6 +81,7 @@ "lang_sockets": "Sockel", "lang_subnet": "Subnetz", "lang_sureDeletePermanent": "M\u00f6chten Sie diese(n) Rechner wirklich unwiderruflich aus der Datenbank entfernen?\r\n\r\nWichtig: L\u00f6schen verhindert nicht, dass ein Rechner nach erneutem Starten von bwLehrpool wieder in die Datenbank aufgenommen wird.", + "lang_sureReplaceNoUndo": "Wollen Sie die Daten ausgew\u00e4hlten Rechner \u00fcbertragen? Diese Aktion kann nicht r\u00fcckg\u00e4ngig gemacht werden.", "lang_tempPart": "Temp. Partition", "lang_tempPartStats": "Tempor\u00e4re Partition", "lang_thoseAreProjectors": "Diese Modellnamen werden als Beamer behandelt, auch wenn die EDID-Informationen des Ger\u00e4tes anderes berichten.", diff --git a/modules-available/statistics/lang/en/messages.json b/modules-available/statistics/lang/en/messages.json index 3471c472..0f290f2e 100644 --- a/modules-available/statistics/lang/en/messages.json +++ b/modules-available/statistics/lang/en/messages.json @@ -1,7 +1,11 @@ { "deleted-n-machines": "Deleted {{0}} clients", + "ignored-both-in-use": "Ignoring machine pair as both still seem to be in use. ({{0}} and {{1}})", "invalid-filter-argument": "{{1}} is not a vald argument for filter {{0}}", "invalid-filter-key": "{{0}} is not a valid filter", + "invalid-replace-format": "Invalid parameter format ({{0}})", + "no-replacement-matches": "No machines match the criteria from above", "notes-saved": "Notes have been saved", - "unknown-machine": "Unknown machine uuid {{0}}" + "unknown-machine": "Unknown machine uuid {{0}}", + "x-machines-replaced": "{{0}} machine(s) replaced" }
\ No newline at end of file diff --git a/modules-available/statistics/lang/en/template-tags.json b/modules-available/statistics/lang/en/template-tags.json index 35c4e68a..b064ee50 100644 --- a/modules-available/statistics/lang/en/template-tags.json +++ b/modules-available/statistics/lang/en/template-tags.json @@ -66,8 +66,14 @@ "lang_ramSlots": "Memory slots", "lang_realCores": "Cores", "lang_reallocatedSectors": "Bad sectors", + "lang_replace": "Replace", + "lang_replaceInstructions": "If some PCs\/clients have been physically replaced, you can re-assign log entries, session data, position information etc. from the old machine to the new one. This requires that the new machine gets assigned the same IP address as the old one and, if the room planner is used -- that it is placed in the same spot as the old one. The list below shows all machine pairs where 1) the last boot of one machine lies before the first boot of the other one 2) both machines had the same IP address last time they booted. The replacement action will reassign all log events, room plan location and special run mode from the old machine to the new machine.", + "lang_replaceMachinesHeading": "Replace machines", + "lang_replaceNew": "Old machine", + "lang_replaceOld": "New machine", "lang_runMode": "Mode of operation", "lang_runmodeMachines": "With special mode of operation", + "lang_runtimeHours": "Runtime (hours)", "lang_screens": "Screens", "lang_serialNo": "Serial no", "lang_showList": "List", @@ -75,6 +81,7 @@ "lang_sockets": "Sockets", "lang_subnet": "Subnet", "lang_sureDeletePermanent": "Are your sure you want to delete the selected machine(s) from the database? This cannot be undone.\r\n\r\nNote: Deleting machines from the database does not prevent booting up bwLehrpool again, which would recreate their respective database entries.", + "lang_sureReplaceNoUndo": "Are you sure you want to replace the selected machine pairs? This action cannot be undone.", "lang_tempPart": "Temp. partition", "lang_tempPartStats": "Temporary partition", "lang_thoseAreProjectors": "These model names will always be treated as beamers, even if the device's EDID data says otherwise.", diff --git a/modules-available/statistics/page.inc.php b/modules-available/statistics/page.inc.php index 77b52e0d..5fe4ebfa 100644 --- a/modules-available/statistics/page.inc.php +++ b/modules-available/statistics/page.inc.php @@ -104,6 +104,11 @@ class Page_Statistics extends Page 'type' => 'string', 'column' => true ], + 'hostname' => [ + 'op' => Page_Statistics::$op_stringcmp, + 'type' => 'string', + 'column' => true + ], 'subnet' => [ 'op' => Page_Statistics::$op_nominal, 'type' => 'string', @@ -119,7 +124,12 @@ class Page_Statistics extends Page 'type' => 'enum', 'column' => true, 'values' => ['occupied', 'on', 'off', 'idle', 'standby'] - ] + ], + 'runtime' => [ + 'op' => Page_Statistics::$op_ordinal, + 'type' => 'int', + 'column' => true + ], ]; if (Module::isAvailable('locations')) { Page_Statistics::$columns['location'] = [ @@ -651,6 +661,13 @@ class Page_Statistics extends Page } } $row['cpumodel'] = preg_replace('/\(R\)|\(TM\)|\bintel\b|\bamd\b|\bcpu\b|dual-core|\bdual\s+core\b|\bdual\b|\bprocessor\b/i', ' ', $row['cpumodel']); + if (!empty($row['rmmodule'])) { + $data = RunMode::getRunMode($row['machineuuid'], RunMode::DATA_STRINGS); + if ($data !== false) { + $row['moduleName'] = $data['moduleName']; + $row['modeName'] = $data['modeName']; + } + } $rows[] = $row; } if ($singleMachine !== false && $singleMachine !== 'none') { @@ -937,9 +954,9 @@ class Page_Statistics extends Page // Not seen in last two weeks $spans['graph'] .= '<div style="background:#444;left:0;width:100%"> </div>'; } - if (isset($client['state_occupied'])) { + if ($client['state'] === 'OCCUPIED') { $spans['graph'] .= '<div style="background:#e99;left:' . round(($client['logintime'] - $cutoff) * $scale, 2) . '%;width:' . round(($NOW - $client['logintime'] + 900) * $scale, 2) . '%"> </div>'; - } elseif (isset($client['state_off'])) { + } elseif ($client['state'] === 'OFFLINE') { $spans['graph'] .= '<div style="background:#444;left:' . round(($client['lastseen'] - $cutoff) * $scale, 2) . '%;width:' . round(($NOW - $client['lastseen'] + 900) * $scale, 2) . '%"> </div>'; } $t = explode('-', date('Y-n-j-G', $cutoff)); @@ -1052,7 +1069,11 @@ class Page_Statistics extends Page public static function getPciId($cat, $id) { - return Database::queryFirst('SELECT value, dateline FROM pciid WHERE category = :cat AND id = :id LIMIT 1', + static $cache = []; + $key = $cat . '-' . $id; + if (isset($cache[$key])) + return $cache[$key]; + return $cache[$key] = Database::queryFirst('SELECT value, dateline FROM pciid WHERE category = :cat AND id = :id LIMIT 1', array('cat' => $cat, 'id' => $id)); } diff --git a/modules-available/statistics/pages/replace.inc.php b/modules-available/statistics/pages/replace.inc.php new file mode 100644 index 00000000..ae9c6108 --- /dev/null +++ b/modules-available/statistics/pages/replace.inc.php @@ -0,0 +1,119 @@ +<?php + +class SubPage +{ + + public static function doPreprocess() + { + $action = Request::post('action', false, 'string'); + if ($action === 'replace') { + self::handleReplace(); + } + if (Request::isPost()) { + Util::redirect('?do=statistics&show=replace'); + } + } + + private static function handleReplace() + { + $replace = Request::post('replace', false, 'array'); + if ($replace === false || empty($replace)) { + Message::addError('main.parameter-empty', 'replace'); + return; + } + $list = []; + foreach ($replace as $p) { + $split = explode('x', $p); + if (count($split) !== 2) { + Message::addError('invalid-replace-format', $p); + continue; + } + $entry = ['old' => $split[0], 'new' => $split[1]]; + $old = Database::queryFirst('SELECT lastseen FROM machine WHERE machineuuid = :old', + ['old' => $entry['old']]); + if ($old === false) { + Message::addError('unknown-machine', $entry['old']); + continue; + } + $new = Database::queryFirst('SELECT firstseen FROM machine WHERE machineuuid = :new', + ['new' => $entry['new']]); + if ($new === false) { + Message::addError('unknown-machine', $entry['new']); + continue; + } + if ($old['lastseen'] - 86400*7 > $new['firstseen']) { + Message::addWarning('ignored-both-in-use', $entry['old'], $entry['new']); + continue; + } + $entry['datelimit'] = min($new['firstseen'], $old['lastseen']); + $list[] = $entry; + } + if (empty($list)) { + Message::addError('main.parameter-empty', 'replace'); + return; + } + + // First handle module internal tables + foreach ($list as $entry) { + Database::exec('UPDATE statistic SET machineuuid = :new WHERE machineuuid = :old AND dateline < :datelimit', $entry); + } + + // Let other modules do their thing + $fun = function($file, $list) { + include $file; + }; + foreach (Hook::load('statistics/machine-replace') as $hook) { + $fun($hook->file, $list); + } + + // Finalize by updating machine table + foreach ($list as $entry) { + unset($entry['datelimit']); + Database::exec('UPDATE machine old, machine new SET + new.fixedlocationid = old.fixedlocationid, + new.position = old.position, + old.position = NULL, + new.notes = old.notes, + old.notes = NULL, + old.lastseen = new.firstseen + WHERE old.machineuuid = :old AND new.machineuuid = :new', $entry); + } + Message::addSuccess('x-machines-replaced', count($list)); + } + + public static function doRender() + { + self::listSuggestions(); + } + + private static function listSuggestions() + { + if (Request::get('debug', false) !== false) { + $oldCutoff = time() - 86400 * 180; + $newCutoff = time() - 86400 * 180; + } else { + $oldCutoff = time() - 86400 * 90; + $newCutoff = time() - 86400 * 30; + } + $res = Database::simpleQuery("SELECT + old.machineuuid AS olduuid, old.locationid AS oldlid, old.hostname AS oldhost, + old.clientip AS oldip, old.macaddr AS oldmac, old.lastseen AS oldlastseen, old.systemmodel AS oldmodel, + new.machineuuid AS newuuid, new.locationid AS newlid, new.hostname AS newhost, + new.clientip AS newip, new.macaddr AS newmac, new.firstseen AS newfirstseen, new.systemmodel AS newmodel + FROM machine old INNER JOIN machine new ON (old.clientip = new.clientip AND old.lastseen < new.firstseen AND old.lastseen > $oldCutoff AND new.firstseen > $newCutoff) + ORDER BY oldhost ASC, oldip ASC"); + $list = []; + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $row['oldlastseen_s'] = Util::prettyTime($row['oldlastseen']); + $row['newfirstseen_s'] = Util::prettyTime($row['newfirstseen']); + $list[] = $row; + } + $data = array('pairs' => $list); + Render::addTemplate('page-replace', $data); + if (empty($list)) { + Message::addInfo('no-replacement-matches'); + } + } + +} + diff --git a/modules-available/statistics/templates/clientlist.html b/modules-available/statistics/templates/clientlist.html index 3d3e66a4..d06eb4f7 100644 --- a/modules-available/statistics/templates/clientlist.html +++ b/modules-available/statistics/templates/clientlist.html @@ -11,27 +11,27 @@ <td></td> <td class="text-right"> <button type="button" class="btn btn-default btn-xs" onclick="popupFilter('lastseen')"> - <span id="btn_filter_lastseen" class="glyphicon glyphicon-filter"></span> + <span class="glyphicon glyphicon-filter"></span> </button> </td> <td> <button type="button" class="btn btn-default btn-xs" onclick="popupFilter('kvmstate')"> - <span id="btn_filter_kvmstate" class="glyphicon glyphicon-filter"></span> + <span class="glyphicon glyphicon-filter"></span> </button> </td> <td class="text-right"> <button type="button" class="btn btn-default btn-xs" onclick="popupFilter('gbram')"> - <span id="btn_filter_gbram" class="glyphicon glyphicon-filter"></span> + <span class="glyphicon glyphicon-filter"></span> </button> </td> <td class="text-right"> <button type="button" class="btn btn-default btn-xs" onclick="popupFilter('hddgb')"> - <span id="btn_filter_hddgb" class="glyphicon glyphicon-filter"></span> + <span class="glyphicon glyphicon-filter"></span> </button> </td> <td> - <button class="btn btn-default btn-xs" onclick="popupFilter('realcores')"> - <span id="btn_filter_cpu" class="glyphicon glyphicon-filter"></span> + <button type="button" class="btn btn-default btn-xs" onclick="popupFilter('realcores')"> + <span class="glyphicon glyphicon-filter"></span> </button> </td> </tr> @@ -72,7 +72,11 @@ {{/state_STANDBY}} <a href="?do=Statistics&uuid={{machineuuid}}"><b>{{hostname}}</b></a> <div class="small">{{machineuuid}}</div> - {{#rmmodule}}<div class="small">{{lang_runMode}}: <a class="slx-bold" href="?do=runmode&module={{rmmodule}}">{{rmmodule}}</a></div>{{/rmmodule}} + {{#rmmodule}} + <div class="small">{{lang_runMode}}: + <a class="slx-bold" href="?do=runmode&module={{rmmodule}}">{{moduleName}}</a> / {{modeName}} + </div> + {{/rmmodule}} </td> <td data-sort-value="{{clientip}}"><b><a href="?do=Statistics&show=list&filters=subnet={{subnet}}">{{subnet}}</a>{{lastoctet}}</b><br>{{macaddr}}</td> <td data-sort-value="{{lastseen_int}}" class="text-right text-nowrap">{{lastseen}}</td> diff --git a/modules-available/statistics/templates/filterbox.html b/modules-available/statistics/templates/filterbox.html index 758bc287..58b66a75 100644 --- a/modules-available/statistics/templates/filterbox.html +++ b/modules-available/statistics/templates/filterbox.html @@ -99,7 +99,9 @@ var slxFilterNames = { state: '{{lang_usageState}}', location: '{{lang_location}}', currentuser: '{{lang_currentUser}}', - subnet: '{{lang_subnet}}' + subnet: '{{lang_subnet}}', + runtime: '{{lang_runtimeHours}}', + hostname: '{{lang_hostname}}' }; slxLocations = {{{locations}}}; diff --git a/modules-available/statistics/templates/page-replace.html b/modules-available/statistics/templates/page-replace.html index f87610a2..d0e9f766 100644 --- a/modules-available/statistics/templates/page-replace.html +++ b/modules-available/statistics/templates/page-replace.html @@ -17,6 +17,10 @@ } </style> +<p> + {{lang_replaceInstructions}} +</p> + <form method="post" action="?do=statistics&show=replace"> <input type="hidden" name="token" value="{{token}}"> <table class="reptable"> diff --git a/modules-available/statistics_reporting/inc/getdata.inc.php b/modules-available/statistics_reporting/inc/getdata.inc.php index ae4d5aa4..13d39502 100644 --- a/modules-available/statistics_reporting/inc/getdata.inc.php +++ b/modules-available/statistics_reporting/inc/getdata.inc.php @@ -10,24 +10,59 @@ class GetData public static $lowerTimeBound = 0; public static $upperTimeBound = 24; public static $salt; + private static $TS_LIST = false; + private static $SECS_LIST = false; + + private static function fillLocation(&$entry, $anonymize) + { + $locations = Location::getLocationsAssoc(); + if ($anonymize) { + $entry['locationname'] = md5($entry['locationid'] . self::$salt); + } elseif (isset($locations[$entry['locationid']])) { + $entry['locationname'] = $locations[$entry['locationid']]['locationname']; + $entry['parentlocations'] = array_reduce($locations[$entry['locationid']]['parents'], function ($carry, $item) { + return $carry . sprintf("%04d", $item); + }) . sprintf("%04d", $entry['locationid']); + } else { + $entry['locationname'] = Dictionary::translate('notAssigned', true); + } + if ($anonymize) { + unset($entry['locationid']); + } + } + + private static function addPrintables(&$entry) + { + if (self::$SECS_LIST === false) { + self::$SECS_LIST = ['totalTime', 'totalOffTime', 'totalIdleTime', 'totalSessionTime', 'totalStandbyTime', 'medianSessionLength']; + } + if (self::$TS_LIST === false) { + self::$TS_LIST = ['lastStart', 'lastLogout']; + } + $perc = isset($entry['totalTime']) && $entry['totalTime'] > 0; + foreach (self::$SECS_LIST as $k) { + if (isset($entry[$k])) { + $entry[$k . '_s'] = self::formatSeconds($entry[$k]); + if ($perc && $k !== 'totalTime') { + $entry[$k . '_p'] = round($entry[$k] / $entry['totalTime'] * 100); + } + } + } + foreach (self::$TS_LIST as $k) { + if (isset($entry[$k])) { + $entry[$k . '_s'] = Util::prettyTime($entry[$k]); + } + } + } // total public static function total($flags = 0) { $printable = 0 !== ($flags & GETDATA_PRINTABLE); // total time online, average time online, total number of logins - $res = Queries::getOverallStatistics(self::$from, self::$to, self::$lowerTimeBound, self::$upperTimeBound); - $row = $res->fetch(PDO::FETCH_ASSOC); - $data = array('totalTime' => $row['sum'], 'medianSessionLength' => self::calcMedian($row['median']), 'longSessions' => $row['longSessions'], 'shortSessions' => $row['shortSessions']); - - //total time offline - $res = Queries::getTotalOfflineStatistics(self::$from, self::$to, self::$lowerTimeBound, self::$upperTimeBound); - $row = $res->fetch(PDO::FETCH_ASSOC); - $data['totalOffTime'] = $row['timeOff']; + $data = Queries::getOverallStatistics(self::$from, self::$to, self::$lowerTimeBound, self::$upperTimeBound); if ($printable) { - $data["totalTime_s"] = self::formatSeconds($data["totalTime"]); - $data["medianSessionLength_s"] = self::formatSeconds($data["medianSessionLength"]); - $data["totalOffTime_s"] = self::formatSeconds($data["totalOffTime"]); + self::addPrintables($data); } $data['uniqueUsers'] = Queries::getUniqueUserCount(self::$from, self::$to); @@ -38,63 +73,29 @@ class GetData public static function perLocation($flags = 0) { $anonymize = 0 !== ($flags & GETDATA_ANONYMOUS); $printable = 0 !== ($flags & GETDATA_PRINTABLE); - $res = Queries::getLocationStatistics(self::$from, self::$to, self::$lowerTimeBound, self::$upperTimeBound); - $data = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - self::nullToZero($row); - $median = self::calcMedian(self::calcMedian($row['medianSessionLength'])); - $entry = array( - 'location' => ($anonymize ? $row['locHash'] : $row['locName']), - 'totalTime' => $row['timeSum'], - 'medianSessionLength' => $median, - 'totalOffTime' => $row['offlineSum'], - 'longSessions' => $row['longSessions'], - 'shortSessions' => $row['shortSessions'] - ); - if (!$anonymize) { - $entry['locationId'] = $row['locId']; - } + $data = Queries::getLocationStatistics(self::$from, self::$to, self::$lowerTimeBound, self::$upperTimeBound); + foreach ($data as &$entry) { + //self::nullToZero($entry); + self::fillLocation($entry, $anonymize); if ($printable) { - $entry['totalTime_s'] = self::formatSeconds($row['timeSum']); - $entry['medianSessionLength_s'] = self::formatSeconds($median); - $entry['totalOffTime_s'] = self::formatSeconds($row['offlineSum']); + self::addPrintables($entry); } - $data[] = $entry; } return $data; } // per client - public static function perClient($flags = 0) { + public static function perClient($flags = 0, $new = false) { $anonymize = 0 !== ($flags & GETDATA_ANONYMOUS); $printable = 0 !== ($flags & GETDATA_PRINTABLE); - $res = Queries::getClientStatistics(self::$from, self::$to, self::$lowerTimeBound, self::$upperTimeBound); - $data = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - self::nullToZero($row); - $median = self::calcMedian(self::calcMedian($row['medianSessionLength'])); - $entry = array( - 'hostname' => ($anonymize ? $row['clientHash'] : $row['clientName']), - 'totalTime' => $row['timeSum'], - 'medianSessionLength' => $median, - 'totalOffTime' => $row['offlineSum'], - 'lastStart' => $row['lastStart'], - 'lastLogout' => $row['lastLogout'], - 'longSessions' => $row['longSessions'], - 'shortSessions' => $row['shortSessions'], - 'location' => ($anonymize ? $row['locHash'] : $row['locName']), - ); - if (!$anonymize) { - $entry['locationId'] = $row['locId']; - } + $data = Queries::getClientStatistics(self::$from, self::$to, self::$lowerTimeBound, self::$upperTimeBound); + foreach ($data as &$entry) { + //self::nullToZero($entry); + $entry['hostname'] = ($anonymize ? md5($entry['clientName'] . self::$salt) : $entry['clientName']); + self::fillLocation($entry, $anonymize); if ($printable) { - $entry['totalTime_s'] = self::formatSeconds($row['timeSum']); - $entry['medianSessionLength_s'] = self::formatSeconds($median); - $entry['totalOffTime_s'] = self::formatSeconds($row['offlineSum']); - $entry['lastStart_s'] = $row['lastStart'] == 0 ? "" : date(DATE_ISO8601, $row['lastStart']); - $entry['lastLogout_s'] = $row['lastLogout'] == 0 ? "" : date(DATE_ISO8601, $row['lastLogout']); + self::addPrintables($entry); } - $data[] = $entry; } return $data; } @@ -104,9 +105,11 @@ class GetData $anonymize = 0 !== ($flags & GETDATA_ANONYMOUS); $res = Queries::getUserStatistics(self::$from, self::$to, self::$lowerTimeBound, self::$upperTimeBound); $data = array(); - $user = $anonymize ? 'userHash' : 'name'; while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - $data[] = array('user' => $row[$user], 'sessions' => $row['count']); + if ($anonymize && $row['name'] !== 'anonymous') { + $row['name'] = md5($row['name'] . self::$salt); + } + $data[] = array('user' => $row['name'], 'sessions' => $row['count']); } return $data; } @@ -117,10 +120,12 @@ class GetData $anonymize = 0 !== ($flags & GETDATA_ANONYMOUS); $res = Queries::getVMStatistics(self::$from, self::$to, self::$lowerTimeBound, self::$upperTimeBound); $data = array(); - $vm = $anonymize ? 'vmHash' : 'name'; while ($row = $res->fetch(PDO::FETCH_ASSOC)) { self::nullToZero($row); - $data[] = array('vm' => $row[$vm], 'sessions' => $row['count']); + if ($anonymize) { + $row['name'] = md5($row['name'] . self::$salt); + } + $data[] = array('vm' => $row['name'], 'sessions' => $row['count']); } return $data; } @@ -140,19 +145,4 @@ class GetData return sprintf('%dd, %02d:%02d:%02d', $seconds / (3600*24), ($seconds % (3600*24)) / 3600, ($seconds%3600) / 60, $seconds%60); } - // Calculate Median - private static function calcMedian($string) { - $arr = explode(",", $string); - sort($arr, SORT_NUMERIC); - $count = count($arr); //total numbers in array - $middleval = floor(($count-1)/2); // find the middle value, or the lowest middle value - if($count % 2) { // odd number, middle is the median - $median = $arr[(int) $middleval]; - } else { // even number, calculate avg of 2 medians - $low = $arr[(int) $middleval]; - $high = $arr[(int) $middleval+1]; - $median = (($low+$high)/2); - } - return round($median); - } }
\ No newline at end of file diff --git a/modules-available/statistics_reporting/inc/queries.inc.php b/modules-available/statistics_reporting/inc/queries.inc.php index bd8eb72e..58e9e63b 100644 --- a/modules-available/statistics_reporting/inc/queries.inc.php +++ b/modules-available/statistics_reporting/inc/queries.inc.php @@ -4,216 +4,344 @@ class Queries { - // Client Data: Name, Time Online, Median Time Online, Time Offline, last start, last logout, Last Time Booted, Number of Sessions > 60Sec, Number of Sessions < 60Sec, name of location, id of location (anonymized), machine uuid (anonymized) - public static function getClientStatistics($from, $to, $lowerTimeBound = 0, $upperTimeBound = 24) { - $notassigned = Dictionary::translate('notAssigned', true); - Database::exec("SET SESSION group_concat_max_len = 1000000000"); - $res = Database::simpleQuery("SELECT t2.name AS clientName, timeSum, medianSessionLength, offlineSum, IFNULL(lastStart, 0) as lastStart, IFNULL(lastLogout, 0) as lastLogout, longSessions, shortSessions, t2.locId, t2.locName, MD5(CONCAT(t2.locId, :salt)) AS locHash, MD5(CONCAT(t2.uuid, :salt)) AS clientHash FROM ( - SELECT machine.machineuuid AS 'uuid', SUM(CAST(sessionTable.length AS UNSIGNED)) AS 'timeSum', GROUP_CONCAT(sessionTable.length) AS 'medianSessionLength', SUM(sessionTable.length >= 60) AS 'longSessions', SUM(sessionTable.length < 60) AS 'shortSessions', MAX(sessionTable.endInBound) AS 'lastLogout' - FROM ".self::getBoundedTableQueryString('~session-length', $from, $to, $lowerTimeBound, $upperTimeBound)." sessionTable - RIGHT JOIN machine ON sessionTable.machineuuid = machine.machineuuid - GROUP BY machine.machineuuid - ) t1 - RIGHT JOIN ( - SELECT IF(machine.hostname = '', machine.clientip, machine.hostname) AS 'name', machine.machineuuid AS 'uuid', SUM(CAST(offlineTable.length AS UNSIGNED)) AS 'offlineSum', MAX(offlineTable.endInBound) AS 'lastStart', IFNULL(location.locationname, '$notassigned') AS 'locName', location.locationid AS 'locId' - FROM ".self::getBoundedTableQueryString('~offline-length', $from, $to, $lowerTimeBound, $upperTimeBound)." offlineTable - RIGHT JOIN machine ON offlineTable.machineuuid = machine.machineuuid - LEFT JOIN location ON machine.locationid = location.locationid - GROUP BY machine.machineuuid - ) t2 - ON t1.uuid = t2.uuid", array("salt" => GetData::$salt)); + private static function keepKeys(&$array, $list) + { + foreach (array_keys($array) as $key) { + if (!in_array($key, $list)) { + unset($array[$key]); + } + } + } - return $res; + public static function getClientStatistics($from, $to, $lowerTimeBound = 0, $upperTimeBound = 24) + { + $res = Database::simpleQuery("SELECT m.machineuuid, m.hostname, m.clientip, + m.locationid, m.firstseen -- , m.lastboot, m.logintime, m.state + FROM machine m WHERE firstseen <= $to"); // " WHERE lastseen >= :from", compact('from')); + $machines = self::getStats3($res, $from, $to, $lowerTimeBound, $upperTimeBound); + foreach ($machines as &$machine) { + $machine['medianSessionLength'] = self::calcMedian($machine['sessions']); + unset($machine['sessions']); + $machine['clientName'] = $machine['hostname'] ? $machine['hostname'] : $machine['clientip']; + } + return $machines; } - // Location Data: Name, ID (anonymized), Time Online, Median Time Online, Time Offline, Number of Sessions > 60Sec, Number of Sessions < 60Sec - public static function getLocationStatistics($from, $to, $lowerTimeBound = 0, $upperTimeBound = 24) { - $notassigned = Dictionary::translate('notAssigned', true); - Database::exec("SET SESSION group_concat_max_len = 1000000000"); - $res = Database::simpleQuery("SELECT t2.locId, t2.locName, MD5(CONCAT(t2.locId, :salt)) AS locHash, timeSum, medianSessionLength, offlineSum, longSessions, shortSessions FROM ( - SELECT location.locationid AS 'locId', SUM(CAST(sessionTable.length AS UNSIGNED)) AS 'timeSum', GROUP_CONCAT(sessionTable.length) AS 'medianSessionLength', SUM(sessionTable.length >= 60) AS 'longSessions', SUM(sessionTable.length < 60) AS 'shortSessions' - FROM ".self::getBoundedTableQueryString('~session-length', $from, $to, $lowerTimeBound, $upperTimeBound)." sessionTable - RIGHT JOIN machine ON sessionTable.machineuuid = machine.machineuuid - LEFT JOIN location ON machine.locationid = location.locationid - GROUP BY machine.locationid - ) t1 - RIGHT JOIN ( - SELECT IFNULL(location.locationname, '$notassigned') AS 'locName', location.locationid AS 'locId', SUM(CAST(offlineTable.length AS UNSIGNED)) AS 'offlineSum' - FROM ".self::getBoundedTableQueryString('~offline-length', $from, $to, $lowerTimeBound, $upperTimeBound)." offlineTable - RIGHT JOIN machine ON offlineTable.machineuuid = machine.machineuuid - LEFT JOIN location ON machine.locationid = location.locationid - GROUP BY machine.locationid - ) t2 - ON t1.locId = t2.locId", array("salt" => GetData::$salt)); - return $res; + public static function getLocationStatistics($from, $to, $lowerTimeBound = 0, $upperTimeBound = 24) + { + $res = Database::simpleQuery("SELECT m.machineuuid, m.hostname, m.clientip, + m.locationid, m.firstseen -- , m.lastboot, m.logintime, m.state + FROM machine m WHERE firstseen <= $to"); // " WHERE lastseen >= :from", compact('from')); + $machines = self::getStats3($res, $from, $to, $lowerTimeBound, $upperTimeBound); + $locations = []; + $keys = ['locationid', 'totalTime', 'totalOffTime', 'totalSessionTime', 'totalStandbyTime', 'totalIdleTime', 'totalIdleTime', 'longSessions', 'shortSessions', 'sessions']; + while ($machine = array_pop($machines)) { + if (!isset($locations[$machine['locationid']])) { + self::keepKeys($machine, $keys); + $locations[$machine['locationid']] = $machine; + } else { + $l =& $locations[$machine['locationid']]; + $l['totalTime'] += $machine['totalTime']; + $l['totalOffTime'] += $machine['totalOffTime']; + $l['totalSessionTime'] += $machine['totalSessionTime']; + $l['totalStandbyTime'] += $machine['totalStandbyTime']; + $l['totalIdleTime'] += $machine['totalIdleTime']; + $l['longSessions'] += $machine['longSessions']; + $l['shortSessions'] += $machine['shortSessions']; + $l['sessions'] = array_merge($l['sessions'], $machine['sessions']); + } + } + foreach ($locations as &$location) { + $location['medianSessionLength'] = self::calcMedian($location['sessions']); + unset($location['sessions']); + } + return $locations; + } + + public static function getOverallStatistics($from, $to, $lowerTimeBound = 0, $upperTimeBound = 24) + { + $res = Database::simpleQuery("SELECT m.machineuuid, m.hostname, m.clientip, + m.locationid, m.firstseen -- , m.lastboot, m.logintime, m.state + FROM machine m WHERE firstseen <= $to"); // " WHERE lastseen >= :from", compact('from')); + $machines = self::getStats3($res, $from, $to, $lowerTimeBound, $upperTimeBound); + $total = false; + $keys = ['totalTime', 'totalOffTime', 'totalSessionTime', 'totalStandbyTime', 'totalIdleTime', 'totalIdleTime', 'longSessions', 'shortSessions', 'sessions']; + while ($machine = array_pop($machines)) { + if ($total === false) { + self::keepKeys($machine, $keys); + $total = $machine; + } else { + $total['totalTime'] += $machine['totalTime']; + $total['totalOffTime'] += $machine['totalOffTime']; + $total['totalSessionTime'] += $machine['totalSessionTime']; + $total['totalStandbyTime'] += $machine['totalStandbyTime']; + $total['totalIdleTime'] += $machine['totalIdleTime']; + $total['longSessions'] += $machine['longSessions']; + $total['shortSessions'] += $machine['shortSessions']; + $total['sessions'] = array_merge($total['sessions'], $machine['sessions']); + } + } + $total['medianSessionLength'] = self::calcMedian($total['sessions']); + unset($total['sessions']); + return $total; + } + + /** + * @param \PDOStatement $res + * @param int $from + * @param int $to + * @param int $lowerTimeBound + * @param int $upperTimeBound + * @return array + */ + private static function getStats3($res, $from, $to, $lowerTimeBound, $upperTimeBound) + { + //$debug = false; + if ($lowerTimeBound === 0 && $upperTimeBound === 24 || $upperTimeBound <= $lowerTimeBound) { + $bounds = false; + } else { + $bounds = [$lowerTimeBound, $upperTimeBound]; + } + $machines = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $row['firstseen'] = max($row['firstseen'], $from); + $row += array( + 'totalTime' => self::timeDiff($row['firstseen'], $to, $bounds), + 'totalOffTime' => 0, + 'totalSessionTime' => 0, + 'totalStandbyTime' => 0, + 'sessions' => [], + 'lastStart' => 0, + 'lastLogout' => 0, + 'longSessions' => 0, + 'shortSessions' => 0, + 'active' => false, + ); + $machines[$row['machineuuid']] = $row; + } + // Don't filter by typeid in the query, still faster by being able to use the machineuuid/dateline index and filtering later + $last = $from - 86400; // Start 24h early to catch sessions already in progress + $dups = []; + // Fetch in batches of 1000 rows (for current 50 machines) + do { + $res = Database::simpleQuery("SELECT logid, dateline, typeid, machineuuid, data + FROM statistic WHERE dateline >= :last AND dateline <= :to AND machineuuid IS NOT NULL + ORDER BY dateline ASC LIMIT 1000", compact('last', 'to')); + $last = false; + $count = 0; + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $count += 1; // Update count first, as we use it as a condition in outer loop. No continue before this + settype($row['logid'], 'int'); + // Update for next query + $last = $row['dateline']; + // Ignore dups, we query >= last dateline as we can have multiple events at the same second, but + // only some of them got returned because of LIMIT. Skip here because excluding in query directly + // would make the query itself rather inefficient. We also cannot use logid > X because the logid + // is not strictly ascending with time, as dateline gets backdated to event start on insert + if ($count === 150) { + $dups = []; + } elseif ($count > 900) { + $dups[] = $row['logid']; + } elseif ($count < 150 && array_key_exists($row['logid'], $dups)) { + continue; + } + if (!isset($machines[$row['machineuuid']])) + continue; + if ($row['typeid'] !== '~offline-length' && $row['typeid'] !== '~suspend-length' && $row['typeid'] !== '~session-length') + continue; + settype($row['dateline'], 'int'); + settype($row['data'], 'int'); + if ($row['data'] <= 0) + continue; + // Clamp to $from and $to + if ($row['dateline'] < $from) { + $diff = $row['dateline'] + $row['data'] - $from; + if ($diff <= 0) + continue; + $row['dateline'] += $diff; + $row['data'] -= $diff; + } + if ($row['dateline'] + $row['data'] > $to) { + $row['data'] = $to - $row['dateline']; + if ($row['data'] < 0) + continue; + } + $machine =& $machines[$row['machineuuid']]; + // Process event if applicable + if ($row['typeid'] === '~session-length') { // SESSION timespan + $row['typeid'] = 'totalSessionTime'; + $machine['lastLogout'] = $row['dateline'] + $row['data']; + } elseif ($row['typeid'] === '~offline-length') { // OFFLINE timespan + $row['typeid'] = 'totalOffTime'; + $machine['lastStart'] = $row['dateline'] + $row['data']; + } else { // STANDBY timespan + $row['typeid'] = 'totalStandbyTime'; + } + self::addTime($machine, $row, $bounds); + } + $dups = array_flip($dups); + } while ($last !== false && $count === 1000); // Check if we need to fetch more rows for current batch + foreach ($machines as &$machine) { + if (!$machine['active']) { + $machine['totalOffTime'] = $machine['totalTime']; + } + $machine['totalIdleTime'] = $machine['totalTime'] - ($machine['totalOffTime'] + $machine['totalStandbyTime'] + $machine['totalSessionTime']); + } + return $machines; + } + + private static function addTime(&$machine, $row, $bounds) + { + // First event, handle difference + if (!$machine['active'] && $row['dateline'] + $row['data'] >= $machine['firstseen']) { + if ($row['dateline'] > $machine['firstseen']) { + $s = $machine['firstseen']; + $e = $row['dateline']; + /* + if ($debug) { + error_log('Initial offline time += ' . self::timeDiff($s, $e, $bounds, true)); + } + */ + $machine['totalOffTime'] += self::timeDiff($s, $e, $bounds); + $machine['active'] = true; + if ($machine['lastStart'] < $row['dateline']) { + $machine['lastStart'] = $row['dateline']; + } + } else { + // Not offline at beginning of period, do nothing + $machine['active'] = true; + } + } + // Current row + if ($bounds === false) { + // Simple case: No bounds + $machine[$row['typeid']] += $row['data']; + } else { + $start = $row['dateline']; + $end = $row['dateline'] + $row['data']; + /* + if ($debug) { + error_log('Adding ' . $row['typeid'] . ' += ' . self::timeDiff($start, $end, $bounds, true)); + } + */ + $machine[$row['typeid']] += self::timeDiff($start, $end, $bounds); + $sh = date('G', $start); + } + if ($row['typeid'] === 'totalSessionTime' && ($bounds === false || ($sh >= $bounds[0] && $sh < $bounds[1]))) { + if ($row['data'] >= 60) { + $machine['longSessions'] += 1; + $machine['sessions'][] = $row['data']; + } else { + $machine['shortSessions'] += 1; + } + } + } + + private static function timeDiff($start, $end, $bounds) + { + // Put given timespan into bounds + /* + if ($debug) { + $os = $start; + $oe = $end; + } + */ + if ($bounds !== false) { + // Put start time into bounds + if ($start !== null) { + $sh = date('G', $start); + if ($sh < $bounds[0]) { + $start = strtotime($bounds[0] . ':00:00', $start); + } elseif ($sh >= $bounds[1]) { + $start = strtotime($bounds[0] . ':00:00 +1day', $start); + } + } + // Put end time into bounds + if ($end !== null && $end > $start) { + $eh = date('G', $end); + if ($eh < $bounds[0]) { + $end = strtotime($bounds[1] . ':00:00 -1day', $end); + } elseif ($eh >= $bounds[1]) { + $end = strtotime($bounds[1] . ':00:00', $end); + } + } + } + if ($end !== null && $start !== null && $end < $start) { + $end = $start; + } + /* + if ($debug) { + if ($start >= $end) { + error_log('END < START: ' . date('d.m.Y H:i:s', $start) . ' - ' . date('d.m.Y H:i:s', $end)); + } else { + if ($os != $start) { + error_log('Corrected start: ' . date('d.m.Y H:i:s', $os) . ' to ' . date('d.m.Y H:i:s', $start)); + } + if ($oe != $end) { + error_log('Corrected end : ' . date('d.m.Y H:i:s', $oe) . ' to ' . date('d.m.Y H:i:s', $end)); + } + } + } + */ + // Calc time excluding out of range hours + return ($end - $start) - self::getIgnoredTime($start, $end, $bounds); + } + + private static function getIgnoredTime($start, $end, $bounds) + { + if ($bounds === false || $start >= $end) + return 0; + $end = strtotime('00:00:00', $end); + if ($start >= $end) + return 0; + /* + if ($debug) { + error_log('From ' . date('d.m.Y H:i:s', $start) . ' to ' . date('d.m.Y H:i:s', $end) . ' = ' . ceil(($end - $start) / 86400) * (24 - ($bounds[1] - $bounds[0]))); + } + */ + return (int)ceil(($end - $start) / 86400) * (24 - ($bounds[1] - $bounds[0])) * 3600; + } + + /** + * Get median of array. + * @param int[] list of values + * @return int The median + */ + private static function calcMedian($array) { + if (empty($array)) + return 0; + sort($array, SORT_NUMERIC); + $count = count($array); //total numbers in array + $middleval = (int)floor(($count-1) / 2); // find the middle value, or the lowest middle value + if($count % 2 === 1) { // odd number, middle is the median + return (int)$array[$middleval]; + } + // even number, calculate avg of 2 medians + $low = $array[$middleval]; + $high = $array[$middleval+1]; + return (int)round(($low+$high) / 2); } // User Data: Name, Name(anonymized), Number of Logins public static function getUserStatistics($from, $to, $lowerTimeBound = 0, $upperTimeBound = 24) { - $res = Database::simpleQuery("SELECT username AS name, IF(username = 'anonymous', 'anonymous', md5(CONCAT(username, :salt))) AS userHash, COUNT(*) AS 'count' + $res = Database::simpleQuery("SELECT username AS name, COUNT(*) AS 'count' FROM statistic WHERE typeid='.vmchooser-session-name' AND dateline >= $from and dateline <= $to AND FROM_UNIXTIME(dateline, '%H') >= $lowerTimeBound AND FROM_UNIXTIME(dateline, '%H') < $upperTimeBound - GROUP BY username - ORDER BY 2 DESC", array("salt" => GetData::$salt)); + GROUP BY username"); return $res; } // Virtual Machine Data: Name, Number of Usages public static function getVMStatistics($from, $to, $lowerTimeBound = 0, $upperTimeBound = 24) { - $res = Database::simpleQuery("SELECT data AS name, MD5(CONCAT(data, :salt)) AS vmHash, COUNT(*) AS 'count' + $res = Database::simpleQuery("SELECT data AS name, COUNT(*) AS 'count' FROM statistic WHERE typeid='.vmchooser-session-name' AND dateline >= $from and dateline <= $to AND FROM_UNIXTIME(dateline, '%H') >= $lowerTimeBound AND FROM_UNIXTIME(dateline, '%H') < $upperTimeBound - GROUP BY data - ORDER BY 2 DESC", array("salt" => GetData::$salt)); + GROUP BY data"); return $res; } - //Total Data: Time Online, Median Time Online, Number of Sessions > 60Sec, Number of Sessions < 60Sec - public static function getOverallStatistics ($from, $to, $lowerTimeBound = 0, $upperTimeBound = 24) { - Database::exec("SET SESSION group_concat_max_len = 1000000000"); - $res = Database::simpleQuery("SELECT SUM(CAST(sessionTable.length AS UNSIGNED)) AS sum, GROUP_CONCAT(sessionTable.length) AS median, SUM(sessionTable.length >= 60) AS longSessions, SUM(sessionTable.length < 60) AS shortSessions - FROM ".self::getBoundedTableQueryString('~session-length', $from, $to, $lowerTimeBound, $upperTimeBound)." sessionTable"); - return $res; - } - - // Total Data(2): Time Offline - public static function getTotalOfflineStatistics($from, $to, $lowerTimeBound = 0, $upperTimeBound = 24) { - $res = Database::simpleQuery("SELECT SUM(CAST(offlineTable.length AS UNSIGNED)) AS timeOff - FROM ".self::getBoundedTableQueryString('~offline-length', $from, $to, $lowerTimeBound, $upperTimeBound)." offlineTable"); - return $res; - } - - // query string which provides table with time-cutoff and time-bounds - private static function getBoundedTableQueryString($typeid, $from, $to, $lowerTimeBound, $upperTimeBound) - { - // get Clients that are currently oflfine (the offline time is not yet recorded in the statistic table) - $union = $typeid == '~offline-length' ? - "union - select CAST(IF(lastseen < $from, $from, lastseen) as UNSIGNED) as start, $to as end, - '~offline-length' as typeid, machineuuid, 'machine' - from machine where lastseen <= $to and UNIX_TIMESTAMP() - lastseen >= 600" : ""; - - - $lowerFormat = "'%y-%m-%d $lowerTimeBound:00:00'"; - $upperFormat = "'%y-%m-%d ".($upperTimeBound-1).":59:59'"; - $queryString = " - select - - # The whole length of the session/offline time. - (end-start - - # Now the time that is not within the daily time bounds is subtracted. - # This includes the time before the first daily bound, the time after the last daily bound - # and the time between the daily bounds (if a session/offline time spans multiple days) - - # Time before the first daily bound is subtracted. - - IF( - start > startUpper, - UNIX_TIMESTAMP(FROM_UNIXTIME(start, $lowerFormat) + INTERVAL 1 DAY) - start, - IF( - start < startLower, - startLower - start, - 0 - ) - ) - - # Time after the last daily bound is subtracted. - - IF( - end > endUpper, - end - (endUpper + 1), - IF( - end < endLower, - end - (UNIX_TIMESTAMP(FROM_UNIXTIME(end, $upperFormat) - INTERVAL 1 DAY) + 1), - 0 - ) - ) - - # Time between the daily bounds is subtracted. - - ( daysDiff - 2 - + IF(start <= startUpper, 1, 0) - + IF(end >= endLower, 1, 0) - ) * ((24 - ($upperTimeBound - $lowerTimeBound)) * 3600) - - # If the session crossed a clock change (to/from daylight saving time), the last subtraction may have subtracted - # one hour too much or too little. This IF will correct this. - - IF( - innerStart < innerEnd, - IF( - timeDiff = 1 AND ($lowerTimeBound >= 2 OR $upperTimeBound <= 2), - 3600, - IF(timeDiff = -1 AND ($lowerTimeBound >= 3 OR $upperTimeBound <= 3), -3600, 0) - ), - 0 - ) - - ) as 'length', - - IF(end < endUpper AND end > endLower AND end < $to, end, 0) as endInBound, - - machineuuid - - - # These nested selects are necessary because some things need to be calculated before others. - # (e.g. start is needed to calculate startLower) - from ( - select - *, - - # timeDiff is the clock change between innerStart and innerEnd. ( 0 = no clock change) - ((CAST(date_format(from_unixtime(innerStart), '%H') as SIGNED) - - CAST(date_format(convert_tz(from_unixtime(innerStart), @@session.time_zone, '+00:00'), '%H') as SIGNED) + 24) % 24 - - - (CAST(date_format(from_unixtime(innerEnd), '%H') as SIGNED) - - CAST(date_format(convert_tz(from_unixtime(innerEnd), @@session.time_zone, '+00:00'), '%H') as SIGNED) + 24) % 24) as timeDiff - from ( - select - *, - - # innerStart and innerEnd are start and end but excluding the time before the first daily upper bound and after the last daily lower bound. - CAST(IF(start <= startUpper, startUpper, UNIX_TIMESTAMP(FROM_UNIXTIME(start, $upperFormat) + INTERVAL 1 DAY)) as UNSIGNED) as innerStart, - CAST(IF(end >= endLower, endLower, UNIX_TIMESTAMP(FROM_UNIXTIME(end, $lowerFormat) - INTERVAL 1 DAY)) as UNSIGNED) as innerEnd - from ( - select - *, - - # daysDiff = how many different days the start and end are apart (0 = start and end on the same day) - (TO_DAYS(FROM_UNIXTIME(end, '%y-%m-%d')) - TO_DAYS(FROM_UNIXTIME(start, '%y-%m-%d'))) as daysDiff, - - # startLower = lower daily time bound on the starting day - CAST(UNIX_TIMESTAMP(FROM_UNIXTIME(start, $lowerFormat)) as UNSIGNED) as startLower, - # startUpper = upper daily time bound on the starting day - CAST(UNIX_TIMESTAMP(FROM_UNIXTIME(start, $upperFormat)) as UNSIGNED) as startUpper, - # endLower = lower daily time bound on the ending day - CAST(UNIX_TIMESTAMP(FROM_UNIXTIME(end, $lowerFormat)) as UNSIGNED) as endLower, - # endUpper = upper daily time bound on the ending day - CAST(UNIX_TIMESTAMP(FROM_UNIXTIME(end, $upperFormat)) as UNSIGNED) as endUpper - from ( - # Statistic logs (combined with currently offline machines if offline times are requested) . - select CAST(IF(dateline < $from, $from, dateline) as UNSIGNED) as start, - CAST(IF(dateline+data > $to, $to, dateline+data) as UNSIGNED) as end, - typeid, machineuuid, 'statistic' - from statistic where dateline+data >= $from and dateline <= $to and typeid = '$typeid' - $union - ) t - ) t - ) t - ) t - - - # Filter out the session that are at least overlapping with the time bounds. - where ( - (daysDiff = 0 and (start <= UNIX_TIMESTAMP(FROM_UNIXTIME(start, $upperFormat)) and end >= UNIX_TIMESTAMP(FROM_UNIXTIME(end, $lowerFormat)))) - or - (daysDiff = 1 and (start <= UNIX_TIMESTAMP(FROM_UNIXTIME(start, $upperFormat)) or end >= UNIX_TIMESTAMP(FROM_UNIXTIME(end, $lowerFormat)))) - or - daysDiff >= 2 - ) - "; - return "(".$queryString.")"; - } - public static function getDozmodStats($from, $to) { if (!Module::isAvailable('dozmod')) diff --git a/modules-available/statistics_reporting/inc/remotereport.inc.php b/modules-available/statistics_reporting/inc/remotereport.inc.php index 98af8888..fa84e7e5 100644 --- a/modules-available/statistics_reporting/inc/remotereport.inc.php +++ b/modules-available/statistics_reporting/inc/remotereport.inc.php @@ -83,7 +83,7 @@ class RemoteReport GetData::$from = $from; GetData::$to = $to; $data = array('total' => GetData::total(GETDATA_ANONYMOUS)); - $data['perLocation'] = GetData::perLocation(GETDATA_ANONYMOUS); + $data['perLocation'] = array_values(GetData::perLocation(GETDATA_ANONYMOUS)); $data['perVM'] = GetData::perVM(GETDATA_ANONYMOUS); $data['tsFrom'] = $from; $data['tsTo'] = $to; diff --git a/modules-available/statistics_reporting/lang/de/template-tags.json b/modules-available/statistics_reporting/lang/de/template-tags.json index f8829b79..34c4b80b 100644 --- a/modules-available/statistics_reporting/lang/de/template-tags.json +++ b/modules-available/statistics_reporting/lang/de/template-tags.json @@ -1,5 +1,4 @@ { - "lang_apply": "Anwenden", "lang_displayColumns": "Auswahl angezeigter Spalten", "lang_displaySelection": "Anzeigemodus, Auswahl Zeitfenster", "lang_downloadReport": "Report herunterladen", @@ -7,7 +6,7 @@ "lang_hostname": "Hostname", "lang_lastLogout": "Letzter Logout", "lang_lastStart": "Letzter Boot", - "lang_location": "Ort", + "lang_locationname": "Ort", "lang_longSessions": "Sitzungen \u2265 60s", "lang_medianSessionLength": "Sitzungsdauer Median", "lang_moduleName": "Statistikauswertung", @@ -15,11 +14,14 @@ "lang_reportingDescription": "Helfen Sie uns bwLehrpool durch das w\u00f6chentliche Verschicken eines anonymisierten Statistikberichts zu verbessern. Wenn Sie den Inhalt eines solchen Reports genauer inspizieren wollen, k\u00f6nnen Sie \u00fcber den untenstehenden Button einen aktuellen Report Ihres Servers herunterladen.", "lang_reportingLabel": "Anonymisierte Nutzungsstatistiken \u00fcbermitteln", "lang_sessions": "Sitzungen", + "lang_settings": "Einstellungen", "lang_shortSessions": "Sitzungen < 60s", "lang_show": "Anzeigen", "lang_total": "Gesamt", - "lang_totalOffTime": "Gesamtzeit offline", + "lang_totalIdleTime": "Ungenutzt", + "lang_totalOffTime": "Offline", + "lang_totalSessionTime": "Belegt", + "lang_totalStandbyTime": "Standby", "lang_totalTime": "Gesamtzeit", - "lang_user": "Nutzer", "lang_vm": "Veranstaltung" }
\ No newline at end of file diff --git a/modules-available/statistics_reporting/lang/en/template-tags.json b/modules-available/statistics_reporting/lang/en/template-tags.json index 73c21112..7ddc3973 100644 --- a/modules-available/statistics_reporting/lang/en/template-tags.json +++ b/modules-available/statistics_reporting/lang/en/template-tags.json @@ -1,5 +1,4 @@ { - "lang_apply": "Apply", "lang_displayColumns": "Select columns to display", "lang_displaySelection": "Select display mode and specify time span", "lang_downloadReport": "Download report", @@ -7,7 +6,7 @@ "lang_hostname": "Hostname", "lang_lastLogout": "Last logout", "lang_lastStart": "Last boot", - "lang_location": "Location", + "lang_locationname": "Location", "lang_longSessions": "Sessions \u2265 60s", "lang_medianSessionLength": "Median Session Length", "lang_moduleName": "Statistics Reporting", @@ -15,11 +14,14 @@ "lang_reportingDescription": "Help us improve bwLehrpool by automatically sending an anonymized statistics report once a week. If you want to check what data the report contains, you can download such a report for reference below.", "lang_reportingLabel": "Send anonymized usage statistics", "lang_sessions": "Sessions", + "lang_settings": "Settings", "lang_shortSessions": "Sessions < 60s", "lang_show": "Show", "lang_total": "Total", - "lang_totalOffTime": "Total Time Offline", + "lang_totalIdleTime": "Idle", + "lang_totalOffTime": "Offline", + "lang_totalSessionTime": "Occupied", + "lang_totalStandbyTime": "Standby", "lang_totalTime": "Total Time", - "lang_user": "User", "lang_vm": "Lecture" }
\ No newline at end of file diff --git a/modules-available/statistics_reporting/lang/pt/template-tags.json b/modules-available/statistics_reporting/lang/pt/template-tags.json deleted file mode 100644 index e7981844..00000000 --- a/modules-available/statistics_reporting/lang/pt/template-tags.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "lang_hello": "Olá" -}
\ No newline at end of file diff --git a/modules-available/statistics_reporting/page.inc.php b/modules-available/statistics_reporting/page.inc.php index 863715d4..b30b5cab 100644 --- a/modules-available/statistics_reporting/page.inc.php +++ b/modules-available/statistics_reporting/page.inc.php @@ -24,8 +24,8 @@ class Page_Statistics_Reporting extends Page /** * @var array Names of columns that are being used by the various tables */ - private $COLUMNS = array('location', 'totalTime', 'medianSessionLength', 'sessions', 'longSessions', 'shortSessions', - 'totalOffTime', 'lastLogout', 'lastStart'); + private $COLUMNS = array('locationname', 'totalTime', 'medianSessionLength', 'sessions', 'longSessions', 'shortSessions', + 'totalOffTime', 'totalStandbyTime', 'totalSessionTime', 'totalIdleTime', 'lastLogout', 'lastStart'); /** * @var array Names of the tables we can display @@ -56,10 +56,16 @@ class Page_Statistics_Reporting extends Page } // timespan you want to see. default = last 7 days - GetData::$from = strtotime("- " . ($this->days - 1) . " days 00:00:00"); + GetData::$from = strtotime("-" . ($this->days - 1) . " days 00:00:00"); GetData::$to = time(); GetData::$lowerTimeBound = $this->lower; GetData::$upperTimeBound = $this->upper; + /* + GetData::$from = strtotime("2017-12-01 00:00:00"); + GetData::$to = strtotime("2017-12-15 00:00:00"); + GetData::$lowerTimeBound = 1; + GetData::$upperTimeBound = 2; + */ // Export - handle in doPreprocess so we don't render the menu etc. if ($this->action === 'export') { @@ -193,20 +199,20 @@ class Page_Statistics_Reporting extends Page if (Request::get('col_' . $column, 'delete', 'string') === 'delete') { foreach ($res as &$row) { unset($row[$column], $row[$column . '_s']); - if ($column === 'location') { - unset($row['locationId']); + if ($column === 'locationname') { + unset($row['locationid']); } } } elseif ($printable && isset($row[0][$column . '_s'])) { foreach ($res as &$row) { unset($row[$column]); } - } elseif ($column === 'location' && (isset($res[0]['location']) || isset($res[0]['locationId']))) { + } elseif ($column === 'locationname' && (isset($res[0]['locationname']) || isset($res[0]['locationid']))) { foreach ($res as &$row) { if ($printable) { - unset($row['locationId']); + unset($row['locationid']); } else { - unset($row['location']); + unset($row['locationname']); } } } @@ -285,7 +291,7 @@ class Page_Statistics_Reporting extends Page $highlight = Request::get('location', false, 'int'); if ($highlight !== false) { foreach ($data as &$row) { - if ($row['locationId'] == $highlight) { + if ($row['locationid'] == $highlight) { $row['highlight'] = true; } } @@ -293,7 +299,7 @@ class Page_Statistics_Reporting extends Page // only show locations which you have permission for $filterLocs = User::getAllowedLocations("table.view.location"); foreach ($data as $key => $row) { - if (!in_array($row['locationId'], $filterLocs)) { + if (!in_array($row['locationid'], $filterLocs)) { unset($data[$key]); } } @@ -301,11 +307,11 @@ class Page_Statistics_Reporting extends Page $data = array_values($data); return $data; case 'client': - $data = GetData::perClient($flags); + $data = GetData::perClient($flags, Request::any('new', false, 'string')); // only show clients from locations which you have permission for $filterLocs = User::getAllowedLocations("table.view.location"); foreach ($data as $key => $row) { - if (!in_array($row['locationId'], $filterLocs)) { + if (!in_array($row['locationid'], $filterLocs)) { unset($data[$key]); } } diff --git a/modules-available/statistics_reporting/style.css b/modules-available/statistics_reporting/style.css index 3cd6653f..c732099d 100644 --- a/modules-available/statistics_reporting/style.css +++ b/modules-available/statistics_reporting/style.css @@ -35,4 +35,13 @@ margin-left: -1.5em; text-align: center; line-height: 1.6em; +} + +.table-nowrap td { + white-space: nowrap; +} + +td.p-addon { + padding: 5px 5px 5px 0 !important; + text-align: right; }
\ No newline at end of file diff --git a/modules-available/statistics_reporting/templates/columnChooser.html b/modules-available/statistics_reporting/templates/columnChooser.html index a6561c47..d6ff90b7 100644 --- a/modules-available/statistics_reporting/templates/columnChooser.html +++ b/modules-available/statistics_reporting/templates/columnChooser.html @@ -2,7 +2,10 @@ <input type="hidden" name="do" value="statistics_reporting"> <div class="page-header"> - <button id="button-settings" type="button" class="btn btn-{{settingsButtonClass}} pull-right" data-toggle="modal" data-target="#modal-settings"><span class="glyphicon glyphicon-cog"></span> Settings</button> + <button id="button-settings" type="button" class="btn + btn-{{settingsButtonClass}} pull-right" data-toggle="modal" + data-target="#modal-settings"><span class="glyphicon + glyphicon-cog"></span> {{lang_settings}}</button> <h1>{{lang_moduleName}}</h1> </div> @@ -82,7 +85,7 @@ <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal">×</button> - <h4 class="modal-title"><b>Settings</b></h4> + <h4 class="modal-title"><b>{{lang_settings}}</b></h4> </div> <div class="modal-body"> <div class="checkbox"> @@ -186,4 +189,4 @@ } }); } -</script>
\ No newline at end of file +</script> diff --git a/modules-available/statistics_reporting/templates/table-client.html b/modules-available/statistics_reporting/templates/table-client.html index 59153e01..7bcf3ecf 100644 --- a/modules-available/statistics_reporting/templates/table-client.html +++ b/modules-available/statistics_reporting/templates/table-client.html @@ -1,29 +1,43 @@ -<table id="table-perclient" class="table table-condensed table-striped stupidtable"> +<table id="table-perclient" class="table table-condensed table-striped table-nowrap stupidtable"> <thead> <tr> - <th data-sort="string" class="text-left col-md-4">{{lang_hostname}}</th> - <th data-sort="string" class="text-left col_location">{{lang_location}}</th> - <th data-sort="int" data-sort-default="desc" class="text-left col_totalTime">{{lang_totalTime}}</th> - <th data-sort="int" data-sort-default="desc" class="text-left col_medianSessionLength">{{lang_medianSessionLength}}</th> - <th data-sort="int" data-sort-default="desc" class="text-left col_longSessions">{{lang_longSessions}}</th> - <th data-sort="int" data-sort-default="desc" class="text-left col_shortSessions">{{lang_shortSessions}}</th> - <th data-sort="int" data-sort-default="desc" class="text-left col_totalOffTime">{{lang_totalOffTime}}</th> - <th data-sort="int" data-sort-default="desc" class="text-left col_lastLogout">{{lang_lastLogout}}</th> - <th data-sort="int" data-sort-default="desc" class="text-left col_lastStart">{{lang_lastStart}}</th> + <th data-sort="string">{{lang_hostname}}</th> + <th data-sort="string" class="col_locationname">{{lang_locationname}}</th> + <th data-sort="int" data-sort-default="desc" class="col_totalTime">{{lang_totalTime}}</th> + <th data-sort="int" data-sort-default="desc" class="col_medianSessionLength">{{lang_medianSessionLength}}</th> + <th data-sort="int" data-sort-default="desc" class="col_longSessions">{{lang_longSessions}}</th> + <th data-sort="int" data-sort-default="desc" class="col_shortSessions">{{lang_shortSessions}}</th> + <th data-sort="int" data-sort-default="desc" class="col_totalOffTime">{{lang_totalOffTime}}</th> + <th data-sort="int" data-sort-default="desc" class="col_totalOffTime">(%)</th> + <th data-sort="int" data-sort-default="desc" class="col_totalStandbyTime">{{lang_totalStandbyTime}}</th> + <th data-sort="int" data-sort-default="desc" class="col_totalStandbyTime">(%)</th> + <th data-sort="int" data-sort-default="desc" class="col_totalSessionTime">{{lang_totalSessionTime}}</th> + <th data-sort="int" data-sort-default="desc" class="col_totalSessionTime">(%)</th> + <th data-sort="int" data-sort-default="desc" class="col_totalIdleTime">{{lang_totalIdleTime}}</th> + <th data-sort="int" data-sort-default="desc" class="col_totalIdleTime">(%)</th> + <th data-sort="int" data-sort-default="desc" class="col_lastLogout">{{lang_lastLogout}}</th> + <th data-sort="int" data-sort-default="desc" class="col_lastStart">{{lang_lastStart}}</th> </tr> </thead> <tbody> {{#data}} <tr> <td class="text-left">{{hostname}}</td> - <td class="text-left col_location"><a class="locationLink" href="#" data-lid="{{locationId}}">{{location}}</a></td> - <td data-sort-value="{{totalTime}}" class="text-left col_totalTime">{{totalTime_s}}</td> - <td data-sort-value="{{medianSessionLength}}" class="text-left col_medianSessionLength">{{medianSessionLength_s}}</td> - <td class="text-left col_longSessions">{{longSessions}}</td> - <td class="text-left col_shortSessions">{{shortSessions}}</td> - <td data-sort-value="{{totalOffTime}}" class="text-left col_totalOffTime">{{totalOffTime_s}}</td> - <td data-sort-value="{{lastLogout}}" class="text-left col_lastLogout">{{lastLogout_s}}</td> - <td data-sort-value="{{lastStart}}" class="text-left col_lastStart">{{lastStart_s}}</td> + <td class="col_locationname"><a class="locationLink" href="#" data-lid="{{locationid}}">{{locationname}}</a></td> + <td data-sort-value="{{totalTime}}" class="col_totalTime">{{totalTime_s}}</td> + <td data-sort-value="{{medianSessionLength}}" class="col_medianSessionLength">{{medianSessionLength_s}}</td> + <td class="col_longSessions">{{longSessions}}</td> + <td class="col_shortSessions">{{shortSessions}}</td> + <td data-sort-value="{{totalOffTime}}" class="text-right col_totalOffTime">{{totalOffTime_s}}</td> + <td class="col_totalOffTime p-addon">{{totalOffTime_p}} %</td> + <td data-sort-value="{{totalStandbyTime}}" class="text-right col_totalStandbyTime">{{totalStandbyTime_s}}</td> + <td class="col_totalStandbyTime p-addon">{{totalStandbyTime_p}} %</td> + <td data-sort-value="{{totalSessionTime}}" class="text-right col_totalSessionTime">{{totalSessionTime_s}}</td> + <td class="col_totalSessionTime p-addon">{{totalSessionTime_p}} %</td> + <td data-sort-value="{{totalIdleTime}}" class="text-right col_totalIdleTime">{{totalIdleTime_s}}</td> + <td class="col_totalIdleTime p-addon">{{totalIdleTime_p}} %</td> + <td data-sort-value="{{lastLogout}}" class="col_lastLogout">{{lastLogout_s}}</td> + <td data-sort-value="{{lastStart}}" class="col_lastStart">{{lastStart_s}}</td> </tr> {{/data}} </tbody> diff --git a/modules-available/statistics_reporting/templates/table-location.html b/modules-available/statistics_reporting/templates/table-location.html index a0867208..e7a0c84d 100644 --- a/modules-available/statistics_reporting/templates/table-location.html +++ b/modules-available/statistics_reporting/templates/table-location.html @@ -1,23 +1,37 @@ -<table id="table-perlocation" class="table table-condensed table-striped stupidtable"> +<table id="table-perlocation" class="table table-condensed table-striped table-nowrap stupidtable"> <thead> <tr> - <th data-sort="string" class="text-left col-md-2">{{lang_location}}</th> - <th data-sort="int" data-sort-default="desc" class="text-left col_totalTime">{{lang_totalTime}}</th> - <th data-sort="int" data-sort-default="desc" class="text-left col_medianSessionLength">{{lang_medianSessionLength}}</th> - <th data-sort="int" data-sort-default="desc" class="text-left col_longSessions">{{lang_longSessions}}</th> - <th data-sort="int" data-sort-default="desc" class="text-left col_shortSessions">{{lang_shortSessions}}</th> - <th data-sort="int" data-sort-default="desc" class="text-left col_totalOffTime">{{lang_totalOffTime}}</th> + <th data-sort="string">{{lang_locationname}}</th> + <th data-sort="int" data-sort-default="desc" class="col_totalTime">{{lang_totalTime}}</th> + <th data-sort="int" data-sort-default="desc" class="col_medianSessionLength">{{lang_medianSessionLength}}</th> + <th data-sort="int" data-sort-default="desc" class="col_longSessions">{{lang_longSessions}}</th> + <th data-sort="int" data-sort-default="desc" class="col_shortSessions">{{lang_shortSessions}}</th> + <th data-sort="int" data-sort-default="desc" class="col_totalOffTime">{{lang_totalOffTime}}</th> + <th data-sort="int" data-sort-default="desc" class="col_totalOffTime">(%)</th> + <th data-sort="int" data-sort-default="desc" class="col_totalStandbyTime">{{lang_totalStandbyTime}}</th> + <th data-sort="int" data-sort-default="desc" class="col_totalStandbyTime">(%)</th> + <th data-sort="int" data-sort-default="desc" class="col_totalSessionTime">{{lang_totalSessionTime}}</th> + <th data-sort="int" data-sort-default="desc" class="col_totalSessionTime">(%)</th> + <th data-sort="int" data-sort-default="desc" class="col_totalIdleTime">{{lang_totalIdleTime}}</th> + <th data-sort="int" data-sort-default="desc" class="col_totalIdleTime">(%)</th> </tr> </thead> <tbody> {{#data}} <tr{{#highlight}} class="info"{{/highlight}}> - <td class="locationName text-left">{{location}}</td> - <td data-sort-value="{{totalTime}}" class="text-left col_totalTime">{{totalTime_s}}</td> - <td data-sort-value="{{medianSessionLength}}" class="text-left col_medianSessionLength">{{medianSessionLength_s}}</td> - <td class="text-left col_longSessions">{{longSessions}}</td> - <td class="text-left col_shortSessions">{{shortSessions}}</td> - <td data-sort-value="{{totalOffTime}}" class="text-left col_totalOffTime">{{totalOffTime_s}}</td> + <td data-sort-value="{{parentlocations}}" class="locationName text-left">{{locationname}}</td> + <td data-sort-value="{{totalTime}}" class="text-right col_totalTime">{{totalTime_s}}</td> + <td data-sort-value="{{medianSessionLength}}" class="col_medianSessionLength">{{medianSessionLength_s}}</td> + <td class="text-right col_longSessions">{{longSessions}}</td> + <td class="text-right col_shortSessions">{{shortSessions}}</td> + <td data-sort-value="{{totalOffTime}}" class="text-right col_totalOffTime">{{totalOffTime_s}}</td> + <td class="col_totalOffTime p-addon">{{totalOffTime_p}} %</td> + <td data-sort-value="{{totalStandbyTime}}" class="text-right col_totalStandbyTime">{{totalStandbyTime_s}}</td> + <td class="col_totalStandbyTime p-addon">{{totalStandbyTime_p}} %</td> + <td data-sort-value="{{totalSessionTime}}" class="text-right col_totalSessionTime">{{totalSessionTime_s}}</td> + <td class="col_totalSessionTime p-addon">{{totalSessionTime_p}} %</td> + <td data-sort-value="{{totalIdleTime}}" class="text-right col_totalIdleTime">{{totalIdleTime_s}}</td> + <td class="col_totalIdleTime p-addon">{{totalIdleTime_p}} %</td> </tr> {{/data}} </tbody> diff --git a/modules-available/statistics_reporting/templates/table-total.html b/modules-available/statistics_reporting/templates/table-total.html index 8d5d7571..83078714 100644 --- a/modules-available/statistics_reporting/templates/table-total.html +++ b/modules-available/statistics_reporting/templates/table-total.html @@ -1,22 +1,36 @@ -<table id="table-total" class="table table-condensed table-striped stupidtable"> +<table id="table-total" class="table table-condensed table-striped table-nowrap stupidtable"> <thead> <tr> - <th class="text-left col-md-2"></th> - <th class="text-left col_totalTime">{{lang_totalTime}}</th> - <th class="text-left col_medianSessionLength">{{lang_medianSessionLength}}</th> - <th class="text-left col_longSessions">{{lang_longSessions}}</th> - <th class="text-left col_shortSessions">{{lang_shortSessions}}</th> - <th class="text-left col_totalOffTime">{{lang_totalOffTime}}</th> + <th></th> + <th class="col_totalTime">{{lang_totalTime}}</th> + <th class="col_medianSessionLength">{{lang_medianSessionLength}}</th> + <th class="col_longSessions">{{lang_longSessions}}</th> + <th class="col_shortSessions">{{lang_shortSessions}}</th> + <th class="col_totalOffTime">{{lang_totalOffTime}}</th> + <th class="col_totalOffTime">(%)</th> + <th class="col_totalStandbyTime">{{lang_totalStandbyTime}}</th> + <th class="col_totalStandbyTime">(%)</th> + <th class="col_totalSessionTime">{{lang_totalSessionTime}}</th> + <th class="col_totalSessionTime">(%)</th> + <th class="col_totalIdleTime">{{lang_totalIdleTime}}</th> + <th class="col_totalIdleTime">(%)</th> </tr> </thead> <tbody> <tr> - <th class="text-left">{{lang_total}}</th> - <td class="text-left col_totalTime">{{data.totalTime_s}}</td> - <td class="text-left col_medianSessionLength">{{data.medianSessionLength_s}}</td> - <td class="text-left col_longSessions">{{data.longSessions}}</td> - <td class="text-left col_shortSessions">{{data.shortSessions}}</td> - <td class="text-left col_totalOffTime">{{data.totalOffTime_s}}</td> + <td>{{lang_total}}</td> + <td class="col_totalTime">{{data.totalTime_s}}</td> + <td class="col_medianSessionLength">{{data.medianSessionLength_s}}</td> + <td class="col_longSessions">{{data.longSessions}}</td> + <td class="col_shortSessions">{{data.shortSessions}}</td> + <td class="text-right col_totalOffTime">{{data.totalOffTime_s}}</td> + <td class="col_totalOffTime p-addon">{{data.totalOffTime_p}} %</td> + <td class="text-right col_totalStandbyTime">{{data.totalStandbyTime_s}}</td> + <td class="col_totalStandbyTime p-addon">{{data.totalStandbyTime_p}} %</td> + <td class="text-right col_totalSessionTime">{{data.totalSessionTime_s}}</td> + <td class="col_totalSessionTime p-addon">{{data.totalSessionTime_p}} %</td> + <td class="text-right col_totalIdleTime">{{data.totalIdleTime_s}}</td> + <td class="col_totalIdleTime p-addon">{{data.totalIdleTime_p}} %</td> </tr> </tbody> </table> diff --git a/modules-available/sysconfig/lang/de/template-tags.json b/modules-available/sysconfig/lang/de/template-tags.json index 9ad29e90..7f8511e6 100644 --- a/modules-available/sysconfig/lang/de/template-tags.json +++ b/modules-available/sysconfig/lang/de/template-tags.json @@ -82,7 +82,6 @@ "lang_noValidCert": "Der Server besitzt kein oder ein nicht valides Zertifikat.", "lang_onProblemSearchBase": "Werden keine Benutzer gefunden, dann \u00fcberpr\u00fcfen Sie bitte die Suchbasis", "lang_or": "oder", - "lang_password": "Passwort", "lang_rebuild": "Neu generieren", "lang_rebuildLong": "Modul oder Konfiguration neu generieren. Das entsprechende Modul bzw. Konfiguration ist aktuell und sollte nicht neu generiert werden m\u00fcssen.", "lang_rebuildOutdatedLong": "Modul oder Konfiguration neu generieren. Das entsprechende Modul bzw. Konfiguration ist veraltet oder nicht vorhanden.", @@ -125,4 +124,4 @@ "lang_userDirectoryInfo1": "Optionale Angabe: Wenn die Clients f\u00fcr die Benutzer ein eigenes Verzeichnis (Homeverzeichnis, Benutzerverzeichnis) von einem Server einbinden sollen, geben Sie bitte hier das Format in UNC-Notation an, also z.B.", "lang_userDirectoryInfo2": "%s ist dabei ein Platzhalter f\u00fcr den Login-Namen des Benutzers.", "lang_userDirectoryInfo3": "Das Verzeichnis wird mit den gleichen Zugangsdaten eingebunden, die der Benutzer beim Login angibt. (D.h. kein Kerberos Support o.\u00e4.)" -}
\ No newline at end of file +} diff --git a/modules-available/sysconfig/lang/en/template-tags.json b/modules-available/sysconfig/lang/en/template-tags.json index e2cfd114..5a73c254 100644 --- a/modules-available/sysconfig/lang/en/template-tags.json +++ b/modules-available/sysconfig/lang/en/template-tags.json @@ -82,7 +82,6 @@ "lang_noValidCert": "The server did not supply a certificate, or the certificate is invalid.", "lang_onProblemSearchBase": "If no users are found, please check the search base", "lang_or": "or", - "lang_password": "Password", "lang_rebuild": "Rebuild", "lang_rebuildLong": "Rebuild module or configuration.", "lang_rebuildOutdatedLong": "Rebuild module or configuration. The module\/configuration is outdated or missing and should be regenerated.", @@ -125,4 +124,4 @@ "lang_userDirectoryInfo1": "Optional: If the clients should embed a separate directory (home directory, user directory) from a server for the user, please enter here the format in UNC notation, eg", "lang_userDirectoryInfo2": "%s is a placeholder for the user's login name.", "lang_userDirectoryInfo3": "The directory is loaded with the same credentials that the user specifies when login. (That is no Kerberos support, etc.)" -}
\ No newline at end of file +} diff --git a/modules-available/sysconfig/lang/pt/template-tags.json b/modules-available/sysconfig/lang/pt/template-tags.json index 2384a891..abf8e075 100644 --- a/modules-available/sysconfig/lang/pt/template-tags.json +++ b/modules-available/sysconfig/lang/pt/template-tags.json @@ -17,7 +17,6 @@ "lang_newConfiguration": "Nova Configura\u00e7\u00e3o", "lang_newModule": "Novo M\u00f3dulo", "lang_noContent": "Sem conte\u00fado!", - "lang_password": "Senha", "lang_searchBase": "Base de Pesquisa", "lang_show": "Mostrar", "lang_systemConfiguration": "Confgura\u00e7\u00e3o do Sistema", @@ -27,4 +26,4 @@ "lang_userDirectoryInfo1": "Opcional: Se os clientes devem incorporar um diret\u00f3rio separado (diret\u00f3rio home, diret\u00f3rio de usu\u00e1rio) de um servidor para o usu\u00e1rio, digite aqui o formato em nota\u00e7\u00e3o UNC, por exemplo,", "lang_userDirectoryInfo2": "%s \u00e9 um marcador para o nome de login do usu\u00e1rio.", "lang_userDirectoryInfo3": "O diret\u00f3rio \u00e9 carregado com as mesmas credenciais que o usu\u00e1rio especifica quando entra. (Isto \u00e9, n\u00e3o h\u00e1 suporte Kerberos, etc)" -}
\ No newline at end of file +} diff --git a/modules-available/syslog/hooks/cron.inc.php b/modules-available/syslog/hooks/cron.inc.php index bae882a9..62516648 100644 --- a/modules-available/syslog/hooks/cron.inc.php +++ b/modules-available/syslog/hooks/cron.inc.php @@ -1,7 +1,28 @@ <?php if (mt_rand(1, 10) === 1) { + // Prune old entries Database::exec("DELETE FROM clientlog WHERE (UNIX_TIMESTAMP() - 86400 * 190) > dateline"); + // Anonymize if requested + $days = Property::get('syslog.anon-days', 0); + if ($days > 0) { + $cutoff = time() - ($days * 86400); + Database::exec("UPDATE clientlog SET description = '[root] User logged in' + WHERE $cutoff > dateline AND logtypeid = 'session-open' AND description NOT LIKE '[root] User %'"); + Database::exec("UPDATE clientlog SET description = '[root] User logged out' + WHERE $cutoff > dateline AND logtypeid = 'session-close' AND description NOT LIKE '[root] User %'"); + Database::exec("UPDATE clientlog SET description = '-', extra = '' + WHERE $cutoff > dateline AND description NOT LIKE '-' + AND logtypeid NOT IN ('session-open', 'session-close', 'idleaction-busy', 'partition-temp', + 'partition-swap', 'smartctl-realloc', 'vmware-netifup', 'vmware-insmod', 'firewall-script-apply', + 'mount-vm-tmp-fail')"); + if (Module::get('statistics') !== false) { + Database::exec("UPDATE statistic SET username = 'anonymous' + WHERE $cutoff > dateline AND username NOT LIKE 'anonymous' AND username NOT LIKE ''"); + Database::exec("UPDATE machine SET currentuser = NULL + WHERE $cutoff > lastseen AND currentuser IS NOT NULL"); + } + } if (mt_rand(1, 100) === 1) { Database::exec("OPTIMIZE TABLE clientlog"); } diff --git a/modules-available/syslog/hooks/statistics/machine-replace.inc.php b/modules-available/syslog/hooks/statistics/machine-replace.inc.php new file mode 100644 index 00000000..6be0dd76 --- /dev/null +++ b/modules-available/syslog/hooks/statistics/machine-replace.inc.php @@ -0,0 +1,5 @@ +<?php + +foreach ($list as $entry) { + Database::exec('UPDATE IGNORE clientlog SET machineuuid = :new WHERE machineuuid = :old AND dateline < :datelimit', $entry); +} diff --git a/modules-available/syslog/lang/de/messages.json b/modules-available/syslog/lang/de/messages.json new file mode 100644 index 00000000..eec31c2f --- /dev/null +++ b/modules-available/syslog/lang/de/messages.json @@ -0,0 +1,4 @@ +{ + "anon-days-out-of-range": "Tage muss zwischen 0 und 180 liegen (war {{0}})", + "anon-days-saved": "Anonymisierungszeitraum gespeichert" +}
\ No newline at end of file diff --git a/modules-available/syslog/lang/de/template-tags.json b/modules-available/syslog/lang/de/template-tags.json index c8b2bb45..b5c6f8c7 100644 --- a/modules-available/syslog/lang/de/template-tags.json +++ b/modules-available/syslog/lang/de/template-tags.json @@ -1,11 +1,12 @@ { + "lang_anonDaysDescription": "Nach wie vielen Tagen sollen Logeintr\u00e4ge anonymisiert werden? Auf 0 setzen, um Funktion zu deaktivieren.", + "lang_applyFilter": "Filter anwenden", "lang_client": "Client", "lang_clientLog": "Client Log", "lang_details": "Details", "lang_event": "Ereignis", "lang_filter": "Filter", - "lang_go": "Go", "lang_not": "not", - "lang_when": "Wann", - "lang_applyFilter": "Filter anwenden" + "lang_settings": "Einstellungen", + "lang_when": "Wann" }
\ No newline at end of file diff --git a/modules-available/syslog/lang/en/messages.json b/modules-available/syslog/lang/en/messages.json new file mode 100644 index 00000000..5578d7b9 --- /dev/null +++ b/modules-available/syslog/lang/en/messages.json @@ -0,0 +1,4 @@ +{ + "anon-days-out-of-range": "Days have to be between 0 and 180 (was {{0}})", + "anon-days-saved": "Anonymization delay saved" +}
\ 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 7dae52d9..1aae1fe9 100644 --- a/modules-available/syslog/lang/en/template-tags.json +++ b/modules-available/syslog/lang/en/template-tags.json @@ -1,11 +1,12 @@ { + "lang_anonDaysDescription": "After how many days should log messages be anonymized? Set to 0 to disable this feature.", + "lang_applyFilter": "Apply Filter", "lang_client": "Client", "lang_clientLog": "Client Log", "lang_details": "Details", "lang_event": "Event", "lang_filter": "Filter", - "lang_go": "Go", "lang_not": "not", - "lang_when": "When", - "lang_applyFilter": "Apply Filter" + "lang_settings": "Settings", + "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 a34ceb53..a26ed9be 100644 --- a/modules-available/syslog/page.inc.php +++ b/modules-available/syslog/page.inc.php @@ -3,6 +3,9 @@ class Page_SysLog extends Page { + const PROP_ANON_DAYS = 'syslog.anon-days'; // Copy in cronjob + + protected function doPreprocess() { User::load(); @@ -11,6 +14,15 @@ class Page_SysLog extends Page Message::addError('main.no-permission'); Util::redirect('?do=Main'); } + if (($days = Request::post('anondays', false, 'int')) !== false) { + if ($days < 0 || $days > 180) { + Message::addError('anon-days-out-of-range', $days); + } else { + Property::set(self::PROP_ANON_DAYS, $days); + Message::addSuccess('anon-days-saved'); + } + Util::redirect('?do=syslog'); + } } protected function doRender() @@ -68,7 +80,7 @@ class Page_SysLog extends Page $allowedLocations = User::getAllowedLocations("view"); $joinClause = ""; if (!in_array(0, $allowedLocations)) { - $joinClause = "INNER JOIN machine ON machine.machineuuid = clientlog.machineuuid"; + $joinClause = "INNER JOIN machine USING (machineuuid)"; if (empty($whereClause)) $whereClause .= ' WHERE '; else @@ -78,7 +90,7 @@ class Page_SysLog extends Page } $lines = array(); - $paginate = new Paginate("SELECT logid, dateline, logtypeid, clientlog.clientip as clientip, description, extra FROM clientlog $joinClause $whereClause ORDER BY logid DESC", 50); + $paginate = new Paginate("SELECT logid, dateline, logtypeid, clientlog.clientip, clientlog.machineuuid, description, extra FROM clientlog $joinClause $whereClause ORDER BY logid DESC", 50); $res = $paginate->exec(array("allowedLocations" => $allowedLocations)); while ($row = $res->fetch(PDO::FETCH_ASSOC)) { $row['date'] = Util::prettyTime($row['dateline']); @@ -92,6 +104,7 @@ class Page_SysLog extends Page 'list' => $lines, 'types' => json_encode(array_values($types)), 'machineuuid' => Request::get('machineuuid'), + 'anondays' => Property::get(self::PROP_ANON_DAYS, 0), )); } diff --git a/modules-available/syslog/templates/page-syslog.html b/modules-available/syslog/templates/page-syslog.html index 9062dbaa..7ab81067 100644 --- a/modules-available/syslog/templates/page-syslog.html +++ b/modules-available/syslog/templates/page-syslog.html @@ -1,3 +1,6 @@ +<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> <style type="text/css"> .selectize-dropdown { max-width: 500px; @@ -44,7 +47,7 @@ <tr> <td><span class="type-button glyphicon {{icon}}" title="{{logtypeid}}"></span></td> <td class="text-center" nowrap="nowrap">{{date}}</td> - <td class="text-left">{{clientip}}</td> + <td class="text-left"><a href="?do=statistics&uuid={{machineuuid}}">{{clientip}}</a></td> <td>{{description}}</td> <td class="text-center">{{#extra}} <a class="btn btn-default btn-xs" onclick="$('#details-body').html($('#extra-{{logid}}').html())" @@ -73,6 +76,29 @@ </div> </div> +<div id="modal-settings" class="modal fade" role="dialog"> + <div class="modal-dialog"> + <div class="modal-content"> + <form method="post" action="?do=syslog"> + <input type="hidden" name="token" value="{{token}}"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal">×</button> + <h4 class="modal-title"><b>{{lang_settings}}</b></h4> + </div> + <div class="modal-body"> + <p>{{lang_anonDaysDescription}}</p> + <input type="number" name="anondays" value="{{anondays}}" min="0" max="180"> + </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">{{lang_save}}</button> + </div> + </form> + </div> + + </div> +</div> + <script type="application/javascript"><!-- document.addEventListener('DOMContentLoaded', function () { diff --git a/modules-available/translation/templates/edit.html b/modules-available/translation/templates/edit.html index 3c66aef6..7a41d9b4 100644 --- a/modules-available/translation/templates/edit.html +++ b/modules-available/translation/templates/edit.html @@ -5,7 +5,7 @@ </div> <form action="?do=Translation" method="post" class="slx-visible-rows"> - <table id="moduleTable" class="table table-condensed table-hover stupidtable"> + <table id="moduleTable" class="table table-condensed table-hover"> <thead> <tr> <th>{{lang_tag}}</th> @@ -35,10 +35,10 @@ <td class="col-sm-4" id="tagid-{{tagid}}"> {{^big}} - <input type="text" class="form-control switchable" value="{{translation}}" ondblclick="slxMb(this)" name="lang#!#{{tag}}" placeholder="{{placeholder}}"> + <input type="text" class="form-control switchable {{#missing}}txt-empty{{/missing}}" value="{{translation}}" ondblclick="slxMb(this)" name="lang#!#{{tag}}" placeholder="{{placeholder}}"> {{/big}} {{#big}} - <textarea rows="3" class="form-control" name="lang#!#{{tag}}" placeholder="{{placeholder}}">{{translation}}</textarea> + <textarea rows="3" class="form-control {{#missing}}txt-empty{{/missing}}" name="lang#!#{{tag}}" placeholder="{{placeholder}}">{{translation}}</textarea> {{/big}} </td> @@ -77,90 +77,6 @@ </form> <br/> -<!-- vorherige Version der "Tabelle" geregelt über Divisions - - <form action="?do=Translation" method="post" class="slx-visible-rows"> - <input type="hidden" name="module" value="{{module}}"> - <input type="hidden" name="destlang" value="{{destlang}}"> - <input type="hidden" name="section" value="{{section}}"> - <input type="hidden" name="subsection" value="{{subsection}}"> - <input type="hidden" name="token" value="{{token}}"> - <div class="row"> - <div class="col-xs-4 col-sm-3">{{lang_tag}}</div> - <div class="col-xs-6 col-sm-4">{{lang_translation}} (<b>{{language}}</b>)</div> - <div class="hidden-xs col-sm-3 col-lg-4">{{lang_sample}}</div> - <div class="col-xs-2 col-sm-2 col-lg-1">!</div> - </div> - {{#tags}} - <div class="row"> - <div class="col-xs-4 col-sm-3"> - {{#unused}} - <span class="label label-danger">{{lang_unused}}</span> - {{/unused}} - {{#missing}} - <span class="label label-warning">{{lang_missing}}</span> - {{/missing}} - {{#isglobal}} - <span class="label label-success" title="{{lang_globalTooltip}}">{{lang_global}}</span> - {{/isglobal}} - {{tag}} - <div class="slx-notebox">{{{notes}}}</div> - </div> - <div class="col-xs-6 col-sm-4" id="tagid-{{tagid}}"> - {{^big}} - <input type="text" class="form-control switchable" value="{{translation}}" ondblclick="slxMb(this)" name="lang#!#{{tag}}" placeholder="{{placeholder}}"> - {{/big}} - {{#big}} - <textarea rows="3" class="form-control" name="lang#!#{{tag}}" placeholder="{{placeholder}}">{{translation}}</textarea> - {{/big}} - </div> - <div class="hidden-xs col-sm-3 col-lg-4"> - <div class="slx-textpreview"><div> - <div class="badge">{{samplelang}}</div> - {{sampletext}} - </div></div> - </div> - <div class="col-xs-2 col-sm-2 col-lg-1"> - <button type="button" class="btn btn-danger btn-xs" onclick="slxDelTag({{tagid}})" tabindex="-1"> - <span class="glyphicon glyphicon-trash"></span> - <span class="hidden-xs">{{lang_delete}}</span> - </button> - </div> - </div> - {{/tags}} - <div id="newTag"></div> - <br/> - <div class="text-right"> - <a class="btn btn-default" href='?do=Translation' >{{lang_back}}</a> - <button class="btn btn-success" type="button" onclick="slxAddTag()" >{{lang_createTag}}</button> - <button type="submit" class="btn btn-primary" name="update" value="true">{{lang_save}}</button> - </div> - </form> - - - - -Function: - $('#newTag').before( - '<div class="row" id="new-delete-' + slxNewTagCounter + '">' + - ' <div class="col-xs-4 col-sm-3">' + - ' <input type="text" name="new-id[' + slxNewTagCounter + ']" class="form-control">' + - ' </div>' + - ' <div class="col-xs-6 col-sm-4">' + - ' <input type="text" class="form-control" name="new-text[' + slxNewTagCounter + ']">' + - ' </div>' + - ' <div class="hidden-xs col-sm-4">' + - ' </div>' + - ' <div class="col-xs-2 col-sm-1">' + - ' <button type="button" class="btn btn-danger btn-xs" onclick="slxDelNew(' + slxNewTagCounter + ')" tabindex="-1"><span class="glyphicon glyphicon-remove"></span> {{lang_delete}}</button>' + - ' </div>' + - '</div>' - ); - - - ---> - <script type="text/javascript"> var slxNewTagCounter = 0; function slxAddTag() @@ -204,4 +120,9 @@ Function: ta.val(old.val()); old.replaceWith(ta); } + + document.addEventListener('DOMContentLoaded', function () { + $('.txt-empty').first().focus(); + }); + </script> diff --git a/modules-available/usermanagement/lang/en/template-tags.json b/modules-available/usermanagement/lang/en/template-tags.json index 66b30c6a..35497fc9 100644 --- a/modules-available/usermanagement/lang/en/template-tags.json +++ b/modules-available/usermanagement/lang/en/template-tags.json @@ -5,11 +5,9 @@ "lang_email": "Email", "lang_login": "Login", "lang_operations": "Operation", - "lang_password": "Password", "lang_remove": "Remove", "lang_telephone": "Telephone", "lang_userAdmin": "Administrator", "lang_userInfo": "On this section, you will be able to create website users, besides editing or removing existing users.", - "lang_userPage": "Users", - "lang_username": "Username" -}
\ No newline at end of file + "lang_userPage": "Users" +} diff --git a/modules-available/usermanagement/lang/pt/template-tags.json b/modules-available/usermanagement/lang/pt/template-tags.json index ad84126a..fefb441d 100644 --- a/modules-available/usermanagement/lang/pt/template-tags.json +++ b/modules-available/usermanagement/lang/pt/template-tags.json @@ -5,11 +5,9 @@ "lang_email": "Email", "lang_login": "Login", "lang_operations": "Opera\u00e7\u00f5es", - "lang_password": "Senha", "lang_remove": "Remover", "lang_telephone": "Telefone", "lang_userAdmin": "Administrador", "lang_userInfo": "Nesta se\u00e7\u00e3o voc\u00ea poder\u00e1 criar usu\u00e1rios para o site, al\u00e9m de editar as informa\u00e7\u00f5es ou remover usu\u00e1rios existentes.", - "lang_userPage": "Usu\u00e1rios", - "lang_username": "Nome do Usu\u00e1rio" -}
\ No newline at end of file + "lang_userPage": "Usu\u00e1rios" +} diff --git a/modules-available/vmstore/lang/de/template-tags.json b/modules-available/vmstore/lang/de/template-tags.json index d434566a..0b56d84f 100644 --- a/modules-available/vmstore/lang/de/template-tags.json +++ b/modules-available/vmstore/lang/de/template-tags.json @@ -9,11 +9,9 @@ "lang_nfsHelp3": "Die erste Zeile erlaubt den Lese- und Schreibzugriff des\r\nSatellitenservers. Die zweite Zeile erteilt allen anderen Rechnern\r\nausschlie\u00dflich Lesezugriff. Sie k\u00f6nnen dies nat\u00fcrlich auch auf\r\nspezielle Subnetze oder IP-Bereiche beschr\u00e4nken.", "lang_nfsHelp4": "Wenn exklusiv DNBD3 verwendet wird, kann die zweite Zeile ausgelassen\r\nwerden. Dies erh\u00f6ht die Sicherheit.", "lang_noAdditionalInformation": "Keine weitere Konfiguration notwendig", - "lang_password": "Passwort", "lang_path": "Pfad", "lang_readOnly": "Nur-Lese-Zugangsdaten", "lang_readWrite": "Lese\/Schreib-Zugangsdaten", - "lang_username": "Benutzerkennung", "lang_vmLocation": "VM Speicherort", "lang_vmLocationChoose": "Bitte w\u00e4hlen Sie, wo die Images der Virtuellen Maschinen gespeichert werden sollen.", "lang_vmLocationConfiguration": "VM Speicherort wird konfiguriert", @@ -21,4 +19,4 @@ "lang_vmLocationHelp2": "Im Produktivbetrieb bietet es sich an, hierf\u00fcr einen performanten\r\nNetzwerkspeicher zu benutzen. Dieser Netzwerkspeicher kann per NFS oder\r\nCIFS\/SMB eingebunden werden. In jedem Fall muss sichergestellt werden,\r\ndass der Satellitenserver zum Hinzuf\u00fcgen neuer Virtueller Maschinen\r\nSchreibzugriff auf diesen Netzwerkspeicher hat. Bei der Nutzung von\r\nNFSv3 kann dies IP-Basiert eingerichtet werden, f\u00fcr die Nutzung von\r\nCIFS\/SMB k\u00f6nnen Sie Zugangsdaten angeben, die zum Schreiben\r\nberechtigen.", "lang_vmLocationHelp3": "Im Fall von NFS und CIFS ben\u00f6tigen die bwLehrpool-Clients\r\nausschlie\u00dflich Lesezugriff auf den Netzwerkspeicher (und sollten aus\r\nSicherheitsgr\u00fcnden auch wirklich nur lesen k\u00f6nnen). Wenn Sie exklusiv\r\nDNBD3 verwenden, sind f\u00fcr die Clients keine Freigaben oder\r\nBerechtigungen notwendig, da der Zugriff \u00fcber den Sallitenserver\r\nstattfindet.", "lang_vmLocationHelp4": "Sie k\u00f6nnen DNBD3 entweder exklusiv oder mit Fallback auf NFS\/CIFS verwenden. Im exklusiven Modus wird der Zugriff der Clients auf den kompletten VM-Speicher verhindert bzw. erschwert. Die Clients ben\u00f6tigen keinerlei Berechtigungen auf dem konfigurierten Share. Wenn Sie den Fallback Modus nutzen, werden f\u00fcr die Clients weiterhin Leseberechtigungen ben\u00f6tigt." -}
\ No newline at end of file +} diff --git a/modules-available/vmstore/lang/en/template-tags.json b/modules-available/vmstore/lang/en/template-tags.json index d78877b9..1e712b13 100644 --- a/modules-available/vmstore/lang/en/template-tags.json +++ b/modules-available/vmstore/lang/en/template-tags.json @@ -9,11 +9,9 @@ "lang_nfsHelp3": "The first line allows read\/write access for the satellite server. The second line grants read-only access for every other IP address. You could limit the second line to specific IP ranges only if desired.", "lang_nfsHelp4": "If using DNBD3 in exclusive mode, you can remove the second line completely, so only the satellite server has access to the NFS store.", "lang_noAdditionalInformation": "No additional cofiguration required", - "lang_password": "Password", "lang_path": "Path", "lang_readOnly": "Read-only Access", "lang_readWrite": "Read\/Write Access", - "lang_username": "Username", "lang_vmLocation": "VM Storage Location", "lang_vmLocationChoose": "Please choose where the images of virtual machines will be stored.", "lang_vmLocationConfiguration": "VM location is configured", @@ -21,4 +19,4 @@ "lang_vmLocationHelp2": "In productive operation, it makes sense for this to use a high-performance network storage. This network storage can be integrated via NFS or CIFS \/ SMB. In any case, it must be ensured that the satellite server has write access to this network storage to add a new Virtual Machine . When using NFSv3 this can be set up IP-based, for the use of CIFS \/ SMB, you can access data disclosures that would entitle them to write.", "lang_vmLocationHelp3": "The bwLehrpool clients only need read access to the network storage (and for security reasons, really can only read). In CIFS \/ SMB You can do this most easily by allowing passwordless guest access with read access to the share.", "lang_vmLocationHelp4": "You can use DNBD3 exclusively or with NFS\/CIFS as fallback. In exclusive mode, you can deny direct access to the NFS\/CIFS store for clients, to prevent anyone from browsing the VM store directly. In fallback mode, the clients first try to use DNBD3 to run a VM and if that fails, they use NFS\/CIFS directly." -}
\ No newline at end of file +} diff --git a/modules-available/vmstore/lang/pt/template-tags.json b/modules-available/vmstore/lang/pt/template-tags.json index 90ae64ad..8e0e4267 100644 --- a/modules-available/vmstore/lang/pt/template-tags.json +++ b/modules-available/vmstore/lang/pt/template-tags.json @@ -6,15 +6,13 @@ "lang_nfsHelp1": "\u00c9 necess\u00e1rio um NFSv3-Share. Ele deve poder ser lido por todas as esta\u00e7\u00f5es de trabalho, e poder ser escrito pelo server. Um exemplo, assumindo que o server possui endere\u00e7o ip 1.2.3.4:", "lang_nfsHelp2": "Configura\u00e7\u00e3o alternativa usando all_squash. O usu\u00e1rio com uid 1234 deve possuir ( e poder escrever em ) o diret\u00f3rio exportado.", "lang_noAdditionalInformation": "Nenhuma configura\u00e7\u00e3o adicional necess\u00e1ria", - "lang_password": "Senha", "lang_readOnly": "Acesso Somente Leitura", "lang_readWrite": "Acesso Leitura\/Escrita", "lang_save": "Salvar", - "lang_username": "Nome de Usu\u00e1rio", "lang_vmLocation": "Localiza\u00e7\u00e3o da VM", "lang_vmLocationChoose": "Por favor, escolha aonde as imagens das m\u00e1quinas virtuais ser\u00e3o armazenadas.", "lang_vmLocationConfiguration": "Localiza\u00e7\u00e3o da VM \u00e9 configurada", "lang_vmLocationHelp1": "Para fins de teste, as VMs podem ser armazenados diretamente no servidor sat\u00e9lite. No entanto, se voc\u00ea operar o vmdk do sat\u00e9lite entregue por favor lembre-se que voc\u00ea tem apenas cerca de 100 GB de mem\u00f3ria.", "lang_vmLocationHelp2": "Em opera\u00e7\u00e3o, faz sentido para este usar um armazenamento de rede de alto desempenho. Este armazenamento de rede pode ser integrado atrav\u00e9s de NFS ou CIFS \/ SMB. Em qualquer caso, deve-se assegurar de que o servidor de sat\u00e9lite tenha acesso de grava\u00e7\u00e3o para este armazenamento de rede para poder adicionar uma nova m\u00e1quina virtual. Ao utilizar NFSv3 este pode ser configurado com base em IP, para o uso de CIFS \/ SMB, voc\u00ea pode acessar as divulga\u00e7\u00f5es de dados que lhe permitiria escrever.", "lang_vmLocationHelp3": "Os clientes bwLehrpool s\u00f3 precisam ter acesso de leitura ao armazenamento de rede (e por raz\u00f5es de seguran\u00e7a, realmente s\u00f3 pode ler). Em CIFS \/ SMB Voc\u00ea pode fazer isso mais facilmente, permitindo o acesso a visitantes sem senha com acesso de leitura." -}
\ No newline at end of file +} |