summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2014-05-23 20:49:02 +0200
committerSimon Rettberg2014-05-23 20:49:02 +0200
commitfe6ac16498b05d0f0c8ed7fda394273815d3d6da (patch)
treecadf5f103ef3db7ba1b40d59d85937c998aad22f
parentServer Setup page (diff)
downloadslx-admin-fe6ac16498b05d0f0c8ed7fda394273815d3d6da.tar.gz
slx-admin-fe6ac16498b05d0f0c8ed7fda394273815d3d6da.tar.xz
slx-admin-fe6ac16498b05d0f0c8ed7fda394273815d3d6da.zip
Stuff (WIP)
-rw-r--r--inc/configmodule.inc.php50
-rw-r--r--inc/database.inc.php1
-rw-r--r--inc/message.inc.php9
-rw-r--r--inc/property.inc.php22
-rw-r--r--inc/taskmanager.inc.php7
-rw-r--r--inc/trigger.inc.php40
-rw-r--r--inc/util.inc.php43
-rw-r--r--modules/adduser.inc.php88
-rw-r--r--modules/ipxe.inc.php40
-rw-r--r--modules/serversetup.inc.php11
-rw-r--r--modules/sysconfig.inc.php173
-rw-r--r--modules/sysconfig/addconfig.inc.php220
-rw-r--r--modules/sysconfig/addmodule_ad.inc.php27
-rw-r--r--modules/sysconfig/addmodule_custom.inc.php12
-rw-r--r--script/taskmanager.js5
-rw-r--r--style/default.css11
-rw-r--r--templates/main-menu.html1
-rw-r--r--templates/messagebox-error.html2
-rw-r--r--templates/messagebox-info.html2
-rw-r--r--templates/messagebox-success.html2
-rw-r--r--templates/messagebox-warning.html2
-rw-r--r--templates/page-ipxe.html21
-rw-r--r--templates/page-serversetup.html30
-rw-r--r--templates/page-sysconfig-main.html94
-rw-r--r--templates/serversetup/ipaddress.html32
-rw-r--r--templates/serversetup/ipxe.html21
-rw-r--r--templates/sysconfig/ad-finish.html4
-rw-r--r--templates/sysconfig/cfg-finish.html12
-rw-r--r--templates/sysconfig/cfg-start.html35
-rw-r--r--templates/sysconfig/custom-fileselect.html4
-rw-r--r--templates/sysconfig/custom-upload.html1
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&amp;type=ipxe&amp;ip={{ip}}&amp;id=compiler&amp;default=hddboot')">Kompilieren (HDD Default)</a>
- <a class="btn btn-success" href="#" onclick="this.parentNode.style.display='none';loadContent('#compiler', 'api.php?do=exec&amp;type=ipxe&amp;ip={{ip}}&amp;id=compiler&amp;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&amp;action=activate&amp;file={{file}}&amp;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&amp;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&amp;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&amp;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&amp;action=addconfig&amp;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 &raquo;</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&amp;action=addmodule&amp;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&amp;action=addmodule&amp;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">