diff options
author | Simon Rettberg | 2014-05-23 20:49:02 +0200 |
---|---|---|
committer | Simon Rettberg | 2014-05-23 20:49:02 +0200 |
commit | fe6ac16498b05d0f0c8ed7fda394273815d3d6da (patch) | |
tree | cadf5f103ef3db7ba1b40d59d85937c998aad22f | |
parent | Server Setup page (diff) | |
download | slx-admin-fe6ac16498b05d0f0c8ed7fda394273815d3d6da.tar.gz slx-admin-fe6ac16498b05d0f0c8ed7fda394273815d3d6da.tar.xz slx-admin-fe6ac16498b05d0f0c8ed7fda394273815d3d6da.zip |
Stuff (WIP)
31 files changed, 778 insertions, 244 deletions
diff --git a/inc/configmodule.inc.php b/inc/configmodule.inc.php new file mode 100644 index 00000000..55f76cf3 --- /dev/null +++ b/inc/configmodule.inc.php @@ -0,0 +1,50 @@ +<?php + +class ConfigModule +{ + + public static function insertAdConfig($title, $server, $searchbase, $binddn, $bindpw) + { + // TODO: Lock table, race condition if about 500 admins insert a config at the same time + Database::exec("INSERT INTO configtgz_module (title, moduletype, filepath, contents) " + . " VALUES (:title, 'AD_AUTH', '', '')", array('title' => $title)); + $id = Database::lastInsertId(); + if (!is_numeric($id)) Util::traceError('Inserting new AD config to DB did not yield a numeric insert id'); + // Entry created, now try to get a free port for the proxy + $res = Database::simpleQuery("SELECT moduleid, contents FROM configtgz_module"); + $ports = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if ($row['moduleid'] == $id) { + // ... + } else { + $data = json_decode($row['contents'], true); + if (isset($data['proxyport'])) $ports[] = $data['proxyport']; + } + } + $port = 3300; + while (in_array($port, $ports)) { + $port++; + } + // Port determined, carry on... + $ownEntry = array( + 'server' => $server, + 'searchbase' => $searchbase, + 'binddn' => $binddn, + 'bindpw' => $bindpw, + 'proxyport' => $port + ); + $data = json_encode($ownEntry); + if ($data === false) Util::traceError('Serializing the AD data failed.'); + $name = CONFIG_TGZ_LIST_DIR . '/modules/AD_AUTH_id_' . $id . '.' . mt_rand() . '.tgz'; + Database::exec("UPDATE configtgz_module SET filepath = :filename, contents = :contents WHERE moduleid = :id LIMIT 1", array( + 'id' => $id, + 'filename' => $name, + 'contents' => json_encode($ownEntry) + )); + // Add archive file name to array before returning it + $ownEntry['moduleid'] = $id; + $ownEntry['filename'] = $name; + return $ownEntry; + } + +} diff --git a/inc/database.inc.php b/inc/database.inc.php index 70f50116..a646e823 100644 --- a/inc/database.inc.php +++ b/inc/database.inc.php @@ -59,6 +59,7 @@ class Database * Note that this will re-use PDOStatements, so if you run the same * query again with different params, do not rely on the first PDOStatement * still being valid. If you need to do something fancy, use Database::prepare + * @return \PDOStatement The query result object */ public static function simpleQuery($query, $args = array()) { diff --git a/inc/message.inc.php b/inc/message.inc.php index cab8fbd3..2ed9e79a 100644 --- a/inc/message.inc.php +++ b/inc/message.inc.php @@ -19,8 +19,9 @@ $error_text = array( 'missing-file' => 'Es wurde keine Datei ausgewählt!', 'invalid-file' => 'Die Datei {{0}} existiert nicht!', 'upload-complete' => 'Upload von {{0}} war erfolgreich', - 'upload-failed' => 'Upload von {{0}} schlug fehl!', - 'config-activated' => 'Konfiguration wurde aktiviert', + 'upload-failed' => 'Upload schlug fehl: {{0}}', + 'config-activated' => 'Konfiguration {{0}} wurde aktiviert', + 'config-invalid' => 'Konfiguration mit ID {{0}} existiert nicht', 'error-write' => 'Fehler beim Schreiben von {{0}}', 'error-read' => 'Fehler beim Lesen von {{0}}', 'error-archive' => 'Korruptes Archiv oder nicht unterstütztes Format', @@ -29,6 +30,8 @@ $error_text = array( 'empty-archive' => 'Das Archiv enthält keine Dateien oder Verzeichnisse', 'error-extract' => 'Konnte Archiv nicht nach {{0}} entpacken - {{1}}', 'module-added' => 'Modul erfolgreich hinzugefügt', + 'module-deleted' => 'Modul {{0}} wurde gelöscht', + 'module-in-use' => 'Modul <b>{{0}}</b> wird noch durch Konfiguration <b>{{1}}</b> verwendet', 'taskmanager-error' => 'Verbindung zum Taskmanager fehlgeschlagen', 'taskmanager-format' => 'Taskmanager hat ungültige Daten zurückgeliefert', 'task-error' => 'Ausführung fehlgeschlagen: {{0}}', @@ -93,7 +96,7 @@ class Message foreach (self::$list as $item) { $message = $error_text[$item['id']]; foreach ($item['params'] as $index => $text) { - $message = str_replace('{{' . $index . '}}', $text, $message); + $message = str_replace('{{' . $index . '}}', '<b>' . htmlspecialchars($text) . '</b>', $message); } Render::addTemplate('messagebox-' . $item['type'], array('message' => $message)); self::$alreadyDisplayed[] = $item; diff --git a/inc/property.inc.php b/inc/property.inc.php index a1c252a5..c6f3e8ad 100644 --- a/inc/property.inc.php +++ b/inc/property.inc.php @@ -54,5 +54,25 @@ class Property { self::set('server-ip', $value); } + + public static function getIPxeIp() + { + return self::get('ipxe-ip', 'none'); + } + + public static function setIPxeIp($value) + { + self::set('ipxe-ip', $value); + } + + public static function getIPxeTaskId() + { + return self::get('ipxe-task'); + } + + public static function setIPxeTaskId($value) + { + self::set('ipxe-task', $value); + } -}
\ No newline at end of file +} diff --git a/inc/taskmanager.inc.php b/inc/taskmanager.inc.php index 27e79dea..e93cc696 100644 --- a/inc/taskmanager.inc.php +++ b/inc/taskmanager.inc.php @@ -66,12 +66,17 @@ class Taskmanager public static function waitComplete($taskId) { + $done = false; for ($i = 0; $i < 10; ++$i) { $status = self::status($taskId); if (!isset($status['statusCode'])) break; - if ($status['statusCode'] != TASK_PROCESSING && $status['statusCode'] != TASK_WAITING) break; + if ($status['statusCode'] != TASK_PROCESSING && $status['statusCode'] != TASK_WAITING) { + $done = true; + break; + } usleep(150000); } + if ($done) self::release ($taskId); return $status; } diff --git a/inc/trigger.inc.php b/inc/trigger.inc.php new file mode 100644 index 00000000..40d9c491 --- /dev/null +++ b/inc/trigger.inc.php @@ -0,0 +1,40 @@ +<?php + +/** + * This is one giant class containing various functions that will generate + * required config files, daemon instances and more, mostly through the Taskmanager. + * Most function *should* only actually do something if it is required to do so. + * eg. a "launchSomething" function should only launch something if it isn't already + * running. Checking if something is running can happen in that very function, or in + * a task that the function is calling. + */ +class Trigger +{ + + /** + * Compile iPXE pxelinux menu. Needs to be done whenever the server's IP + * address changes. + * + * @param boolean $force force recompilation even if it seems up to date + * @return boolean|string true if already up to date, false if launching task failed, task-id otherwise + */ + public static function ipxe($force = false) + { + if (!$force && Property::getIPxeIp() === Property::getServerIp()) + return true; // Nothing to do + $last = Property::getIPxeTaskId(); + if ($last !== false) { + $status = Taskmanager::status($last); + if (isset($status['statusCode']) && ($status['statusCode'] === TASK_WAITING || $status['statusCode'] === TASK_RUNNING)) + return false; // Already compiling + } + $task = Taskmanager::submit('CompileIPxe', array( + 'ip' => Property::getServerIp() + )); + if (!isset($task['id'])) + return false; + Property::setIPxeTaskId($task['id']); + return $task['id']; + } + +}
\ No newline at end of file diff --git a/inc/util.inc.php b/inc/util.inc.php index 4b974f6d..63680023 100644 --- a/inc/util.inc.php +++ b/inc/util.inc.php @@ -173,6 +173,49 @@ class Util } return sprintf("%.{$decimals}f ", $bytes / pow(1024, $factor)) . $sz[$factor]; } + + public static function sanitizeFilename($name) + { + return preg_replace('/[^a-zA-Z0-9_\-]+/', '_', $name); + } + + /** + * Create human readable error description from a $_FILES[<..>]['error'] code + * + * @param int $code the code to turn into an error description + * @return string the error description + */ + public static function uploadErrorString($code) + { + switch ($code) { + case UPLOAD_ERR_INI_SIZE: + $message = "The uploaded file exceeds the upload_max_filesize directive in php.ini"; + break; + case UPLOAD_ERR_FORM_SIZE: + $message = "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"; + break; + case UPLOAD_ERR_PARTIAL: + $message = "The uploaded file was only partially uploaded"; + break; + case UPLOAD_ERR_NO_FILE: + $message = "No file was uploaded"; + break; + case UPLOAD_ERR_NO_TMP_DIR: + $message = "Missing a temporary folder"; + break; + case UPLOAD_ERR_CANT_WRITE: + $message = "Failed to write file to disk"; + break; + case UPLOAD_ERR_EXTENSION: + $message = "File upload stopped by extension"; + break; + + default: + $message = "Unknown upload error"; + break; + } + return $message; + } } diff --git a/modules/adduser.inc.php b/modules/adduser.inc.php index 6a5faf3a..19fa5425 100644 --- a/modules/adduser.inc.php +++ b/modules/adduser.inc.php @@ -1,49 +1,55 @@ <?php -User::load(); +class Page_AddUser extends Page +{ -if (isset($_POST['action']) && $_POST['action'] === 'adduser') { - // Check required fields - if (empty($_POST['user']) || empty($_POST['pass1']) || empty($_POST['pass2']) || empty($_POST['fullname']) || empty($_POST['phone']) || empty($_POST['email'])) { - Message::addError('empty-field'); - Util::redirect('?do=AddUser'); - } elseif ($_POST['pass1'] !== $_POST['pass2']) { - Message::addError('password-mismatch'); - Util::redirect('?do=AddUser'); - } elseif (Database::queryFirst('SELECT userid FROM user LIMIT 1') !== false) { - Message::addError('adduser-disabled'); - Util::redirect('?do=Session&action=login'); - } else { - $data = array( - 'user' => $_POST['user'], - 'pass' => Crypto::hash6($_POST['pass1']), - 'fullname' => $_POST['fullname'], - 'phone' => $_POST['phone'], - 'email' => $_POST['email'], - ); - if (strlen($data['pass']) < 50) Util::traceError('Error hashing password using SHA-512'); - if (Database::exec('INSERT INTO user SET login = :user, passwd = :pass, fullname = :fullname, phone = :phone, email = :email', $data) != 1) { - Util::traceError('Could not create new user in DB'); - } - // Make it superadmin if first user. This method sucks as it's a race condition but hey... - $ret = Database::queryFirst('SELECT Count(*) AS num FROM user'); - if ($ret !== false && $ret['num'] == 1) { - Database::exec('UPDATE user SET permissions = 1'); + protected function doPreprocess() + { + User::load(); + + if (isset($_POST['action']) && $_POST['action'] === 'adduser') { + // Check required fields + if (empty($_POST['user']) || empty($_POST['pass1']) || empty($_POST['pass2']) || empty($_POST['fullname']) || empty($_POST['phone']) || empty($_POST['email'])) { + Message::addError('empty-field'); + Util::redirect('?do=AddUser'); + } elseif ($_POST['pass1'] !== $_POST['pass2']) { + Message::addError('password-mismatch'); + Util::redirect('?do=AddUser'); + } elseif (Database::queryFirst('SELECT userid FROM user LIMIT 1') !== false) { + Message::addError('adduser-disabled'); + Util::redirect('?do=Session&action=login'); + } else { + $data = array( + 'user' => $_POST['user'], + 'pass' => Crypto::hash6($_POST['pass1']), + 'fullname' => $_POST['fullname'], + 'phone' => $_POST['phone'], + 'email' => $_POST['email'], + ); + if (Database::exec('INSERT INTO user SET login = :user, passwd = :pass, fullname = :fullname, phone = :phone, email = :email', $data) != 1) { + Util::traceError('Could not create new user in DB'); + } + // Make it superadmin if first user. This method sucks as it's a race condition but hey... + $ret = Database::queryFirst('SELECT Count(*) AS num FROM user'); + if ($ret !== false && $ret['num'] == 1) { + Database::exec('UPDATE user SET permissions = 1'); + } + Message::addInfo('adduser-success'); + Util::redirect('?do=Session&action=login'); + } } - Message::addInfo('adduser-success'); - Util::redirect('?do=Session&action=login'); } -} -function render_module() -{ - // No user was added, check if current user is allowed to add a new user - // Currently you can only add users if there is no user yet. :) - if (Database::queryFirst('SELECT userid FROM user LIMIT 1') !== false) { - Message::addError('adduser-disabled'); - } else { - Render::setTitle('Benutzer anlegen'); - Render::addTemplate('page-adduser', $_POST); + protected function doRender() + { + // No user was added, check if current user is allowed to add a new user + // Currently you can only add users if there is no user yet. :) + if (Database::queryFirst('SELECT userid FROM user LIMIT 1') !== false) { + Message::addError('adduser-disabled'); + } else { + Render::setTitle('Benutzer anlegen'); + Render::addTemplate('page-adduser', $_POST); + } } -} +} diff --git a/modules/ipxe.inc.php b/modules/ipxe.inc.php deleted file mode 100644 index d479bd15..00000000 --- a/modules/ipxe.inc.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -class Page_iPxe extends Page -{ - - protected function doPreprocess() - { - User::load(); - - if (!User::hasPermission('superadmin')) { - Message::addError('no-permission'); - Util::redirect('?do=Main'); - } - - if (isset($_POST['action'])) { - if ($_POST['action'] === 'compile') { - if (!Util::verifyToken()) { - Util::redirect('?do=Main'); - } - } - } - } - - protected function doRender() - { - $ips = array(); - $current = CONFIG_IPXE_DIR . '/last-ip'; - if (file_exists($current)) $current = file_get_contents($current); - exec('/bin/ip a', $retval); - foreach ($retval as $ip) { - if (preg_match('#inet (\d+\.\d+\.\d+\.\d+)/\d+.*scope#', $ip, $out) && $out[1] !== '127.0.0.1') { - $ips[] = array( - 'ip' => $out[1], - 'current' => ($out[1] == $current) - ); - } - } - Render::addTemplate('page-ipxe', array('ips' => $ips, 'token' => Session::get('token'))); - } -} diff --git a/modules/serversetup.inc.php b/modules/serversetup.inc.php index 3f2b8768..daa3b4e8 100644 --- a/modules/serversetup.inc.php +++ b/modules/serversetup.inc.php @@ -13,7 +13,7 @@ class Page_ServerSetup extends Page Message::addError('no-permission'); Util::redirect('?do=Main'); } - + $this->currentAddress = Property::getServerIp(); $newAddress = Request::post('ip', 'none'); @@ -23,7 +23,7 @@ class Page_ServerSetup extends Page Util::redirect('?do=Main'); } - if ($this->taskStatus['statusCode'] === TASK_WAITING) { + if ($this->taskStatus['statusCode'] === TASK_WAITING) { // TODO: Async if just displaying $this->taskStatus = Taskmanager::waitComplete($this->taskStatus['id']); } @@ -54,6 +54,7 @@ class Page_ServerSetup extends Page } if ($valid) { Property::setServerIp($newAddress); + Trigger::ipxe(); } else { Message::addError('invalid-ip', $newAddress); } @@ -64,9 +65,13 @@ class Page_ServerSetup extends Page protected function doRender() { - Render::addTemplate('page-serversetup', array( + Render::addTemplate('serversetup/ipaddress', array( 'ips' => $this->taskStatus['data']['addresses'], 'token' => Session::get('token') )); + Render::addTemplate('serversetup/ipxe', array( + 'token' => Session::get('token'), + 'taskid' => Property::getIPxeTaskId() + )); } }
\ No newline at end of file diff --git a/modules/sysconfig.inc.php b/modules/sysconfig.inc.php index f177f0f1..7c23ce56 100644 --- a/modules/sysconfig.inc.php +++ b/modules/sysconfig.inc.php @@ -6,6 +6,11 @@ class Page_SysConfig extends Page protected function doPreprocess() { User::load(); + + if (!User::hasPermission('superadmin')) { + Message::addError('no-permission'); + Util::redirect('?do=SysConfig'); + } $action = Request::any('action', 'list'); @@ -15,29 +20,28 @@ class Page_SysConfig extends Page AddModule_Base::preprocess(); } - // Action "activate" (set sysconfig as active) - if ($action === 'activate') { - if (!User::hasPermission('superadmin')) { - Message::addError('no-permission'); - Util::redirect('?do=SysConfig'); + if ($action === 'module') { + // Action: "delmodule" (delete module) + if (Request::post('del', 'no') !== 'no') { + $this->delModule(); } - if (!isset($_REQUEST['file'])) { - Message::addError('missing-file'); - Util::redirect('?do=SysConfig'); + } + + // Action: "addconfig" (compose config from one or more modules) + if ($action === 'addconfig') { + $this->initAddConfig(); + AddConfig_Base::preprocess(); + } + + if ($action === 'config') { + // Action: "delconfig" (delete config) + if (Request::post('del', 'no') !== 'no') { + $this->delConfig(); } - $file = preg_replace('/[^a-z0-9\-_\.]/', '', $_REQUEST['file']); - $path = CONFIG_TGZ_LIST_DIR . '/' . $file; - if (!file_exists($path)) { - Message::addError('invalid-file', $file); - Util::redirect('?do=SysConfig'); + // Action "activate" (set sysconfig as active) + if (Request::post('activate', 'no') !== 'no') { + $this->activateConfig(); } - mkdir(CONFIG_HTTP_DIR . '/default', 0755, true); - $linkname = CONFIG_HTTP_DIR . '/default/config.tgz'; - @unlink($linkname); - if (file_exists($linkname)) Util::traceError('Could not delete old config.tgz link!'); - if (!symlink($path, $linkname)) Util::traceError("Could not symlink to $path at $linkname!"); - Message::addSuccess('config-activated'); - Util::redirect('?do=SysConfig'); } } @@ -52,40 +56,122 @@ class Page_SysConfig extends Page case 'addmodule': AddModule_Base::render(); break; + case 'addconfig': + AddConfig_Base::render(); + break; case 'list': - $this->rr_list_configs(); + $this->listConfigs(); break; default: Message::addError('invalid-action', $action); break; } } - - protected function doAjax() - { - $action = Request::any('action', 'list'); - // Action: "addmodule" (upload new module) - if ($action === 'addmodule') { - $this->initAddModule(); - AddModule_Base::ajax(); - } - } - - private function rr_list_configs() + /** + * List all configurations and configuration modules. + */ + private function listConfigs() { - if (!User::hasPermission('superadmin')) { - Message::addError('no-permission'); - return; + // Configs + $res = Database::simpleQuery("SELECT configid, title, filepath FROM configtgz ORDER BY title ASC"); + $configs = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $configs[] = array( + 'configid' => $row['configid'], + 'config' => $row['title'], + 'current' => readlink('/srv/openslx/www/boot/default/config.tgz') === $row['filepath'] + ); } - $res = Database::simpleQuery("SELECT title FROM configtgz_module ORDER BY title ASC"); + // Config modules + $res = Database::simpleQuery("SELECT moduleid, title FROM configtgz_module ORDER BY title ASC"); $modules = array(); while ($row = $res->fetch(PDO::FETCH_ASSOC)) { $modules[] = array( + 'moduleid' => $row['moduleid'], 'module' => $row['title'] ); } - Render::addTemplate('page-sysconfig-main', array('modules' => $modules, 'token' => Session::get('token'))); + Render::addTemplate('page-sysconfig-main', array( + 'configs' => $configs, + 'modules' => $modules, + 'token' => Session::get('token') + )); + } + + private function activateConfig() + { + $configid = Request::post('activate', 'MISSING'); + $row = Database::queryFirst("SELECT title, filepath FROM configtgz WHERE configid = :configid LIMIT 1", array('configid' => $configid)); + if ($row === false) { + Message::addError('config-invalid', $configid); + Util::redirect('?do=SysConfig'); + } + $task = Taskmanager::submit('LinkConfigTgz', array( + 'destination' => $row['filepath'] + )); + if (isset($task['statusCode']) && $task['statusCode'] === TASK_WAITING) { + $task = Taskmanager::waitComplete($task['id']); + } + if (!isset($task['statusCode']) || $task['statusCode'] === TASK_ERROR) { + Message::addError('task-error', $task['data']['error']); + } elseif ($task['statusCode'] === TASK_FINISHED) { + Message::addSuccess('config-activated', $row['title']); + } + Util::redirect('?do=SysConfig'); + } + + private function delModule() + { + $moduleid = Request::post('del', 'MISSING'); + $row = Database::queryFirst("SELECT title, filepath FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleid)); + if ($row === false) { + Message::addError('config-invalid', $moduleid); + Util::redirect('?do=SysConfig'); + } + $existing = Database::queryFirst("SELECT title FROM configtgz_x_module" + . " INNER JOIN configtgz USING (configid)" + . " WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleid)); + if ($existing !== false) { + Message::addError('module-in-use', $row['title'], $existing['title']); + Util::redirect('?do=SysConfig'); + } + $task = Taskmanager::submit('DeleteFile', array( + 'file' => $row['filepath'] + )); + if (isset($task['statusCode']) && $task['statusCode'] === TASK_WAITING) { + $task = Taskmanager::waitComplete($task['id']); + } + if (!isset($task['statusCode']) || $task['statusCode'] === TASK_ERROR) { + Message::addWarning('task-error', $task['data']['error']); + } elseif ($task['statusCode'] === TASK_FINISHED) { + Message::addSuccess('module-deleted', $row['title']); + } + Database::exec("DELETE FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleid)); + Util::redirect('?do=SysConfig'); + } + + private function delConfig() + { + $configid = Request::post('del', 'MISSING'); + $row = Database::queryFirst("SELECT title, filepath FROM configtgz WHERE configid = :configid LIMIT 1", array('configid' => $configid)); + if ($row === false) { + Message::addError('config-invalid', $configid); + Util::redirect('?do=SysConfig'); + } + $task = Taskmanager::submit('DeleteFile', array( + 'file' => $row['filepath'] + )); + if (isset($task['statusCode']) && $task['statusCode'] === TASK_WAITING) { + $task = Taskmanager::waitComplete($task['id']); + } + if (!isset($task['statusCode']) || $task['statusCode'] === TASK_ERROR) { + Message::addWarning('task-error', $task['data']['error']); + } elseif ($task['statusCode'] === TASK_FINISHED) { + Message::addSuccess('module-deleted', $row['title']); + } + Database::exec("DELETE FROM configtgz WHERE configid = :configid LIMIT 1", array('configid' => $configid)); + Util::redirect('?do=SysConfig'); } private function initAddModule() @@ -98,5 +184,16 @@ class Page_SysConfig extends Page } AddModule_Base::setStep($step); } + + private function initAddConfig() + { + $step = Request::any('step', 0); + if ($step === 0) $step = 'AddConfig_Start'; + require_once 'modules/sysconfig/addconfig.inc.php'; + foreach (glob('modules/sysconfig/addconfig_*.inc.php') as $file) { + require_once $file; + } + AddConfig_Base::setStep($step); + } } diff --git a/modules/sysconfig/addconfig.inc.php b/modules/sysconfig/addconfig.inc.php new file mode 100644 index 00000000..6f076a12 --- /dev/null +++ b/modules/sysconfig/addconfig.inc.php @@ -0,0 +1,220 @@ +<?php + +/** + * Addconfig subpage base - makes sure + * we have the two required methods preprocess and render + */ +abstract class AddConfig_Base +{ + + /** + * + * @var array Known module types + */ + protected static $types = array( + 'AD_AUTH' => array( + 'unique' => true, + 'group' => 'Authentifizierung' + ), + 'custom' => array( + 'unique' => false, + 'group' => 'Generisch' + ) + ); + + /** + * Holds the instance for the currently executing step + * @var \AddConfig_Base + */ + private static $instance = false; + + /** + * + * @param type $step + * @return \AddConfig_Base + */ + public static function setStep($step) + { + if (empty($step) || !class_exists($step) || get_parent_class($step) !== 'AddConfig_Base') { + Message::addError('invalid-action', $step); + Util::redirect('?do=SysConfig'); + } + self::$instance = new $step(); + } + + protected function tmError() + { + Message::addError('taskmanager-error'); + Util::redirect('?do=SysConfig'); + } + + protected function taskError($status) + { + if (isset($status['data']['error'])) { + $error = $status['data']['error']; + } elseif (isset($status['statusCode'])) { + $error = $status['statusCode']; + } else { + $error = 'Unbekannter Taskmanager-Fehler'; // TODO: No text + } + Message::addError('task-error', $error); + Util::redirect('?do=SysConfig'); + } + + /** + * Called before any HTML rendering happens, so you can + * pepare stuff, validate input, and optionally redirect + * early if something is wrong, or you received post + * data etc. + */ + protected function preprocessInternal() + { + // void + } + + /** + * Do page rendering. + */ + protected function renderInternal() + { + // void + } + + /** + * Handle ajax stuff + */ + protected function ajaxInternal() + { + // void + } + + public static function preprocess() + { + if (self::$instance === false) { + Util::traceError('No step instance yet'); + } + self::$instance->preprocessInternal(); + } + + public static function render() + { + if (self::$instance === false) { + Util::traceError('No step instance yet'); + } + self::$instance->renderInternal(); + } + + public static function ajax() + { + if (self::$instance === false) { + Util::traceError('No step instance yet'); + } + self::$instance->ajaxInternal(); + } + +} + +/** + * Start dialog for adding config. Ask for title, + * show selection of modules. + */ +class AddConfig_Start extends AddConfig_Base +{ + + protected function renderInternal() + { + $res = Database::simpleQuery("SELECT moduleid, title, moduletype, filepath FROM configtgz_module" + . " ORDER BY title ASC"); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if (!isset(self::$types[$row['moduletype']])) { + self::$types[$row['moduletype']] = array( + 'unique' => false, + 'group' => 'Undefined moduletype in addconfig.inc.php' + ); + } + if (!isset(self::$types[$row['moduletype']]['modules'])) { + self::$types[$row['moduletype']]['modules'] = array(); + self::$types[$row['moduletype']]['groupid'] = $row['moduletype']; + } + if (empty($row['filepath']) || !file_exists($row['filepath'])) $row['missing'] = true; + self::$types[$row['moduletype']]['modules'][] = $row; + } + Render::addDialog('Konfiguration zusammenstellen', false, 'sysconfig/cfg-start', array( + 'token' => Session::get('token'), + 'step' => 'AddConfig_Finish', + 'groups' => array_values(self::$types) + )); + } + +} + +/** + * Start dialog for adding config. Ask for title, + * show selection of modules. + */ +class AddConfig_Finish extends AddConfig_Base +{ + private $task = false; + private $destFile = false; + private $title = false; + private $moduleids = array(); + + protected function preprocessInternal() + { + $modules = Request::post('module'); + $this->title = Request::post('title'); + if (!is_array($modules)) { + Message::addError('missing-file'); + Util::redirect('?do=SysConfig&action=addconfig'); + } + if (empty($this->title)) { + Message::addError('empty-field'); + Util::redirect('?do=SysConfig&action=addconfig'); + } + // Get all input modules + $moduleids = '0'; // Passed directly in query. Make sure no SQL injection is possible + foreach ($modules as $module) { + $moduleids .= ',' . (int)$module; // Casting to int should make it safe + } + $res = Database::simpleQuery("SELECT moduleid, filepath FROM configtgz_module WHERE moduleid IN ($moduleids)"); + $files = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $files[] = $row['filepath']; + $this->moduleids[] = $row['moduleid']; + } + // Create output file name (config.tgz) + do { + $this->destFile = CONFIG_TGZ_LIST_DIR . '/config-' . Util::sanitizeFilename($this->title) . '-' . mt_rand() . '.tgz'; + } while (file_exists($this->destFile)); + // Hand over to tm + $this->task = Taskmanager::submit('RecompressArchive', array( + 'inputFiles' => $files, + 'outputFile' => $this->destFile + )); + } + + protected function renderInternal() + { + if (isset($this->task['statusCode']) && $this->task['statusCode'] === TASK_WAITING) { + $this->task = Taskmanager::waitComplete($this->task['id']); + } + if ($this->task === false) $this->tmError(); + if (!isset($this->task['statusCode']) || $this->task['statusCode'] !== TASK_FINISHED) $this->taskError($this->task); + Database::exec("INSERT INTO configtgz (title, filepath) VALUES (:title, :filepath)", array( + 'title' => $this->title, + 'filepath' => $this->destFile + )); + $confid = Database::lastInsertId(); + foreach ($this->moduleids as $moduleid) { + Database::exec("INSERT INTO configtgz_x_module (configid, moduleid) VALUES (:configid, :moduleid)", array( + 'configid' => $confid, + 'moduleid' => $moduleid + )); + } + Render::addDialog('Konfiguration zusammenstellen', false, 'sysconfig/cfg-finish', array( + 'token' => Session::get('token'), + 'configid' => $confid + )); + } + +} diff --git a/modules/sysconfig/addmodule_ad.inc.php b/modules/sysconfig/addmodule_ad.inc.php index ab897096..ac820bbc 100644 --- a/modules/sysconfig/addmodule_ad.inc.php +++ b/modules/sysconfig/addmodule_ad.inc.php @@ -86,23 +86,14 @@ class AdModule_Finish extends AddModule_Base protected function preprocessInternal() { - $data = json_encode(array( - 'server' => Request::post('server'), - 'searchbase' => Request::post('searchbase'), - 'binddn' => Request::post('binddn'), - 'bindpw' => Request::post('bindpw'), - )); - Database::exec("INSERT INTO configtgz_module (title, moduletype, filename, contents) " - . " VALUES (:title, 'AD_AUTH', '', :content)", array( - 'title' => 'AD: ' . Request::post('server'), - 'content' => $data)); - $id = Database::lastInsertId(); - $name = CONFIG_TGZ_LIST_DIR . '/modules/AD_AUTH_id_' . $id . '.' . mt_rand() . '.tgz'; - Database::exec("UPDATE configtgz_module SET filename = :filename WHERE moduleid = :id LIMIT 1", array( - 'id' => $id, - 'filename' => $name - )); - $tgz = Taskmanager::submit('DummyTask', array()); + $config = ConfigModule::insertAdConfig('AD: ' . Request::post('server'), + Request::post('server'), + Request::post('searchbase'), + Request::post('binddn'), + Request::post('bindpw') + ); + $config['proxyip'] = Property::getServerIp(); + $tgz = Taskmanager::submit('CreateAdConfig', $config); if (isset($tgz['id'])) { $ldadp = Taskmanager::submit('DummyTask', array('parentTask' => $tgz['id'])); } @@ -111,7 +102,7 @@ class AdModule_Finish extends AddModule_Base return; } $this->taskIds = array( - 'tm-module' => $tgz['id'], + 'tm-config' => $tgz['id'], 'tm-ldadp' => $ldadp['id'] ); } diff --git a/modules/sysconfig/addmodule_custom.inc.php b/modules/sysconfig/addmodule_custom.inc.php index ec2c8af7..070c1fd4 100644 --- a/modules/sysconfig/addmodule_custom.inc.php +++ b/modules/sysconfig/addmodule_custom.inc.php @@ -20,7 +20,10 @@ class CustomModule_UploadForm extends AddModule_Base protected function renderInternal() { Session::set('mod_temp', false); - Render::addDialog('Eigenes Modul hinzufügen', false, 'sysconfig/custom-upload', array('step' => 'CustomModule_ProcessUpload')); + Render::addDialog('Eigenes Modul hinzufügen', false, 'sysconfig/custom-upload', array( + 'token' => Session::get('token'), + 'step' => 'CustomModule_ProcessUpload' + )); } } @@ -42,7 +45,7 @@ class CustomModule_ProcessUpload extends AddModule_Base Util::redirect('?do=SysConfig'); } if ($_FILES['modulefile']['error'] != UPLOAD_ERR_OK) { - Message::addError('upload-failed', $_FILES['modulefile']['name']); + Message::addError('upload-failed', Util::uploadErrorString($_FILES['modulefile']['error'])); Util::redirect('?do=SysConfig'); } $tempfile = $_FILES['modulefile']['tmp_name'] . '.tmp'; @@ -91,6 +94,7 @@ class CustomModule_ProcessUpload extends AddModule_Base } } Render::addDialog('Eigenes Modul hinzufügen', false, 'sysconfig/custom-fileselect', array( + 'token' => Session::get('token'), 'step' => 'CustomModule_CompressModule', 'files' => $list, )); @@ -114,7 +118,7 @@ class CustomModule_CompressModule extends AddModule_Base } // Recompress using task manager $this->taskId = 'tgzmod' . mt_rand() . '-' . microtime(true); - $destFile = CONFIG_TGZ_LIST_DIR . '/modules/mod-' . preg_replace('/[^a-z0-9_\-]+/is', '_', $title) . '-' . microtime(true) . '.tgz'; + $destFile = CONFIG_TGZ_LIST_DIR . '/modules/mod-' . Util::sanitizeFilename($title) . '-' . microtime(true) . '.tgz'; Taskmanager::submit('RecompressArchive', array( 'id' => $this->taskId, 'inputFiles' => array($tempfile), @@ -129,7 +133,7 @@ class CustomModule_CompressModule extends AddModule_Base $this->taskError($status); } // Seems ok, create entry in DB - $ret = Database::exec("INSERT INTO configtgz_module (title, moduletype, filename, contents) VALUES (:title, 'custom', :file, '')", + $ret = Database::exec("INSERT INTO configtgz_module (title, moduletype, filepath, contents) VALUES (:title, 'custom', :file, '')", array('title' => $title, 'file' => $destFile)); if ($ret === false) { unlink($destFile); diff --git a/script/taskmanager.js b/script/taskmanager.js index da3a0586..a78d6776 100644 --- a/script/taskmanager.js +++ b/script/taskmanager.js @@ -12,7 +12,7 @@ function tmInit() item.append('<div class="data-tm-progress"><div class="progress"><div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div></div></div>'); } if (item.is('[data-tm-log]')) { - item.append('<pre class="data-tm-log" />'); + item.append('<pre class="data-tm-log" style="display:none" />'); } item.prepend('<span class="data-tm-icon" />'); }); @@ -90,6 +90,8 @@ function tmResult(data, status) } else { icon.attr('class', 'data-tm-icon glyphicon glyphicon-trash'); } + } else { + console.log('Icon for ' + obj + ': ' + icon); } var progress = obj.find('.data-tm-progress'); if (progress) { @@ -105,6 +107,7 @@ function tmResult(data, status) var lKey = obj.attr('data-tm-log'); if (task.data && task.data[lKey]) { log.text(task.data[lKey]); + log.attr('style', (task.data[lKey] !== '' ? '' : 'display:none')); } } var cb = obj.attr('data-tm-callback'); diff --git a/style/default.css b/style/default.css index c1e5684a..2929f06d 100644 --- a/style/default.css +++ b/style/default.css @@ -69,4 +69,15 @@ body { .isdir { font-weight: bold; +} + +.slx-litehead { + margin: 5px 10px; + color: #aaa; + text-transform: uppercase; + font-weight: bold; +} + +.red { + color: red; }
\ No newline at end of file diff --git a/templates/main-menu.html b/templates/main-menu.html index 71800915..8510bfec 100644 --- a/templates/main-menu.html +++ b/templates/main-menu.html @@ -23,7 +23,6 @@ <li class="divider"></li> <li class="dropdown-header">Server</li> <li><a href="?do=ServerSetup">Grundkonfiguration</a></li> - <li><a href="?do=iPxe">iPXE</a></li> </ul> </li> </ul> diff --git a/templates/messagebox-error.html b/templates/messagebox-error.html index dc2dbc8a..63f59a96 100644 --- a/templates/messagebox-error.html +++ b/templates/messagebox-error.html @@ -1 +1 @@ -<div class="alert alert-danger">{{message}}</div> +<div class="alert alert-danger">{{{message}}}</div> diff --git a/templates/messagebox-info.html b/templates/messagebox-info.html index b4464094..7136298c 100644 --- a/templates/messagebox-info.html +++ b/templates/messagebox-info.html @@ -1 +1 @@ -<div class="alert alert-info">{{message}}</div> +<div class="alert alert-info">{{{message}}}</div> diff --git a/templates/messagebox-success.html b/templates/messagebox-success.html index 180a0466..3fa263ef 100644 --- a/templates/messagebox-success.html +++ b/templates/messagebox-success.html @@ -1 +1 @@ -<div class="alert alert-success">{{message}}</div> +<div class="alert alert-success">{{{message}}}</div> diff --git a/templates/messagebox-warning.html b/templates/messagebox-warning.html index 1ce2c0e4..48a95b4d 100644 --- a/templates/messagebox-warning.html +++ b/templates/messagebox-warning.html @@ -1 +1 @@ -<div class="alert alert-warning">{{message}}</div> +<div class="alert alert-warning">{{{message}}}</div> diff --git a/templates/page-ipxe.html b/templates/page-ipxe.html deleted file mode 100644 index ba56582e..00000000 --- a/templates/page-ipxe.html +++ /dev/null @@ -1,21 +0,0 @@ -<div class="container"> - <div class="panel panel-default"> - <div class="panel-heading"> - Bitte die IP-Adresse auswählen, über die der Server von den Clients angesprochen wird. - </div> - {{#ips}} - <div class="panel-body">{{ip}} - <span> - <a class="btn btn-success" href="#" onclick="this.parentNode.style.display='none';loadContent('#compiler', 'api.php?do=exec&type=ipxe&ip={{ip}}&id=compiler&default=hddboot')">Kompilieren (HDD Default)</a> - <a class="btn btn-success" href="#" onclick="this.parentNode.style.display='none';loadContent('#compiler', 'api.php?do=exec&type=ipxe&ip={{ip}}&id=compiler&default=openslx')">Kompilieren (OpenSLX Default)</a> - {{#current}}(Aktuelle Konfiguration){{/current}} - </span> - </div> - {{/ips}} - </div> - <div id="compiler"> - </div> - {{^ips}} - <div class="alert alert-danger">Konnte lokale IP-Adressen nicht ermitteln.</div> - {{/ips}} -</div> diff --git a/templates/page-serversetup.html b/templates/page-serversetup.html deleted file mode 100644 index 2f900a03..00000000 --- a/templates/page-serversetup.html +++ /dev/null @@ -1,30 +0,0 @@ -<div class="panel panel-default"> - <div class="panel-heading"> - Boot-Adresse des Servers - </div> - <div class="panel-body"> - <p> - Bitte wählen Sie die IP-Adresse, über die der Server von den Clients zum Booten angesprochen werden soll. - </p> - <form method="post"> - <input type="hidden" name="token" value="{{token}}"> - <table> - {{#ips}} - <tr> - <td>{{ip}}</td> - {{#default}} - <td> - <span class="btn btn-success btn-xs"><span class="glyphicon glyphicon-ok"></span> Aktiv</span> - </td> - {{/default}} - {{^default}} - <td> - <button class="btn btn-primary btn-xs" name="ip" value="{{ip}}"><span class="glyphicon glyphicon-flag"></span> Setzen</button> - </td> - {{/default}} - </tr> - {{/ips}} - </table> - </form> - </div> -</div>
\ No newline at end of file diff --git a/templates/page-sysconfig-main.html b/templates/page-sysconfig-main.html index 32b3a6bd..447be0bb 100644 --- a/templates/page-sysconfig-main.html +++ b/templates/page-sysconfig-main.html @@ -8,26 +8,44 @@ Verfügbare Systemkonfigurationen <a class="btn btn-default" data-toggle="modal" data-target="#help-config"><span class="glyphicon glyphicon-question-sign"></span></a> </div> - <table class="table table-condensed"> - {{#files}} - <tr> - <td class=col-md-8">{{file}}</td> - <td class="col-md-4"> - {{^current}} - <a class="btn btn-primary" href="?do=SysConfig&action=activate&file={{file}}&token={{token}}">Aktivieren</a> - {{/current}} - {{#current}} - <span class="btn btn-success">Bereits aktiv</span> - {{/current}} - </td> - </tr> - {{/files}} - </table> - {{^files}} - <div class="alert alert-warning">Keine Systemkonfigurationen gefunden!</div> - {{/files}} <div class="panel-body"> - <a class="btn btn-primary">Neue Konfiguration zusammenstellen</a> + <form method="post" action="?do=SysConfig"> + <input type="hidden" name="token" value="{{token}}"> + <input type="hidden" name="action" value="config"> + <table> + {{#configs}} + <tr> + <td>{{config}}</td> + <td> + {{^current}} + <button class="btn btn-primary btn-xs" name="activate" value="{{configid}}"> + <span class="glyphicon glyphicon-flag"></span> + Aktivieren + </button> + {{/current}} + {{#current}} + <span class="btn btn-success btn-xs"> + <span class="glyphicon glyphicon-ok"></span> + Aktiv + </span> + {{/current}} + </td> + <td> + <button class="btn btn-danger btn-xs" name="del" value="{{configid}}"><span class="glyphicon glyphicon-trash"></span> Löschen</button> + </td> + </tr> + {{/configs}} + </table> + {{^configs}} + <div class="alert alert-warning"> + Keine Systemkonfigurationen gefunden. + <br>Erstellen Sie eine neue Konfiguration aus den unten aufgeführten Konfigurationsmodulen. + </div> + {{/configs}} + </form> + </div> + <div class="panel-footer"> + <a class="btn btn-primary" href="?do=SysConfig&action=addconfig">Neue Konfiguration</a> </div> </div> <div class="panel panel-default"> @@ -35,22 +53,30 @@ Verfügbare Konfigurationsmodule <a class="btn btn-default" data-toggle="modal" data-target="#help-module"><span class="glyphicon glyphicon-question-sign"></span></a> </div> - <table class="table table-condensed"> - {{#modules}} - <tr> - <td>{{module}}</td> - <td nowrap> - <a class="btn btn-default btn-xs"><span class="glyphicon glyphicon-edit"></span> Bearbeiten</a> - <a class="btn btn-danger btn-xs"><span class="glyphicon glyphicon-trash"></span> Löschen</a> - </td> - </tr> - {{/modules}} - </table> - {{^modules}} - <div class="alert alert-warning">Keine Konfigurationsmodule gefunden!</div> - {{/modules}} <div class="panel-body"> - <a class="btn btn-primary" href="?do=SysConfig&action=addmodule">Neues Modul erstellen</a> + <form method="post" action="?do=SysConfig"> + <input type="hidden" name="token" value="{{token}}"> + <input type="hidden" name="action" value="module"> + <table> + {{#modules}} + <tr> + <td>{{module}}</td> + <td> + <!-- a class="btn btn-default btn-xs"><span class="glyphicon glyphicon-edit"></span> Bearbeiten</a --> + </td> + <td> + <button class="btn btn-danger btn-xs" name="del" value="{{moduleid}}"><span class="glyphicon glyphicon-trash"></span> Löschen</button> + </td> + </tr> + {{/modules}} + </table> + {{^modules}} + <div class="alert alert-warning">Keine Konfigurationsmodule gefunden!</div> + {{/modules}} + </form> + </div> + <div class="panel-footer"> + <a class="btn btn-primary" href="?do=SysConfig&action=addmodule">Neues Modul</a> </div> </div> </div> diff --git a/templates/serversetup/ipaddress.html b/templates/serversetup/ipaddress.html new file mode 100644 index 00000000..a9048dcf --- /dev/null +++ b/templates/serversetup/ipaddress.html @@ -0,0 +1,32 @@ +<div class="container"> + <div class="panel panel-default"> + <div class="panel-heading"> + Boot-Adresse des Servers + </div> + <div class="panel-body"> + <p> + Bitte wählen Sie die IP-Adresse, über die der Server von den Clients zum Booten angesprochen werden soll. + </p> + <form method="post"> + <input type="hidden" name="token" value="{{token}}"> + <table> + {{#ips}} + <tr> + <td>{{ip}}</td> + {{#default}} + <td> + <span class="btn btn-success btn-xs"><span class="glyphicon glyphicon-ok"></span> Aktiv</span> + </td> + {{/default}} + {{^default}} + <td> + <button class="btn btn-primary btn-xs" name="ip" value="{{ip}}"><span class="glyphicon glyphicon-flag"></span> Setzen</button> + </td> + {{/default}} + </tr> + {{/ips}} + </table> + </form> + </div> + </div> +</div>
\ No newline at end of file diff --git a/templates/serversetup/ipxe.html b/templates/serversetup/ipxe.html new file mode 100644 index 00000000..9fc83a40 --- /dev/null +++ b/templates/serversetup/ipxe.html @@ -0,0 +1,21 @@ +<div class="container"> + <div class="panel panel-default"> + <div class="panel-heading"> + iPXE Menü + </div> + <div class="panel-body"> + <p> + Das iPXE-Menü muss nach einer Änderung der IP-Adresse neu generiert werden. In der Regel geschieht dies + automatisch, der Vorgang kann hier allerdings auch manuell ausgelöst werden. In diesem Feld sehen Sie außerdem + die Log-Ausgaben der letzten Ausführung, falls noch im Cache. + </p> + <div data-tm-id="{{taskid}}" data-tm-log="output">Status</div> + </div> + <div class="panel-footer"> + <form method="post"> + <input type="hidden" name="token" value="{{token}}"> + <button class="btn btn-primary" name="action" value="ipxe">Bootmenü erzeugen</button> + </form> + </div> + </div> +</div>
\ No newline at end of file diff --git a/templates/sysconfig/ad-finish.html b/templates/sysconfig/ad-finish.html index f20a2ce1..e79e4262 100644 --- a/templates/sysconfig/ad-finish.html +++ b/templates/sysconfig/ad-finish.html @@ -3,8 +3,8 @@ </p> <div id="zeug"> - <div data-tm-id="{{tm-tgz}}" data-tm-log="message">Konfiguration erzeugen</div> - <div data-tm-id="{{tm-ldadp}}" data-tm-log="message" data-tm-callback="ldapCb">ldadp starten</div> + <div data-tm-id="{{tm-config}}" data-tm-log="error">Konfiguration erzeugen</div> + <div data-tm-id="{{tm-ldadp}}" data-tm-log="error" data-tm-callback="ldapCb">ldadp starten</div> </div> <br> <div id="back" class="pull-left" style="display:none"> diff --git a/templates/sysconfig/cfg-finish.html b/templates/sysconfig/cfg-finish.html new file mode 100644 index 00000000..2bf63420 --- /dev/null +++ b/templates/sysconfig/cfg-finish.html @@ -0,0 +1,12 @@ +<p> + Die Konfiguration wurde erfolgreich erstellt. +</p> + +<form role="form" method="post" action="?do=SysConfig"> + <input type="hidden" name="token" value="{{token}}"> + <input type="hidden" name="action" value="config"> + <input type="hidden" name="activate" value="{{configid}}"> + <div class="pull-left"> + <button type="submit" class="btn btn-primary">Konfiguration aktivieren</button> + </div> +</form> diff --git a/templates/sysconfig/cfg-start.html b/templates/sysconfig/cfg-start.html new file mode 100644 index 00000000..b6eaf9c9 --- /dev/null +++ b/templates/sysconfig/cfg-start.html @@ -0,0 +1,35 @@ +<form role="form" method="post" action="?do=SysConfig&action=addconfig&step={{step}}"> + <input type="hidden" name="token" value="{{token}}"> + <div class="input-group"> + <span class="input-group-addon">Name</span> + <input type="text" name="title" class="form-control" placeholder="Meine Konfiguration" autofocus="autofocus"> + </div> + <hr> + <p>Bitte wählen Sie, welche Module für diese Konfiguration verwendet werden sollen.</p> + {{#groups}} + <div class="panel panel-default"> + <div class="slx-litehead">{{group}}</div> + <div class="panel-body"> + {{#modules}} + <div class="input-group"> + <span class="input-group-addon"> + {{#unique}} + <input type="radio" name="module[{{groupid}}]" value="{{moduleid}}"> + {{/unique}} + {{^unique}} + <input type="checkbox" name="module[{{moduleid}}]" value="{{moduleid}}"> + {{/unique}} + </span> + <span class="form-control">{{title}}</span> + {{#missing}} + <span class="input-group-addon" title="Modul beschädigt! Bitte neu generieren."><span class="red glyphicon glyphicon-exclamation-sign"></span></span> + {{/missing}} + </div> + {{/modules}} + </div> + </div> + {{/groups}} + <div class="pull-right"> + <button type="submit" class="btn btn-primary">Weiter »</button> + </div> +</form> diff --git a/templates/sysconfig/custom-fileselect.html b/templates/sysconfig/custom-fileselect.html index 5a0a26f3..21537c49 100644 --- a/templates/sysconfig/custom-fileselect.html +++ b/templates/sysconfig/custom-fileselect.html @@ -1,12 +1,12 @@ <form role="form" method="post" action="?do=SysConfig&action=addmodule&step={{step}}"> <input type="hidden" name="modid" value="{{modid}}"> + <input type="hidden" name="token" value="{{token}}"> <div class="input-group"> <span class="input-group-addon">Modulname</span> <input type="text" name="title" class="form-control" placeholder="Mein Konfigurationsmodul" autofocus="autofocus"> </div> <hr> - <p>Hier haben Sie die Möglichkeit, den Inhalt des Archivs noch einmal zu überprüfen, und - die Liste der zu übernehmenden Dateien bei Bedarf noch einschränken.</p> + <p>Hier haben Sie die Möglichkeit, den Inhalt des Archivs noch einmal zu überprüfen.</p> <table class="table table-bordered table-condensed"> {{#files}} <tr> diff --git a/templates/sysconfig/custom-upload.html b/templates/sysconfig/custom-upload.html index c5a43522..1f312009 100644 --- a/templates/sysconfig/custom-upload.html +++ b/templates/sysconfig/custom-upload.html @@ -7,6 +7,7 @@ so wird auf einem gebooteten Client diese Datei als <strong>/etc/beispiel.conf</strong> zu finden sein.</p> <form role="form" enctype="multipart/form-data" method="post" action="?do=SysConfig&action=addmodule&step={{step}}"> + <input type="hidden" name="token" value="{{token}}"> <div class="input-group"> <span class="input-group-addon">Archiv</span> <input class="form-control" type="file" name="modulefile"> |