diff options
61 files changed, 715 insertions, 261 deletions
diff --git a/doc/baseconfig_-_config-variables b/doc/baseconfig_-_config-variables new file mode 100644 index 00000000..6c85b7de --- /dev/null +++ b/doc/baseconfig_-_config-variables @@ -0,0 +1,42 @@ +The baseconfig module (configuration variables) is now modularized in two +ways. + + +1) The definition of configuration variables has moved from the database to +dedicated .json files, which can be defined by one or multiple additional +modules. This means the database table "settings" is no longer in use. +As an example, see the module "baseconfig_bwlp". +If you only enable the module "baseconfig", you will find that visiting +the "configuration variables" menu entry in slx-admin shows a pretty empty +page. However, if you additionally enable the baseconfig_bwlp module, many +configuration variables will be shown. +baseconfig_bwlp doesn't contain any config.json or page.inc.php, as its +sole purpose is to supply wanted configuration variables. This is achieved +by supplying two .json files. + +./modules/<module>/baseconfig/categories.json: Defines categories for +configuration variables. Syntax is <id>:<sort-order> + +./modules/<module>/baseconfig/settings.json: Defines configuration variables +to be shown by the baseconfig page. This has taken the place of the settings +table from the database and contains pretty much the same information. + +Note that you can have multiple modules that supply categories.json or +settings.json files. They will all be honored by the baseconfig module. + +2) You can hook into the baseconfig API mechanism (previously known as +the "getconfig" API) which creates the plain text output intended for +the clients. This way you can create more sophisticated output logic, or +simply have a nicer guy for some values you want to generate. +To hook into the API, you need a hook: +./modules/<module>/baseconfig/getconfig.inc.php +This code will run directly in the context of the baseconfig API request, +so you write code straight away that should add any configuration variables +to the associative array called $configVars, e.g. +$configVars['SLX_FOO'] = 'bar'; +These values can be static, or queried from anywhere in the database, etc. +For an example of this, see the module "baseconfig_partitions_cdn" +Which is basically a normal slx-admin module with a GUI for creating a +partitioning config that get stored in the database. +It also contains the .../baseconfig/getconfig.inc.php hook that adds +the partition configuration to the output of the baseconfig API. diff --git a/doc/dashboard b/doc/dashboard new file mode 100644 index 00000000..3040bb6f --- /dev/null +++ b/doc/dashboard @@ -0,0 +1,20 @@ +The dashboard is usually created automatically and doesn't require any +special setup. The order of the settings and categories in the menu +can be influenced by the two variables $MENU_CAT_SORT_ORDER and +$MENU_SETTING_SORT_ORDER in config.php (see config.php.example) + +Menu categories can be defined implicitly in the form of +modulename.catname, i.e. main.content + +To assign an icon to the category, create a file called category-icons.json +in the module's root dir (i.e. ./modules/main/category-icons.json) +and add a key value pair of the form "catname":"glyphicon", i.e. +{ "content":"th" }. Notice the module name is not prefixed again. + +To translate the category's name, visit the translation page and go +to the module in which the category is defined (in this example, main). +You'll see a panel dedicated to menu categories, it should contain the +Note: The menu section will only appear in the translation list of the +module if any active module is assigned to this category. However, you +can still manually create the entry by using the "create tag" button. +As an example, see the "main" or the "citymanagement" module. diff --git a/doc/enabling_modules b/doc/enabling_modules new file mode 100644 index 00000000..dd36d000 --- /dev/null +++ b/doc/enabling_modules @@ -0,0 +1,16 @@ +As a convention, modules should be placed in another directory and then +get selectively enabled by symlinking. +Currently, all modules are placed in ./modules-available, and the symlinks +reside in ./modules +Note that you can name the symlink differently. The name of the symlink +will define by what identifier the module is referred to. This way +you can have different implementations or versions of the same module +and switch them out. +An example of this is the module "serversetup-bwlp" which is symlinked +as just "serversetup", so we can have "serversetup-cdn" in the future, +as it seems the two locations have quite different requirements for this +module's functionality. + +Note that a module identifier cannot contain dashes (-), so this is an +easy way to hint that a module is supposed to be aliased when being +activated. diff --git a/doc/installation b/doc/installation new file mode 100644 index 00000000..14fba625 --- /dev/null +++ b/doc/installation @@ -0,0 +1,28 @@ +Installation of slx-admin is modularized, that means there is no central, +global SQL dump, but every module can provide an install hook, that is +supposed to install everything it needs to the database (or anything else). + +The hook file is called install.inc.php and needs to reside in the module's +root directory. Some modules might not have an install hook. + +A few simple helper functions are provided to make it easier to create +tables, check for their existence, and create feedback of the installation +process. + +It is important to make as little assumptions as possible about what is +already present in the database, so try not to call functions like +User::load() in your install hook. There might be no user table yet. + +The installation hook should be written in a way that it is non-destructive, +and can recognize if an old version of the module's schema is already +present in the database, in which case it should update the table(s) +instead of erroring out or losing data. See the install hook of the +'main' module for an example. + +Installation/Upgrading can be triggered manually through the browser +by accessing "install.php" in slx-admin's root. Please note that this +page does not require any form of authentication. While any actions +this page can perform should not be destructive, you might want to +consider moving the file away after each install/upgrade. +The other way of triggering the process is running 'install-all' from +the command line. This comes in handy for automated install scripts. diff --git a/doc/javascript_css b/doc/javascript_css new file mode 100644 index 00000000..de4896d5 --- /dev/null +++ b/doc/javascript_css @@ -0,0 +1,19 @@ +In order to prevent the global ./style/default.css from growing until +it's a complete mess, it is also possible to modularize css stylesheets +and javascript files. + +If a module contains a file called style.css, it is automatically included +in the generated HTML page of the module, or any module that declared this +module as a dependency. +The same goes for a file called clientscript.js. Any module containing such +a file will have this script included in the generated HTML. + +This way you can have module specific CSS definitions. Some of the +definitions in ./style/default.css should be moved into modules over +time (TODO :)), since they are only used in one module. + +Thanks to the dependency mechanism it is possible to have dedicated css/js +modules, like "js_chart" or "js_circles". An example is the module +"statistics", which defines js_chart as a dependency in its config.json. +The result is that js_chart/clientscript.js is automatically included +in the HTML output of the statistics module. diff --git a/doc/main_page_warnings b/doc/main_page_warnings new file mode 100644 index 00000000..e69a6125 --- /dev/null +++ b/doc/main_page_warnings @@ -0,0 +1,17 @@ +The warning messages that can appear on the main site (after logging in) +can be added by hooks now, too, so the page.inc.php of the "main" module +doesn't contain code specific to a dozen other modules. + +If you want to add a hook, create a file in your module: +./modules/<module>/hooks/main-warning.inc.php +The file contains flat code, no classes or functions. You should +do your checks, and then call Message::addError (or the likes). +You could also render your own template if you feel fancy. +Remember you're running in the context of the main module, so your +message ID should use the full syntax module.id, or add your +module's name to the Render::addTemplate call. + +If you also want to enable the "configuration incomplete" global +warning, set $needSetup to true. + +See modules "vmstore", "minilinux" or "sysconfig" for an example. diff --git a/doc/module-dependencies b/doc/module-dependencies new file mode 100644 index 00000000..3da27b16 --- /dev/null +++ b/doc/module-dependencies @@ -0,0 +1,31 @@ +Modules can define other modules as dependencies. You should do this +whenever you require resources (like database tables) that belong +to module A in module B, if you want to use automatic css/js +inclusion (see javascript_css doc), or if you just feel like your +module should not be accessible if another module is not present. + +"baseconfig_partitions_cdn" has "baseconfig" as a dependency; it +does not directly access any tables of the module "baseconfig", but +since it depends on the getconfig-API of the baseconfig module, one +might argue that having the partition setup feature is of limited +use if the clients can't fetch it. + +"statistics" module has "js_chart" as a dependency, since it +makes use of the automatic clientscript.js inclusion. The statistics +module uses the chart javascript library to draw, well, charts. + + +You can write modules that can optionally interact or take advantage +of other modules if they are present. The statistics module can +make use of the locations module to show which location a client +belongs to, but it would still work without the locations module +being present. You can check for the availability of a module +by calling Module::isAvailable('<module_name>') +If the module is available, the call will return true, and +the class autoloader for the requested module is also installed, +so you can use any classes from ./modules/<module_name>/inc/*.inc.php +Again, see how the statistics module calls Location::getFromIp() +and other functions after calling Module::isAvailable(). +If you'd try to access Location::* without doing so, you'd get +a class not found error. + diff --git a/inc/dashboard.inc.php b/inc/dashboard.inc.php index ddea6166..3c913b76 100644 --- a/inc/dashboard.inc.php +++ b/inc/dashboard.inc.php @@ -4,6 +4,7 @@ class Dashboard { private static $iconCache = array(); + private static $subMenu = array(); public static function createMenu() { @@ -16,6 +17,7 @@ class Dashboard continue; $modByCategory[$cat][] = $module; } + $currentPage = Page::getModule()->getIdentifier(); $categories = array(); $catSort = array(); foreach ($modByCategory as $catId => $modList) { @@ -23,11 +25,15 @@ class Dashboard $sectionSort = array(); foreach ($modList as $module) { $modId = $module->getIdentifier(); - $modules[] = array( + $newEntry = array( 'displayName' => $module->getDisplayName(), - 'identifier' => $module->getIdentifier(), - 'className' => ($module->getIdentifier() === Page::getModule()->getIdentifier()) ? 'active' : '' + 'identifier' => $module->getIdentifier() ); + if ($module->getIdentifier() === $currentPage) { + $newEntry['className'] = 'active'; + $newEntry['subMenu'] = self::$subMenu; + } + $modules[] = $newEntry; if (isset($MENU_SETTING_SORT_ORDER[$modId])) { $sectionSort[] = (string)($MENU_SETTING_SORT_ORDER[$modId] + 1000); } else { @@ -82,5 +88,10 @@ class Dashboard } return 'glyphicon glyphicon-' . self::$iconCache[$module][$icon]; } + + public static function addSubmenu($url, $name) + { + self::$subMenu[] = array('url' => $url, 'name' => $name); + } }
\ No newline at end of file diff --git a/inc/dictionary.inc.php b/inc/dictionary.inc.php index 6ae8fb9b..01651b88 100644 --- a/inc/dictionary.inc.php +++ b/inc/dictionary.inc.php @@ -79,7 +79,8 @@ class Dictionary public static function translateFile($path, $tag) { - // TODO: Handle case where we have no active module/no page class + if (!class_exists('Page') || Page::getModule() === false) + return false; // We have no page - return false for now, as we're most likely running in api or install mode return self::translateFileModule(Page::getModule()->getIdentifier(), $path, $tag); } diff --git a/inc/module.inc.php b/inc/module.inc.php index 13d9c1e4..58484f34 100644 --- a/inc/module.inc.php +++ b/inc/module.inc.php @@ -197,7 +197,10 @@ class Module public function getPageTitle() { - return Dictionary::translateFileModule($this->name, 'module', 'page_title'); + $val = Dictionary::translateFileModule($this->name, 'module', 'page_title'); + if ($val !== false) + return $val; + return $this->getDisplayName(); } public function getCategory() diff --git a/inc/property.inc.php b/inc/property.inc.php index eae5033c..4a6c3720 100644 --- a/inc/property.inc.php +++ b/inc/property.inc.php @@ -100,7 +100,7 @@ class Property if (!isset($task['id'])) return 'Could not start list download (' . Message::asString() . ')'; if ($task['statusCode'] !== TASK_FINISHED) { - $task = Taskmanager::waitComplete($task['id'], 4000); + $task = Taskmanager::waitComplete($task['id'], 5000); } if ($task['statusCode'] !== TASK_FINISHED || !isset($task['data']['content'])) { return $task['data']['error']; diff --git a/inc/taskmanagercallback.inc.php b/inc/taskmanagercallback.inc.php index f40db297..c2a05609 100644 --- a/inc/taskmanagercallback.inc.php +++ b/inc/taskmanagercallback.inc.php @@ -29,15 +29,16 @@ class TaskmanagerCallback 'task' => $task, 'callback' => $callback, ); - if (Property::getCurrentSchemaVersion() >= 9) { - if (is_null($args)) - $data['args'] = ''; - else - $data['args'] = serialize($args); - Database::exec("INSERT INTO callback (taskid, dateline, cbfunction, args) VALUES (:task, UNIX_TIMESTAMP(), :callback, :args)", $data); + if (is_null($args)) { + $data['args'] = ''; } else { - Database::exec("INSERT INTO callback (taskid, dateline, cbfunction) VALUES (:task, UNIX_TIMESTAMP(), :callback)", $data); + $data['args'] = serialize($args); } + if (Database::exec("INSERT INTO callback (taskid, dateline, cbfunction, args)" + . " VALUES (:task, UNIX_TIMESTAMP(), :callback, :args)", $data, true) !== false) { + return; + } + Database::exec("INSERT INTO callback (taskid, dateline, cbfunction) VALUES (:task, UNIX_TIMESTAMP(), :callback)", $data); } /** @@ -47,10 +48,10 @@ class TaskmanagerCallback */ public static function getPendingCallbacks() { - if (Property::getCurrentSchemaVersion() < 9) + $res = Database::simpleQuery("SELECT taskid, cbfunction, args FROM callback", array(), true); + if ($res === false) return array(); $retval = array(); - $res = Database::simpleQuery("SELECT taskid, cbfunction, args FROM callback"); while ($row = $res->fetch(PDO::FETCH_ASSOC)) { $retval[$row['taskid']][] = $row; } diff --git a/install-all b/install-all new file mode 100755 index 00000000..42f8e5e4 --- /dev/null +++ b/install-all @@ -0,0 +1,53 @@ +#!/bin/bash + +FILE=$(mktemp) +if [ -z "$FILE" ]; then + echo "Something's fishy: No temp file!" + exit 1 +fi + +trap "rm -f -- '$FILE'" EXIT SIGINT SIGTERM + +START=$(php install.php | grep -m1 '^MODULE=' | cut -d= -f2-) + +if [ -z "$START" ]; then + echo "Cannot install slxadmin - did you configure the DB access properly?" + exit 1 +fi + +echo "Started with $START" + +declare -A COUNTER + +COUNTER["$START"]=1 + +NEXT=$START +RETRY=0 +while true; do + php install.php "$NEXT" > "$FILE" + MODULE=$(grep -m1 '^MODULE=' "$FILE" | cut -d= -f2-) + if [ -z "$MODULE" ]; then + echo "Barfed after $NEXT - no module name found in next run" + exit 1 + fi + echo "Next module was ${MODULE}..." + (( COUNTER["$MODULE"]++ )) + if [ ${COUNTER["$MODULE"]} -gt 3 ]; then + echo "Iterated too many times" + exit 1 + fi + STATUS=$(grep -m1 '^STATUS=' "$FILE" | cut -d= -f2-) + echo "Result: $STATUS" + if [ -z "$STATUS" ] || [ "$STATUS" = "UPDATE_RETRY" ]; then + RETRY=1 + fi + if [ "$MODULE" = "$START" ]; then + if [ "$RETRY" = "0" ]; then + break + fi + RETRY=0 + fi + NEXT=$MODULE +done +echo "Done." + diff --git a/modules-available/adduser/page.inc.php b/modules-available/adduser/page.inc.php index cb2987bd..87aaeef1 100644 --- a/modules-available/adduser/page.inc.php +++ b/modules-available/adduser/page.inc.php @@ -51,8 +51,6 @@ class Page_AddUser extends Page if (!User::hasPermission('superadmin') && Database::queryFirst('SELECT userid FROM user LIMIT 1') !== false) { Message::addError('adduser-disabled'); } else { - - Render::setTitle(Dictionary::translate('lang_createUser')); Render::addTemplate('page-adduser', $_POST); } } diff --git a/modules-available/backup/page.inc.php b/modules-available/backup/page.inc.php index 3f2388a5..34777db8 100644 --- a/modules-available/backup/page.inc.php +++ b/modules-available/backup/page.inc.php @@ -23,7 +23,6 @@ class Page_Backup extends Page protected function doRender() { - Render::setTitle(Dictionary::translate('lang_titleBackup')); if ($this->action === 'restore') { Render::addTemplate('restore', $this->templateData); } else { @@ -51,7 +50,7 @@ class Page_Backup extends Page Util::redirect('?do=Backup'); } Header('Content-Type: application/octet-stream', true); - Header('Content-Disposition: attachment; filename=' . 'satellite-backup_v' . Database::getExpectedSchemaVersion() . '_' . date('Y.m.d-H.i.s') . '.tgz'); + Header('Content-Disposition: attachment; filename=' . 'satellite-backup_v16_' . date('Y.m.d-H.i.s') . '.tgz'); Header('Content-Length: ' . @filesize($task['data']['backupFile'])); while (!feof($fh)) { $data = fread($fh, 16000); @@ -96,7 +95,7 @@ class Page_Backup extends Page $this->templateData['mountid'] = $task['id']; $parent = $task['id']; } - EventLog::info('Creating backup, v' . Database::getExpectedSchemaVersion() . ' on ' . Property::getServerIp()); + EventLog::info('Creating backup on ' . Property::getServerIp()); // Finally run restore $task = Taskmanager::submit('BackupRestore', array( 'mode' => 'restore', diff --git a/modules-available/baseconfig/api.inc.php b/modules-available/baseconfig/api.inc.php index 3aac322f..000015f5 100644 --- a/modules-available/baseconfig/api.inc.php +++ b/modules-available/baseconfig/api.inc.php @@ -45,18 +45,28 @@ foreach (glob('modules/*/baseconfig/getconfig.inc.php') as $file) { $defaults = BaseConfigUtil::getVariables(); // Dump global config from DB -$res = Database::simpleQuery('SELECT setting, value FROM setting_global'); +$res = Database::simpleQuery('SELECT setting, value, enabled FROM setting_global'); while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - if (isset($configVars[$row['setting']]) || !isset($defaults[$row['setting']])) + if (isset($configVars[$row['setting']]) // Already set by a hook above, ignore + || !isset($defaults[$row['setting']])) // Setting is not defined in any <module>/baseconfig/settings.json continue; - $configVars[$row['setting']] = $row['value']; + if ($row['enabled'] != 1) { + // Setting is disabled + $configVars[$row['setting']] = false; + } else { + $configVars[$row['setting']] = $row['value']; + } } // Fallback to default values from json files foreach ($defaults as $setting => $value) { - if (isset($configVars[$setting])) - continue; - $configVars[$setting] = $value; + if (isset($configVars[$setting])) { + if ($configVars[$setting] === false) { + unset($configVars[$setting]); + } + } else { + $configVars[$setting] = $value['defaultvalue']; + } } // All done, now output diff --git a/modules-available/baseconfig/install.inc.php b/modules-available/baseconfig/install.inc.php index 8f1cb1e7..b4eada5d 100644 --- a/modules-available/baseconfig/install.inc.php +++ b/modules-available/baseconfig/install.inc.php @@ -6,7 +6,7 @@ $res[] = tableCreate('setting_global', " `setting` varchar(28) NOT NULL, `value` text NOT NULL, `displayvalue` text NOT NULL, - `enabled` tinyint(1) UNSIGNED NOT NULL DEFAULT '1' + `enabled` tinyint(1) UNSIGNED NOT NULL DEFAULT '1', PRIMARY KEY (`setting`) "); diff --git a/modules-available/baseconfig/lang/de/template-tags.json b/modules-available/baseconfig/lang/de/template-tags.json index 34163ca9..cdd54f6a 100644 --- a/modules-available/baseconfig/lang/de/template-tags.json +++ b/modules-available/baseconfig/lang/de/template-tags.json @@ -1,5 +1,7 @@ { "lang_basicConfiguration": "Basiskonfiguration", "lang_clientRelatedConfig": "Die Optionen auf dieser Seite beziehen sich auf das Verhalten der bwLehrpool-Clients.", - "lang_enableOverride": "\u00dcberschreiben" + "lang_editOverrideNotice": "Sie bearbeiten die Einstellungen f\u00fcr einen Unterbereich", + "lang_enableOverride": "\u00dcberschreiben", + "lang_settingActive": "Einstellung aktiv" }
\ No newline at end of file diff --git a/modules-available/baseconfig/lang/en/template-tags.json b/modules-available/baseconfig/lang/en/template-tags.json index 6adfd682..8e75e5ac 100644 --- a/modules-available/baseconfig/lang/en/template-tags.json +++ b/modules-available/baseconfig/lang/en/template-tags.json @@ -1,22 +1,7 @@ { "lang_basicConfiguration": "Basic Configuration", - "lang_catPartition": "Partition Managment", - "lang_catUser": "User Managment", "lang_clientRelatedConfig": "The options on this page are related to the bwLehrpool client machines.", - "lang_confirm": "Would you like to save the settings on [ \/srv\/openslx\/www\/boot\/config ] ?", + "lang_editOverrideNotice": "You're editing the settings of a sub-section", "lang_enableOverride": "Override", - "lang_helpId": "Partition Id", - "lang_helpMountPoint": "Must be a directory: \/example\/directory\/", - "lang_helpOptions": "Currently, only option 'bootable' is available", - "lang_helpSize": "Must be in Gigabytes e.g. 15G", - "lang_newPartition": "New Partition", - "lang_newUser": "New User", - "lang_partitionId": "Id", - "lang_partitionMountPoint": "Mount Point", - "lang_partitionOptions": "Options", - "lang_partitionSize": "Size", - "lang_resetConfirm": "Do you really wish to reset the variable to their default values?", - "lang_resetDefault": "Reset Default", - "lang_userName": "Username", - "lang_userPasswd": "Password" + "lang_settingActive": "Setting active" }
\ No newline at end of file diff --git a/modules-available/baseconfig/lang/pt/template-tags.json b/modules-available/baseconfig/lang/pt/template-tags.json index 28b9cb76..14367665 100644 --- a/modules-available/baseconfig/lang/pt/template-tags.json +++ b/modules-available/baseconfig/lang/pt/template-tags.json @@ -1,21 +1,3 @@ { - "lang_basicConfiguration": "Configura\u00e7\u00e3o B\u00e1sica", - "lang_catPartition": "Gerenciamento de Parti\u00e7\u00f5es", - "lang_catUser": "Gerenciamente de Usu\u00e1rios", - "lang_confirm": "Voc\u00ea deseja salvar configura\u00e7\u00f5es em [ \/srv\/openslx\/www\/boot\/default\/config ] ?", - "lang_create": "Criar", - "lang_helpId": "Id da parti\u00e7\u00e3o", - "lang_helpMountPoint": "Precisa ser um diret\u00f3rio: \/exemplo\/diret\u00f3rio\/", - "lang_helpOptions": "Atualmente, apenas a op\u00e7\u00e3o 'bootable' est\u00e1 dispon\u00edvel", - "lang_helpSize": "Precisa estar em Gigabytes, por exemplo 15G", - "lang_newPartition": "Nova Parti\u00e7\u00e3o", - "lang_newUser": "Novo Usu\u00e1rio", - "lang_partitionId": "Id", - "lang_partitionMountPoint": "Mount Point", - "lang_partitionOptions": "Op\u00e7\u00f5es", - "lang_partitionSize": "Tamanho", - "lang_resetConfirm": "Voc\u00ea realmente deseja restaurar as vari\u00e1veis para seus valores padr\u00f5es?", - "lang_resetDefault": "Restaurar Padr\u00e3o", - "lang_userName": "Nome", - "lang_userPasswd": "Senha" + "lang_basicConfiguration": "Configura\u00e7\u00e3o B\u00e1sica" }
\ No newline at end of file diff --git a/modules-available/baseconfig/page.inc.php b/modules-available/baseconfig/page.inc.php index d14c64ac..056dbab1 100644 --- a/modules-available/baseconfig/page.inc.php +++ b/modules-available/baseconfig/page.inc.php @@ -3,6 +3,7 @@ class Page_BaseConfig extends Page { private $qry_extra = array(); + private $categories; /** * @var bool|string in case we're in module mode, set to the id of the module @@ -23,36 +24,47 @@ class Page_BaseConfig extends Page Util::redirect('?do=baseconfig'); } // Build variables for specific sub-settings - if (empty($this->qry_extra['field'])) { + if ($this->targetModule === false) { + // We're editing global settings - use the 'enabled' field + $qry_insert = ', enabled'; + $qry_values = ', :enabled'; + $qry_update = ', enabled = :enabled'; + $params = array(); + } elseif (empty($this->qry_extra['field'])) { + // Module specific, but module doesn't have an extra field $qry_insert = ''; $qry_values = ''; - $params = array(); + $qry_update = ''; } else { + // Module with extra field $qry_insert = ', ' . $this->qry_extra['field']; $qry_values = ', :field_value'; + $qry_update = ''; $params = array('field_value' => $this->qry_extra['field_value']); $delExtra = " AND {$this->qry_extra['field']} = :field_value "; $delParams = array('field_value' => $this->qry_extra['field_value']); - } - if ($this->targetModule === false) { - $override = false; - } else { // Not editing global settings if ($this->getCurrentModuleName() === false) { Message::addError('main.value-invalid', $this->qry_extra['field'], $this->qry_extra['field_value']); Util::redirect('?do=BaseConfig'); } - // Honor override checkbox - $override = Request::post('override', array()); } + // Honor override/enabled checkbox + $override = Request::post('override', array()); // Load all existing config options to validate input $vars = BaseConfigUtil::getVariables(); foreach ($vars as $key => $var) { - if (is_array($override) && (!isset($override[$key]) || $override[$key] !== 'on')) { - // module mode - override not set - delete - $delParams['key'] = $key; - Database::exec("DELETE FROM {$this->qry_extra['table']} WHERE setting = :key $delExtra", $delParams); - continue; + if ($this->targetModule === false) { + // Global mode + $params['enabled'] = (is_array($override) && isset($override[$key]) && $override[$key] === 'on') ? 1 : 0; + } else { + // Module mode + if (is_array($override) && (!isset($override[$key]) || $override[$key] !== 'on')) { + // override not set - delete + $delParams['key'] = $key; + Database::exec("DELETE FROM {$this->qry_extra['table']} WHERE setting = :key $delExtra", $delParams); + continue; + } } $validator = $var['validator']; $displayValue = (isset($newValues[$key]) ? $newValues[$key] : ''); @@ -65,7 +77,7 @@ class Page_BaseConfig extends Page // Now put into DB Database::exec("INSERT INTO {$this->qry_extra['table']} (setting, value, displayvalue $qry_insert)" . " VALUES (:key, :value, :displayvalue $qry_values)" - . " ON DUPLICATE KEY UPDATE value = :value, displayvalue = :displayvalue", + . " ON DUPLICATE KEY UPDATE value = :value, displayvalue = :displayvalue $qry_update", array( 'key' => $key, 'value' => $mangledValue, @@ -82,6 +94,15 @@ class Page_BaseConfig extends Page Util::redirect('?do=BaseConfig&module=' . $this->targetModule . '&' . $this->qry_extra['field'] . '=' . $this->qry_extra['field_value']); } } + // Load categories so we can define them as sub menu items + $this->categories = BaseConfigUtil::getCategories(); + asort($this->categories, SORT_DESC); + foreach ($this->categories as $catid => $val) { + Dashboard::addSubmenu( + '#category_' . $catid, + Dictionary::translateFileModule($this->categories[$catid]['module'], 'config-variable-categories', $catid) + ); + } } protected function doRender() @@ -101,17 +122,22 @@ class Page_BaseConfig extends Page // List config options $settings = array(); $vars = BaseConfigUtil::getVariables(); - $cats = BaseConfigUtil::getCategories(); // Get stuff that's set in DB already - if (isset($this->qry_extra['field'])) { + if ($this->targetModule === false) { + $fields = ', enabled'; + $where = ''; + $params = array(); + } elseif (isset($this->qry_extra['field'])) { + $fields = ''; $where = " WHERE {$this->qry_extra['field']} = :field_value"; $params = array('field_value' => $this->qry_extra['field_value']); } else { + $fields = ''; $where = ''; $params = array(); } // Populate structure with existing config from db - $res = Database::simpleQuery("SELECT setting, value, displayvalue FROM {$this->qry_extra['table']} " + $res = Database::simpleQuery("SELECT setting, value, displayvalue $fields FROM {$this->qry_extra['table']} " . " {$where} ORDER BY setting ASC", $params); while ($row = $res->fetch(PDO::FETCH_ASSOC)) { if (!isset($vars[$row['setting']]) || !is_array($vars[$row['setting']])) { @@ -119,9 +145,6 @@ class Page_BaseConfig extends Page continue; } $row += $vars[$row['setting']]; - if (is_null($row['displayvalue'])) { - $row['displayvalue'] = $row['defaultvalue']; - } if (!isset($row['catid'])) { $row['catid'] = 'unknown'; } @@ -129,39 +152,48 @@ class Page_BaseConfig extends Page } // Add entries that weren't in the db (global), setup override checkbox (module specific) foreach ($vars as $key => $var) { - if (isset($settings[$var['catid']]['settings'][$key])) { - // Value is set in DB + if (isset($settings[$var['catid']]['settings'][$key]['enabled'])) { + // Global settings - honor enabled field in db + if ($settings[$var['catid']]['settings'][$key]['enabled'] == 1) { + $settings[$var['catid']]['settings'][$key]['checked'] = 'checked'; + } + } elseif (isset($settings[$var['catid']]['settings'][$key])) { + // Module specific - value is set in DB $settings[$var['catid']]['settings'][$key]['checked'] = 'checked'; } else { - // Value is not set in DB + // Module specific - value is not set in DB $settings[$var['catid']]['settings'][$key] = $var + array( 'setting' => $key ); } + if (!isset($settings[$var['catid']]['settings'][$key]['displayvalue'])) { + $settings[$var['catid']]['settings'][$key]['displayvalue'] = $var['defaultvalue']; + } $settings[$var['catid']]['settings'][$key] += array( - 'item' => $this->makeInput($var['validator'], $key, $var['defaultvalue']), + 'item' => $this->makeInput($var['validator'], $key, $settings[$var['catid']]['settings'][$key]['displayvalue']), 'description' => Util::markup(Dictionary::translateFileModule($var['module'], 'config-variables', $key)) ); } // Sort categories $sortvals = array(); foreach ($settings as $catid => &$setting) { - $sortvals[] = isset($cats[$catid]) ? (int)$cats[$catid]['sortpos'] : 99999; + $sortvals[] = isset($this->categories[$catid]) ? (int)$this->categories[$catid]['sortpos'] : 99999; $setting['category_id'] = $catid; - $setting['category_name'] = Dictionary::translateFileModule($cats[$catid]['module'], 'config-variable-categories', $catid); + $setting['category_name'] = Dictionary::translateFileModule($this->categories[$catid]['module'], 'config-variable-categories', $catid); if ($setting['category_name'] === false) { $setting['category_name'] = $catid; } + ksort($setting['settings']); $setting['settings'] = array_values($setting['settings']); } unset($setting); array_multisort($sortvals, SORT_ASC, SORT_NUMERIC, $settings); Render::addTemplate('_page', array( - 'userid' => User::getId(), 'override' => $this->targetModule !== false, 'categories' => array_values($settings), 'target_module' => $this->targetModule, ) + $this->qry_extra); + Module::isAvailable('bootstrap_switch'); } private function getCurrentModuleName() diff --git a/modules-available/baseconfig/templates/_page.html b/modules-available/baseconfig/templates/_page.html index d5548ae9..4d4c05aa 100644 --- a/modules-available/baseconfig/templates/_page.html +++ b/modules-available/baseconfig/templates/_page.html @@ -10,58 +10,59 @@ <input name="module" type="hidden" value="{{target_module}}"> <input name="{{field}}" type="hidden" value="{{field_value}}"> {{/override}} - <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> - <input type="text" name="prevent_autofill" id="prevent_autofill" value="" style="display:none;"> - <input type="password" name="password_fake" id="password_fake" value="" style="display:none;"> - {{#categories}} - <div class="panel panel-default"> - <div class="panel-heading" role="tab" id="heading{{category_id}}"> - <a data-toggle="collapse" data-parent="#accordion" href="#collapse{{category_id}}" aria-expanded="false" aria-controls="collapse{{category_id}}"> - {{category_name}} - </a> - </div> - <div id="collapse{{category_id}}" class="accordion-body collapse" role="tabpanel" aria-labelledby="heading{{category_id}}"> - <div class="panel-body"> - <div class="list-group"> - {{#settings}} - <div class="list-group-item"> - <div class="row"> - <div class="col-md-5 slx-cfg-toggle"> - <div>{{setting}}</div> - {{^override}} - <div class="slx-default"> - {{defaultvalue}} - </div> - {{/override}} - {{#override}} - <input name="override[{{setting}}]" id="CB_{{setting}}" type="checkbox" {{checked}}> <label for="CB_{{setting}}">{{lang_enableOverride}}</label> - {{/override}} - </div> - <div class="col-md-5"> - {{{item}}} - </div> - <div class="col-md-2"> - <a class="btn btn-default" data-toggle="modal" data-target="#help-{{setting}}"><span class="glyphicon glyphicon-question-sign"></span></a> - </div> + <input type="text" name="prevent_autofill" id="prevent_autofill" value="" style="display:none;"> + <input type="password" name="password_fake" id="password_fake" value="" style="display:none;"> + {{#categories}} + <div class="panel panel-default"> + <div class="panel-heading" role="tab" id="heading{{category_id}}"> + <a name="category_{{category_id}}"></a> + {{category_name}} + </div> + <div class="panel-body"> + <div class="list-group"> + {{#settings}} + <div class="list-group-item"> + <div class="row"> + <div class="col-md-5 slx-cfg-toggle"> + <div>{{setting}}</div> + {{^override}} + <div class="slx-default"> + {{defaultvalue}} </div> + <input class="bs-switch" name="override[{{setting}}]" id="CB_{{setting}}" type="checkbox" {{checked}}> <label for="CB_{{setting}}">{{lang_settingActive}}</label> + {{/override}} + {{#override}} + <input class="bs-switch" name="override[{{setting}}]" id="CB_{{setting}}" type="checkbox" {{checked}}> <label for="CB_{{setting}}">{{lang_enableOverride}}</label> + {{/override}} </div> - <div class="modal fade" id="help-{{setting}}" tabindex="-1" role="dialog"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header">{{setting}}</div> - <div class="modal-body">{{{description}}}</div> - <div class="modal-footer"><a class="btn btn-primary" data-dismiss="modal">{{lang_close}}</a></div> - </div> - </div> + <div class="col-md-5"> + {{{item}}} + </div> + <div class="col-md-2"> + <a class="btn btn-default" data-toggle="modal" data-target="#help-{{setting}}"><span class="glyphicon glyphicon-question-sign"></span></a> + </div> + </div> + </div> + <div class="modal fade" id="help-{{setting}}" tabindex="-1" role="dialog"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header">{{setting}}</div> + <div class="modal-body">{{{description}}}</div> + <div class="modal-footer"><a class="btn btn-primary" data-dismiss="modal">{{lang_close}}</a></div> </div> - {{/settings}} </div> </div> + {{/settings}} </div> </div> - {{/categories}} </div> + {{/categories}} <button class="btn btn-primary" type="submit">{{lang_save}}</button> <button class="btn btn-default" type="reset">{{lang_reset}}</button> + {{^override}} <a class="btn btn-default" href="api.php?do=baseconfig&user={{userid}}">Download</a> + {{/override}} + {{#override}} + <a class="btn btn-default" href="api.php?do=baseconfig&user={{userid}}&module={{target_module}}&value={{field_value}}&force=1">Download</a> + {{/override}} </form> diff --git a/modules-available/baseconfig_partitions_cdn/install.inc.php b/modules-available/baseconfig_partitions_cdn/install.inc.php new file mode 100644 index 00000000..f4092078 --- /dev/null +++ b/modules-available/baseconfig_partitions_cdn/install.inc.php @@ -0,0 +1,31 @@ +<?php + +$res = array(); + +$res[] = tableCreate('setting_partition', " + `id` int(11) NOT NULL AUTO_INCREMENT, + `partition_id` varchar(110) NOT NULL, + `size` varchar(110) NOT NULL, + `mount_point` varchar(110) NOT NULL, + `options` varchar(110) NOT NULL, + `user` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + KEY `user` (`user`) +"); + +if (in_array(UPDATE_DONE, $res)) { + Database::exec("ALTER TABLE `setting_partition` + ADD CONSTRAINT `setting_partition_ibfk_1` FOREIGN KEY (`user`) REFERENCES `user` (`userid`)"); +} + +// Update path + +// -- none -- + +// Create response for browser + +if (in_array(UPDATE_DONE, $res)) { + finalResponse(UPDATE_DONE, 'Tables created successfully'); +} + +finalResponse(UPDATE_NOOP, 'Everything already up to date'); diff --git a/modules-available/baseconfig_partitions_cdn/lang/de/template-tags.json b/modules-available/baseconfig_partitions_cdn/lang/de/template-tags.json index 91cca0db..ea4f9f5c 100644 --- a/modules-available/baseconfig_partitions_cdn/lang/de/template-tags.json +++ b/modules-available/baseconfig_partitions_cdn/lang/de/template-tags.json @@ -1,5 +1,4 @@ { - "lang_addPartition": "Partition anlegen", "lang_confirm": "Wollen Sie die Einstellungen unter \/srv\/openslx\/www\/boot\/config speichern?", "lang_create": "Anlegen", "lang_explanationText": "Hier k\u00f6nnen Sie bestimmen, welche Partitionen auf dem Client angelegt werden.", diff --git a/modules-available/bootstrap_switch/README b/modules-available/bootstrap_switch/README new file mode 100644 index 00000000..405ce13a --- /dev/null +++ b/modules-available/bootstrap_switch/README @@ -0,0 +1,19 @@ +This is basically just bootstrap-switch (js + css), with +the addition of these three lines in the .js file: +<snip> +// SLX-Admin: Enable for class bs-switch +$.fn.bootstrapSwitch.defaults.size = 'mini'; +$("input.bs-switch").bootstrapSwitch(); +</snip> + +This makes sure any checkboxes or radio buttons with the +class "bs-switch" will automatically be turned into +bootstrap switches. + +To apply bootstrap-switch to your checkboxes you need to +a) give them the class bs-switch +b) either make bootstrap_switch a dependency of your module (config.json) + or, as a "soft dependency", just call + Module::isAvailable('boostrap_switch') + somewhere in your code. If bootstrap_switch is not activated, your + page will still load and show old fashioned checkboxes
\ No newline at end of file diff --git a/modules-available/bootstrap_switch/clientscript.js b/modules-available/bootstrap_switch/clientscript.js new file mode 100644 index 00000000..43259451 --- /dev/null +++ b/modules-available/bootstrap_switch/clientscript.js @@ -0,0 +1,25 @@ +/* ======================================================================== + * bootstrap-switch - v3.3.2 + * http://www.bootstrap-switch.org + * ======================================================================== + * Copyright 2012-2013 Mattia Larentis + * + * ======================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== + */ + +(function(){var t=[].slice;!function(e,i){"use strict";var n;return n=function(){function t(t,i){null==i&&(i={}),this.$element=e(t),this.options=e.extend({},e.fn.bootstrapSwitch.defaults,{state:this.$element.is(":checked"),size:this.$element.data("size"),animate:this.$element.data("animate"),disabled:this.$element.is(":disabled"),readonly:this.$element.is("[readonly]"),indeterminate:this.$element.data("indeterminate"),inverse:this.$element.data("inverse"),radioAllOff:this.$element.data("radio-all-off"),onColor:this.$element.data("on-color"),offColor:this.$element.data("off-color"),onText:this.$element.data("on-text"),offText:this.$element.data("off-text"),labelText:this.$element.data("label-text"),handleWidth:this.$element.data("handle-width"),labelWidth:this.$element.data("label-width"),baseClass:this.$element.data("base-class"),wrapperClass:this.$element.data("wrapper-class")},i),this.prevOptions={},this.$wrapper=e("<div>",{"class":function(t){return function(){var e;return e=[""+t.options.baseClass].concat(t._getClasses(t.options.wrapperClass)),e.push(t.options.state?t.options.baseClass+"-on":t.options.baseClass+"-off"),null!=t.options.size&&e.push(t.options.baseClass+"-"+t.options.size),t.options.disabled&&e.push(t.options.baseClass+"-disabled"),t.options.readonly&&e.push(t.options.baseClass+"-readonly"),t.options.indeterminate&&e.push(t.options.baseClass+"-indeterminate"),t.options.inverse&&e.push(t.options.baseClass+"-inverse"),t.$element.attr("id")&&e.push(t.options.baseClass+"-id-"+t.$element.attr("id")),e.join(" ")}}(this)()}),this.$container=e("<div>",{"class":this.options.baseClass+"-container"}),this.$on=e("<span>",{html:this.options.onText,"class":this.options.baseClass+"-handle-on "+this.options.baseClass+"-"+this.options.onColor}),this.$off=e("<span>",{html:this.options.offText,"class":this.options.baseClass+"-handle-off "+this.options.baseClass+"-"+this.options.offColor}),this.$label=e("<span>",{html:this.options.labelText,"class":this.options.baseClass+"-label"}),this.$element.on("init.bootstrapSwitch",function(e){return function(){return e.options.onInit.apply(t,arguments)}}(this)),this.$element.on("switchChange.bootstrapSwitch",function(i){return function(n){return!1===i.options.onSwitchChange.apply(t,arguments)?i.$element.is(":radio")?e("[name='"+i.$element.attr("name")+"']").trigger("previousState.bootstrapSwitch",!0):i.$element.trigger("previousState.bootstrapSwitch",!0):void 0}}(this)),this.$container=this.$element.wrap(this.$container).parent(),this.$wrapper=this.$container.wrap(this.$wrapper).parent(),this.$element.before(this.options.inverse?this.$off:this.$on).before(this.$label).before(this.options.inverse?this.$on:this.$off),this.options.indeterminate&&this.$element.prop("indeterminate",!0),this._init(),this._elementHandlers(),this._handleHandlers(),this._labelHandlers(),this._formHandler(),this._externalLabelHandler(),this.$element.trigger("init.bootstrapSwitch",this.options.state)}return t.prototype._constructor=t,t.prototype.setPrevOptions=function(){return this.prevOptions=e.extend(!0,{},this.options)},t.prototype.state=function(t,i){return"undefined"==typeof t?this.options.state:this.options.disabled||this.options.readonly?this.$element:this.options.state&&!this.options.radioAllOff&&this.$element.is(":radio")?this.$element:(this.$element.is(":radio")?e("[name='"+this.$element.attr("name")+"']").trigger("setPreviousOptions.bootstrapSwitch"):this.$element.trigger("setPreviousOptions.bootstrapSwitch"),this.options.indeterminate&&this.indeterminate(!1),t=!!t,this.$element.prop("checked",t).trigger("change.bootstrapSwitch",i),this.$element)},t.prototype.toggleState=function(t){return this.options.disabled||this.options.readonly?this.$element:this.options.indeterminate?(this.indeterminate(!1),this.state(!0)):this.$element.prop("checked",!this.options.state).trigger("change.bootstrapSwitch",t)},t.prototype.size=function(t){return"undefined"==typeof t?this.options.size:(null!=this.options.size&&this.$wrapper.removeClass(this.options.baseClass+"-"+this.options.size),t&&this.$wrapper.addClass(this.options.baseClass+"-"+t),this._width(),this._containerPosition(),this.options.size=t,this.$element)},t.prototype.animate=function(t){return"undefined"==typeof t?this.options.animate:(t=!!t,t===this.options.animate?this.$element:this.toggleAnimate())},t.prototype.toggleAnimate=function(){return this.options.animate=!this.options.animate,this.$wrapper.toggleClass(this.options.baseClass+"-animate"),this.$element},t.prototype.disabled=function(t){return"undefined"==typeof t?this.options.disabled:(t=!!t,t===this.options.disabled?this.$element:this.toggleDisabled())},t.prototype.toggleDisabled=function(){return this.options.disabled=!this.options.disabled,this.$element.prop("disabled",this.options.disabled),this.$wrapper.toggleClass(this.options.baseClass+"-disabled"),this.$element},t.prototype.readonly=function(t){return"undefined"==typeof t?this.options.readonly:(t=!!t,t===this.options.readonly?this.$element:this.toggleReadonly())},t.prototype.toggleReadonly=function(){return this.options.readonly=!this.options.readonly,this.$element.prop("readonly",this.options.readonly),this.$wrapper.toggleClass(this.options.baseClass+"-readonly"),this.$element},t.prototype.indeterminate=function(t){return"undefined"==typeof t?this.options.indeterminate:(t=!!t,t===this.options.indeterminate?this.$element:this.toggleIndeterminate())},t.prototype.toggleIndeterminate=function(){return this.options.indeterminate=!this.options.indeterminate,this.$element.prop("indeterminate",this.options.indeterminate),this.$wrapper.toggleClass(this.options.baseClass+"-indeterminate"),this._containerPosition(),this.$element},t.prototype.inverse=function(t){return"undefined"==typeof t?this.options.inverse:(t=!!t,t===this.options.inverse?this.$element:this.toggleInverse())},t.prototype.toggleInverse=function(){var t,e;return this.$wrapper.toggleClass(this.options.baseClass+"-inverse"),e=this.$on.clone(!0),t=this.$off.clone(!0),this.$on.replaceWith(t),this.$off.replaceWith(e),this.$on=t,this.$off=e,this.options.inverse=!this.options.inverse,this.$element},t.prototype.onColor=function(t){var e;return e=this.options.onColor,"undefined"==typeof t?e:(null!=e&&this.$on.removeClass(this.options.baseClass+"-"+e),this.$on.addClass(this.options.baseClass+"-"+t),this.options.onColor=t,this.$element)},t.prototype.offColor=function(t){var e;return e=this.options.offColor,"undefined"==typeof t?e:(null!=e&&this.$off.removeClass(this.options.baseClass+"-"+e),this.$off.addClass(this.options.baseClass+"-"+t),this.options.offColor=t,this.$element)},t.prototype.onText=function(t){return"undefined"==typeof t?this.options.onText:(this.$on.html(t),this._width(),this._containerPosition(),this.options.onText=t,this.$element)},t.prototype.offText=function(t){return"undefined"==typeof t?this.options.offText:(this.$off.html(t),this._width(),this._containerPosition(),this.options.offText=t,this.$element)},t.prototype.labelText=function(t){return"undefined"==typeof t?this.options.labelText:(this.$label.html(t),this._width(),this.options.labelText=t,this.$element)},t.prototype.handleWidth=function(t){return"undefined"==typeof t?this.options.handleWidth:(this.options.handleWidth=t,this._width(),this._containerPosition(),this.$element)},t.prototype.labelWidth=function(t){return"undefined"==typeof t?this.options.labelWidth:(this.options.labelWidth=t,this._width(),this._containerPosition(),this.$element)},t.prototype.baseClass=function(t){return this.options.baseClass},t.prototype.wrapperClass=function(t){return"undefined"==typeof t?this.options.wrapperClass:(t||(t=e.fn.bootstrapSwitch.defaults.wrapperClass),this.$wrapper.removeClass(this._getClasses(this.options.wrapperClass).join(" ")),this.$wrapper.addClass(this._getClasses(t).join(" ")),this.options.wrapperClass=t,this.$element)},t.prototype.radioAllOff=function(t){return"undefined"==typeof t?this.options.radioAllOff:(t=!!t,t===this.options.radioAllOff?this.$element:(this.options.radioAllOff=t,this.$element))},t.prototype.onInit=function(t){return"undefined"==typeof t?this.options.onInit:(t||(t=e.fn.bootstrapSwitch.defaults.onInit),this.options.onInit=t,this.$element)},t.prototype.onSwitchChange=function(t){return"undefined"==typeof t?this.options.onSwitchChange:(t||(t=e.fn.bootstrapSwitch.defaults.onSwitchChange),this.options.onSwitchChange=t,this.$element)},t.prototype.destroy=function(){var t;return t=this.$element.closest("form"),t.length&&t.off("reset.bootstrapSwitch").removeData("bootstrap-switch"),this.$container.children().not(this.$element).remove(),this.$element.unwrap().unwrap().off(".bootstrapSwitch").removeData("bootstrap-switch"),this.$element},t.prototype._width=function(){var t,e;return t=this.$on.add(this.$off),t.add(this.$label).css("width",""),e="auto"===this.options.handleWidth?Math.max(this.$on.width(),this.$off.width()):this.options.handleWidth,t.width(e),this.$label.width(function(t){return function(i,n){return"auto"!==t.options.labelWidth?t.options.labelWidth:e>n?e:n}}(this)),this._handleWidth=this.$on.outerWidth(),this._labelWidth=this.$label.outerWidth(),this.$container.width(2*this._handleWidth+this._labelWidth),this.$wrapper.width(this._handleWidth+this._labelWidth)},t.prototype._containerPosition=function(t,e){return null==t&&(t=this.options.state),this.$container.css("margin-left",function(e){return function(){var i;return i=[0,"-"+e._handleWidth+"px"],e.options.indeterminate?"-"+e._handleWidth/2+"px":t?e.options.inverse?i[1]:i[0]:e.options.inverse?i[0]:i[1]}}(this)),e?setTimeout(function(){return e()},50):void 0},t.prototype._init=function(){var t,e;return t=function(t){return function(){return t.setPrevOptions(),t._width(),t._containerPosition(null,function(){return t.options.animate?t.$wrapper.addClass(t.options.baseClass+"-animate"):void 0})}}(this),this.$wrapper.is(":visible")?t():e=i.setInterval(function(n){return function(){return n.$wrapper.is(":visible")?(t(),i.clearInterval(e)):void 0}}(this),50)},t.prototype._elementHandlers=function(){return this.$element.on({"setPreviousOptions.bootstrapSwitch":function(t){return function(e){return t.setPrevOptions()}}(this),"previousState.bootstrapSwitch":function(t){return function(e){return t.options=t.prevOptions,t.options.indeterminate&&t.$wrapper.addClass(t.options.baseClass+"-indeterminate"),t.$element.prop("checked",t.options.state).trigger("change.bootstrapSwitch",!0)}}(this),"change.bootstrapSwitch":function(t){return function(i,n){var o;return i.preventDefault(),i.stopImmediatePropagation(),o=t.$element.is(":checked"),t._containerPosition(o),o!==t.options.state?(t.options.state=o,t.$wrapper.toggleClass(t.options.baseClass+"-off").toggleClass(t.options.baseClass+"-on"),n?void 0:(t.$element.is(":radio")&&e("[name='"+t.$element.attr("name")+"']").not(t.$element).prop("checked",!1).trigger("change.bootstrapSwitch",!0),t.$element.trigger("switchChange.bootstrapSwitch",[o]))):void 0}}(this),"focus.bootstrapSwitch":function(t){return function(e){return e.preventDefault(),t.$wrapper.addClass(t.options.baseClass+"-focused")}}(this),"blur.bootstrapSwitch":function(t){return function(e){return e.preventDefault(),t.$wrapper.removeClass(t.options.baseClass+"-focused")}}(this),"keydown.bootstrapSwitch":function(t){return function(e){if(e.which&&!t.options.disabled&&!t.options.readonly)switch(e.which){case 37:return e.preventDefault(),e.stopImmediatePropagation(),t.state(!1);case 39:return e.preventDefault(),e.stopImmediatePropagation(),t.state(!0)}}}(this)})},t.prototype._handleHandlers=function(){return this.$on.on("click.bootstrapSwitch",function(t){return function(e){return e.preventDefault(),e.stopPropagation(),t.state(!1),t.$element.trigger("focus.bootstrapSwitch")}}(this)),this.$off.on("click.bootstrapSwitch",function(t){return function(e){return e.preventDefault(),e.stopPropagation(),t.state(!0),t.$element.trigger("focus.bootstrapSwitch")}}(this))},t.prototype._labelHandlers=function(){return this.$label.on({click:function(t){return t.stopPropagation()},"mousedown.bootstrapSwitch touchstart.bootstrapSwitch":function(t){return function(e){return t._dragStart||t.options.disabled||t.options.readonly?void 0:(e.preventDefault(),e.stopPropagation(),t._dragStart=(e.pageX||e.originalEvent.touches[0].pageX)-parseInt(t.$container.css("margin-left"),10),t.options.animate&&t.$wrapper.removeClass(t.options.baseClass+"-animate"),t.$element.trigger("focus.bootstrapSwitch"))}}(this),"mousemove.bootstrapSwitch touchmove.bootstrapSwitch":function(t){return function(e){var i;if(null!=t._dragStart&&(e.preventDefault(),i=(e.pageX||e.originalEvent.touches[0].pageX)-t._dragStart,!(i<-t._handleWidth||i>0)))return t._dragEnd=i,t.$container.css("margin-left",t._dragEnd+"px")}}(this),"mouseup.bootstrapSwitch touchend.bootstrapSwitch":function(t){return function(e){var i;if(t._dragStart)return e.preventDefault(),t.options.animate&&t.$wrapper.addClass(t.options.baseClass+"-animate"),t._dragEnd?(i=t._dragEnd>-(t._handleWidth/2),t._dragEnd=!1,t.state(t.options.inverse?!i:i)):t.state(!t.options.state),t._dragStart=!1}}(this),"mouseleave.bootstrapSwitch":function(t){return function(e){return t.$label.trigger("mouseup.bootstrapSwitch")}}(this)})},t.prototype._externalLabelHandler=function(){var t;return t=this.$element.closest("label"),t.on("click",function(e){return function(i){return i.preventDefault(),i.stopImmediatePropagation(),i.target===t[0]?e.toggleState():void 0}}(this))},t.prototype._formHandler=function(){var t;return t=this.$element.closest("form"),t.data("bootstrap-switch")?void 0:t.on("reset.bootstrapSwitch",function(){return i.setTimeout(function(){return t.find("input").filter(function(){return e(this).data("bootstrap-switch")}).each(function(){return e(this).bootstrapSwitch("state",this.checked)})},1)}).data("bootstrap-switch",!0)},t.prototype._getClasses=function(t){var i,n,o,s;if(!e.isArray(t))return[this.options.baseClass+"-"+t];for(n=[],o=0,s=t.length;s>o;o++)i=t[o],n.push(this.options.baseClass+"-"+i);return n},t}(),e.fn.bootstrapSwitch=function(){var i,o,s;return o=arguments[0],i=2<=arguments.length?t.call(arguments,1):[],s=this,this.each(function(){var t,a;return t=e(this),a=t.data("bootstrap-switch"),a||t.data("bootstrap-switch",a=new n(this,o)),"string"==typeof o?s=a[o].apply(a,i):void 0}),s},e.fn.bootstrapSwitch.Constructor=n,e.fn.bootstrapSwitch.defaults={state:!0,size:null,animate:!0,disabled:!1,readonly:!1,indeterminate:!1,inverse:!1,radioAllOff:!1,onColor:"primary",offColor:"default",onText:"ON",offText:"OFF",labelText:" ",handleWidth:"auto",labelWidth:"auto",baseClass:"bootstrap-switch",wrapperClass:"wrapper",onInit:function(){},onSwitchChange:function(){}}}(window.jQuery,window)}).call(this); +// SLX-Admin: Enable for class bs-switch +$.fn.bootstrapSwitch.defaults.size = 'mini'; +$("input.bs-switch").bootstrapSwitch();
\ No newline at end of file diff --git a/modules-available/bootstrap_switch/config.json b/modules-available/bootstrap_switch/config.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/modules-available/bootstrap_switch/config.json @@ -0,0 +1 @@ +{}
\ No newline at end of file diff --git a/modules-available/bootstrap_switch/style.css b/modules-available/bootstrap_switch/style.css new file mode 100644 index 00000000..c63cfe2f --- /dev/null +++ b/modules-available/bootstrap_switch/style.css @@ -0,0 +1,22 @@ +/* ======================================================================== + * bootstrap-switch - v3.3.2 + * http://www.bootstrap-switch.org + * ======================================================================== + * Copyright 2012-2013 Mattia Larentis + * + * ======================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== + */ + +.bootstrap-switch{display:inline-block;direction:ltr;cursor:pointer;border-radius:4px;border:1px solid #ccc;position:relative;text-align:left;overflow:hidden;line-height:8px;z-index:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.bootstrap-switch .bootstrap-switch-container{display:inline-block;top:0;border-radius:4px;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on,.bootstrap-switch .bootstrap-switch-label{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;cursor:pointer;display:inline-block!important;height:100%;padding:6px 12px;font-size:14px;line-height:20px}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on{text-align:center;z-index:1}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary{color:#fff;background:#337ab7}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info{color:#fff;background:#5bc0de}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success{color:#fff;background:#5cb85c}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning{background:#f0ad4e;color:#fff}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger{color:#fff;background:#d9534f}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default{color:#000;background:#eee}.bootstrap-switch .bootstrap-switch-label{text-align:center;margin-top:-1px;margin-bottom:-1px;z-index:100;color:#333;background:#fff}.bootstrap-switch .bootstrap-switch-handle-on{border-bottom-left-radius:3px;border-top-left-radius:3px}.bootstrap-switch .bootstrap-switch-handle-off{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch input[type=radio],.bootstrap-switch input[type=checkbox]{position:absolute!important;top:0;left:0;margin:0;z-index:-1;opacity:0;filter:alpha(opacity=0)}.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label{padding:1px 5px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label{padding:5px 10px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label{padding:6px 16px;font-size:18px;line-height:1.3333333}.bootstrap-switch.bootstrap-switch-disabled,.bootstrap-switch.bootstrap-switch-indeterminate,.bootstrap-switch.bootstrap-switch-readonly{cursor:default!important}.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label{opacity:.5;filter:alpha(opacity=50);cursor:default!important}.bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container{-webkit-transition:margin-left .5s;-o-transition:margin-left .5s;transition:margin-left .5s}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on{border-radius:0 3px 3px 0}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off{border-radius:3px 0 0 3px}.bootstrap-switch.bootstrap-switch-focused{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label{border-bottom-left-radius:3px;border-top-left-radius:3px}
\ No newline at end of file diff --git a/modules-available/citymanagement/config.json b/modules-available/citymanagement/config.json index 2eb2d680..e87cbf7d 100644 --- a/modules-available/citymanagement/config.json +++ b/modules-available/citymanagement/config.json @@ -1,4 +1,4 @@ { - "category":"main.settings-server", + "category":"citymanagement.cities", "permission":"0" } diff --git a/modules-available/citymanagement/install.inc.php b/modules-available/citymanagement/install.inc.php new file mode 100644 index 00000000..b051ca5c --- /dev/null +++ b/modules-available/citymanagement/install.inc.php @@ -0,0 +1,22 @@ +<?php + +$res = array(); + +$res[] = tableCreate('cities', " + `cityid` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(100) NOT NULL, + `ip` varchar(10) NOT NULL, + PRIMARY KEY (`cityid`) +"); + +// Update path + +// -- none -- + +// Create response for browser + +if (in_array(UPDATE_DONE, $res)) { + finalResponse(UPDATE_DONE, 'Tables created successfully'); +} + +finalResponse(UPDATE_NOOP, 'Everything already up to date'); diff --git a/modules-available/citymanagement/lang/de/categories.json b/modules-available/citymanagement/lang/de/categories.json new file mode 100644 index 00000000..0c348a06 --- /dev/null +++ b/modules-available/citymanagement/lang/de/categories.json @@ -0,0 +1,3 @@ +{ + "cities": "St\u00e4dte" +}
\ No newline at end of file diff --git a/modules-available/citymanagement/lang/en/categories.json b/modules-available/citymanagement/lang/en/categories.json new file mode 100644 index 00000000..2233db93 --- /dev/null +++ b/modules-available/citymanagement/lang/en/categories.json @@ -0,0 +1,3 @@ +{ + "cities": "Cities" +}
\ No newline at end of file diff --git a/modules-available/eventlog/page.inc.php b/modules-available/eventlog/page.inc.php index 0e56c82d..87957479 100644 --- a/modules-available/eventlog/page.inc.php +++ b/modules-available/eventlog/page.inc.php @@ -15,7 +15,6 @@ class Page_EventLog extends Page protected function doRender() { - Render::setTitle(Dictionary::translate('lang_titleEventLog')); $today = date('d.m.Y'); $yesterday = date('d.m.Y', time() - 86400); $lines = array(); diff --git a/modules-available/locations/baseconfig/getconfig.inc.php b/modules-available/locations/baseconfig/getconfig.inc.php index d4a4f40b..2cf5495d 100644 --- a/modules-available/locations/baseconfig/getconfig.inc.php +++ b/modules-available/locations/baseconfig/getconfig.inc.php @@ -1,7 +1,14 @@ <?php // Location handling: figure out location -$locationId = false; // TODO: machine specific mapping +$locationId = false; +if (Request::any('force', 0, 'int') === 1 && Request::any('module', false, 'string') === 'locations') { + // Force location for testing, but require logged in admin + if (User::load()) { + $locationId = Request::any('value', 0, 'int'); + } +} +// TODO: machine specific mapping if ($locationId === false) { // Fallback to subnets $locationId = Location::getFromIp($ip); diff --git a/modules-available/locations/lang/de/template-tags.json b/modules-available/locations/lang/de/template-tags.json index 13e21217..09ee8a35 100644 --- a/modules-available/locations/lang/de/template-tags.json +++ b/modules-available/locations/lang/de/template-tags.json @@ -6,11 +6,14 @@ "lang_deleteChildLocations": "Untergeordnete Orte ebenfalls l\u00f6schen", "lang_deleteLocation": "Ort l\u00f6schen", "lang_deleteSubnet": "Bereich l\u00f6schen", + "lang_editConfigVariables": "Konfiguration anpassen", "lang_endAddress": "Endadresse", "lang_listOfSubnets": "Liste der Subnetze", "lang_location": "Ort", "lang_locationInfo": "Details zu diesem Ort", "lang_locationName": "Name", + "lang_locationOtherOverlap": "Achtung! Folgende Orte haben sich \u00fcberlappende Netzbereiche", + "lang_locationSelfOverlap": "Folgender Ort hat mehrere sich \u00fcberschneidende Netzbereiche", "lang_locationSettings": "Raum\/Ort bearbeiten", "lang_locationsMainHeading": "Verwaltung von R\u00e4umen\/Orten", "lang_matchingMachines": "Enthaltene Rechner", @@ -21,5 +24,6 @@ "lang_startAddress": "Startadresse", "lang_subnet": "IP-Bereich", "lang_thisListByLocation": "Zur Ortsansicht", - "lang_thisListBySubnet": "Nach Subnetzen auflisten" + "lang_thisListBySubnet": "Nach Subnetzen auflisten", + "lang_unassignedMachines": "Rechner, die in keinen definierten Ort fallen" }
\ No newline at end of file diff --git a/modules-available/locations/lang/en/template-tags.json b/modules-available/locations/lang/en/template-tags.json index 1ea0f56e..bd64554f 100644 --- a/modules-available/locations/lang/en/template-tags.json +++ b/modules-available/locations/lang/en/template-tags.json @@ -6,11 +6,14 @@ "lang_deleteChildLocations": "Delete child locations aswell", "lang_deleteLocation": "Delete location", "lang_deleteSubnet": "Delete range", + "lang_editConfigVariables": "Edit config variables", "lang_endAddress": "End address", "lang_listOfSubnets": "List of subnets", "lang_location": "Ort", "lang_locationInfo": "Location details", "lang_locationName": "Name", + "lang_locationOtherOverlap": "Warning! These locations have overlapping address ranges", + "lang_locationSelfOverlap": "The following location has multiple address ranges that are overlapping", "lang_locationSettings": "Edit this room or location", "lang_locationsMainHeading": "Manage rooms and locations", "lang_matchingMachines": "Matching clients", @@ -21,5 +24,6 @@ "lang_startAddress": "Start address", "lang_subnet": "IP range", "lang_thisListByLocation": "List by location", - "lang_thisListBySubnet": "List by subnet" + "lang_thisListBySubnet": "List by subnet", + "lang_unassignedMachines": "Machines not matching any location" }
\ No newline at end of file diff --git a/modules-available/locations/page.inc.php b/modules-available/locations/page.inc.php index 9de5e521..84bc11a9 100644 --- a/modules-available/locations/page.inc.php +++ b/modules-available/locations/page.inc.php @@ -80,9 +80,9 @@ class Page_Locations extends Page $this->deleteLocation($location); } // Update subnets - $this->updateLocationSubnets($location); + $this->updateLocationSubnets(); // Insert subnets - $this->addNewLocationSubnets($location); // TODO + $this->addNewLocationSubnets($location); // Update location! $this->updateLocationData($location); Util::redirect('?do=Locations'); @@ -148,9 +148,8 @@ class Page_Locations extends Page } } - private function updateLocationSubnets($location) + private function updateLocationSubnets() { - $locationId = (int)$location['locationid']; // Deletion first $dels = Request::post('deletesubnet', false); if (is_array($dels)) { @@ -240,7 +239,6 @@ class Page_Locations extends Page protected function doRender() { - //Render::setTitle(Dictionary::translate('lang_titleBackup')); $getAction = Request::get('action'); if (empty($getAction)) { // Until we have a main landing page? diff --git a/modules-available/main/hooks/translation.inc.php b/modules-available/main/hooks/translation.inc.php index c831cbfd..03fa5f79 100644 --- a/modules-available/main/hooks/translation.inc.php +++ b/modules-available/main/hooks/translation.inc.php @@ -6,7 +6,7 @@ $HANDLER = array(); * List of valid subsections */ $HANDLER['subsections'] = array( - 'categories', 'global-tags' + 'global-tags' ); /* @@ -15,21 +15,6 @@ $HANDLER['subsections'] = array( */ /** - * Configuration categories - */ -$HANDLER['grep_categories'] = function($module) { - $skip = strlen($module->getIdentifier()) + 1; - $want = array(); - foreach (Module::getAll() as $module) { - $cat = $module->getCategory(); - if (is_string($cat)) { - $want[substr($cat, $skip)] = true; - } - } - return $want; -}; - -/** * Global tags. * This just returns the union of global tags of all languages, as there is no * way to define a definite set of required global tags. diff --git a/modules-available/main/templates/main-menu.html b/modules-available/main/templates/main-menu.html index b14d1ace..7c104e92 100644 --- a/modules-available/main/templates/main-menu.html +++ b/modules-available/main/templates/main-menu.html @@ -33,6 +33,9 @@ <ul class="dropdown-menu sidebar-visible-block" role="menu"> {{#modules}} <li class="{{className}}"><a href="?do={{identifier}}">{{displayName}}</a></li> + {{#subMenu}} + <li class="slx-submenu"><a href="{{url}}">{{name}}</a></li> + {{/subMenu}} {{/modules}} </ul> </li> diff --git a/modules-available/minilinux/page.inc.php b/modules-available/minilinux/page.inc.php index a7a70d13..2623500b 100644 --- a/modules-available/minilinux/page.inc.php +++ b/modules-available/minilinux/page.inc.php @@ -27,9 +27,10 @@ class Page_MiniLinux extends Page { $data = Property::getVersionCheckInformation(); if (!is_array($data) || !isset($data['systems'])) { - echo Render::parse('messagebox-error', array( + echo Render::parse('messagebox', array( + 'type' => 'danger', 'message' => 'Failed to retrieve the list: ' . print_r($data, true) - ),'main'); + ), 'main'); return; } $action = Request::any('action'); diff --git a/modules-available/news/lang/de/template-tags.json b/modules-available/news/lang/de/template-tags.json index 94aee385..7bd4c0e0 100644 --- a/modules-available/news/lang/de/template-tags.json +++ b/modules-available/news/lang/de/template-tags.json @@ -1,5 +1,4 @@ { - "lang_activeNews": "Aktive News", "lang_content": "Inhalt", "lang_date": "Datum", "lang_editNews": "News bearbeiten", diff --git a/modules-available/news/lang/en/template-tags.json b/modules-available/news/lang/en/template-tags.json index 8630e8e8..e33f6945 100644 --- a/modules-available/news/lang/en/template-tags.json +++ b/modules-available/news/lang/en/template-tags.json @@ -1,5 +1,4 @@ { - "lang_activeNews": "News", "lang_content": "Content", "lang_date": "Date", "lang_editNews": "Edit news", diff --git a/modules-available/news/lang/pt/template-tags.json b/modules-available/news/lang/pt/template-tags.json index 127aeec9..f1d71682 100644 --- a/modules-available/news/lang/pt/template-tags.json +++ b/modules-available/news/lang/pt/template-tags.json @@ -1,5 +1,4 @@ { - "lang_activeNews": "Novidades", "lang_content": "Conte\u00fado", "lang_date": "Data", "lang_latestUpdate": "\u00daltima Atualiza\u00e7\u00e3o", diff --git a/modules-available/serversetup-bwlp/lang/de/template-tags.json b/modules-available/serversetup-bwlp/lang/de/template-tags.json index 12881540..593eb9e8 100644 --- a/modules-available/serversetup-bwlp/lang/de/template-tags.json +++ b/modules-available/serversetup-bwlp/lang/de/template-tags.json @@ -7,7 +7,6 @@ "lang_bootMenu": "Bootmen\u00fc", "lang_bootMenuCreate": "Bootmen\u00fc erzeugen", "lang_chooseIP": "Bitte w\u00e4hlen Sie die IP-Adresse, \u00fcber die der Server von den Clients zum Booten angesprochen werden soll.", - "lang_compile": "Kompilieren", "lang_customEntry": "Eigener Eintrag", "lang_downloadImage": "USB-Image herunterladen", "lang_example": "Beispiel", diff --git a/modules-available/serversetup-bwlp/lang/en/template-tags.json b/modules-available/serversetup-bwlp/lang/en/template-tags.json index 61ac6ade..f2c8aa41 100644 --- a/modules-available/serversetup-bwlp/lang/en/template-tags.json +++ b/modules-available/serversetup-bwlp/lang/en/template-tags.json @@ -7,18 +7,10 @@ "lang_bootMenu": "Boot Menu", "lang_bootMenuCreate": "Create Boot Menu", "lang_chooseIP": "Please select the IP address that the client server will use to boot.", - "lang_compile": "Compile", - "lang_compilingIpxe": "Compiling iPXE", "lang_customEntry": "Custom entry", - "lang_customScript": "Custom script", - "lang_download": "Download", "lang_downloadImage": "Download USB image", "lang_example": "Example", - "lang_extension": "Extension", "lang_generationFailed": "Could not generate boot menu. The bwLehrpool-System might not work properly. If you can't fix the problem, please report the error message above to the bwLehrpool project.", - "lang_ipxeInfo": "Here it is possible to compile iPXE using a custom script.", - "lang_ipxeWarning": "If this is your first time compiling, it may take 1 to 4 minutes to finish.", - "lang_loading": "Loading", "lang_localHDD": "Local HDD", "lang_masterPassword": "Master password", "lang_masterPasswordHelp": "The master password is required to edit a boot menu entry. This should be set for security reasons.", @@ -28,10 +20,6 @@ "lang_menuCustomHint3": "and select as the default boot behavior custom as well.", "lang_menuDisplayTime": "Menu Display Time", "lang_menuGeneration": "Generating boot menu...", - "lang_mountIpxe": "Mount iPXE", - "lang_restoreDefault": "Restore Default", - "lang_saveScript": "Save Script", "lang_seconds": "Seconds", - "lang_set": "Set", - "lang_success": "Successfully create file:" + "lang_set": "Set" }
\ No newline at end of file diff --git a/modules-available/session/page.inc.php b/modules-available/session/page.inc.php index ef135f9d..853f20e4 100644 --- a/modules-available/session/page.inc.php +++ b/modules-available/session/page.inc.php @@ -29,7 +29,6 @@ class Page_Session extends Page protected function doRender() { - Render::setTitle(Dictionary::translate('lang_login')); Render::addTemplate('page-login'); } diff --git a/modules-available/statistics/lang/de/template-tags.json b/modules-available/statistics/lang/de/template-tags.json index bd4785c0..40f3dbc4 100644 --- a/modules-available/statistics/lang/de/template-tags.json +++ b/modules-available/statistics/lang/de/template-tags.json @@ -6,6 +6,7 @@ "lang_cpuCores": "CPU-Kerne", "lang_cpuModel": "CPU", "lang_details": "Details", + "lang_devices": "Ger\u00e4te", "lang_event": "Ereignis", "lang_firstSeen": "Erste Aktivit\u00e4t", "lang_gbRam": "RAM", diff --git a/modules-available/statistics/lang/en/template-tags.json b/modules-available/statistics/lang/en/template-tags.json index aae26ae6..beb9f4f6 100644 --- a/modules-available/statistics/lang/en/template-tags.json +++ b/modules-available/statistics/lang/en/template-tags.json @@ -6,6 +6,7 @@ "lang_cpuCores": "CPU cores", "lang_cpuModel": "CPU", "lang_details": "Details", + "lang_devices": "Devices", "lang_event": "Event", "lang_firstSeen": "First seen", "lang_gbRam": "RAM", diff --git a/modules-available/statistics/page.inc.php b/modules-available/statistics/page.inc.php index 266c4277..1e6f7400 100644 --- a/modules-available/statistics/page.inc.php +++ b/modules-available/statistics/page.inc.php @@ -37,7 +37,6 @@ class Page_Statistics extends Page protected function doRender() { - Render::setTitle(Dictionary::translate('lang_titleClientStatistics')); $uuid = Request::get('uuid', false, 'string'); if ($uuid !== false) { $this->showMachine($uuid); diff --git a/modules-available/sysconfig/addmodule_adauth.inc.php b/modules-available/sysconfig/addmodule_adauth.inc.php index b855761a..2949928b 100644 --- a/modules-available/sysconfig/addmodule_adauth.inc.php +++ b/modules-available/sysconfig/addmodule_adauth.inc.php @@ -108,7 +108,7 @@ class AdAuth_SelfSearch extends AddModule_Base $bindpw = Request::post('bindpw'); $ssl = Request::post('ssl', 'off') === 'on'; if ($ssl && !Request::post('fingerprint')) { - Message::addError('error-read', 'fingerprint'); + Message::addError('main.error-read', 'fingerprint'); AddModule_Base::setStep('AdAuth_Start'); // Continues with AdAuth_Start for render() return; } diff --git a/modules-available/sysconfig/addmodule_ldapauth.inc.php b/modules-available/sysconfig/addmodule_ldapauth.inc.php index a6ac145c..d7122001 100644 --- a/modules-available/sysconfig/addmodule_ldapauth.inc.php +++ b/modules-available/sysconfig/addmodule_ldapauth.inc.php @@ -96,7 +96,7 @@ class LdapAuth_CheckCredentials extends AddModule_Base $bindpw = Request::post('bindpw'); $ssl = Request::post('ssl', 'off') === 'on'; if ($ssl && !Request::post('fingerprint')) { - Message::addError('error-read', 'fingerprint'); + Message::addError('main.error-read', 'fingerprint'); AddModule_Base::setStep('LdapAuth_Start'); // Continues with LdapAuth_Start for render() return; } diff --git a/modules-available/sysconfig/addmodule_sshconfig.inc.php b/modules-available/sysconfig/addmodule_sshconfig.inc.php index 79857085..19272c32 100644 --- a/modules-available/sysconfig/addmodule_sshconfig.inc.php +++ b/modules-available/sysconfig/addmodule_sshconfig.inc.php @@ -45,7 +45,11 @@ class SshConfig_Finish extends AddModule_Base Util::redirect('?do=SysConfig&action=addmodule&step=SshConfig_Start'); } $module->setData('allowPasswordLogin', Request::post('allowPasswordLogin') === 'yes'); - if (!$module->setData('listenPort', Request::post('listenPort'))) { + $port = Request::post('listenPort', ''); + if ($port === '') { + $port = 22; + } + if (!$module->setData('listenPort', $port)) { Message::addError('main.value-invalid', 'port', Request::post('listenPort')); Util::redirect('?do=SysConfig&action=addmodule&step=SshConfig_Start'); } diff --git a/modules-available/sysconfig/install.inc.php b/modules-available/sysconfig/install.inc.php index 0b7bbc1b..35697d1f 100644 --- a/modules-available/sysconfig/install.inc.php +++ b/modules-available/sysconfig/install.inc.php @@ -34,7 +34,8 @@ $res[] = tableCreate('configtgz_x_module', " // Constraints if (in_array(UPDATE_DONE, $res)) { Database::exec("ALTER TABLE `configtgz_x_module` - ADD CONSTRAINT `configtgz_x_module_ibfk_1` FOREIGN KEY (`configid`) REFERENCES `configtgz` (`configid`) ON DELETE CASCADE, + ADD CONSTRAINT `configtgz_x_module_ibfk_1` FOREIGN KEY (`configid`) REFERENCES `configtgz` (`configid`) ON DELETE CASCADE"); + Database::exec("ALTER TABLE `configtgz_x_module` ADD CONSTRAINT `configtgz_x_module_ibfk_2` FOREIGN KEY (`moduleid`) REFERENCES `configtgz_module` (`moduleid`)"); } diff --git a/modules-available/sysconfig/page.inc.php b/modules-available/sysconfig/page.inc.php index efe34136..121e4cee 100644 --- a/modules-available/sysconfig/page.inc.php +++ b/modules-available/sysconfig/page.inc.php @@ -106,8 +106,6 @@ class Page_SysConfig extends Page */ protected function doRender() { - Render::setTitle(Dictionary::translate('lang_location')); - $action = Request::any('action', 'list'); switch ($action) { case 'addmodule': diff --git a/modules-available/sysconfignew/style.css b/modules-available/sysconfignew/style.css new file mode 100644 index 00000000..43f708d1 --- /dev/null +++ b/modules-available/sysconfignew/style.css @@ -0,0 +1,59 @@ +.mod-nav { + width: 250px; + float: left; + margin-right: 4px; + border-right: solid 1px #DDD; + padding: 4px; + height: 545px; + overflow: auto; +} + +.item { + padding: 4px; + margin: 3px 0; + border-radius: 3px; +} + +.item:HOVER { + background-color: #EEE; + cursor: pointer; +} + +.item-link { + color: #5893C6; +} + +.item-lock { + color: #939393; +} + +.dir-icon { + color: #2E6DA4; +} + +.folder { + padding-left: 16px; + display: none; + border-left: dashed 1px #CCC; +} + +#editor-header{ + display: block; + float: none; + width: 875px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.mod-editor{ + display: block; + float: none; + resize: none; + width: 875px; + border-top-right-radius: 0; + border-top-left-radius: 0; +} + +textarea.form-control .editor-box{ + height: 520px; +} diff --git a/modules-available/translation/lang/de/template-tags.json b/modules-available/translation/lang/de/template-tags.json index 3f381751..b8497bd9 100644 --- a/modules-available/translation/lang/de/template-tags.json +++ b/modules-available/translation/lang/de/template-tags.json @@ -2,6 +2,7 @@ "lang_createTag": "Tag erstellen", "lang_global": "Global", "lang_globalTooltip": "Dieser Tag ist global verf\u00fcgbar und braucht normalerweise nicht explizit f\u00fcr dieses Modul \u00fcbersetzt zu werden", + "lang_menuCategories": "Men\u00fckategorien", "lang_messages": "Benachrichtigungen", "lang_missing": "Fehlend", "lang_module": "Modul", diff --git a/modules-available/translation/lang/en/template-tags.json b/modules-available/translation/lang/en/template-tags.json index 5aee54c3..ec2a26a3 100644 --- a/modules-available/translation/lang/en/template-tags.json +++ b/modules-available/translation/lang/en/template-tags.json @@ -2,6 +2,7 @@ "lang_createTag": "Create Tag", "lang_global": "Global", "lang_globalTooltip": "This tag is global; usually there is no need to translate it explicitly for this module", + "lang_menuCategories": "Menu categories", "lang_messages": "Messages", "lang_missing": "Missing", "lang_module": "Module", diff --git a/modules-available/translation/page.inc.php b/modules-available/translation/page.inc.php index 5b2d24e1..74cf27a8 100644 --- a/modules-available/translation/page.inc.php +++ b/modules-available/translation/page.inc.php @@ -42,7 +42,7 @@ class Page_Translation extends Page public function __construct() { - $this->builtInSections = array('template', 'messages', 'module', 'custom'); + $this->builtInSections = array('template', 'messages', 'module', 'menucategory', 'custom'); } private function isValidSection($section) @@ -167,6 +167,13 @@ class Page_Translation extends Page return; } + // Menu Category + if ($this->section === 'menucategory') { + $this->ensureValidDestLanguage(); + $this->showMenuCategoryEdit(); + return; + } + // Custom if ($this->section === 'custom') { $this->ensureValidDestLanguage(); @@ -174,7 +181,7 @@ class Page_Translation extends Page return; } - $this->redirect(); + $this->redirect(1); } private function showListOfModules() @@ -212,6 +219,8 @@ class Page_Translation extends Page $this->showModuleMessages(); // Other/hardcoded strings $this->showModuleStrings(); + // Menu categories + $this->showModuleMenuCategories(); // Module specific $this->showModuleCustom(); Render::closeTag('div'); @@ -285,6 +294,23 @@ class Page_Translation extends Page } Render::addTemplate('string-list', $data); } + + private function showModuleMenuCategories() + { + $moduleTags = $this->loadUsedMenuCategories(); + $data = array('module' => $this->module->getIdentifier()); + $data['tagcount'] = count($moduleTags); + foreach (Dictionary::getLanguages(true) as $lang) { + list($missing, $unused) = $this->getModuleTranslationStatus($lang['cc'], 'menucategory', true, $moduleTags); + $data['langs'][] = array( + 'cc' => $lang['cc'], + 'name' => $lang['name'], + 'missing' => $missing, + 'unused' => $unused + ); + } + Render::addTemplate('menu-category-list', $data); + } private function showModuleCustom() { @@ -352,6 +378,17 @@ class Page_Translation extends Page )); } + private function showMenuCategoryEdit() + { + Render::addTemplate('edit', array( + 'destlang' => $this->destLang, + 'language' => Dictionary::getLanguageName($this->destLang), + 'tags' => $this->loadMenuCategoryEditArray(), + 'module' => $this->module->getIdentifier(), + 'section' => $this->section + )); + } + private function showCustomEdit() { Render::addTemplate('edit', array( @@ -453,6 +490,23 @@ class Page_Translation extends Page } return $tags; } + + private function loadUsedMenuCategories($module = false) + { + if ($module === false) { + $module = $this->module; + } + $skip = strlen($module->getIdentifier()) + 1; + $match = $module->getIdentifier() . '.'; + $want = array(); + foreach (Module::getAll() as $module) { + $cat = $module->getCategory(); + if (is_string($cat) && substr($cat, 0, $skip) === $match) { + $want[substr($cat, $skip)] = true; + } + } + return $want; + } private function loadUsedCustomTags($subsection) { @@ -657,6 +711,13 @@ class Page_Translation extends Page $table = $this->buildTranslationTable('module', array_keys($tags), true); return $table; } + + private function loadMenuCategoryEditArray() + { + $tags = $this->loadUsedMenuCategories(); + $table = $this->buildTranslationTable('categories', array_keys($tags), true); + return $table; + } /** * Get array to pass to edit page with all the message ids. @@ -865,6 +926,9 @@ class Page_Translation extends Page if ($this->section === 'module') { return $prefix . '/module.json'; } + if ($this->section === 'menucategory') { + return $prefix . '/categories.json'; + } // Custom submodule if ($this->section === 'custom') { if ($this->customHandler === false || !isset($this->customHandler['subsections'])) { diff --git a/modules-available/translation/templates/menu-category-list.html b/modules-available/translation/templates/menu-category-list.html new file mode 100644 index 00000000..4a0df819 --- /dev/null +++ b/modules-available/translation/templates/menu-category-list.html @@ -0,0 +1,23 @@ +<div class="col-lg-6"> + <div class="panel panel-default"> + <div class="panel-heading"> + {{lang_menuCategories}} + </div> + <div class="panel-body"> + <div class="row"> + <div class="col-sm-6"> + {{#langs}} + <a href="?do=Translation&module={{module}}&section=menucategory&destlang={{cc}}">{{name}} »</a> + <ul> + <li>{{lang_missing}}: {{missing}}</li> + <li>{{lang_unused}}: {{unused}}</li> + </ul> + {{/langs}} + </div> + <div class="col-sm-6"> + {{lang_tags}}: {{tagcount}} + </div> + </div> + </div> + </div> +</div>
\ No newline at end of file diff --git a/modules-available/vmstore/page.inc.php b/modules-available/vmstore/page.inc.php index 23b7375c..6ef04669 100644 --- a/modules-available/vmstore/page.inc.php +++ b/modules-available/vmstore/page.inc.php @@ -57,7 +57,9 @@ class Page_VmStore extends Page Util::redirect('?do=VmStore'); } $this->mountTask = Trigger::mount($vmstore); - TaskmanagerCallback::addCallback($this->mountTask, 'manualMount', $vmstore); + if ($this->mountTask !== false) { + TaskmanagerCallback::addCallback($this->mountTask, 'manualMount', $vmstore); + } } }
\ No newline at end of file diff --git a/style/default.css b/style/default.css index d49bcb18..9b93bfda 100644 --- a/style/default.css +++ b/style/default.css @@ -310,7 +310,7 @@ nav.sidebar li { } .slx-topbar div { - margin-left: 225px; + margin-left: 270px; } .sidebar-bg { @@ -324,6 +324,10 @@ nav.sidebar li { /* .....NavBar: Fully showing nav bar..... */ @media (min-width: 1200px) { + + nav.navbar.sidebar { + z-index: 200; + } .navbar-right { float: left !important; @@ -465,76 +469,12 @@ section{ padding-left: 15px; } -/* - * Sysconfig Module Editor - */ - -.mod-nav { - width: 250px; - float: left; - margin-right: 4px; - border-right: solid 1px #DDD; - padding: 4px; - height: 545px; - overflow: auto; -} - -.item { - padding: 4px; - margin: 3px 0; - border-radius: 3px; -} - -.item:HOVER { - background-color: #EEE; - cursor: pointer; -} - -.item-link { - color: #5893C6; -} - -.item-lock { - color: #939393; -} - -.dir-icon { - color: #2E6DA4; -} - -.folder { - padding-left: 16px; - display: none; - border-left: dashed 1px #CCC; -} - -#editor-header{ - display: block; - float: none; - width: 875px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} - -.mod-editor{ - display: block; - float: none; - resize: none; - width: 875px; - border-top-right-radius: 0; - border-top-left-radius: 0; -} - -.slx-cfg-toggle > input[type=checkbox] ~ label { - font-weight: normal; -} - -.slx-cfg-toggle > input[type=checkbox]:checked ~ label { +li.slx-submenu a { + font-size: 80%; font-weight: bold; + padding-left: 3em; } -/* what's this? -textarea.form-control .editor-box{ - height: 520px; +nav.navbar.sidebar { + color: #eee; } -*/ |