summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2014-05-15 18:28:24 +0200
committerSimon Rettberg2014-05-15 18:28:24 +0200
commit63c0cf521f8097b0dadaf1228176dc38c7d897f6 (patch)
tree83f5da6dc130ac7db575b0eee41ed6c7a2f994fb
parentFix handle leak in downloading, better error reporting on failed downloads, a... (diff)
downloadslx-admin-63c0cf521f8097b0dadaf1228176dc38c7d897f6.tar.gz
slx-admin-63c0cf521f8097b0dadaf1228176dc38c7d897f6.tar.xz
slx-admin-63c0cf521f8097b0dadaf1228176dc38c7d897f6.zip
Working on config.tgz composition through config modules
-rw-r--r--api.php2
-rw-r--r--inc/database.inc.php (renamed from inc/db.inc.php)31
-rw-r--r--inc/message.inc.php14
-rw-r--r--inc/render.inc.php18
-rw-r--r--inc/request.inc.php45
-rw-r--r--inc/session.inc.php6
-rw-r--r--inc/taskmanager.inc.php86
-rw-r--r--inc/util.inc.php19
-rw-r--r--index.php38
-rw-r--r--modules/sysconfig.inc.php81
-rw-r--r--modules/sysconfig/addmodule.inc.php196
-rw-r--r--modules/syslog.inc.php35
-rw-r--r--script/custom.js11
-rw-r--r--style/default.css3
-rw-r--r--templates/dialog-generic.html15
-rw-r--r--templates/page-sysconfig-main.html86
-rw-r--r--templates/page-tgz-list.html33
-rw-r--r--templates/sysconfig/custom-fileselect.html26
-rw-r--r--templates/sysconfig/custom-upload.html16
19 files changed, 642 insertions, 119 deletions
diff --git a/api.php b/api.php
index 9491cd37..16408440 100644
--- a/api.php
+++ b/api.php
@@ -4,7 +4,7 @@ error_reporting(E_ALL);
require_once('inc/user.inc.php');
require_once('inc/util.inc.php');
-require_once('inc/db.inc.php');
+require_once('inc/database.inc.php');
require_once('inc/permission.inc.php');
require_once('inc/crypto.inc.php');
require_once('inc/validator.inc.php');
diff --git a/inc/db.inc.php b/inc/database.inc.php
index a797ae93..70f50116 100644
--- a/inc/db.inc.php
+++ b/inc/database.inc.php
@@ -43,6 +43,16 @@ class Database
if ($res === false) return false;
return $res->rowCount();
}
+
+ /**
+ * Get id (promary key) of last row inserted.
+ *
+ * @return int the id
+ */
+ public static function lastInsertId()
+ {
+ return self::$dbh->lastInsertId();
+ }
/**
* Execute the given query and return the corresponding PDOStatement object
@@ -53,16 +63,19 @@ class Database
public static function simpleQuery($query, $args = array())
{
self::init();
- //if (empty($args)) Util::traceError('Query with zero arguments!');
- if (!isset(self::$statements[$query])) {
- self::$statements[$query] = self::$dbh->prepare($query);
- } else {
- self::$statements[$query]->closeCursor();
- }
- if (self::$statements[$query]->execute($args) === false) {
- Util::traceError("Database Error: \n" . implode("\n", self::$statements[$query]->errorInfo()));
+ try {
+ if (!isset(self::$statements[$query])) {
+ self::$statements[$query] = self::$dbh->prepare($query);
+ } else {
+ self::$statements[$query]->closeCursor();
+ }
+ if (self::$statements[$query]->execute($args) === false) {
+ Util::traceError("Database Error: \n" . implode("\n", self::$statements[$query]->errorInfo()));
+ }
+ return self::$statements[$query];
+ } catch (Exception $e) {
+ return false;
}
- return self::$statements[$query];
}
/**
diff --git a/inc/message.inc.php b/inc/message.inc.php
index 3117630b..6c95764c 100644
--- a/inc/message.inc.php
+++ b/inc/message.inc.php
@@ -1,6 +1,7 @@
<?php
// TODO: Move to extra file
+global $error_text;
$error_text = array(
'loginfail' => 'Benutzername oder Kennwort falsch',
'token' => 'Ungültiges Token. CSRF Angriff?',
@@ -21,11 +22,21 @@ $error_text = array(
'upload-failed' => 'Upload von {{0}} schlug fehl!',
'config-activated' => 'Konfiguration wurde aktiviert',
'error-write' => 'Fehler beim Schreiben von {{0}}',
+ 'error-read' => 'Fehler beim Lesen von {{0}}',
+ 'error-archive' => 'Korruptes Archiv oder nicht unterstütztes Format',
+ 'error-rename' => 'Konnte {{0}} nicht in {{1}} umbenennen',
+ 'error-nodir' => 'Das Verzeichnis {{0}} existiert nicht.',
+ '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',
+ 'taskmanager-error' => 'Verbindung zum Taskmanager fehlgeschlagen',
+ 'task-error' => 'Ausführung fehlgeschlagen: {{0}}',
);
class Message
{
private static $list = array();
+ private static $alreadyDisplayed = array();
private static $flushed = false;
/**
@@ -83,6 +94,7 @@ class Message
$message = str_replace('{{' . $index . '}}', $text, $message);
}
Render::addTemplate('messagebox-' . $item['type'], array('message' => $message));
+ self::$alreadyDisplayed[] = $item;
}
self::$list = array();
self::$flushed = true;
@@ -109,7 +121,7 @@ class Message
public static function toRequest()
{
$parts = array();
- foreach (self::$list as $item) {
+ foreach (array_merge(self::$list, self::$alreadyDisplayed) as $item) {
$str = 'message[]=' . urlencode($item['type'] . '|' .$item['id']);
if (!empty($item['params'])) {
$str .= '|' . implode('|', $item['params']);
diff --git a/inc/render.inc.php b/inc/render.inc.php
index a3cef516..dff32798 100644
--- a/inc/render.inc.php
+++ b/inc/render.inc.php
@@ -39,6 +39,7 @@ class Render
<html>
<head>
<title>', RENDER_DEFAULT_TITLE, self::$title, '</title>
+ <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Bootstrap -->
<link href="style/bootstrap.min.css" rel="stylesheet" media="screen">
@@ -93,6 +94,23 @@ class Render
{
self::$body .= self::$mustache->render(self::getTemplate($template), $params);
}
+
+ /**
+ * Add a dialog to the page output.
+ *
+ * @param string $title Title of the dialog window
+ * @param boolean $next URL to next dialog step, or false to hide the next button
+ * @param string $template template used to fill the dialog body
+ * @param array $params parameters for rendering the body template
+ */
+ public static function addDialog($title, $next, $template, $params = false)
+ {
+ self::addTemplate('dialog-generic', array(
+ 'title' => $title,
+ 'next' => $next,
+ 'body' => self::$mustache->render(self::getTemplate($template), $params)
+ ));
+ }
/**
* Add error message to page
diff --git a/inc/request.inc.php b/inc/request.inc.php
new file mode 100644
index 00000000..bb212dfd
--- /dev/null
+++ b/inc/request.inc.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * Wrapper for getting fields from the request (GET, POST, ...)
+ */
+class Request
+{
+
+ /**
+ *
+ * @param string $key Key of field to get from $_GET
+ * @param string $default Value to return if $_GET does not contain $key
+ * @return mixed Field from $_GET, or $default if not set
+ */
+ public static function get($key, $default = false)
+ {
+ if (!isset($_GET[$key])) return $default;
+ return $_GET[$key];
+ }
+
+ /**
+ *
+ * @param string $key Key of field to get from $_POST
+ * @param string $default Value to return if $_POST does not contain $key
+ * @return mixed Field from $_POST, or $default if not set
+ */
+ public static function post($key, $default = false)
+ {
+ if (!isset($_POST[$key])) return $default;
+ return $_POST[$key];
+ }
+
+ /**
+ *
+ * @param string $key Key of field to get from $_REQUEST
+ * @param string $default Value to return if $_REQUEST does not contain $key
+ * @return mixed Field from $_REQUEST, or $default if not set
+ */
+ public static function any($key, $default = false)
+ {
+ if (!isset($_REQUEST[$key])) return $default;
+ return $_REQUEST[$key];
+ }
+
+}
diff --git a/inc/session.inc.php b/inc/session.inc.php
index 3ba614f2..a0f8ab4c 100644
--- a/inc/session.inc.php
+++ b/inc/session.inc.php
@@ -52,7 +52,11 @@ class Session
public static function set($key, $value)
{
if (self::$data === false) Util::traceError('Tried to set session data with no active session');
- self::$data[$key] = $value;
+ if ($value === false) {
+ unset(self::$data[$key]);
+ } else {
+ self::$data[$key] = $value;
+ }
}
private static function loadSessionId()
diff --git a/inc/taskmanager.inc.php b/inc/taskmanager.inc.php
new file mode 100644
index 00000000..f2f337be
--- /dev/null
+++ b/inc/taskmanager.inc.php
@@ -0,0 +1,86 @@
+<?php
+
+/**
+ * Interface to the external task manager.
+ */
+class Taskmanager
+{
+
+ private static $sock = false;
+
+ private static function init()
+ {
+ if (self::$sock !== false) return;
+ self::$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+ socket_set_option(self::$sock, SOL_SOCKET, SO_RCVTIMEO, array('sec' => 0, 'usec' => 100000));
+ socket_set_option(self::$sock, SOL_SOCKET, SO_SNDTIMEO, array('sec' => 0, 'usec' => 100000));
+ socket_connect(self::$sock, '127.0.0.1', 9215);
+ }
+
+ public static function submit($task, $data, $async)
+ {
+ self::init();
+ $seq = (string)mt_rand();
+ $data = json_encode($data);
+ $message = "$seq, $task, $data";
+ $sent = socket_send(self::$sock, $message, strlen($message), 0);
+ if ($async) return true;
+ $reply = self::readReply($seq);
+ if (!is_array($reply)) return false;
+ return $reply;
+ }
+
+ public static function status($taskId)
+ {
+ self::init();
+ $seq = (string)mt_rand();
+ $message = "$seq, status, $taskId";
+ $sent = socket_send(self::$sock, $message, strlen($message), 0);
+ $reply = self::readReply($seq);
+ if (!is_array($reply)) return false;
+ return $reply;
+ }
+
+ public static function waitComplete($taskId)
+ {
+ 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;
+ usleep(150000);
+ }
+ return $status;
+ }
+
+ public static function release($taskId)
+ {
+ self::init();
+ $seq = (string)mt_rand();
+ $message = "$seq, release, $taskId";
+ socket_send(self::$sock, $message, strlen($message), 0);
+ }
+
+ /**
+ *
+ * @param type $seq
+ * @return mixed the decoded json data for that message as an array, or null on error
+ */
+ private static function readReply($seq)
+ {
+ $tries = 0;
+ while (($bytes = socket_recvfrom(self::$sock, $buf, 90000, 0, $bla1, $bla2)) !== false) {
+ $parts = explode(',', $buf, 2);
+ if (count($parts) == 2 && $parts[0] == $seq) {
+ return json_decode($parts[1], true);
+ }
+ if (++$tries > 10) return false;
+ }
+ //error_log(socket_strerror(socket_last_error(self::$sock)));
+ return false;
+ }
+
+}
+
+foreach (array('TASK_FINISHED', 'TASK_ERROR', 'TASK_WAITING', 'NO_SUCH_TASK', 'TASK_PROCESSING') as $i) {
+ define($i, $i);
+}
diff --git a/inc/util.inc.php b/inc/util.inc.php
index 67e4b73d..8235edd0 100644
--- a/inc/util.inc.php
+++ b/inc/util.inc.php
@@ -150,6 +150,25 @@ class Util
}
return true;
}
+
+ /**
+ * Convert given number to human readable file size string.
+ * Will append Bytes, KiB, etc. depending on magnitude of number.
+ *
+ * @param type $bytes numeric value of the filesize to make readable
+ * @param type $decimals number of decimals to show, -1 for automatic
+ * @return type human readable string representing the given filesize
+ */
+ public static function readableFileSize($bytes, $decimals = -1) {
+ static $sz = array('Byte', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB');
+ $factor = floor((strlen($bytes) - 1) / 3);
+ if ($factor == 0) {
+ $decimals = 0;
+ } elseif ($decimals === -1) {
+ $decimals = 2 - floor((strlen($bytes) - 1) % 3);
+ }
+ return sprintf("%.{$decimals}f ", $bytes / pow(1024, $factor)) . $sz[$factor];
+ }
}
diff --git a/index.php b/index.php
index d693b9e8..d50d1d71 100644
--- a/index.php
+++ b/index.php
@@ -2,37 +2,41 @@
error_reporting(E_ALL);
-require_once('inc/user.inc.php');
-require_once('inc/render.inc.php');
-require_once('inc/menu.inc.php');
-require_once('inc/util.inc.php');
-require_once('inc/message.inc.php');
-require_once('inc/db.inc.php');
-require_once('inc/permission.inc.php');
-require_once('inc/crypto.inc.php');
-require_once('inc/validator.inc.php');
+// Autoload classes from ./inc which adhere to naming scheme <lowercasename>.inc.php
+function slxAutoloader($class) {
+ $file = 'inc/' . preg_replace('/[^a-z0-9]/', '', mb_strtolower($class)) . '.inc.php';
+ if (!file_exists($file)) return;
+ require_once $file;
+}
+
+spl_autoload_register('slxAutoloader');
if (empty($_REQUEST['do'])) {
// No specific module - set default
- $module = 'main';
+ $moduleName = 'main';
} else {
- $module = preg_replace('/[^a-z]/', '', $_REQUEST['do']);
+ $moduleName = preg_replace('/[^a-z]/', '', $_REQUEST['do']);
}
-$module = 'modules/' . $module . '.inc.php';
+$modulePath = 'modules/' . $moduleName . '.inc.php';
-if (!file_exists($module)) {
- Util::traceError('Invalid module: ' . $module);
+if (!file_exists($modulePath)) {
+ Util::traceError('Invalid module: ' . $moduleName);
}
-// Display any messages
+// Deserialize any messages
if (isset($_REQUEST['message'])) {
Message::fromRequest();
}
+// CSRF/XSS
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && !Util::verifyToken()) {
+ Util::redirect('?do=' . $moduleName);
+}
+
// Load module - it will execute pre-processing, or act upon request parameters
-require_once($module);
-unset($module);
+require_once($modulePath);
+unset($modulePath);
// Main menu
$menu = new Menu;
diff --git a/modules/sysconfig.inc.php b/modules/sysconfig.inc.php
index c883eb68..faa26787 100644
--- a/modules/sysconfig.inc.php
+++ b/modules/sysconfig.inc.php
@@ -1,37 +1,29 @@
<?php
+/*
+@include_once('Archive/Tar.php');
+if (!class_exists('Archive_Tar')) {
+ Message::addError('Broken php installation: pear extension Archive_Tar missing!');
+ Util::redirect('?do=main');
+}
+ */
+
User::load();
-if (isset($_POST['action']) && $_POST['action'] === 'upload') {
- if (!Util::verifyToken()) {
- Util::redirect('?do=sysconfig');
- }
- if (!User::hasPermission('superadmin')) {
- Message::addError('no-permission');
- Util::redirect('?do=sysconfig');
- }
- if (!isset($_FILES['customtgz'])) {
- Message::addError('missing-file');
- Util::redirect('?do=sysconfig');
- }
- $dest = $_FILES['customtgz']['name'];
- $dest = preg_replace('/[^a-z0-9\-_]/', '', $dest);
- $dest = substr($dest, 0, 30);
- if (substr($dest, -3) === 'tgz') $dest = substr($dest, 0, -3);
- $dest .= '.tgz';
- # TODO: Validate its a (compressed) tar?
- if (move_uploaded_file($_FILES['customtgz']['tmp_name'], CONFIG_TGZ_LIST_DIR . '/' . $dest)) {
- Message::addSuccess('upload-complete', $dest);
- } else {
- Message::addError('upload-failed', $dest);
- }
- Util::redirect('?do=sysconfig');
+// Read request vars
+$action = Request::any('action', 'list');
+$step = Request::any('step', 0);
+$nextStep = $step + 1;
+
+// Action: "addmodule" (upload new module)
+if ($action === 'addmodule') {
+ require_once 'modules/sysconfig/addmodule.inc.php';
+ $handler = AddModule_Base::get($step);
+ $handler->preprocess();
}
-if (isset($_REQUEST['action']) && $_REQUEST['action'] === 'activate') {
- if (!Util::verifyToken()) {
- Util::redirect('?do=sysconfig');
- }
+// Action "activate" (set sysconfig as active)
+if ($action === 'activate') {
if (!User::hasPermission('superadmin')) {
Message::addError('no-permission');
Util::redirect('?do=sysconfig');
@@ -55,15 +47,19 @@ if (isset($_REQUEST['action']) && $_REQUEST['action'] === 'activate') {
Util::redirect('?do=sysconfig');
}
+/**
+ * Render module; called by main script when this module page should render
+ * its content.
+ */
function render_module()
{
- if (!isset($_REQUEST['action'])) $_REQUEST['action'] = 'list';
- switch ($_REQUEST['action']) {
- case 'remotelist':
- list_remote_configs();
+ global $action, $handler;
+ switch ($action) {
+ case 'addmodule':
+ $handler->render();
break;
case 'list':
- list_configs();
+ rr_list_configs();
break;
default:
Message::addError('invalid-action', $_REQUEST['action']);
@@ -71,25 +67,23 @@ function render_module()
}
}
-function list_configs()
+function rr_list_configs()
{
if (!User::hasPermission('superadmin')) {
Message::addError('no-permission');
return;
}
- $current = '<none>';
- if (file_exists(CONFIG_HTTP_DIR . '/default/config.tgz')) $current = realpath(CONFIG_HTTP_DIR . '/default/config.tgz');
- $files = array();
- foreach (glob(CONFIG_TGZ_LIST_DIR . '/*.tgz') as $file) {
- $files[] = array(
- 'file' => basename($file),
- 'current' => ($current === realpath($file))
+ $res = Database::simpleQuery("SELECT title FROM configtgz_module ORDER BY title ASC");
+ $modules = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $modules[] = array(
+ 'module' => $row['title']
);
}
- Render::addTemplate('page-tgz-list', array('files' => $files, 'token' => Session::get('token')));
+ Render::addTemplate('page-sysconfig-main', array('modules' => $modules, 'token' => Session::get('token')));
}
-function list_remote_configs()
+function rr_list_remote_configs()
{
if (!User::hasPermission('superadmin')) {
Message::addError('no-permission');
@@ -111,4 +105,3 @@ function list_remote_configs()
}
Render::addTemplate('page-remote-tgz-list', array('files' => $list));
}
-
diff --git a/modules/sysconfig/addmodule.inc.php b/modules/sysconfig/addmodule.inc.php
new file mode 100644
index 00000000..aca2f762
--- /dev/null
+++ b/modules/sysconfig/addmodule.inc.php
@@ -0,0 +1,196 @@
+<?php
+
+/**
+ * Addmodule subpage base - makes sure
+ * we have the two required methods preprocess and render
+ */
+abstract class AddModule_Base
+{
+
+ /**
+ *
+ * @param type $step
+ * @return \AddModule_Base
+ */
+ public static function get($step)
+ {
+ switch ($step) {
+ case 0: // Upload form
+ return new AddModule_UploadForm();
+ case 1: // Handle config module uploading
+ return new AddModule_ProcessUpload();
+ case 2: // ?
+ return new AddModule_CompressModule();
+ }
+ Message::addError('invalid-action', $step);
+ Util::redirect('?do=sysconfig');
+ }
+
+ 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.
+ */
+ public function preprocess()
+ {
+ // void
+ }
+
+ /**
+ * Do page rendering
+ */
+ public function render()
+ {
+ // void
+ }
+
+}
+
+class AddModule_UploadForm extends AddModule_Base
+{
+
+ public function render()
+ {
+ global $nextStep;
+ Session::set('mod_temp', false);
+ Render::addDialog('Eigenes Modul hinzufügen', false, 'sysconfig/custom-upload', array('step' => $nextStep));
+ }
+
+}
+
+/**
+ * Some file has just been uploaded. Try to store it, then try to unpack/analyze it.
+ * If this succeeds, we proceed to the next step where we present the user our findings
+ * and ask what to do with this.
+ */
+class AddModule_ProcessUpload extends AddModule_Base
+{
+
+ private $taskId = false;
+
+ public function preprocess()
+ {
+ if (!isset($_FILES['modulefile'])) {
+ Message::addError('missing-file');
+ return;
+ }
+ if ($_FILES['modulefile']['error'] != UPLOAD_ERR_OK) {
+ Message::addError('upload-failed', $_FILE['modulefile']['name']);
+ return;
+ }
+ $tempfile = $_FILES['modulefile']['tmp_name'] . '.tmp';
+ if (!move_uploaded_file($_FILES['modulefile']['tmp_name'], $tempfile)) {
+ Message:addError('error-write', $tempfile);
+ return;
+ }
+ $this->taskId = 'tgzmod' . mt_rand() . '-' . microtime(true);
+ Taskmanager::submit('ListArchive', array(
+ 'id' => $this->taskId,
+ 'file' => $tempfile
+ ), true);
+ Session::set('mod_temp', $tempfile);
+ }
+
+ public function render()
+ {
+ $status = Taskmanager::waitComplete($this->taskId);
+ Taskmanager::release($this->taskId);
+ $tempfile = Session::get('mod_temp');
+ if (!isset($status['statusCode'])) {
+ unlink($tempfile);
+ $this->tmError();
+ }
+ if ($status['statusCode'] != TASK_FINISHED) {
+ unlink($tempfile);
+ $this->taskError($status);
+ }
+ // Sort files for better display
+ $dirs = array();
+ foreach ($status['data']['entries'] as $file) {
+ if ($file['isdir']) continue;
+ $dirs[dirname($file['name'])][] = $file;
+ }
+ ksort($dirs);
+ $list = array();
+ foreach ($dirs as $dir => $files) {
+ $list[] = array(
+ 'name' => $dir,
+ 'isdir' => true
+ );
+ sort($files);
+ foreach ($files as $file) {
+ $file['size'] = Util::readableFileSize($file['size']);
+ $list[] = $file;
+ }
+ }
+ global $nextStep;
+ Render::addDialog('Eigenes Modul hinzufügen', false, 'sysconfig/custom-fileselect', array(
+ 'step' => $nextStep,
+ 'files' => $list,
+ ));
+ Session::save();
+ }
+
+}
+
+class AddModule_CompressModule extends AddModule_Base
+{
+
+ private $taskId = false;
+
+ public function preprocess()
+ {
+ $title = Request::post('title');
+ $tempfile = Session::get('mod_temp');
+ if (empty($title) || empty($tempfile) || !file_exists($tempfile)) {
+ Message::addError('empty-field');
+ return;
+ }
+ // 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';
+ Taskmanager::submit('RecompressArchive', array(
+ 'id' => $this->taskId,
+ 'inputFiles' => array($tempfile),
+ 'outputFile' => $destFile
+ ), true);
+ $status = Taskmanager::waitComplete($this->taskId);
+ unlink($tempfile);
+ if (!isset($status['statusCode'])) {
+ $this->tmError();
+ }
+ if ($status['statusCode'] != TASK_FINISHED) {
+ $this->taskError($status);
+ }
+ // Seems ok, create entry in DB
+ $ret = Database::exec("INSERT INTO configtgz_module (title, moduletype, filename, contents) VALUES (:title, 'custom', :file, '')",
+ array('title' => $title, 'file' => $destFile));
+ if ($ret === false) {
+ unlink($destFile);
+ Util::traceError("Could not insert module into Database");
+ }
+ Message::addSuccess('module-added');
+ Util::redirect('?do=sysconfig');
+ }
+
+}
diff --git a/modules/syslog.inc.php b/modules/syslog.inc.php
index f8d99de3..62c94edf 100644
--- a/modules/syslog.inc.php
+++ b/modules/syslog.inc.php
@@ -12,26 +12,35 @@ function render_module()
{
Render::setTitle('Client Log');
- $filter = '';
- $not = '';
- if (isset($_POST['filter'])) $filter = $_POST['filter'];
+ if (isset($_GET['filter'])) {
+ $filter = $_GET['filter'];
+ $not = isset($_GET['not']) ? 'NOT' : '';
+ } elseif (isset($_POST['filter'])) {
+ $filter = $_POST['filter'];
+ $not = isset($_POST['not']) ? 'NOT' : '';
+ Session::set('log_filter', $filter);
+ Session::set('log_not', $not);
+ Session::save();
+ } else {
+ $filter = Session::get('log_filter');
+ $not = Session::get('log_not') ? 'NOT' : '';
+ }
if (!empty($filter)) {
- $parts = explode(',', $filter);
- $opt = array();
- foreach ($parts as $part) {
- $part = preg_replace('/[^a-z0-9_\-]/', '', trim($part));
- if (empty($part) || in_array($part, $opt)) continue;
- $opt[] = "'$part'";
+ $filterList = explode(',', $filter);
+ $whereClause = array();
+ foreach ($filterList as $filterItem) {
+ $filterItem = preg_replace('/[^a-z0-9_\-]/', '', trim($filterItem));
+ if (empty($filterItem) || in_array($filterItem, $whereClause)) continue;
+ $whereClause[] = "'$filterItem'";
}
- if (isset($_POST['not'])) $not = 'NOT';
- if (!empty($opt)) $opt = ' WHERE logtypeid ' . $not . ' IN (' . implode(', ', $opt) . ')';
+ if (!empty($whereClause)) $whereClause = ' WHERE logtypeid ' . $not . ' IN (' . implode(', ', $whereClause) . ')';
}
- if (!isset($opt) || empty($opt)) $opt = '';
+ if (!isset($whereClause) || empty($whereClause)) $whereClause = '';
$today = date('d.m.Y');
$yesterday = date('d.m.Y', time() - 86400);
$lines = array();
- $paginate = new Paginate("SELECT logid, dateline, logtypeid, clientip, description, extra FROM clientlog $opt ORDER BY logid DESC", 50);
+ $paginate = new Paginate("SELECT logid, dateline, logtypeid, clientip, description, extra FROM clientlog $whereClause ORDER BY logid DESC", 50);
$res = $paginate->exec();
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
$day = date('d.m.Y', $row['dateline']);
diff --git a/script/custom.js b/script/custom.js
index f2906813..3269749e 100644
--- a/script/custom.js
+++ b/script/custom.js
@@ -4,3 +4,14 @@ function loadContent(elem, source)
$(elem).load(source);
}
+function selectDir(obj)
+{
+ dirname = $(obj).parent().parent().find('td.isdir').text() + '/';
+ console.log("CALLED! Dirname: " + dirname);
+ $('td.fileEntry').each(function() {
+ var text = $(this).text();
+ if (text.length < dirname.length) return;
+ if (text.substr(0, dirname.length) !== dirname) return;
+ $(this).parent().find('.fileBox')[0].checked = obj.checked;
+ });
+} \ No newline at end of file
diff --git a/style/default.css b/style/default.css
index 1a4a0be0..98ecadd6 100644
--- a/style/default.css
+++ b/style/default.css
@@ -57,3 +57,6 @@ body {
height: 34px;
}
+.isdir {
+ font-weight: bold;
+} \ No newline at end of file
diff --git a/templates/dialog-generic.html b/templates/dialog-generic.html
new file mode 100644
index 00000000..5e875e8b
--- /dev/null
+++ b/templates/dialog-generic.html
@@ -0,0 +1,15 @@
+<div class="container">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h4 class="modal-title">{{title}}</h4>
+ </div>
+ <div class="modal-body">
+ {{{body}}}
+ </div>
+ <div class="modal-footer">
+ {{#next}}<a class="btn btn-primary" href="{{next}}">Weiter &raquo;</a>{{/next}}
+ </div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/templates/page-sysconfig-main.html b/templates/page-sysconfig-main.html
new file mode 100644
index 00000000..c160fd0b
--- /dev/null
+++ b/templates/page-sysconfig-main.html
@@ -0,0 +1,86 @@
+<ol class="breadcrumb">
+ <li><a href="?do=main">Start</a></li>
+ <li class="active">SystemKonfiguration</li>
+</ol>
+<div class="container">
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ 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>
+ </div>
+ </div>
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ 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">Bearbeiten</a>
+ <a class="btn btn-danger btn-xs">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>
+ </div>
+ </div>
+</div>
+
+<div class="modal fade" id="help-config" tabindex="-1" role="dialog">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">Systemkonfiguration</div>
+ <div class="modal-body">
+ Über eine Systemkonfiguration wird die grundlegende Lokalisierung des bwLehrpool-Systems
+ durchgeführt. Dazu gehören Aspekte wie das Authentifizierungsverfahren für Benutzer
+ (z.B. Active Directory, LDAP), Druckerkonfiguration, Home-Verzeichnisse, etc.<br>
+ Eine Systemkonfiguration setzt sich aus einem oder mehreren Konfigurationsmodulen zusammen,
+ welche im unteren Bereich dieser Seite verwaltet werden können.
+ </div>
+ <div class="modal-footer"><a class="btn btn-primary" data-dismiss="modal">Schließen</a></div>
+ </div>
+ </div>
+</div>
+
+<div class="modal fade" id="help-module" tabindex="-1" role="dialog">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">Konfigurationsmodule</div>
+ <div class="modal-body">
+ Konfigurationsmodule sind die Bausteine, aus denen eine Systemkonfiguration erstellt wird.
+ Hier lassen sich sowohl generische Module durch einen Wizard anlegen, als auch komplett eigene
+ Module erstellen (fortgeschritten, Linuxkenntnisse erforderlich).
+ </div>
+ <div class="modal-footer"><a class="btn btn-primary" data-dismiss="modal">Schließen</a></div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/templates/page-tgz-list.html b/templates/page-tgz-list.html
deleted file mode 100644
index fdc11933..00000000
--- a/templates/page-tgz-list.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<ol class="breadcrumb">
- <li><a href="?do=main">Start</a></li>
- <li class="active">SystemKonfiguration</li>
-</ol>
-<div class="container">
- {{#files}}
- <div class="row well well-sm">
- {{file}}
- {{^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}}
- </div>
- {{/files}}
- {{^files}}
- <div class="row well well-sm">Keine Konfigurationspakete gefunden!</div>
- {{/files}}
- <a class="btn btn-md btn-primary" href="?do=sysconfig&amp;action=remotelist">Konfigurationsvolagen</a>
- <a class="btn btn-md btn-primary" href="#" data-toggle="collapse" data-target="#uploadform">Eigene Konfiguration hochladen</a>
- <a class="btn btn-md btn-primary" href="/boot/default/config.tgz">Aktive Konfiguration herunterladen</a>
- <div class="collapse" id="uploadform">
- <div class="well well-sm" style="margin: 5px 0px">
- <form method="post" action="?do=sysconfig" enctype="multipart/form-data">
- <input type="file" size="40" class="form-control" name="customtgz">
- <input type="hidden" name="action" value="upload">
- <input type="hidden" name="token" value="{{token}}">
- <button class="btn btn-primary form-control-addon" type="submit">Hochladen</button>
- </form>
- </div>
- </div>
-</div>
diff --git a/templates/sysconfig/custom-fileselect.html b/templates/sysconfig/custom-fileselect.html
new file mode 100644
index 00000000..c61edc7f
--- /dev/null
+++ b/templates/sysconfig/custom-fileselect.html
@@ -0,0 +1,26 @@
+<form role="form" method="post" action="?do=sysconfig&amp;action=addmodule&amp;step={{step}}">
+ <input type="hidden" name="modid" value="{{modid}}">
+ <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>
+ <table class="table table-bordered table-condensed">
+ {{#files}}
+ <tr>
+ {{#isdir}}
+ <td class="fileEntry isdir" colspan="2">{{name}}</td>
+ {{/isdir}}
+ {{^isdir}}
+ <td class="fileEntry">{{name}}</td>
+ <td>{{size}}</td>
+ {{/isdir}}
+ </tr>
+ {{/files}}
+ </table>
+ <div class="pull-right">
+ <button type="submit" class="btn btn-primary">Weiter &raquo;</button>
+ </div>
+</form>
diff --git a/templates/sysconfig/custom-upload.html b/templates/sysconfig/custom-upload.html
new file mode 100644
index 00000000..2c0ff723
--- /dev/null
+++ b/templates/sysconfig/custom-upload.html
@@ -0,0 +1,16 @@
+<p>Über ein benutzerdefiniertes Modul ist es möglich, beliebige Dateien
+ zum Linux-Grundsystem, das auf den Clients gebootet wird, hinzuzufügen.
+ Dazu kann ein Archiv mit einer Dateisystemstruktur hochgeladen werden, die
+ in dieser Form 1:1 in das gebootete Linux extrahiert wird.</p>
+
+<p>Beispiel: Enthält das hochgeladene Archiv eine Datei <strong>etc/beispiel.conf</strong>,
+ 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}}">
+ <div class="input-group">
+ <span class="input-group-addon">Archiv</span>
+ <input class="form-control" type="file" name="modulefile">
+ </div>
+ <p class="help-block">Unterstützte Archivformate: .tar.gz, .tar.bz2, .zip</p>
+ <button type="submit" class="btn btn-primary">Hochladen</button>
+</form> \ No newline at end of file