diff --git a/src/mibew/configs/routing.yml b/src/mibew/configs/routing.yml
index a583da5d..713490c6 100644
--- a/src/mibew/configs/routing.yml
+++ b/src/mibew/configs/routing.yml
@@ -678,6 +678,20 @@ translations:
_access_permissions: [CAN_ADMINISTRATE]
## Updates
+update:
+ path: /update
+ defaults:
+ _controller: Mibew\Controller\UpdateController::indexAction
+ _access_check: Mibew\AccessControl\Check\PermissionsCheck
+ _access_permissions: [CAN_ADMINISTRATE]
+
+update_run:
+ path: /update/run
+ defaults:
+ _controller: Mibew\Controller\UpdateController::runUpdateAction
+ _access_check: Mibew\AccessControl\Check\PermissionsCheck
+ _access_permissions: [CAN_ADMINISTRATE]
+
updates:
path: /operator/updates
defaults:
diff --git a/src/mibew/libs/classes/Mibew/Controller/HomeController.php b/src/mibew/libs/classes/Mibew/Controller/HomeController.php
index 8c97c429..23be72a2 100644
--- a/src/mibew/libs/classes/Mibew/Controller/HomeController.php
+++ b/src/mibew/libs/classes/Mibew/Controller/HomeController.php
@@ -52,11 +52,11 @@ class HomeController extends AbstractController
$page = array(
'version' => MIBEW_VERSION,
'localeLinks' => get_locale_links(),
- 'needUpdate' => Settings::get('dbversion') != DB_VERSION,
+ 'needUpdate' => version_compare(Settings::get('dbversion'), DB_VERSION, '<'),
'profilePage' => $this->generateUrl('operator_edit', array('operator_id' => $operator['operatorid'])),
// Use another entry point as an install URL.
// TODO: Use real update route when the System Updater will be ready
- 'updateWizard' => $request->getBasePath() . '/install.php',
+ 'updateWizard' => $this->generateUrl('update'),
'newFeatures' => Settings::get('featuresversion') != FEATURES_VERSION,
'featuresPage' => $this->generateUrl('settings_features'),
'isOnline' => $is_online,
diff --git a/src/mibew/libs/classes/Mibew/Controller/UpdateController.php b/src/mibew/libs/classes/Mibew/Controller/UpdateController.php
new file mode 100644
index 00000000..74e6279a
--- /dev/null
+++ b/src/mibew/libs/classes/Mibew/Controller/UpdateController.php
@@ -0,0 +1,102 @@
+ MIBEW_VERSION,
+ 'fixedwrap' => true,
+ 'title' => getlocal('Update'),
+ );
+
+ return $this->render('update_intro', $parameters);
+ }
+
+ /**
+ * Runs the Updater.
+ *
+ * @param Request $request Incoming request.
+ * @return Response|string Rendered page contents or Symfony's response
+ * object.
+ */
+ public function runUpdateAction(Request $request)
+ {
+ $upd = $this->getUpdater();
+ $upd->run();
+
+ $parameters = array(
+ 'version' => MIBEW_VERSION,
+ 'fixedwrap' => true,
+ 'title' => getlocal('Update'),
+ 'done' => $this->getUpdater()->getLog(),
+ 'errors' => $this->getUpdater()->getErrors(),
+ );
+
+ return $this->render('update_progress', $parameters);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getStyle()
+ {
+ if (is_null($this->style)) {
+ $this->style = $this->prepareStyle(new PageStyle('default'));
+ }
+
+ return $this->style;
+ }
+
+ /**
+ * Returns an instance of Updater.
+ *
+ * @return Updater
+ */
+ protected function getUpdater()
+ {
+ if (is_null($this->updater)) {
+ $this->updater = new Updater();
+ }
+
+ return $this->updater;
+ }
+}
diff --git a/src/mibew/libs/classes/Mibew/Maintenance/Installer.php b/src/mibew/libs/classes/Mibew/Maintenance/Installer.php
index 879c1174..5c3d61d9 100644
--- a/src/mibew/libs/classes/Mibew/Maintenance/Installer.php
+++ b/src/mibew/libs/classes/Mibew/Maintenance/Installer.php
@@ -563,7 +563,7 @@ class Installer
*
* If Mibew is not installed yet boolean false will be returned.
*
- * @return int|boolean Database structure version or boolean false if the
+ * @return string|boolean Database structure version or boolean false if the
* version cannot be determined.
*/
protected function getDatabaseVersion()
@@ -585,10 +585,10 @@ class Installer
if (!$result) {
// It seems that database structure version isn't stored in the
// database.
- return 0;
+ return '0.0.0';
}
- return intval($result['version']);
+ return $result['version'];
}
/**
@@ -598,7 +598,7 @@ class Installer
*/
protected function tablesNeedUpdate()
{
- return ($this->getDatabaseVersion() < DB_VERSION);
+ return version_compare($this->getDatabaseVersion(), DB_VERSION, '<');
}
/**
diff --git a/src/mibew/libs/classes/Mibew/Maintenance/Updater.php b/src/mibew/libs/classes/Mibew/Maintenance/Updater.php
new file mode 100644
index 00000000..9932d23d
--- /dev/null
+++ b/src/mibew/libs/classes/Mibew/Maintenance/Updater.php
@@ -0,0 +1,271 @@
+errors;
+ }
+
+ /**
+ * Returns list of all information messages.
+ *
+ * @return string[]
+ */
+ public function getLog()
+ {
+ return $this->log;
+ }
+
+ /**
+ * Performs all actions that are needed to update database structure.
+ *
+ * @return boolean True if the system updated successfully and false
+ * otherwise.
+ */
+ public function run()
+ {
+ $current_version = $this->getDatabaseVersion();
+
+ if (!preg_match("/^([0-9]{1,2}\.){2}[0-9]{1,2}(-beta\.[0-9]+)?$/", $current_version)) {
+ $this->errors[] = getlocal(
+ 'The current version ({0}) is unknown or wrong formated',
+ array($current_version)
+ );
+
+ return false;
+ }
+
+ if (version_compare($current_version, self::MIN_VERSION) < 0) {
+ $this->errors[] = getlocal(
+ 'You can update the system only from {0} and later versions. The current version is {1}',
+ array(
+ self::MIN_VERSION,
+ $current_version
+ )
+ );
+
+ return false;
+ }
+
+ // Get list of all available updates
+ $updates = $this->getUpdates();
+
+ // Check if updates should be performed
+ $versions = array_keys($updates);
+ $last_version = end($versions);
+ if (version_compare($current_version, $last_version) >= 0) {
+ $this->log[] = getlocal('The database is already up to date');
+
+ return true;
+ }
+
+ try {
+ // Perform incremental updates
+ foreach ($updates as $version => $method) {
+ if (version_compare($version, $current_version) <= 0) {
+ // Skip updates to lower versions.
+ continue;
+ }
+
+ // Run the update
+ if (!$this->{$method}()) {
+ $this->errors[] = getlocal('Cannot update to {0}', array($version));
+
+ return false;
+ }
+
+ // Store new version number in the database. With this info
+ // we can rerun the updating process if one of pending
+ // updates fails.
+ if (!$this->setDatabaseVersion($version)) {
+ $this->errors[] = getlocal('Cannot store new version number');
+
+ return false;
+ } else {
+ $this->log[] = getlocal('Updated to {0}', array($version));
+ }
+
+ $current_version = $version;
+ }
+ } catch (\Exception $e) {
+ // Something went wrong
+ $this->errors[] = getlocal(
+ 'Update failed: {0}',
+ array($e->getMessage())
+ );
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns initialized database object.
+ *
+ * @return \Mibew\Database|boolean A database class instance or boolean
+ * false if something went wrong.
+ */
+ protected function getDatabase()
+ {
+ try {
+ $db = Database::getInstance();
+ $db->throwExeptions(true);
+
+ return $db;
+ } catch (\Exception $e) {
+ $this->errors[] = getlocal(
+ "Could not retrieve database instance. Error: {0}",
+ array($e->getMessage())
+ );
+
+ return false;
+ }
+ }
+
+ /**
+ * Gets version of existing database structure.
+ *
+ * If Mibew is not installed yet boolean false will be returned.
+ *
+ * @return int|boolean Database structure version or boolean false if the
+ * version cannot be determined.
+ */
+ protected function getDatabaseVersion()
+ {
+ if (!($db = $this->getDatabase())) {
+ return false;
+ }
+
+ try {
+ $result = $db->query(
+ "SELECT vcvalue AS version FROM {config} WHERE vckey = :key LIMIT 1",
+ array(':key' => 'dbversion'),
+ array('return_rows' => Database::RETURN_ONE_ROW)
+ );
+ } catch (\Exception $e) {
+ return false;
+ }
+
+ if (!$result) {
+ // It seems that database structure version isn't stored in the
+ // database.
+ return '0.0.0';
+ }
+
+ return $result['version'];
+ }
+
+ /**
+ * Updates version of database tables tructure.
+ *
+ * @param string $version Current version
+ * @return boolean True if the version is set and false otherwise.
+ */
+ protected function setDatabaseVersion($version)
+ {
+ if (!($db = $this->getDatabase())) {
+ return false;
+ }
+
+ try {
+ return $db->query(
+ 'UPDATE {config} SET vcvalue = :version WHERE vckey = :key LIMIT 1',
+ array(
+ ':version' => $version,
+ ':key' => 'dbversion',
+ )
+ );
+ } catch (\Exception $e) {
+ // The query fails by some reason.
+ return false;
+ }
+ }
+
+ /**
+ * Gets list of all available updates.
+ *
+ * @return array The keys of this array are version numbers and values are
+ * methods of the {@link \Mibew\Maintenance\Updater} class that should be
+ * performed.
+ */
+ protected function getUpdates()
+ {
+ $updates = array();
+
+ $self_reflection = new \ReflectionClass($this);
+ foreach ($self_reflection->getMethods() as $method_reflection) {
+ // Filter update methods
+ $name = $method_reflection->getName();
+ if (preg_match("/^update([0-9]+)(?:Beta([0-9]+))?$/", $name, $matches)) {
+ $version = Utils::formatVersionId($matches[1]);
+ // Check if a beta version is defined.
+ if (!empty($matches[2])) {
+ $version .= '-beta.' . $matches[2];
+ }
+
+ $updates[$version] = $name;
+ }
+ }
+
+ uksort($updates, 'version_compare');
+
+ return $updates;
+ }
+}
diff --git a/src/mibew/libs/common/constants.php b/src/mibew/libs/common/constants.php
index 3587a7ab..b31c34a5 100644
--- a/src/mibew/libs/common/constants.php
+++ b/src/mibew/libs/common/constants.php
@@ -20,17 +20,17 @@
/**
* Current version of Mibew Messenger
*/
-define('MIBEW_VERSION', '2.0');
+define('MIBEW_VERSION', '2.0.0-beta.1');
/**
* Current version of database structure
*/
-define('DB_VERSION', 20000);
+define('DB_VERSION', '2.0.0-beta.1');
/**
* Current version of implemented features
*/
-define('FEATURES_VERSION', '2.0');
+define('FEATURES_VERSION', '2.0.0-beta.1');
/**
* Prefix for session variables.
diff --git a/src/mibew/styles/pages/default/css/default.css b/src/mibew/styles/pages/default/css/default.css
index bf5901eb..2e70734c 100644
--- a/src/mibew/styles/pages/default/css/default.css
+++ b/src/mibew/styles/pages/default/css/default.css
@@ -907,7 +907,7 @@ table.awaiting .no-threads, table.awaiting .no-visitors {
z-index:101;
}
-/* install */
+/* install/update */
#install li {
list-style-type: circle;
@@ -933,6 +933,10 @@ table.awaiting .no-threads, table.awaiting .no-visitors {
font-weight: bold;
}
+#install .error {
+ color: #c13030;
+}
+
/* chat */
.message {
diff --git a/src/mibew/styles/pages/default/templates_src/server_side/update_intro.handlebars b/src/mibew/styles/pages/default/templates_src/server_side/update_intro.handlebars
new file mode 100644
index 00000000..589a4355
--- /dev/null
+++ b/src/mibew/styles/pages/default/templates_src/server_side/update_intro.handlebars
@@ -0,0 +1,29 @@
+{{#extends "_layout"}}
+ {{#override "content"}}
+ {{l10n "Follow the wizard to update your database."}}
+
+
+
+