summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apis/update.inc.php12
-rw-r--r--inc/configmodule.inc.php67
-rw-r--r--inc/configmodule/adauth.inc.php1
-rw-r--r--inc/configmodule/branding.inc.php6
-rw-r--r--inc/configtgz.inc.php160
-rw-r--r--inc/event.inc.php5
-rw-r--r--inc/taskmanager.inc.php1
-rw-r--r--inc/util.inc.php40
-rw-r--r--lang/de/config-module.json8
-rw-r--r--lang/de/messages-hardcoded.json7
-rw-r--r--lang/de/messages.json5
-rw-r--r--lang/de/templates/main-menu.json2
-rw-r--r--lang/de/templates/sysconfig/_page.json2
-rw-r--r--lang/de/templates/sysconfig/branding-start.json2
-rw-r--r--lang/de/templates/webinterface/passwords.json2
-rw-r--r--lang/en/messages.json5
-rw-r--r--lang/en/templates/sysconfig/_page.json2
-rw-r--r--lang/pt/templates/sysconfig/branding-start.json2
-rw-r--r--modules/sysconfig.inc.php83
-rw-r--r--modules/sysconfig/addconfig.inc.php52
-rw-r--r--modules/sysconfig/addmodule_adauth.inc.php6
-rw-r--r--modules/sysconfig/addmodule_branding.inc.php6
-rw-r--r--modules/sysconfig/addmodule_custommodule.inc.php4
-rw-r--r--script/custom.js16
-rw-r--r--style/default.css15
-rw-r--r--templates/sysconfig/_page.html40
26 files changed, 458 insertions, 93 deletions
diff --git a/apis/update.inc.php b/apis/update.inc.php
index 13a5f593..ac680aef 100644
--- a/apis/update.inc.php
+++ b/apis/update.inc.php
@@ -39,6 +39,16 @@ while ($currentVersion < $targetVersion) {
}
}
+// TEMPORARY HACK; Rebuild AD configs.. move somewhere else
+$list = ConfigModule::getAll('AdAuth');
+if ($list === false) {
+ Message::addError('ad-config-failed');
+} else {
+ foreach ($list as $ad) {
+ $ad->generate(false);
+ }
+}
+
Message::addSuccess('db-update-done');
if (tableExists('eventlog'))
EventLog::info("Database updated to version $currentVersion");
@@ -209,5 +219,7 @@ function update_8()
Database::exec("ALTER TABLE `configtgz_module` ADD `status` ENUM( 'OK', 'MISSING', 'OUTDATED' ) NOT NULL DEFAULT 'MISSING'");
if (!tableHasColumn('callback', 'args'))
Database::exec("ALTER TABLE `callback` ADD `args` TEXT NOT NULL DEFAULT ''");
+ if (!tableHasColumn('configtgz', 'status'))
+ Database::exec("ALTER TABLE `configtgz` ADD `status` ENUM( 'OK', 'OUTDATED', 'MISSING' ) NOT NULL DEFAULT 'MISSING'");
return true;
}
diff --git a/inc/configmodule.inc.php b/inc/configmodule.inc.php
index 5eff92ba..6fd9e1b8 100644
--- a/inc/configmodule.inc.php
+++ b/inc/configmodule.inc.php
@@ -115,6 +115,33 @@ abstract class ConfigModule
}
/**
+ * Get module instances from module type.
+ *
+ * @param int $moduleType module type to get
+ * @return array The requested modules from DB, or false on error
+ */
+ public static function getAll($moduleType)
+ {
+ $ret = Database::simpleQuery("SELECT moduleid, title, moduletype, filepath, contents, version FROM configtgz_module "
+ . " WHERE moduletype = :moduletype", array('moduletype' => $moduleType));
+ if ($ret === false)
+ return false;
+ $list = array();
+ while ($row = $ret->fetch(PDO::FETCH_ASSOC)) {
+ $instance = self::getInstance($row['moduletype']);
+ if ($instance === false)
+ return false;
+ $instance->currentVersion = $row['version'];
+ $instance->moduleArchive = $row['filepath'];
+ $instance->moduleData = json_decode($row['contents'], true);
+ $instance->moduleId = $row['moduleid'];
+ $instance->moduleTitle = $row['title'];
+ $list[] = $instance;
+ }
+ return $list;
+ }
+
+ /**
* Get the module version.
*
* @return int module version
@@ -164,6 +191,16 @@ abstract class ConfigModule
}
/**
+ * Get module title.
+ *
+ * @return string
+ */
+ public final function title()
+ {
+ return $this->moduleTitle;
+ }
+
+ /**
* Get module archive file name.
*
* @return string tgz file absolute path
@@ -243,8 +280,12 @@ abstract class ConfigModule
// Wait for generation if requested
if ($timeoutMs > 0 && isset($ret['id']) && !Taskmanager::isFinished($ret))
$ret = Taskmanager::waitComplete($ret, $timeoutMs);
- if ($ret === true || (isset($ret['statusCode']) && $ret['statusCode'] === TASK_FINISHED))
- return $this->markUpdated($tmpTgz); // Finished
+ if ($ret === true || (isset($ret['statusCode']) && $ret['statusCode'] === TASK_FINISHED)) {
+ // Already Finished
+ if (file_exists($this->moduleArchive) && !file_exists($tmpTgz))
+ $tmpTgz = false;
+ return $this->markUpdated($tmpTgz);
+ }
if (!is_array($ret) || !isset($ret['id']) || Taskmanager::isFailed($ret)) {
if (is_array($ret)) // Failed
Taskmanager::addErrorMessage($ret);
@@ -271,8 +312,8 @@ abstract class ConfigModule
if ($this->moduleId === 0)
Util::traceError('ConfigModule::delete called with invalid module id!');
$ret = Database::exec("DELETE FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array(
- 'moduleid' => $id
- )) !== false;
+ 'moduleid' => $this->moduleId
+ ), true) !== false;
if ($ret !== false) {
if ($this->moduleArchive)
Taskmanager::submit('DeleteFile', array('file' => $this->moduleArchive), true);
@@ -281,6 +322,7 @@ abstract class ConfigModule
$this->moduleTitle = false;
$this->moduleArchive = false;
}
+ return $ret;
}
private final function markUpdated($tmpTgz)
@@ -312,11 +354,17 @@ abstract class ConfigModule
}
}
// Update DB entry
- return Database::exec("UPDATE configtgz_module SET filepath = :filename, version = :version, status = 'OK' WHERE moduleid = :id LIMIT 1", array(
+ $retval = Database::exec("UPDATE configtgz_module SET filepath = :filename, version = :version, status = 'OK' WHERE moduleid = :id LIMIT 1", array(
'id' => $this->moduleId,
'filename' => $this->moduleArchive,
'version' => $this->moduleVersion()
)) !== false;
+ // Update related config.tgzs
+ $configs = ConfigTgz::getAllForModule($this->moduleId);
+ foreach ($configs as $config) {
+ $config->generate();
+ }
+ return $retval;
}
private final function markFailed()
@@ -356,11 +404,18 @@ abstract class ConfigModule
*/
public static function serverIpChanged()
{
- self::loadDb();
+ self::loadDb(); // Quick hack: Hard code AdAuth, should be a property of the config module class....
+ $list = self::getAll('AdAuth');
+ error_log('Changed: Telling ' - count($list) . ' modules');
+ foreach ($list as $mod) {
+ $mod->event_serverIpChanged();
+ }
+ /* // TODO: Make this work
foreach (self::$moduleTypes as $module) {
$instance = new $module['moduleClass'];
$instance->event_serverIpChanged();
}
+ */
}
/**
diff --git a/inc/configmodule/adauth.inc.php b/inc/configmodule/adauth.inc.php
index 06ac5460..828469c3 100644
--- a/inc/configmodule/adauth.inc.php
+++ b/inc/configmodule/adauth.inc.php
@@ -54,6 +54,7 @@ class ConfigModule_AdAuth extends ConfigModule
*/
public function event_serverIpChanged()
{
+ error_log('Calling generate on ' . $this->title());
$this->generate(false);
}
diff --git a/inc/configmodule/branding.inc.php b/inc/configmodule/branding.inc.php
index e8bd1da7..6e452a93 100644
--- a/inc/configmodule/branding.inc.php
+++ b/inc/configmodule/branding.inc.php
@@ -19,9 +19,7 @@ class ConfigModule_Branding extends ConfigModule
protected function generateInternal($tgz, $parent)
{
if (!$this->validateConfig()) {
- if ($this->archive() !== false && file_exists($this->archive()))
- return true; // No new temp file given, old archive still exists, pretend it worked...
- return false;
+ return $this->archive() !== false && file_exists($this->archive()); // No new temp file given, old archive still exists, pretend it worked...
}
$task = Taskmanager::submit('MoveFile', array(
'source' => $this->tmpFile,
@@ -44,7 +42,7 @@ class ConfigModule_Branding extends ConfigModule
public function setData($key, $value)
{
- if ($key !== 'tmpFile' || !file_exists($value))
+ if ($key !== 'tmpFile' || !is_string($value) || !file_exists($value))
return false;
$this->tmpFile = $value;
}
diff --git a/inc/configtgz.inc.php b/inc/configtgz.inc.php
new file mode 100644
index 00000000..783f8b80
--- /dev/null
+++ b/inc/configtgz.inc.php
@@ -0,0 +1,160 @@
+<?php
+
+class ConfigTgz
+{
+
+ private $configId = 0;
+ private $configTitle = false;
+ private $file = false;
+ private $modules = array();
+
+ private function __construct()
+ {
+ ;
+ }
+
+ public function id()
+ {
+ return $this->configId;
+ }
+
+ public function title()
+ {
+ return $this->configTitle;
+ }
+
+ public static function insert($title, $moduleIds)
+ {
+ if (!is_array($moduleIds))
+ return false;
+ $instance = new ConfigTgz;
+ $instance->configTitle = $title;
+ // Create output file name (config.tgz)
+ do {
+ $instance->file = CONFIG_TGZ_LIST_DIR . '/config-' . Util::sanitizeFilename($instance->configTitle) . '-' . mt_rand() . '-' . time() . '.tgz';
+ } while (file_exists($instance->file));
+ Database::exec("INSERT INTO configtgz (title, filepath, status) VALUES (:title, :filepath, :status)", array(
+ 'title' => $instance->configTitle,
+ 'filepath' => $instance->file,
+ 'status' => 'MISSING'
+ ));
+ $instance->configId = Database::lastInsertId();
+ $instance->modules = array();
+ // Get all modules to put in config
+ $idstr = '0'; // Passed directly in query. Make sure no SQL injection is possible
+ foreach ($moduleIds as $module) {
+ $idstr .= ',' . (int)$module; // Casting to int should make it safe
+ }
+ $res = Database::simpleQuery("SELECT moduleid, filepath, status FROM configtgz_module WHERE moduleid IN ($idstr)");
+ // Make connection
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ Database::exec("INSERT INTO configtgz_x_module (configid, moduleid) VALUES (:configid, :moduleid)", array(
+ 'configid' => $instance->configId,
+ 'moduleid' => $row['moduleid']
+ ));
+ $instance->modules[] = $row;
+ }
+ return $instance;
+ }
+
+ public static function get($configId)
+ {
+ $ret = Database::queryFirst("SELECT configid, title, filepath FROM configtgz WHERE configid = :configid", array(
+ 'configid' => $configId
+ ));
+ if ($ret === false)
+ return false;
+ $instance = new ConfigTgz;
+ $instance->configId = $ret['configid'];
+ $instance->configTitle = $ret['title'];
+ $instance->file = $ret['filepath'];
+ $ret = Database::simpleQuery("SELECT moduleid, filepath, status FROM configtgz_x_module "
+ . " INNER JOIN configtgz_module USING (moduleid) "
+ . " WHERE configid = :configid", array('configid' => $instance->configId));
+ $instance->modules = array();
+ while ($row = $ret->fetch(PDO::FETCH_ASSOC)) {
+ $instance->modules[] = $row;
+ }
+ return $instance;
+ }
+
+ public static function getAllForModule($moduleId)
+ {
+ $res = Database::simpleQuery("SELECT configid, title, filepath FROM configtgz_x_module "
+ . " INNER JOIN configtgz USING (configid) "
+ . " WHERE moduleid = :moduleid", array(
+ 'moduleid' => $moduleId
+ ));
+ if ($res === false)
+ return false;
+ $list = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $instance = new ConfigTgz;
+ $instance->configId = $row['configid'];
+ $instance->configTitle = $row['title'];
+ $instance->file = $row['filepath'];
+ $innerRes = Database::simpleQuery("SELECT moduleid, filepath, status FROM configtgz_x_module "
+ . " INNER JOIN configtgz_module USING (moduleid) "
+ . " WHERE configid = :configid", array('configid' => $instance->configId));
+ $instance->modules = array();
+ while ($innerRow = $innerRes->fetch(PDO::FETCH_ASSOC)) {
+ $instance->modules[] = $innerRow;
+ }
+ $list[] = $instance;
+ }
+ return $list;
+ }
+
+ public function generate()
+ {
+ if (!($this->configId > 0) || !is_array($this->modules) || $this->file === false)
+ Util::traceError ('configId <= 0 or modules not array in ConfigTgz::rebuild()');
+ $files = array();
+ $successStatus = 'OK';
+ foreach ($this->modules as $module) {
+ if (!empty($module['filepath']) && file_exists($module['filepath'])) {
+ $files[] = $module['filepath'];
+ if ($module['status'] !== 'OK')
+ $successStatus = 'OUTDATED';
+ } else {
+ $successStatus = 'OUTDATED';
+ }
+ }
+ // Hand over to tm
+ $task = Taskmanager::submit('RecompressArchive', array(
+ 'inputFiles' => $files,
+ 'outputFile' => $this->file
+ ));
+ // Wait for completion
+ if (!Taskmanager::isFailed($task) && !Taskmanager::isFinished($task))
+ $task = Taskmanager::waitComplete($task, 5000);
+ // Failed...
+ if (Taskmanager::isFailed($task)) {
+ Taskmanager::addErrorMessage($task);
+ $successStatus = file_exists($this->file) ? 'OUTDATED' : 'MISSING';
+ }
+ Database::exec("UPDATE configtgz SET status = :status WHERE configid = :configid LIMIT 1", array(
+ 'configid' => $this->configId,
+ 'status' => $successStatus
+ ));
+ return $successStatus;
+ }
+
+ public function delete()
+ {
+ if ($this->configId === 0)
+ Util::traceError('ConfigTgz::delete called with invalid config id!');
+ $ret = Database::exec("DELETE FROM configtgz WHERE configid = :configid LIMIT 1", array(
+ 'configid' => $this->configId
+ ), true) !== false;
+ if ($ret !== false) {
+ if ($this->file !== false)
+ Taskmanager::submit('DeleteFile', array('file' => $this->file), true);
+ $this->configId = 0;
+ $this->modules = false;
+ $this->file = false;
+ }
+ return $ret;
+ }
+
+}
diff --git a/inc/event.inc.php b/inc/event.inc.php
index 95d75e33..6b303493 100644
--- a/inc/event.inc.php
+++ b/inc/event.inc.php
@@ -73,9 +73,10 @@ class Event
*/
public static function serverIpChanged()
{
- global $tidAdModules, $tidIpxe;
- $tidAdModules = Trigger::rebuildAdModules();
+ error_log('Server ip changed');
+ global $tidIpxe;
$tidIpxe = Trigger::ipxe();
+ ConfigModule::serverIpChanged();
}
/**
diff --git a/inc/taskmanager.inc.php b/inc/taskmanager.inc.php
index 5045fc75..2cdaf69f 100644
--- a/inc/taskmanager.inc.php
+++ b/inc/taskmanager.inc.php
@@ -216,7 +216,6 @@ class Taskmanager
if (++$tries > 10)
return false;
}
- //error_log(socket_strerror(socket_last_error(self::$sock)));
return false;
}
diff --git a/inc/util.inc.php b/inc/util.inc.php
index 9d0eced9..d2ecba6f 100644
--- a/inc/util.inc.php
+++ b/inc/util.inc.php
@@ -232,5 +232,45 @@ SADFACE;
}
return true;
}
+
+ /**
+ * Send a file to user for download.
+ *
+ * @param type $file path of local file
+ * @param type $name name of file to send to user agent
+ * @param type $delete delete the file when done?
+ * @return boolean false: file could not be opened.
+ * true: error while reading the file
+ * - on success, the function does not return
+ */
+ public static function sendFile($file, $name, $delete = false)
+ {
+ while ((@ob_get_level()) > 0)
+ @ob_end_clean();
+ $fh = @fopen($file, 'rb');
+ if ($fh === false) {
+ Message::addError('error-read', $file);
+ return false;
+ }
+ Header('Content-Type: application/octet-stream', true);
+ Header('Content-Disposition: attachment; filename=' . str_replace(array(' ', '=', ',', '/', '\\', ':', '?'), '_', iconv('UTF-8', 'ASCII//TRANSLIT', $name)));
+ Header('Content-Length: ' . @filesize($file));
+ while (!feof($fh)) {
+ $data = fread($fh, 16000);
+ if ($data === false) {
+ echo "\r\n\nDOWNLOAD INTERRUPTED!\n";
+ if ($delete)
+ @unlink($file);
+ return true;
+ }
+ echo $data;
+ @ob_flush();
+ @flush();
+ }
+ @fclose($fh);
+ if ($delete)
+ @unlink($file);
+ exit(0);
+ }
}
diff --git a/lang/de/config-module.json b/lang/de/config-module.json
new file mode 100644
index 00000000..623b5eca
--- /dev/null
+++ b/lang/de/config-module.json
@@ -0,0 +1,8 @@
+{
+ "adAuth_title": "Active Directory Authentifizierung",
+ "adAuth_description": "Mit diesem Modul ist die Anmeldung an den Client PCs mit den Benutzerkonten eines Active Directory m\u00f6glich. Je nach Konfiguration ist auch die Nutzung eines Benutzerverzeichnisses auf dem Client m\u00f6glich.",
+ "custom_title": "Generisches Modul hinzuf\u00fcgen",
+ "custom_description": "Mit einem Erweiterten Modul ist es m\u00f6glich, beliebige Dateien zum Grundsystem hinzuzuf\u00fcgen Nutzen Sie dieses Modul, um z.B. spezielle Konfigurationsdateien auf den Client PCs zu verwenden, die sich nicht mit einem der anderen Wizards erstellen l\u00e4sst. Das Hinzuf\u00fcgen eines Erweiterten Moduls erfordert in der Regel zumindest grundlegende Systemkenntnisse im Linuxbereich.",
+ "branding_title": "Einrichtungsspezifisches Logo",
+ "branding_description": "Das Logo wird am Anmeldebildschirm und beim Starten des Systems angezeigt."
+}
diff --git a/lang/de/messages-hardcoded.json b/lang/de/messages-hardcoded.json
index 6a0c0629..1b490d11 100644
--- a/lang/de/messages-hardcoded.json
+++ b/lang/de/messages-hardcoded.json
@@ -1,8 +1,6 @@
{
- "lang_adAuthentication": "Active Directory Authentifizierung",
- "lang_addCustomModule": "Generisches Modul hinzuf\u00fcgen",
"lang_configurationCompilation": "Konfiguration zusammenstellen",
- "lang_contentOf": "Inhalt von \u0022",
+ "lang_contentOf": "Inhalt von",
"lang_createUser": "Benutzer anlegen",
"lang_days": "Tag(e)",
"lang_hours": "Stunde(n)",
@@ -10,11 +8,10 @@
"lang_login": "Anmelden",
"lang_moduleAdd": "Modul hinzuf\u00fcgen",
"lang_serverConfiguration": "Serverseitige Konfiguration",
- "lang_specificLogo": "Einrichtungsspezifisches Logo",
"lang_titleBackup": "Sichern und Wiederherstellen",
"lang_titleEventLog": "Ereignisprotokoll",
"lang_titleWebinterface": "Webschnittstelle",
"lang_unknwonTaskManager": "Unbekannter Taskmanager-Fehler",
"today": "Heute",
"yesterday": "Gestern"
-} \ No newline at end of file
+}
diff --git a/lang/de/messages.json b/lang/de/messages.json
index 1e779b3f..51540c15 100644
--- a/lang/de/messages.json
+++ b/lang/de/messages.json
@@ -1,4 +1,5 @@
{
+ "ad-config-failed": "Wiederherstellen der Active Directory Konfiguration fehlgeschlagen",
"adduser-disabled": "Keine ausreichenden Rechte, um weitere Benutzer hinzuzuf\u00fcgen",
"adduser-success": "Benutzer erfolgreich hinzugef\u00fcgt",
"backup-failed": "Sicherung fehlgeschlagen!",
@@ -24,9 +25,13 @@
"invalid-template": "Ausgew\u00e4hlte Template ist nicht g\u00fcltig",
"loginfail": "Benutzername oder Kennwort falsch",
"missing-file": "Es wurde keine Datei ausgew\u00e4hlt!",
+ "missing-title": "Kein Titel eingegeben",
"module-added": "Modul erfolgreich hinzugef\u00fcgt",
"module-deleted": "Modul {{0}} wurde gel\u00f6scht",
"module-in-use": "Modul {{0}} wird noch durch Konfiguration {{1}} verwendet",
+ "module-rebuild-failed": "Neubau des Moduls fehlgeschlagen",
+ "module-rebuilding": "Modul wird neu generiert",
+ "module-rebuilt": "Modul wurde neu generiert",
"news-del-success": "News gel\u00f6scht",
"news-empty": "Es wurde keine News in der Datenbank gefunden",
"news-save-success": "News erfolgreich aktualisiert",
diff --git a/lang/de/templates/main-menu.json b/lang/de/templates/main-menu.json
index 88febce6..98896969 100644
--- a/lang/de/templates/main-menu.json
+++ b/lang/de/templates/main-menu.json
@@ -7,7 +7,7 @@
"lang_eventLog": "Server Log",
"lang_internetAccess": "Internetzugriff",
"lang_language": "Sprachen",
- "lang_localization": "Lokalisierung",
+ "lang_localization": "Lokalisierung + Integration",
"lang_login": "Anmelden",
"lang_logout": "Abmelden",
"lang_needsSetup": "Einrichtung unvollst\u00e4ndig",
diff --git a/lang/de/templates/sysconfig/_page.json b/lang/de/templates/sysconfig/_page.json
index 7c10aa8c..cd8dc79d 100644
--- a/lang/de/templates/sysconfig/_page.json
+++ b/lang/de/templates/sysconfig/_page.json
@@ -6,11 +6,13 @@
"lang_close": "Schlie\u00dfen",
"lang_configurationModuleNotFound": "Keine Konfigurationsmodule gefunden!",
"lang_delete": "L\u00f6schen",
+ "lang_download": "Herunterladen",
"lang_helpModuleConfiguration": "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).",
"lang_helpSystemConfiguration": "\u00dcber eine Systemkonfiguration wird die grundlegende Lokalisierung des bwLehrpool-Systems durchgef\u00fchrt. Dazu geh\u00f6ren Aspekte wie das Authentifizierungsverfahren f\u00fcr Benutzer (z.B. Active Directory, LDAP), Druckerkonfiguration, Home-Verzeichnisse, etc. Eine Systemkonfiguration setzt sich aus einem oder mehreren Konfigurationsmodulen zusammen, welche im unteren Bereich dieser Seite verwaltet werden k\u00f6nnen.",
"lang_moduleConfiguration": "Konfigurationsmodule",
"lang_newConfiguration": "Neue Konfiguration",
"lang_newModule": "Neues Modul",
+ "lang_rebuild": "Neu generieren",
"lang_show": "Ansehen",
"lang_systemConfiguration": "Systemkonfiguration",
"lang_systemConfigurationAlert": "Bevor Sie eine Systemkonfiguration erstellen k\u00f6nnen, m\u00fcssen Sie zun\u00e4chst ein Konfigurationsmodul erzeugen.",
diff --git a/lang/de/templates/sysconfig/branding-start.json b/lang/de/templates/sysconfig/branding-start.json
index 1b7a660b..8b367397 100644
--- a/lang/de/templates/sysconfig/branding-start.json
+++ b/lang/de/templates/sysconfig/branding-start.json
@@ -1,5 +1,5 @@
{
- "lang_branding": "F\u00fcr beste Ergebnisse sollten Sie ihr Einrichtungslogo im SVG Format hochladen. Das SVG-Format ist ein Vektorgrafikformat, was zum Skalieren vorteilhaft ist. Eine Gute Quelle f\u00fcr SVG-Logos von Unis und Hochschulen ist ihr jeweiliger Wikipedia-Artikel.",
+ "lang_branding": "Hier k\u00f6nnen Sie ihr Einrichtungslogo im SVG Format hochladen. Das SVG-Format ist ein Vektorgrafikformat, was zum Skalieren vorteilhaft ist. Eine Gute Quelle f\u00fcr SVG-Logos von Unis und Hochschulen ist ihr jeweiliger Wikipedia-Artikel.",
"lang_computerLoad": "Bild von lokalem Rechner hochladen",
"lang_or": "oder",
"lang_upload": "Hochladen",
diff --git a/lang/de/templates/webinterface/passwords.json b/lang/de/templates/webinterface/passwords.json
index da63843f..17c156c3 100644
--- a/lang/de/templates/webinterface/passwords.json
+++ b/lang/de/templates/webinterface/passwords.json
@@ -1,5 +1,5 @@
{
- "lang_description": "Legen Sie fest, ob Passwortfelder in der Web-Schnittstelle maskiert sein sollen, oder ob Ihr Inhalt sichtbar sein soll. Wenn Sie die Schnittstelle in einer sicheren Umgebung nutzen (keine neugierigen Augen), kann dies den Komfort erh\u00f6hen. Das Passwortfeld der Anmeldemaske ist von dieser Einstellung ausgenommen.",
+ "lang_description": "Legen Sie fest, ob Passwortfelder in der Web-Schnittstelle maskiert werden, oder ob Ihr Inhalt sichtbar sein soll. Wenn Sie die Schnittstelle in einer sicheren Umgebung nutzen (keine neugierigen Augen), kann dies den Komfort erh\u00f6hen. Das Passwortfeld der Anmeldemaske ist von dieser Einstellung ausgenommen.",
"lang_hidePasswords": "Passw\u00f6rter maskieren",
"lang_passwordFields": "Passwortfelder",
"lang_save": "Speichern",
diff --git a/lang/en/messages.json b/lang/en/messages.json
index dc4dd318..8d3ac4df 100644
--- a/lang/en/messages.json
+++ b/lang/en/messages.json
@@ -1,4 +1,5 @@
{
+ "ad-config-failed": "Rebuilding the Active Directory configuration failed",
"adduser-disabled": "Insufficient privileges to add more users",
"adduser-success": "User successfully added",
"backup-failed": "Backup failed!",
@@ -24,9 +25,13 @@
"invalid-template": "Selected template is not valid",
"loginfail": "Username or Password incorrect",
"missing-file": "There was no file selected!",
+ "missing-title": "No title given",
"module-added": "Module successfully added",
"module-deleted": "Module {{0}} was deleted",
"module-in-use": "Module {{0}} is still used by Configuration {{1}}",
+ "module-rebuild-failed": "Rebuilding module failed",
+ "module-rebuilding": "Module is rebuilding...",
+ "module-rebuilt": "Module was rebuilt",
"news-del-success": "News deleted",
"news-empty": "There was no news found in the database",
"news-save-success": "News updated successfully",
diff --git a/lang/en/templates/sysconfig/_page.json b/lang/en/templates/sysconfig/_page.json
index e6c643a0..86245267 100644
--- a/lang/en/templates/sysconfig/_page.json
+++ b/lang/en/templates/sysconfig/_page.json
@@ -6,11 +6,13 @@
"lang_close": "Close",
"lang_configurationModuleNotFound": "Configuration module not found!",
"lang_delete": "Delete",
+ "lang_download": "Download",
"lang_helpModuleConfiguration": "Configuration modules are the building blocks from which a system configuration is created. Here you can create both generic modules by a wizard, as well as create completely custom modules (advanced Linux knowledge required).",
"lang_helpSystemConfiguration": "The fundamental localization of the bwLehrpool system is done through a system configuration. These include aspects such as the authentication method for users (eg Active Directory, LDAP), printer configuration, home directories, etc. A system configuration is composed of one or more configuration modules, which can be managed at the bottom of this page.",
"lang_moduleConfiguration": "Module Configuration",
"lang_newConfiguration": "New Configuration",
"lang_newModule": "New Module",
+ "lang_rebuild": "Rebuild",
"lang_show": "Show",
"lang_systemConfiguration": "System Configuration",
"lang_systemConfigurationAlert": "Before you can create a system configuration, you must first create a configuration module.",
diff --git a/lang/pt/templates/sysconfig/branding-start.json b/lang/pt/templates/sysconfig/branding-start.json
index 84f4b687..690da79f 100644
--- a/lang/pt/templates/sysconfig/branding-start.json
+++ b/lang/pt/templates/sysconfig/branding-start.json
@@ -1,6 +1,6 @@
{
"lang_branding": "Para melhores resultados, voc\u00ea deve fazer o upload do logotipo em formato SVG. SVG \u00e9 um formato de gr\u00e1ficos vetoriais, o que \u00e9 vantajoso para a amplia\u00e7\u00e3o. Uma boa fonte para logotipos SVG das universidades e faculdades \u00e9 o seu respectivo artigo da Wikipedia.",
- "lang_computerLoad": "Carregar imagem do computador local ",
+ "lang_computerLoad": "Carregar imagem do computador local",
"lang_or": "ou",
"lang_upload": "Enviar",
"lang_urlLoad": "Carregar imagem a partir de uma URL"
diff --git a/modules/sysconfig.inc.php b/modules/sysconfig.inc.php
index 0da58767..94e2455a 100644
--- a/modules/sysconfig.inc.php
+++ b/modules/sysconfig.inc.php
@@ -25,6 +25,12 @@ class Page_SysConfig extends Page
if (Request::post('del', 'no') !== 'no') {
$this->delModule();
}
+ if (Request::post('download', 'no') !== 'no') {
+ $this->downloadModule();
+ }
+ if (Request::post('rebuild', 'no') !== 'no') {
+ $this->rebuildModule();
+ }
}
// Action: "addconfig" (compose config from one or more modules)
@@ -42,6 +48,10 @@ class Page_SysConfig extends Page
if (Request::post('activate', 'no') !== 'no') {
$this->activateConfig();
}
+ // Action "rebuild" (rebuild config.tgz from its modules)
+ if (Request::post('rebuild', 'no') !== 'no') {
+ $this->rebuildConfig();
+ }
}
}
@@ -88,7 +98,7 @@ class Page_SysConfig extends Page
private function listConfigs()
{
// Configs
- $res = Database::simpleQuery("SELECT configtgz.configid, configtgz.title, configtgz.filepath, GROUP_CONCAT(configtgz_x_module.moduleid) AS modlist"
+ $res = Database::simpleQuery("SELECT configtgz.configid, configtgz.title, configtgz.filepath, configtgz.status, GROUP_CONCAT(configtgz_x_module.moduleid) AS modlist"
. " FROM configtgz"
. " INNER JOIN configtgz_x_module USING (configid)"
. " GROUP BY configid"
@@ -99,22 +109,31 @@ class Page_SysConfig extends Page
'configid' => $row['configid'],
'config' => $row['title'],
'modlist' => $row['modlist'],
- 'current' => readlink(CONFIG_HTTP_DIR . '/default/config.tgz') === $row['filepath']
+ 'current' => readlink(CONFIG_HTTP_DIR . '/default/config.tgz') === $row['filepath'],
+ 'needrebuild' => ($row['status'] !== 'OK')
);
}
// Config modules
- $res = Database::simpleQuery("SELECT moduleid, title FROM configtgz_module ORDER BY title ASC");
+ $res = Database::simpleQuery("SELECT moduleid, title, moduletype, status FROM configtgz_module ORDER BY title ASC");
$modules = array();
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
$modules[] = array(
'moduleid' => $row['moduleid'],
- 'module' => $row['title']
+ 'moduletype' => $row['moduletype'],
+ 'module' => $row['title'],
+ 'iscustom' => ($row['moduletype'] === 'CustomModule' || $row['moduletype'] === 'Branding'),
+ 'needrebuild' => ($row['status'] !== 'OK')
);
}
Render::addTemplate('sysconfig/_page', array(
'configs' => $configs,
'modules' => $modules
));
+ Render::addScriptTop('custom');
+ Render::addFooter('<script> $(window).load(function (e) {
+ forceTable($("#modtable"));
+ forceTable($("#conftable"));
+ }); // </script>');
}
private function listModuleContents($moduleid)
@@ -216,6 +235,25 @@ class Page_SysConfig extends Page
Util::redirect('?do=SysConfig');
}
+ private function rebuildConfig()
+ {
+ $configid = Request::post('rebuild', 'MISSING');
+ $config = ConfigTgz::get($configid);
+ if ($config === false) {
+ Message::addError('config-invalid', $configid);
+ Util::redirect('?do=SysConfig');
+ }
+ //$ret = $config->generate(false, 350); // TODO
+ $ret = $config->generate(false, 350) === 'OK'; // TODO
+ if ($ret === true)
+ Message::addSuccess('module-rebuilt', $config->title());
+ elseif ($ret === false)
+ Message::addError('module-rebuild-failed', $config->title());
+ else
+ Message::addInfo('module-rebuilding', $config->title());
+ Util::redirect('?do=SysConfig');
+ }
+
private function delModule()
{
$moduleid = Request::post('del', 'MISSING');
@@ -246,15 +284,46 @@ class Page_SysConfig extends Page
Util::redirect('?do=SysConfig');
}
+ private function downloadModule()
+ {
+ $moduleid = Request::post('download', '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');
+ }
+ if (!Util::sendFile($row['filepath'], $row['title'] . '.tgz'))
+ Util::redirect('?do=SysConfig');
+ exit(0);
+ }
+
+ private function rebuildModule()
+ {
+ $moduleid = Request::post('rebuild', 'MISSING');
+ $module = ConfigModule::get($moduleid);
+ if ($module === false) {
+ Message::addError('config-invalid', $moduleid);
+ Util::redirect('?do=SysConfig');
+ }
+ $ret = $module->generate(false, 250);
+ if ($ret === true)
+ Message::addSuccess('module-rebuilt', $module->title());
+ elseif ($ret === false)
+ Message::addError('module-rebuild-failed', $module->title());
+ else
+ Message::addInfo('module-rebuilding', $module->title());
+ Util::redirect('?do=SysConfig');
+ }
+
private function delConfig()
{
$configid = Request::post('del', 'MISSING');
- $module = ConfigModule::get($configid);
- if ($module === false) {
+ $config = ConfigTgz::get($configid);
+ if ($config === false) {
Message::addError('config-invalid', $configid);
Util::redirect('?do=SysConfig');
}
- $module->delete();
+ $config->delete();
Util::redirect('?do=SysConfig');
}
diff --git a/modules/sysconfig/addconfig.inc.php b/modules/sysconfig/addconfig.inc.php
index 4949539e..55194069 100644
--- a/modules/sysconfig/addconfig.inc.php
+++ b/modules/sysconfig/addconfig.inc.php
@@ -138,65 +138,31 @@ class AddConfig_Start extends AddConfig_Base
*/
class AddConfig_Finish extends AddConfig_Base
{
- private $task = false;
- private $destFile = false;
- private $title = false;
- private $moduleids = array();
+ private $config = false;
protected function preprocessInternal()
{
$modules = Request::post('module');
- $this->title = Request::post('title');
+ $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');
+ if (empty($title)) {
+ Message::addError('missing-title');
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'];
+ $this->config = ConfigTgz::insert($title, $modules);
+ if ($this->config === false || $this->config->generate() !== 'OK') {
+ Message::addError('unsuccessful-action');
+ Util::redirect('?do=SysConfig&action=addconfig');
}
- // 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['statusCode'] === TASK_PROCESSING)) {
- $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(Dictionary::translate('lang_configurationCompilation'), false, 'sysconfig/cfg-finish', array(
- 'configid' => $confid
+ 'configid' => $this->config->id()
));
}
diff --git a/modules/sysconfig/addmodule_adauth.inc.php b/modules/sysconfig/addmodule_adauth.inc.php
index baa705b2..2418916e 100644
--- a/modules/sysconfig/addmodule_adauth.inc.php
+++ b/modules/sysconfig/addmodule_adauth.inc.php
@@ -11,7 +11,7 @@ class AdAuth_Start extends AddModule_Base
{
Session::set('ad_check', false);
Session::save();
- Render::addDialog(Dictionary::translate('lang_adAuthentication'), false, 'sysconfig/ad-start', array(
+ Render::addDialog(Dictionary::translate('config-module', 'adAuth_title'), false, 'sysconfig/ad-start', array(
'step' => 'AdAuth_CheckConnection',
'title' => Request::post('title'),
'server' => Request::post('server'),
@@ -79,7 +79,7 @@ class AdAuth_CheckConnection extends AddModule_Base
protected function renderInternal()
{
- Render::addDialog(Dictionary::translate('lang_adAuthentication'), false, 'sysconfig/ad-checkconnection', array_merge($this->taskIds, array(
+ Render::addDialog(Dictionary::translate('config-module', 'adAuth_title'), false, 'sysconfig/ad-checkconnection', array_merge($this->taskIds, array(
'title' => Request::post('title'),
'server' => Request::post('server'),
'searchbase' => Request::post('searchbase'),
@@ -148,7 +148,7 @@ class AdAuth_Finish extends AddModule_Base
protected function renderInternal()
{
- Render::addDialog(Dictionary::translate('lang_adAuthentication'), false, 'sysconfig/ad-finish', $this->taskIds);
+ Render::addDialog(Dictionary::translate('config-module', 'adAuth_title'), false, 'sysconfig/ad-finish', $this->taskIds);
}
}
diff --git a/modules/sysconfig/addmodule_branding.inc.php b/modules/sysconfig/addmodule_branding.inc.php
index 8bcc10a1..7b7dace9 100644
--- a/modules/sysconfig/addmodule_branding.inc.php
+++ b/modules/sysconfig/addmodule_branding.inc.php
@@ -9,7 +9,7 @@ class Branding_Start extends AddModule_Base
protected function renderInternal()
{
- Render::addDialog(Dictionary::translate('lang_specificLogo'), false, 'sysconfig/branding-start', array(
+ Render::addDialog(Dictionary::translate('config-module', 'branding_title'), false, 'sysconfig/branding-start', array(
'step' => 'Branding_ProcessFile',
));
}
@@ -74,7 +74,7 @@ class Branding_ProcessFile extends AddModule_Base
$png = base64_encode(file_get_contents($this->task['data']['pngFile']));
if (filesize($this->svgFile) < 1000000)
$svg = base64_encode(file_get_contents($this->svgFile));
- Render::addDialog(Dictionary::translate('lang_specificLogo'), false, 'sysconfig/branding-check', array(
+ Render::addDialog(Dictionary::translate('config-module', 'branding_title'), false, 'sysconfig/branding-check', array(
'png' => $png,
'svg' => $svg,
'error' => $this->task['data']['error'],
@@ -114,7 +114,7 @@ class Branding_ProcessFile extends AddModule_Base
// [wikipedia] Try to be nice and detect links that might give a hint where the svg can be found
if (preg_match_all('#href="([^"]*upload.wikimedia.org/[^"]*/[^"]*/[^"]*\.svg|[^"]+/[^"]+:[^"]+\.svg[^"]*)"#', $content, $out, PREG_PATTERN_ORDER)) {
if ($title === false && preg_match('#<title>([^<]*)</title>#i', $content, $tout))
- $title = trim($tout[1]);
+ $title = trim(preg_replace('/\W*Wikipedia.*/', '', $tout[1]));
foreach ($out[1] as $res) {
if (strpos($res, 'action=edit') !== false)
continue;
diff --git a/modules/sysconfig/addmodule_custommodule.inc.php b/modules/sysconfig/addmodule_custommodule.inc.php
index c42453db..1be9c10f 100644
--- a/modules/sysconfig/addmodule_custommodule.inc.php
+++ b/modules/sysconfig/addmodule_custommodule.inc.php
@@ -12,7 +12,7 @@ class CustomModule_Start extends AddModule_Base
protected function renderInternal()
{
Session::set('mod_temp', false);
- Render::addDialog(Dictionary::translate('lang_addCustomModule'), false, 'sysconfig/custom-upload', array(
+ Render::addDialog(Dictionary::translate('config-module', 'custom_title'), false, 'sysconfig/custom-upload', array(
'step' => 'CustomModule_ProcessUpload'
));
}
@@ -84,7 +84,7 @@ class CustomModule_ProcessUpload extends AddModule_Base
$list[] = $file;
}
}
- Render::addDialog(Dictionary::translate('lang_addCustomModule'), false, 'sysconfig/custom-fileselect', array(
+ Render::addDialog(Dictionary::translate('config-module', 'custom_title'), false, 'sysconfig/custom-fileselect', array(
'step' => 'CustomModule_CompressModule',
'files' => $list,
));
diff --git a/script/custom.js b/script/custom.js
index 12fe817e..675bf581 100644
--- a/script/custom.js
+++ b/script/custom.js
@@ -9,3 +9,19 @@ function loadContent(elem, source)
}
waitForIt();
}
+
+function forceTable(t)
+{
+ var pwidth = t.parent().innerWidth();
+ t.width(pwidth - 5);
+ var rows = t.find('tr');
+ var sum = 0;
+ rows.first().find('td').each(function (index) {
+ if (!$(this).hasClass('slx-width-ignore'))
+ sum += $(this).outerWidth();
+ });
+ var w = pwidth - (sum + 30);
+ rows.find('.slx-dyn-ellipsis').each(function (index) {
+ $(this).width(w).css('width', w + 'px').css('max-width', w + 'px');
+ });
+} \ No newline at end of file
diff --git a/style/default.css b/style/default.css
index f189a84a..c154a203 100644
--- a/style/default.css
+++ b/style/default.css
@@ -135,6 +135,17 @@ body {
max-width: 240px;
}
+.slx-dyn-ellipsis {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ display: block;
+}
+
+.slx-nowrap {
+ white-space: nowrap;
+}
+
.slx-rotation {
animation-name: rotateThis;
animation-duration: .75s;
@@ -156,10 +167,6 @@ body {
padding: 5px;
}
-.slx-bold {
- font-weight: bold;
-}
-
.slx-fade {
opacity: 0.5;
}
diff --git a/templates/sysconfig/_page.html b/templates/sysconfig/_page.html
index c86c4b14..02c0cecd 100644
--- a/templates/sysconfig/_page.html
+++ b/templates/sysconfig/_page.html
@@ -9,10 +9,10 @@
<form method="post" action="?do=SysConfig">
<input type="hidden" name="token" value="{{token}}">
<input type="hidden" name="action" value="config">
- <table class="slx-table">
+ <table id="conftable" class="slx-table" style="max-width:100px !important">
{{#configs}}
<tr class="slx-pointer" data-modlist="{{modlist}}" onclick="showmod(this, 'bold')" onmouseover="showmod(this, 'fade')" onmouseout="showmod(this, 'reset')">
- <td>{{config}}</td>
+ <td class="modrow slx-width-ignore slx-nowrap"><div class="slx-dyn-ellipsis">{{config}}</div></td>
<td>
{{^current}}
<button class="btn btn-primary btn-xs" name="activate" value="{{configid}}">
@@ -27,8 +27,18 @@
</span>
{{/current}}
</td>
+ <td class="slx-nowrap">
+ <button
+ {{#needrebuild}}
+ class="btn btn-primary btn-xs"
+ {{/needrebuild}}
+ {{^needrebuild}}
+ class="btn btn-default btn-xs"
+ {{/needrebuild}}
+ name="rebuild" value="{{configid}}" title="{{lang_rebuild}}"><span class="glyphicon glyphicon-refresh"></span></button>
+ </td>
<td>
- <button class="btn btn-danger btn-xs" name="del" value="{{configid}}"><span class="glyphicon glyphicon-trash"></span> {{lang_delete}}</button>
+ <button class="btn btn-danger btn-xs" name="del" value="{{configid}}" title="{{lang_delete}}"><span class="glyphicon glyphicon-trash"></span></button>
</td>
</tr>
{{/configs}}
@@ -61,16 +71,28 @@
<form method="post" action="?do=SysConfig">
<input type="hidden" name="token" value="{{token}}">
<input type="hidden" name="action" value="module">
- <table class="slx-table">
+ <table id="modtable" class="slx-table" style="max-width:100px !important">
{{#modules}}
<tr>
- <td data-id="{{moduleid}}" class="modrow">{{module}}</td>
- <td>
- <button class="btn btn-default btn-xs" name="list" value="{{moduleid}}"><span class="glyphicon glyphicon-eye-open"></span> {{lang_show}}</button>
+ <td class="badge slx-nowrap">{{moduletype}}</td>
+ <td data-id="{{moduleid}}" class="modrow slx-width-ignore slx-nowrap"><div class="slx-dyn-ellipsis">{{module}}</div></td>
+ <td class="slx-nowrap">
+ {{#iscustom}}
+ <button class="btn btn-default btn-xs" name="list" value="{{moduleid}}" title="{{lang_show}}"><span class="glyphicon glyphicon-eye-open"></span></button>
<!-- a class="btn btn-default btn-xs"><span class="glyphicon glyphicon-edit"></span> Bearbeiten</a -->
+ <button class="btn btn-default btn-xs" name="download" value="{{moduleid}}" title="{{lang_download}}"><span class="glyphicon glyphicon-download-alt"></span></button>
+ {{/iscustom}}
</td>
- <td>
- <button class="btn btn-danger btn-xs" name="del" value="{{moduleid}}"><span class="glyphicon glyphicon-trash"></span> {{lang_delete}}</button>
+ <td class="slx-nowrap">
+ <button
+ {{#needrebuild}}
+ class="btn btn-primary btn-xs"
+ {{/needrebuild}}
+ {{^needrebuild}}
+ class="btn btn-default btn-xs"
+ {{/needrebuild}}
+ name="rebuild" value="{{moduleid}}" title="{{lang_rebuild}}"><span class="glyphicon glyphicon-refresh"></span></button>
+ <button class="btn btn-danger btn-xs" name="del" value="{{moduleid}}" title="{{lang_delete}}"><span class="glyphicon glyphicon-trash"></span></button>
</td>
</tr>
{{/modules}}