diff options
29 files changed, 722 insertions, 294 deletions
@@ -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; + } + } @@ -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 |