summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2015-01-20 18:07:24 +0100
committerSimon Rettberg2015-01-20 18:07:24 +0100
commit1ff2bc4f3c694b7c76df8e57056c51ca39a23a34 (patch)
tree0eb19164af66b3d4e8bf639a710f323b631d23ee
parentRework config module class structure. Still some TODOs though.... (diff)
downloadslx-admin-1ff2bc4f3c694b7c76df8e57056c51ca39a23a34.tar.gz
slx-admin-1ff2bc4f3c694b7c76df8e57056c51ca39a23a34.tar.xz
slx-admin-1ff2bc4f3c694b7c76df8e57056c51ca39a23a34.zip
config module structure completed. Many other fixes. Hidden pw field support.
-rw-r--r--api.php3
-rw-r--r--apis/cb.inc.php3
-rw-r--r--apis/update.inc.php19
-rw-r--r--inc/configmodule.inc.php419
-rw-r--r--inc/configmodule/adauth.inc.php130
-rw-r--r--inc/configmodule/branding.inc.php58
-rw-r--r--inc/configmodule/customodule.inc.php37
-rw-r--r--inc/configmodules.inc.php94
-rw-r--r--inc/database.inc.php6
-rw-r--r--inc/property.inc.php24
-rw-r--r--inc/render.inc.php5
-rw-r--r--inc/taskmanager.inc.php4
-rw-r--r--inc/taskmanagercallback.inc.php49
-rw-r--r--inc/trigger.inc.php5
-rw-r--r--inc/util.inc.php17
-rw-r--r--index.php5
-rw-r--r--modules/sysconfig.inc.php27
-rw-r--r--modules/sysconfig/addconfig.inc.php2
-rw-r--r--modules/sysconfig/addmodule.inc.php2
-rw-r--r--modules/sysconfig/addmodule_adauth.inc.php19
-rw-r--r--modules/sysconfig/addmodule_branding.inc.php41
-rw-r--r--modules/sysconfig/addmodule_custommodule.inc.php23
-rw-r--r--modules/syslog.inc.php7
-rw-r--r--templates/footer.html2
-rw-r--r--templates/internetaccess/_page.html2
-rw-r--r--templates/page-vmstore.html4
-rw-r--r--templates/serversetup/ipxe.html2
-rw-r--r--templates/sysconfig/ad-finish.html2
-rw-r--r--templates/tm-callback-trigger.html5
29 files changed, 722 insertions, 294 deletions
diff --git a/api.php b/api.php
index b7745c22..e3ef94ce 100644
--- a/api.php
+++ b/api.php
@@ -5,6 +5,9 @@ chdir(dirname($_SERVER['SCRIPT_FILENAME']));
require_once 'config.php';
+define('API', true);
+define('AJAX', false);
+
if (CONFIG_SQL_PASS === '%MYSQL_OPENSLX_PASS%')
exit(0); // Ignore API calls if not configured yet
diff --git a/apis/cb.inc.php b/apis/cb.inc.php
new file mode 100644
index 00000000..da5d8756
--- /dev/null
+++ b/apis/cb.inc.php
@@ -0,0 +1,3 @@
+<?php
+
+Trigger::checkCallbacks();
diff --git a/apis/update.inc.php b/apis/update.inc.php
index 878dc47f..d478c14a 100644
--- a/apis/update.inc.php
+++ b/apis/update.inc.php
@@ -188,13 +188,26 @@ function update_6()
// #######################
// ##### 2014-12-12
-// Rename config modules, add "has changed" column to modules
+// Rename config modules
function update_7()
{
- if (!tableHasColumn('configtgz_module', 'haschanged'))
- Database::exec("ALTER TABLE configtgz_module ADD `haschanged` TINYINT DEFAULT '0'");
Database::exec("UPDATE configtgz_module SET moduletype = 'Branding' WHERE moduletype = 'BRANDING'");
Database::exec("UPDATE configtgz_module SET moduletype = 'AdAuth' WHERE moduletype = 'AD_AUTH'");
Database::exec("UPDATE configtgz_module SET moduletype = 'CustomModule' WHERE moduletype = 'custom'");
return true;
}
+
+// #######################
+// ##### 2015-01-16
+// Extend config module db table, add argument feature to callbacks
+function update_8()
+{
+ tableDropColumn('configtgz_module', 'haschanged');
+ if (!tableHasColumn('configtgz_module', 'version'))
+ Database::exec("ALTER TABLE `configtgz_module` ADD `version` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'");
+ if (!tableHasColumn('configtgz_module', 'status'))
+ Database::exec("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 ''");
+ return true;
+}
diff --git a/inc/configmodule.inc.php b/inc/configmodule.inc.php
new file mode 100644
index 00000000..5eff92ba
--- /dev/null
+++ b/inc/configmodule.inc.php
@@ -0,0 +1,419 @@
+<?php
+
+/**
+ * Base class for config modules
+ */
+abstract class ConfigModule
+{
+
+ /**
+ * @var array list of known module types
+ */
+ private static $moduleTypes = false;
+
+ private $moduleId = 0;
+ private $moduleArchive = false;
+ private $moduleTitle = false;
+ private $currentVersion = 0;
+ protected $moduleData = false;
+
+ /**
+ * Load all known config module types. This is done
+ * by including *.inc.php from inc/configmodule/. The
+ * files there should in turn call ConfigModule::registerModule()
+ * to register themselves.
+ */
+ public static function loadDb()
+ {
+ if (self::$moduleTypes !== false)
+ return;
+ self::$moduleTypes = array();
+ foreach (glob('inc/configmodule/*.inc.php') as $file) {
+ require_once $file;
+ }
+ }
+
+ /**
+ * Get all known config modules.
+ *
+ * @return array list of modules
+ */
+ public static function getList()
+ {
+ self::loadDb();
+ return self::$moduleTypes;
+ }
+
+ /**
+ * Add a known configuration module. Every inc/configmodule/*.inc.php should call this.
+ *
+ * @param string $id Identifier for the module.
+ * The module class must be called ConfigModule_{$id}, the wizard start class {$id}_Start.
+ * The wizard's classes should be located in modules/sysconfig/addmodule_{$id_lowercase}.inc.php
+ * @param string $title Title of this module type
+ * @param string $description Description for this module type
+ * @param string $group Title for group this module type belongs to
+ * @param bool $unique Can only one such module be added to a config?
+ * @param int $sortOrder Lower comes first, alphabetical ordering otherwiese
+ */
+ public static function registerModule($id, $title, $description, $group, $unique, $sortOrder = 0)
+ {
+ if (isset(self::$moduleTypes[$id]))
+ Util::traceError("Config Module $id already registered!");
+ $moduleClass = 'ConfigModule_' . $id;
+ $wizardClass = $id . '_Start';
+ if (!class_exists($moduleClass))
+ Util::traceError("Class $moduleClass does not exist!");
+ if (get_parent_class($moduleClass) !== 'ConfigModule')
+ Util::traceError("$moduleClass does not have ConfigModule as its parent!");
+ self::$moduleTypes[$id] = array(
+ 'title' => $title,
+ 'description' => $description,
+ 'group' => $group,
+ 'unique' => $unique,
+ 'sortOrder' => $sortOrder,
+ 'moduleClass' => $moduleClass,
+ 'wizardClass' => $wizardClass
+ );
+ }
+
+ /**
+ * Get fresh instance of ConfigModule subclass for given module type.
+ *
+ * @param string $moduleType name of module type
+ * @return \ConfigModule module instance
+ */
+ public static function getInstance($moduleType)
+ {
+ self::loadDb();
+ if (!isset(self::$moduleTypes[$moduleType]))
+ return false;
+ return new self::$moduleTypes[$moduleType]['moduleClass'];
+ }
+
+ /**
+ * Get module instance from id.
+ *
+ * @param int $moduleId module id to get
+ * @return ConfigModule The requested module from DB, or false on error
+ */
+ public static function get($moduleId)
+ {
+ $ret = Database::queryFirst("SELECT title, moduletype, filepath, contents, version FROM configtgz_module "
+ . " WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleId));
+ if ($ret === false)
+ return false;
+ $instance = self::getInstance($ret['moduletype']);
+ if ($instance === false)
+ return false;
+ $instance->currentVersion = $ret['version'];
+ $instance->moduleArchive = $ret['filepath'];
+ $instance->moduleData = json_decode($ret['contents'], true);
+ $instance->moduleId = $moduleId;
+ $instance->moduleTitle = $ret['title'];
+ return $instance;
+ }
+
+ /**
+ * Get the module version.
+ *
+ * @return int module version
+ */
+ protected abstract function moduleVersion();
+
+ /**
+ * Validate the module's configuration.
+ *
+ * @return boolean ok or not
+ */
+ protected abstract function validateConfig();
+
+ /**
+ * Set module specific data.
+ *
+ * @param string $key key, name or id of data being set
+ * @param mixed $value Module specific data
+ * @return boolean true if data was successfully set, false otherwise (i.e. invalid data being set)
+ */
+ public abstract function setData($key, $value);
+
+ /**
+ * Module specific version of generate.
+ *
+ * @param string $tgz File name of tgz module to write final output to
+ * @return array|boolean true if generation is completed immediately,
+ * a task struct if some task needs to be run for generation,
+ * false on error
+ */
+ protected abstract function generateInternal($tgz, $parent);
+
+ private final function createFileName()
+ {
+ return CONFIG_TGZ_LIST_DIR . '/modules/'
+ . $this->moduleType() . '_id-' . $this->moduleId . '__' . mt_rand() . '-' . time() . '.tgz';
+ }
+
+ /**
+ * Get module id (in db)
+ *
+ * @return int id
+ */
+ public final function id()
+ {
+ return $this->moduleId;
+ }
+
+ /**
+ * Get module archive file name.
+ *
+ * @return string tgz file absolute path
+ */
+ public final function archive()
+ {
+ return $this->moduleArchive;
+ }
+
+ /**
+ * Get the module type.
+ *
+ * @return string module type
+ */
+ public final function moduleType()
+ {
+ $name = get_class($this);
+ if ($name === false)
+ Util::traceError('ConfigModule::moduleType: get_class($this) returned false!');
+ // ConfigModule_*
+ if (!preg_match('/^ConfigModule_(\w+)$/', $name, $out))
+ Util::traceError('ConfigModule::moduleType: get_class($this) returned "' . $name . '"');
+ return $out[1];
+ }
+
+ /**
+ * Insert this config module into DB. Only
+ * valid if the object was created using the creating constructor,
+ * not if the instance was created using a database entry (static get method).
+ *
+ * @param string $title display name of the module
+ * @return boolean true if inserted successfully, false if module config is invalid
+ */
+ public final function insert($title)
+ {
+ if ($this->moduleId !== 0)
+ Util::traceError('ConfigModule::insert called when moduleId != 0');
+ if (!$this->validateConfig())
+ return false;
+ $this->moduleTitle = $title;
+ // Insert
+ Database::exec("INSERT INTO configtgz_module (title, moduletype, filepath, contents, version, status) "
+ . " VALUES (:title, :type, '', :contents, :version, :status)", array(
+ 'title' => $title,
+ 'type' => $this->moduleType(),
+ 'contents' => json_encode($this->moduleData),
+ 'version' => 0,
+ 'status' => 'MISSING'
+ ));
+ $this->moduleId = Database::lastInsertId();
+ if (!is_numeric($this->moduleId))
+ Util::traceError('Inserting new config module into DB did not yield a numeric insert id');
+ $this->moduleArchive = $this->createFileName();
+ Database::exec("UPDATE configtgz_module SET filepath = :path WHERE moduleid = :moduleid LIMIT 1", array(
+ 'path' => $this->moduleArchive,
+ 'moduleid' => $this->moduleId
+ ));
+ return true;
+ }
+
+ /**
+ * Generate the module's tgz, don't wait for completion.
+ * Updating the database etc. will happen later through a callback.
+ *
+ * @param boolean $deleteOnError if true, the db entry will be deleted if generation failed
+ * @param int $timeoutMs maximum time in milliseconds we wait for completion
+ * @return string|boolean task id if deferred generation was started,
+ * true if generation succeeded (without using a task or within $timeoutMs)
+ * false on error
+ */
+ public final function generate($deleteOnError, $parent = NULL, $timeoutMs = 0)
+ {
+ if ($this->moduleId === 0 || $this->moduleTitle === false)
+ Util::traceError('ConfigModule::generateAsync called on uninitialized/uninserted module!');
+ $tmpTgz = '/tmp/bwlp-id-' . $this->moduleId . '_' . mt_rand() . '_' . time() . '.tgz';
+ $ret = $this->generateInternal($tmpTgz, $parent);
+ // 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 (!is_array($ret) || !isset($ret['id']) || Taskmanager::isFailed($ret)) {
+ if (is_array($ret)) // Failed
+ Taskmanager::addErrorMessage($ret);
+ if ($deleteOnError)
+ $this->delete();
+ else
+ $this->markFailed();
+ return false;
+ }
+ // Still running, add callback
+ TaskmanagerCallback::addCallback($ret, 'cbConfModCreated', array(
+ 'moduleid' => $this->moduleId,
+ 'deleteOnError' => $deleteOnError,
+ 'tmpTgz' => $tmpTgz
+ ));
+ return $ret['id'];
+ }
+
+ /**
+ * Delete the module.
+ */
+ public final function delete()
+ {
+ 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;
+ if ($ret !== false) {
+ if ($this->moduleArchive)
+ Taskmanager::submit('DeleteFile', array('file' => $this->moduleArchive), true);
+ $this->moduleId = 0;
+ $this->moduleData = false;
+ $this->moduleTitle = false;
+ $this->moduleArchive = false;
+ }
+ }
+
+ private final function markUpdated($tmpTgz)
+ {
+ if ($this->moduleId === 0)
+ Util::traceError('ConfigModule::markUpdated called with invalid module id!');
+ if ($this->moduleArchive === false)
+ $this->moduleArchive = $this->createFileName();
+ // Move file
+ if ($tmpTgz === false) {
+ if (!file_exists($this->moduleArchive)) {
+ EventLog::failure('ConfigModule::markUpdated for "' . $this->moduleTitle . '" called with no tmpTgz and no existing tgz!');
+ $this->markFailed();
+ return false;
+ }
+ } else {
+ $task = Taskmanager::submit('MoveFile', array(
+ 'source' => $tmpTgz,
+ 'destination' => $this->moduleArchive
+ ));
+ $task = Taskmanager::waitComplete($task, 5000);
+ if (Taskmanager::isFailed($task) || !Taskmanager::isFinished($task)) {
+ if (!API && !AJAX)
+ Taskmanager::addErrorMessage($task);
+ else
+ EventLog::failure('Could not move ' . $tmpTgz . ' to ' . $this->moduleArchive . ' while generating "' . $this->moduleTitle . '"');
+ $this->markFailed();
+ return false;
+ }
+ }
+ // Update DB entry
+ return 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;
+ }
+
+ private final function markFailed()
+ {
+ if ($this->moduleId === 0)
+ Util::traceError('ConfigModule::markFailed called with invalid module id!');
+ if ($this->moduleArchive === false)
+ $this->moduleArchive = $this->createFileName();
+ if (!file_exists($this->moduleArchive))
+ $status = 'MISSING';
+ else
+ $status = 'OUTDATED';
+ return Database::exec("UPDATE configtgz_module SET filepath = :filename, status = :status WHERE moduleid = :id LIMIT 1", array(
+ 'id' => $this->moduleId,
+ 'filename' => $this->moduleArchive,
+ 'status' => $status
+ )) !== false;
+ }
+
+ ################# Callbacks ##############
+
+ /**
+ * Event callback for when the server ip changed.
+ * Override this if you need to handle this, otherwise
+ * the base implementation does nothing.
+ */
+ public function event_serverIpChanged()
+ {
+ // Do::Nothing()
+ }
+
+ ##################### STATIC CALLBACKS #####################
+
+ /**
+ * Will be called if the server's IP address changes. The event will be propagated
+ * to all config module classes so action can be taken if appropriate.
+ */
+ public static function serverIpChanged()
+ {
+ self::loadDb();
+ foreach (self::$moduleTypes as $module) {
+ $instance = new $module['moduleClass'];
+ $instance->event_serverIpChanged();
+ }
+ }
+
+ /**
+ * Called when (re)generating a config module failed, so we can
+ * update the status in the DB and add a server log entry.
+ *
+ * @param array $task
+ * @param array $args contains 'moduleid' and optionally 'deleteOnError' and 'tmpTgz'
+ */
+ public static function generateFailed($task, $args)
+ {
+ if (!isset($args['moduleid']) || !is_numeric($args['moduleid'])) {
+ EventLog::warning('Ignoring generateFailed event as it has no moduleid assigned.');
+ return;
+ }
+ $module = self::get($args['moduleid']);
+ if ($module === false) {
+ EventLog::warning('generateFailed callback for module id ' . $args['moduleid'] . ', but no instance could be generated.');
+ return;
+ }
+ if (isset($task['data']['error']))
+ $error = $task['data']['error'];
+ elseif (isset($task['data']['messages']))
+ $error = $task['data']['messages'];
+ else
+ $error = '';
+ EventLog::failure("Generating module '" . $module->moduleTitle . "' failed.", $error);
+ if ($args['deleteOnError'])
+ $module->delete();
+ else
+ $module->markFailed();
+ }
+
+ /**
+ * (Re)generating a config module succeeded. Update db entry.
+ *
+ * @param array $args contains 'moduleid' and optionally 'deleteOnError' and 'tmpTgz'
+ */
+ public static function generateSucceeded($args)
+ {
+ if (!isset($args['moduleid']) || !is_numeric($args['moduleid'])) {
+ EventLog::warning('Ignoring generateSucceeded event as it has no moduleid assigned.');
+ return;
+ }
+ $module = self::get($args['moduleid']);
+ if ($module === false) {
+ EventLog::warning('generateSucceeded callback for module id ' . $args['moduleid'] . ', but no instance could be generated.');
+ return;
+ }
+ if (isset($args['tmpTgz']))
+ $module->markUpdated($args['tmpTgz']);
+ else
+ $module->markUpdated(false);
+ }
+
+}
diff --git a/inc/configmodule/adauth.inc.php b/inc/configmodule/adauth.inc.php
index c0d4860c..06ac5460 100644
--- a/inc/configmodule/adauth.inc.php
+++ b/inc/configmodule/adauth.inc.php
@@ -1,7 +1,7 @@
<?php
-ConfigModules::registerModule(
- ConfigModule_AdAuth::MODID, // ID
+ConfigModule::registerModule(
+ 'AdAuth', // ID
Dictionary::translate('config-module', 'adAuth_title'), // Title
Dictionary::translate('config-module', 'adAuth_description'), // Description
Dictionary::translate('config-module', 'group_authentication'), // Group
@@ -10,115 +10,51 @@ ConfigModules::registerModule(
class ConfigModule_AdAuth extends ConfigModule
{
- const MODID = 'AdAuth';
- public static function insert($title, $server, $searchbase, $binddn, $bindpw, $home)
+ const VERSION = 1;
+
+ private static $REQUIRED_FIELDS = array('server', 'searchbase', 'binddn');
+ private static $OPTIONAL_FIELDS = array('bindpw', 'home');
+
+ protected function generateInternal($tgz, $parent)
{
- Database::exec("LOCK TABLE configtgz_module WRITE");
- Database::exec("INSERT INTO configtgz_module (title, moduletype, filepath, contents) "
- . " VALUES (:title, :modid, '', '')", array('title' => $title, 'modid' => self::MODID));
- $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 WHERE moduletype = :modid", array(
- 'modid' => self::MODID
- ));
- $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,
- 'home' => $home,
- 'proxyport' => $port
- );
- $data = json_encode($ownEntry);
- if ($data === false) Util::traceError('Serializing the AD data failed.');
- $moduleTgz = 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' => $moduleTgz,
- 'contents' => $data
- ));
- Database::exec("UNLOCK TABLES");
- // Add archive file name to array before returning it
- $ownEntry['moduleid'] = $id;
- $ownEntry['filename'] = $moduleTgz;
- return $ownEntry;
+ $config = $this->moduleData;
+ $config['parentTask'] = $parent;
+ $config['failOnParentFail'] = false;
+ $config['proxyip'] = Property::getServerIp();
+ $config['proxyport'] = 3100 + $this->id();
+ $config['filename'] = $tgz;
+ $config['moduleid'] = $this->id();
+ return Taskmanager::submit('CreateAdConfig', $config);
}
- /**
- * To be called if the server ip changes, as it's embedded in the AD module configs.
- * This will then recreate all AD tgz modules.
- */
- private static function rebuildAll($parent = NULL)
+ protected function moduleVersion()
{
- // Stop all running instances of ldadp
- $task = Taskmanager::submit('LdadpLauncher', array(
- 'parentTask' => $parent,
- 'failOnParentFail' => false,
- 'ids' => array()
- ));
- $ads = self::getAll();
- if (empty($ads)) // Nothing to do
- return false;
+ return self::VERSION;
+ }
- if (isset($task['id']))
- $parent = $task['id'];
- foreach ($ads as $ad) {
- $ad['parentTask'] = $parent;
- $ad['failOnParentFail'] = false;
- $ad['proxyip'] = Property::getServerIp();
- $task = Taskmanager::submit('CreateAdConfig', $ad);
- if (isset($task['id']))
- $parent = $task['id'];
- }
- Trigger::ldadp($parent);
- return $parent;
+ protected function validateConfig()
+ {
+ // Check if required fields are filled
+ return Util::hasAllKeys($this->moduleData, self::$REQUIRED_FIELDS);
}
-
- /**
- * Get all existing AD proxy configs.
- *
- * @return array array of ad configs in DB with fields:
- * moduleid, filename, server, searchbase, binddn, bindpw, home, proxyport
- */
- public static function getAll()
+
+ public function setData($key, $value)
{
- $res = Database::simpleQuery("SELECT moduleid, filepath, contents FROM configtgz_module WHERE moduletype = :modid", array(
- 'modid' => self::MODID
- ));
- $mods = array();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $data = json_decode($row['contents'], true);
- $data['moduleid'] = $row['moduleid'];
- $data['filename'] = $row['filepath'];
- $mods[] = $data;
- }
- return $mods;
+ if (!in_array($key, self::$REQUIRED_FIELDS) && !in_array($key, self::$OPTIONAL_FIELDS))
+ return false;
+ $this->moduleData[$key] = $value;
+ return true;
}
-
+
// ############## Callbacks #############################
-
+
/**
* Server IP changed - rebuild all AD modules.
*/
public function event_serverIpChanged()
{
- self::rebuildAll();
+ $this->generate(false);
}
-
+
}
diff --git a/inc/configmodule/branding.inc.php b/inc/configmodule/branding.inc.php
index f293fda6..e8bd1da7 100644
--- a/inc/configmodule/branding.inc.php
+++ b/inc/configmodule/branding.inc.php
@@ -1,6 +1,6 @@
<?php
-ConfigModules::registerModule(
+ConfigModule::registerModule(
ConfigModule_Branding::MODID, // ID
Dictionary::translate('config-module', 'branding_title'), // Title
Dictionary::translate('config-module', 'branding_description'), // Description
@@ -10,35 +10,43 @@ ConfigModules::registerModule(
class ConfigModule_Branding extends ConfigModule
{
+
const MODID = 'Branding';
+ const VERSION = 1;
+
+ private $tmpFile = false;
- public static function insert($title, $archive)
+ protected function generateInternal($tgz, $parent)
{
- Database::exec("INSERT INTO configtgz_module (title, moduletype, filepath, contents) "
- . " VALUES (:title, :modid, '', '')", array('title' => $title, 'modid' => self::MODID));
- $id = Database::lastInsertId();
- if (!is_numeric($id))
- Util::traceError('Inserting new Branding Module into DB did not yield a numeric insert id');
- // Move tgz
- $moduleTgz = CONFIG_TGZ_LIST_DIR . '/modules/BRANDING_id_' . $id . '.' . mt_rand() . '.tgz';
- $task = Taskmanager::submit('MoveFile', array(
- 'source' => $archive,
- 'destination' => $moduleTgz
- ));
- $task = Taskmanager::waitComplete($task, 3000);
- if (Taskmanager::isFailed($task) || $task['statusCode'] !== TASK_FINISHED) {
- Taskmanager::addErrorMessage($task);
- Database::exec("DELETE FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array(
- 'moduleid' => $id
- ));
+ 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;
}
- // Update with path
- Database::exec("UPDATE configtgz_module SET filepath = :filename WHERE moduleid = :id LIMIT 1", array(
- 'id' => $id,
- 'filename' => $moduleTgz
+ $task = Taskmanager::submit('MoveFile', array(
+ 'source' => $this->tmpFile,
+ 'destination' => $tgz,
+ 'parentTask' => $parent,
+ 'failOnParentFail' => false
));
- return true;
+ return $task;
}
-
+
+ protected function moduleVersion()
+ {
+ return self::VERSION;
+ }
+
+ protected function validateConfig()
+ {
+ return $this->tmpFile !== false && file_exists($this->tmpFile);
+ }
+
+ public function setData($key, $value)
+ {
+ if ($key !== 'tmpFile' || !file_exists($value))
+ return false;
+ $this->tmpFile = $value;
+ }
+
}
diff --git a/inc/configmodule/customodule.inc.php b/inc/configmodule/customodule.inc.php
index 89f0aca6..195f738f 100644
--- a/inc/configmodule/customodule.inc.php
+++ b/inc/configmodule/customodule.inc.php
@@ -1,6 +1,6 @@
<?php
-ConfigModules::registerModule(
+ConfigModule::registerModule(
ConfigModule_CustomModule::MODID, // ID
Dictionary::translate('config-module', 'custom_title'), // Title
Dictionary::translate('config-module', 'custom_description'), // Description
@@ -12,5 +12,40 @@ ConfigModules::registerModule(
class ConfigModule_CustomModule extends ConfigModule
{
const MODID = 'CustomModule';
+ const VERSION = 1;
+
+ private $tmpFile = false;
+ 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;
+ }
+ $task = Taskmanager::submit('MoveFile', array(
+ 'source' => $this->tmpFile,
+ 'destination' => $tgz,
+ 'parentTask' => $parent,
+ 'failOnParentFail' => false
+ ));
+ return $task;
+ }
+
+ protected function moduleVersion()
+ {
+ return self::VERSION;
+ }
+
+ protected function validateConfig()
+ {
+ return $this->tmpFile !== false && file_exists($this->tmpFile);
+ }
+
+ public function setData($key, $value)
+ {
+ if ($key !== 'tmpFile' || !file_exists($value))
+ return false;
+ $this->tmpFile = $value;
+ }
}
diff --git a/inc/configmodules.inc.php b/inc/configmodules.inc.php
deleted file mode 100644
index bf870e4f..00000000
--- a/inc/configmodules.inc.php
+++ /dev/null
@@ -1,94 +0,0 @@
-<?php
-
-class ConfigModules
-{
-
- private static $moduleTypes = false;
-
- /**
- * Load all known config module types. This is done
- * by including *.inc.php from inc/configmodule/. The
- * files there should in turn call ConfigModule::registerModule()
- * to register themselves.
- */
- public static function loadDb()
- {
- if (self::$moduleTypes !== false)
- return;
- self::$moduleTypes = array();
- foreach (glob('inc/configmodule/*.inc.php') as $file) {
- require_once $file;
- }
- }
-
- /**
- * Get all known config modules.
- *
- * @return array list of modules
- */
- public static function getList()
- {
- self::loadDb();
- return self::$moduleTypes;
- }
-
- /**
- * Add a known configuration module. Every inc/configmodule/*.inc.php should call this.
- *
- * @param string $id Identifier for the module.
- * The module class must be called ConfigModule_{$id}, the wizard start class {$id}_Start.
- * The wizard's classes should be located in modules/sysconfig/addmodule_{$id_lowercase}.inc.php
- * @param string $title Title of this module type
- * @param string $description Description for this module type
- * @param string $group Title for group this module type belongs to
- * @param bool $unique Can only one such module be added to a config?
- * @param int $sortOrder Lower comes first, alphabetical ordering otherwiese
- */
- public static function registerModule($id, $title, $description, $group, $unique, $sortOrder = 0)
- {
- if (isset(self::$moduleTypes[$id]))
- Util::traceError("Config Module $id already registered!");
- $moduleClass = 'ConfigModule_' . $id;
- $wizardClass = $id . '_Start';
- if (!class_exists($moduleClass))
- Util::traceError("Class $moduleClass does not exist!");
- if (get_parent_class($moduleClass) !== 'ConfigModule')
- Util::traceError("$moduleClass does not have ConfigModule as its parent!");
- self::$moduleTypes[$id] = array(
- 'title' => $title,
- 'description' => $description,
- 'group' => $group,
- 'unique' => $unique,
- 'sortOrder' => $sortOrder,
- 'moduleClass' => $moduleClass,
- 'wizardClass' => $wizardClass
- );
- }
-
- /**
- * Will be called if the server's IP address changes. The event will be propagated
- * to all config module classes so action can be taken if appropriate.
- */
- public static function serverIpChanged()
- {
- self::loadDb();
- foreach (self::$moduleTypes as $module) {
- $instance = new $module['moduleClass'];
- $instance->event_serverIpChanged();
- }
- }
-
-}
-
-/**
- * Base class for config modules
- */
-abstract class ConfigModule
-{
-
- public function event_serverIpChanged()
- {
-
- }
-
-}
diff --git a/inc/database.inc.php b/inc/database.inc.php
index 2c535d04..efc330fe 100644
--- a/inc/database.inc.php
+++ b/inc/database.inc.php
@@ -20,7 +20,7 @@ class Database
*/
public static function getExpectedSchemaVersion()
{
- return 8;
+ return 9;
}
public static function needSchemaUpdate()
@@ -47,7 +47,8 @@ class Database
/**
* If you just need the first row of a query you can use this.
- * Will return an associative array, or false if no row matches the query
+ *
+ * @return array|boolean Associative array representing row, or false if no row matches the query
*/
public static function queryFirst($query, $args = array(), $ignoreError = false)
{
@@ -64,6 +65,7 @@ class Database
*
* @param string $query Query to run
* @param array $args Arguments to query
+ * @param boolean $ignoreError Ignore query errors and just return false
* @return int|boolean Number of rows affected, or false on error
*/
public static function exec($query, $args = array(), $ignoreError = false)
diff --git a/inc/property.inc.php b/inc/property.inc.php
index c16c8ad7..7c3b7d37 100644
--- a/inc/property.inc.php
+++ b/inc/property.inc.php
@@ -41,12 +41,14 @@ class Property
*/
private static function set($key, $value, $minage = 0)
{
- Database::exec("INSERT INTO property (name, value, dateline) VALUES (:key, :value, :dateline)"
- . " ON DUPLICATE KEY UPDATE value = VALUES(value), dateline = VALUES(dateline)", array(
- 'key' => $key,
- 'value' => $value,
- 'dateline' => ($minage === 0 ? 0 : time() + ($minage * 60))
- ));
+ if (self::$cache === false || self::get($key) != $value) { // Simple compare, so it works for numbers accidentally casted to string somewhere
+ Database::exec("INSERT INTO property (name, value, dateline) VALUES (:key, :value, :dateline)"
+ . " ON DUPLICATE KEY UPDATE value = VALUES(value), dateline = VALUES(dateline)", array(
+ 'key' => $key,
+ 'value' => $value,
+ 'dateline' => ($minage === 0 ? 0 : time() + ($minage * 60))
+ ));
+ }
if (self::$cache !== false) {
self::$cache[$key] = $value;
}
@@ -181,5 +183,15 @@ class Property
{
return self::get('password-type', 'password');
}
+
+ public static function setNeedsCallback($value)
+ {
+ return self::set('need-callback', $value, 5);
+ }
+
+ public static function getNeedsCallback()
+ {
+ return self::get('need-callback', 0);
+ }
}
diff --git a/inc/render.inc.php b/inc/render.inc.php
index 5827dcf9..31ba5a7d 100644
--- a/inc/render.inc.php
+++ b/inc/render.inc.php
@@ -69,7 +69,8 @@ class Render
',
self::$footer
,
- '</html>'
+ '</body>
+ </html>'
;
if ($zip) {
Header('Content-Encoding: gzip');
@@ -98,7 +99,7 @@ class Render
}
/**
- * Add raw html data to the footer-section of the generated page (after the closing body tag)
+ * Add raw html data to the footer-section of the generated page (right before the closing body tag)
*/
public static function addFooter($html)
{
diff --git a/inc/taskmanager.inc.php b/inc/taskmanager.inc.php
index 05db5b5d..5045fc75 100644
--- a/inc/taskmanager.inc.php
+++ b/inc/taskmanager.inc.php
@@ -29,7 +29,7 @@ class Taskmanager
* @param array $data data to pass to the task. the structure depends on the task.
* @param boolean $async if true, the function will not wait for the reply of the taskmanager, which means
* the return value is just true (and you won't know if the task could acutally be started)
- * @return array struct representing the task status, or result of submit, false on communication error
+ * @return array struct representing the task status (as a result of submit); false on communication error
*/
public static function submit($task, $data = false, $async = false)
{
@@ -154,7 +154,7 @@ class Taskmanager
{
if (!is_array($task) || !isset($task['statusCode']) || !isset($task['id']))
return false;
- if ($task['statusCode'] === TASK_ERROR || $task['statusCode'] === TASK_FINISHED)
+ if ($task['statusCode'] !== TASK_WAITING && $task['statusCode'] !== TASK_PROCESSING)
return true;
return false;
}
diff --git a/inc/taskmanagercallback.inc.php b/inc/taskmanagercallback.inc.php
index 951a1de3..a42f4819 100644
--- a/inc/taskmanagercallback.inc.php
+++ b/inc/taskmanagercallback.inc.php
@@ -13,7 +13,7 @@ class TaskmanagerCallback
* @param string|array $task Task or Task ID to define callback for
* @param string $callback name of callback function, must be a static method in this class
*/
- public static function addCallback($task, $callback)
+ public static function addCallback($task, $callback, $args = NULL)
{
if (!call_user_func_array('method_exists', array('TaskmanagerCallback', $callback))) {
EventLog::warning("addCallback: Invalid callback function: $callback");
@@ -25,10 +25,16 @@ class TaskmanagerCallback
EventLog::warning("addCallback: Not a valid task id: $task");
return;
}
- Database::exec("INSERT INTO callback (taskid, dateline, cbfunction) VALUES (:task, UNIX_TIMESTAMP(), :callback)", array(
+ if (is_null($args))
+ $args = '';
+ else
+ $args = serialize($args);
+ Database::exec("INSERT INTO callback (taskid, dateline, cbfunction, args) VALUES (:task, UNIX_TIMESTAMP(), :callback, :args)", array(
'task' => $task,
- 'callback' => $callback
+ 'callback' => $callback,
+ 'args' => $args
));
+ Property::setNeedsCallback(1);
}
/**
@@ -39,7 +45,7 @@ class TaskmanagerCallback
public static function getPendingCallbacks()
{
$retval = array();
- $res = Database::simpleQuery("SELECT taskid, cbfunction FROM callback");
+ $res = Database::simpleQuery("SELECT taskid, cbfunction, args FROM callback");
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
$retval[$row['taskid']][] = $row;
}
@@ -50,7 +56,7 @@ class TaskmanagerCallback
* Handle the given callback. Will delete the entry from the callback
* table if appropriate.
*
- * @param array $callback entry from the callback table (cbfunction + taskid)
+ * @param array $callback entry from the callback table (cbfunction + taskid + args)
* @param array $status status of the task as returned by the taskmanager. If NULL it will be queried.
*/
public static function handleCallback($callback, $status = NULL)
@@ -69,7 +75,10 @@ class TaskmanagerCallback
if (!call_user_func_array('method_exists', $func)) {
Eventlog::warning("handleCallback: Callback {$callback['cbfunction']} doesn't exist.");
} else {
- call_user_func($func, $status);
+ if (empty($callback['args']))
+ call_user_func($func, $status);
+ else
+ call_user_func($func, $status, unserialize($callback['args']));
}
}
}
@@ -85,13 +94,35 @@ class TaskmanagerCallback
EventLog::warning("Could not start/stop LDAP-AD-Proxy instances", $task['data']['messages']);
}
+ /**
+ * Result of restoring the server configuration
+ */
public static function dbRestored($task)
{
- error_log("dbRestored.");
- if (Taskmanager::isFinished($task) && !Taskmanager::isFailed($task)) {
- error_log("LOGGING.");
+ if (!Taskmanager::isFailed($task)) {
EventLog::info('Configuration backup restored.');
}
}
+
+ public static function adConfigCreate($task)
+ {
+ if (Taskmanager::isFailed($task))
+ EventLog::warning("Could not generate Active Directory configuration", $task['data']['error']);
+ }
+
+ /**
+ * Generating a config module has finished.
+ *
+ * @param array $task task obj
+ * @param array $args has keys 'moduleid' and optionally 'deleteOnError' and 'tmpTgz'
+ */
+ public static function cbConfModCreated($task, $args)
+ {
+ if (Taskmanager::isFailed($task)) {
+ ConfigModule::generateFailed($task, $args);
+ } else {
+ ConfigModule::generateSucceeded($args);
+ }
+ }
}
diff --git a/inc/trigger.inc.php b/inc/trigger.inc.php
index 38b25540..0410b2be 100644
--- a/inc/trigger.inc.php
+++ b/inc/trigger.inc.php
@@ -136,6 +136,7 @@ class Trigger
*/
public static function checkCallbacks()
{
+ $tasksLeft = false;
$callbackList = TaskmanagerCallback::getPendingCallbacks();
foreach ($callbackList as $taskid => $callbacks) {
$status = Taskmanager::status($taskid);
@@ -146,7 +147,11 @@ class Trigger
}
if (Taskmanager::isFailed($status) || Taskmanager::isFinished($status))
Taskmanager::release($status);
+ else
+ $tasksLeft = true;
}
+ if (!$tasksLeft)
+ Property::setNeedsCallback(0);
}
private static function triggerDaemons($action, $parent, &$taskids)
diff --git a/inc/util.inc.php b/inc/util.inc.php
index cce46536..9d0eced9 100644
--- a/inc/util.inc.php
+++ b/inc/util.inc.php
@@ -216,4 +216,21 @@ SADFACE;
return true;
}
+ /**
+ * Check whether $arrax contains all keys given in $keyList
+ * @param array $array An array
+ * @param array $keyList A list of strings which must all be valid keys in $array
+ * @return boolean
+ */
+ public static function hasAllKeys($array, $keyList)
+ {
+ if (!is_array($array))
+ return false;
+ foreach ($keyList as $key) {
+ if (!isset($array[$key]))
+ return false;
+ }
+ return true;
+ }
+
}
diff --git a/index.php b/index.php
index 969a3d47..884a6190 100644
--- a/index.php
+++ b/index.php
@@ -80,6 +80,7 @@ if ((isset($_REQUEST['async'])) || (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) &&
} else {
define('AJAX', false);
}
+define('API', false);
// Autoload classes from ./inc which adhere to naming scheme <lowercasename>.inc.php
function slxAutoloader($class)
@@ -144,5 +145,9 @@ if (defined('CONFIG_FOOTER')) {
Render::addTemplate('footer', array('text' => CONFIG_FOOTER));
}
+if (Property::getNeedsCallback()) {
+ Render::addTemplate('tm-callback-trigger');
+}
+
// Send page to client.
Render::output();
diff --git a/modules/sysconfig.inc.php b/modules/sysconfig.inc.php
index 134f3143..0da58767 100644
--- a/modules/sysconfig.inc.php
+++ b/modules/sysconfig.inc.php
@@ -249,34 +249,21 @@ class Page_SysConfig extends Page
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) {
+ $module = ConfigModule::get($configid);
+ if ($module === 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));
+ $module->delete();
Util::redirect('?do=SysConfig');
}
private function initAddModule()
{
- ConfigModules::loadDb();
+ ConfigModule::loadDb();
require_once 'modules/sysconfig/addmodule.inc.php';
- $step = Request::any('step', 0);
- if ($step === 0) {
- $step = 'AddModule_Start';
- } elseif (!class_exists($step) && preg_match('/^([a-zA-Z0-9]+)_/', $step, $out)) {
+ $step = Request::any('step', 'AddModule_Start');
+ if (!class_exists($step) && preg_match('/^([a-zA-Z0-9]+)_/', $step, $out)) {
require_once 'modules/sysconfig/addmodule_' . strtolower($out[1]) . '.inc.php';
}
AddModule_Base::setStep($step);
@@ -284,7 +271,7 @@ class Page_SysConfig extends Page
private function initAddConfig()
{
- ConfigModules::loadDb();
+ ConfigModule::loadDb();
require_once 'modules/sysconfig/addconfig.inc.php';
$step = Request::any('step', 0);
if ($step === 0)
diff --git a/modules/sysconfig/addconfig.inc.php b/modules/sysconfig/addconfig.inc.php
index 0f35095f..4949539e 100644
--- a/modules/sysconfig/addconfig.inc.php
+++ b/modules/sysconfig/addconfig.inc.php
@@ -108,7 +108,7 @@ class AddConfig_Start extends AddConfig_Base
protected function renderInternal()
{
- $mods = ConfigModules::getList();
+ $mods = ConfigModule::getList();
$res = Database::simpleQuery("SELECT moduleid, title, moduletype, filepath FROM configtgz_module"
. " ORDER BY title ASC");
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
diff --git a/modules/sysconfig/addmodule.inc.php b/modules/sysconfig/addmodule.inc.php
index 7fa5b5b9..69160964 100644
--- a/modules/sysconfig/addmodule.inc.php
+++ b/modules/sysconfig/addmodule.inc.php
@@ -109,7 +109,7 @@ class AddModule_Start extends AddModule_Base
protected function renderInternal()
{
$title = $order = array();
- $mods = ConfigModules::getList();
+ $mods = ConfigModule::getList();
foreach ($mods as $module) {
$title[] = $module['title'];
$order[] = $module['sortOrder'];
diff --git a/modules/sysconfig/addmodule_adauth.inc.php b/modules/sysconfig/addmodule_adauth.inc.php
index 4873d505..baa705b2 100644
--- a/modules/sysconfig/addmodule_adauth.inc.php
+++ b/modules/sysconfig/addmodule_adauth.inc.php
@@ -125,15 +125,24 @@ class AdAuth_Finish extends AddModule_Base
$title = Request::post('title');
if (empty($title))
$title = 'AD: ' . Request::post('server');
- $config = ConfigModule_AdAuth::insert($title, Request::post('server'), $searchbase, $binddn, Request::post('bindpw', ''), Request::post('home', ''));
- $config['proxyip'] = Property::getServerIp();
- $tgz = Taskmanager::submit('CreateAdConfig', $config);
- if (!isset($tgz['id'])) {
+ $module = ConfigModule::getInstance('AdAuth');
+ $module->setData('server', Request::post('server'));
+ $module->setData('searchbase', $searchbase);
+ $module->setData('binddn', $binddn);
+ $module->setData('bindpw', Request::post('bindpw'));
+ $module->setData('home', Request::post('home'));
+ if (!$module->insert($title)) {
+ Message::addError('value-invalid', 'any', 'any');
+ $tgz = false;
+ } else {
+ $tgz = $module->generate(true);
+ }
+ if ($tgz === false) {
AddModule_Base::setStep('AdAuth_Start'); // Continues with AdAuth_Start for render()
return;
}
$this->taskIds = array(
- 'tm-config' => $tgz['id'],
+ 'tm-config' => $tgz,
);
}
diff --git a/modules/sysconfig/addmodule_branding.inc.php b/modules/sysconfig/addmodule_branding.inc.php
index a6c16d9d..8bcc10a1 100644
--- a/modules/sysconfig/addmodule_branding.inc.php
+++ b/modules/sysconfig/addmodule_branding.inc.php
@@ -46,8 +46,10 @@ class Branding_ProcessFile extends AddModule_Base
// URL - launch task that fetches the SVG file from it
if (strpos($url, '://') === false)
$url = "http://$url";
- if (!$this->downloadSvg($this->svgFile, $url))
+ $title = false;
+ if (!$this->downloadSvg($this->svgFile, $url, $title))
Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start');
+ Session::set('logo_name', $title);
}
chmod($this->svgFile, 0644);
$this->tarFile = '/tmp/bwlp-' . time() . '-' . mt_rand() . '.tgz';
@@ -83,7 +85,7 @@ class Branding_ProcessFile extends AddModule_Base
}
/**
- * Downlaod an svg file from the given url. This function has "wikipedia support", it tries to detect
+ * Download an svg file from the given url. This function has "wikipedia support", it tries to detect
* URLs in wikipedia articles or thumbnails and then find the actual svg file.
*
* @param string $svgName file to download to
@@ -91,8 +93,9 @@ class Branding_ProcessFile extends AddModule_Base
* @return boolean true of download succeded, false on download error (also returns true if downloaded file doesn't
* seem to be svg!)
*/
- private function downloadSvg($svgName, $url)
+ private function downloadSvg($svgName, $url, &$title)
{
+ $title = false;
// [wikipedia] Did someone paste a link to a thumbnail of the svg? Let's fix that...
if (preg_match('#^(.*)/thumb/(.*\.svg)/.*\.svg#', $url, $out)) {
$url = $out[1] . '/' . $out[2];
@@ -110,12 +113,14 @@ 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]);
foreach ($out[1] as $res) {
- if (!strpos($res, 'action=edit')) {
- $new = $this->internetCombineUrl($url, html_entity_decode($res, ENT_COMPAT, 'UTF-8'));
- if ($new !== $url)
- break;
- }
+ if (strpos($res, 'action=edit') !== false)
+ continue;
+ $new = $this->internetCombineUrl($url, html_entity_decode($res, ENT_COMPAT, 'UTF-8'));
+ if ($new !== $url)
+ break;
}
if ($new === $url)
break;
@@ -124,7 +129,7 @@ class Branding_ProcessFile extends AddModule_Base
}
break;
}
- return true;
+ return false;
}
/**
@@ -141,7 +146,6 @@ class Branding_ProcessFile extends AddModule_Base
return $relative;
extract(parse_url($absolute));
-
$path = dirname($path);
if ($relative{0} === '/') {
@@ -188,18 +192,29 @@ class Branding_Finish extends AddModule_Base
protected function preprocessInternal()
{
$title = Request::post('title');
+ if ($title === false || empty($title))
+ $title = Session::get('logo_name');
if ($title === false || empty($title)) {
- Message::addError('missing-file'); // TODO: Ask for title again instead of starting over
+ Message::addError('missing-title'); // TODO: Ask for title again instead of starting over
Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start');
}
$tgz = Session::get('logo_tgz');
if ($tgz === false || !file_exists($tgz)) {
- Message::addError('missing-file');
+ Message::addError('error-read', $tgz);
+ Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start');
+ }
+ $module = ConfigModule::getInstance('Branding');
+ if ($module === false) {
+ Message::addError('error-read', 'branding.inc.php');
Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start');
}
- if (!ConfigModule_Branding::insert($title, $tgz))
+ $module->setData('tmpFile', $tgz);
+ if (!$module->insert($title))
+ Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start');
+ elseif (!$module->generate(true))
Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start');
Session::set('logo_tgz', false);
+ Session::set('logo_name', false);
Session::save();
// Yay
Message::addSuccess('module-added');
diff --git a/modules/sysconfig/addmodule_custommodule.inc.php b/modules/sysconfig/addmodule_custommodule.inc.php
index 66ba33a5..c42453db 100644
--- a/modules/sysconfig/addmodule_custommodule.inc.php
+++ b/modules/sysconfig/addmodule_custommodule.inc.php
@@ -39,7 +39,7 @@ class CustomModule_ProcessUpload extends AddModule_Base
Message::addError('upload-failed', Util::uploadErrorString($_FILES['modulefile']['error']));
Util::redirect('?do=SysConfig');
}
- $tempfile = '/tmp/bwlp-' . mt_rand(1, 100000) . '-' . crc32($_SERVER['REMOTE_HOST']) . '.tmp';
+ $tempfile = '/tmp/bwlp-' . mt_rand(1, 100000) . '-' . crc32($_SERVER['REMOTE_ADDR']) . '.tmp';
if (!move_uploaded_file($_FILES['modulefile']['tmp_name'], $tempfile)) {
Message::addError('error-write', $tempfile);
Util::redirect('?do=SysConfig');
@@ -108,7 +108,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-' . Util::sanitizeFilename($title) . '-' . microtime(true) . '.tgz';
+ $destFile = tempnam(sys_get_temp_dir(), 'bwlp-') . '.tgz';
Taskmanager::submit('RecompressArchive', array(
'id' => $this->taskId,
'inputFiles' => array($tempfile),
@@ -122,13 +122,20 @@ class CustomModule_CompressModule extends AddModule_Base
if ($status['statusCode'] != TASK_FINISHED) {
$this->taskError($status);
}
- // Seems ok, create entry in DB
- $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);
- Util::traceError("Could not insert module into Database");
+ // Seems ok, create entry
+ $module = ConfigModule::getInstance('CustomModule');
+ if ($module === false) {
+ Message::addError('error-read', 'custommodule.inc.php');
+ Util::redirect('?do=SysConfig&action=addmodule&step=CustomModule_Start');
}
+ $module->setData('tmpFile', $destFile);
+ if (!$module->insert($title))
+ Util::redirect('?do=SysConfig&action=addmodule&step=CustomModule_Start');
+ elseif (!$module->generate(true))
+ Util::redirect('?do=SysConfig&action=addmodule&step=CustomModule_Start');
+ Session::set('mod_temp', false);
+ Session::save();
+ // Yay
Message::addSuccess('module-added');
Util::redirect('?do=SysConfig');
}
diff --git a/modules/syslog.inc.php b/modules/syslog.inc.php
index 7bd0c197..43a9bd28 100644
--- a/modules/syslog.inc.php
+++ b/modules/syslog.inc.php
@@ -42,6 +42,13 @@ class Page_SysLog extends Page
if (!empty($whereClause)) $whereClause = ' WHERE logtypeid ' . $not . ' IN (' . implode(', ', $whereClause) . ')';
}
if (!isset($whereClause) || empty($whereClause)) $whereClause = '';
+ if (Request::get('ip')) {
+ if (empty($whereClause))
+ $whereClause .= ' WHERE ';
+ else
+ $whereClause .= ' AND ';
+ $whereClause .= "clientip LIKE '" . preg_replace('/[^0-9\.\:]/', '', Request::get('ip')) . "%'";
+ }
$today = date('d.m.Y');
$yesterday = date('d.m.Y', time() - 86400);
diff --git a/templates/footer.html b/templates/footer.html
index 603c0f83..8cf71a5c 100644
--- a/templates/footer.html
+++ b/templates/footer.html
@@ -1,2 +1,2 @@
<hr>
-<div class="pull-right slx-footer">{{text}}</div>
+<div class="pull-right slx-footer">{{text}}</div> \ No newline at end of file
diff --git a/templates/internetaccess/_page.html b/templates/internetaccess/_page.html
index c5b6efe9..f4e0aa55 100644
--- a/templates/internetaccess/_page.html
+++ b/templates/internetaccess/_page.html
@@ -30,7 +30,7 @@
</div>
<div class="input-group">
<span class="input-group-addon slx-ga">{{lang_proxyPassword}}</span>
- <input name="PROXY_PASSWORD" value="{{PROXY_PASSWORD}}" type="text" class="form-control">
+ <input name="PROXY_PASSWORD" value="{{PROXY_PASSWORD}}" type="{{password_type}}" class="form-control">
</div>
<button class="btn btn-primary" type="submit">{{lang_save}}</button>
</div>
diff --git a/templates/page-vmstore.html b/templates/page-vmstore.html
index ba000fed..0fc53e92 100644
--- a/templates/page-vmstore.html
+++ b/templates/page-vmstore.html
@@ -42,7 +42,7 @@
<span class="input-group-addon">
{{lang_password}}
</span>
- <input type="text" class="form-control" name="cifspasswd" value="{{cifspasswd}}" placeholder="{{lang_password}}">
+ <input type="{{password_type}}" class="form-control" name="cifspasswd" value="{{cifspasswd}}" placeholder="{{lang_password}}">
</div>
<br>
<label for="cifsuserro">{{lang_readOnly}}</label>
@@ -54,7 +54,7 @@
<span class="input-group-addon">
{{lang_password}}
</span>
- <input type="text" class="form-control" name="cifspasswdro" value="{{cifspasswdro}}" placeholder="{{lang_password}}">
+ <input type="{{password_type}}" class="form-control" name="cifspasswdro" value="{{cifspasswdro}}" placeholder="{{lang_password}}">
</div>
</div>
</div>
diff --git a/templates/serversetup/ipxe.html b/templates/serversetup/ipxe.html
index 3d3d9b1d..60817f3e 100644
--- a/templates/serversetup/ipxe.html
+++ b/templates/serversetup/ipxe.html
@@ -29,7 +29,7 @@
<div class="form-group">
<strong>{{lang_masterPassword}}</strong>
<div class="form-narrow">
- <input type="text" class="form-control" name="masterpassword" value="{{masterpasswordclear}}">
+ <input type="{{password_type}}" class="form-control" name="masterpassword" value="{{masterpasswordclear}}">
</div>
<i>{{lang_masterPasswordHelp}}</i>
</div>
diff --git a/templates/sysconfig/ad-finish.html b/templates/sysconfig/ad-finish.html
index 0a16fc70..f73cad9d 100644
--- a/templates/sysconfig/ad-finish.html
+++ b/templates/sysconfig/ad-finish.html
@@ -24,4 +24,6 @@ function ldapCb(task)
$('#back').attr('style', '');
}
}
+if ('{{tm-config}}' == '')
+ $('#finish').attr('style', '');
</script>
diff --git a/templates/tm-callback-trigger.html b/templates/tm-callback-trigger.html
new file mode 100644
index 00000000..26b362ca
--- /dev/null
+++ b/templates/tm-callback-trigger.html
@@ -0,0 +1,5 @@
+<script type="text/javascript">
+ setTimeout(function () {
+ $.post('api.php?do=cb', { token: TOKEN });
+ }, 500);
+</script> \ No newline at end of file