diff --git a/src/mibew/configs/routing.yml b/src/mibew/configs/routing.yml
index bad93070..6ac5baff 100644
--- a/src/mibew/configs/routing.yml
+++ b/src/mibew/configs/routing.yml
@@ -570,6 +570,13 @@ plugin_uninstall:
_access_check: Mibew\AccessControl\Check\PermissionsCheck
_access_permissions: [CAN_ADMINISTRATE]
+plugin_update:
+ path: /operator/plugin/{plugin_name}/update
+ defaults:
+ _controller: Mibew\Controller\PluginController::updateAction
+ _access_check: Mibew\AccessControl\Check\PermissionsCheck
+ _access_permissions: [CAN_ADMINISTRATE]
+
plugins:
path: /operator/plugin
defaults:
diff --git a/src/mibew/libs/classes/Mibew/Controller/PluginController.php b/src/mibew/libs/classes/Mibew/Controller/PluginController.php
index 7076fb60..e5950829 100644
--- a/src/mibew/libs/classes/Mibew/Controller/PluginController.php
+++ b/src/mibew/libs/classes/Mibew/Controller/PluginController.php
@@ -158,6 +158,40 @@ class PluginController extends AbstractController
return $this->redirect($this->generateUrl('plugins'));
}
+ /**
+ * Updates a plugin.
+ *
+ * @param Request $request Incoming request.
+ * @return string Rendered page content.
+ * @throws NotFoundException If the plugin with specified name is not found
+ * in the system.
+ */
+ public function updateAction(Request $request)
+ {
+ csrf_check_token($request);
+
+ $plugin_name = $request->attributes->get('plugin_name');
+
+ if (!PluginUtils::pluginExists($plugin_name)) {
+ throw new NotFoundException('The plugin is not found.');
+ }
+
+ // Update the plugin
+ if (!PluginManager::getInstance()->update($plugin_name)) {
+ $error = getlocal(
+ 'Plugin "{0}" cannot be updated.',
+ array($plugin_name)
+ );
+ $request->attributes->set('errors', array($error));
+
+ // The plugin cannot be updated by some reasons. Just rebuild
+ // index page and show errors there.
+ return $this->indexAction($request);
+ }
+
+ return $this->redirect($this->generateUrl('plugins'));
+ }
+
/**
* Builds plugins list that will be passed to templates engine.
*
@@ -170,13 +204,15 @@ class PluginController extends AbstractController
$plugin = new PluginInfo($plugin_name);
$plugins[] = array(
'name' => $plugin_name,
- 'version' => $plugin->getInstalledVersion() ?: $plugin->getVersion(),
+ 'version' => $plugin->isInstalled() ? $plugin->getInstalledVersion() : $plugin->getVersion(),
'dependencies' => $plugin->getDependencies(),
'enabled' => $plugin->isEnabled(),
'installed' => $plugin->isInstalled(),
+ 'needsUpdate' => $plugin->needsUpdate(),
'canBeEnabled' => $plugin->canBeEnabled(),
'canBeDisabled' => $plugin->canBeDisabled(),
'canBeUninstalled' => $plugin->canBeUninstalled(),
+ 'canBeUpdated' => $plugin->canBeUpdated(),
);
}
diff --git a/src/mibew/libs/classes/Mibew/Plugin/PluginInfo.php b/src/mibew/libs/classes/Mibew/Plugin/PluginInfo.php
index 573b6bab..4820bf91 100644
--- a/src/mibew/libs/classes/Mibew/Plugin/PluginInfo.php
+++ b/src/mibew/libs/classes/Mibew/Plugin/PluginInfo.php
@@ -217,6 +217,17 @@ class PluginInfo
return $this->getState()->installed;
}
+ /**
+ * Checks if the plugin needs to be updated.
+ *
+ * @return bool
+ */
+ public function needsUpdate()
+ {
+ return $this->isInstalled()
+ && (version_compare($this->getVersion(), $this->getInstalledVersion()) > 0);
+ }
+
/**
* Checks if the plugin can be enabled.
*
@@ -294,6 +305,27 @@ class PluginInfo
return true;
}
+ /**
+ * Checks if the plugin can be updated.
+ *
+ * @return boolean
+ */
+ public function canBeUpdated()
+ {
+ if (!$this->needsUpdate()) {
+ return false;
+ }
+
+ foreach (array_keys($this->getDependencies()) as $dependency_name) {
+ $dependency = new PluginInfo($dependency_name);
+ if ($dependency->needsUpdate()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
/**
* Creates plugin info object based on a state object.
*
diff --git a/src/mibew/libs/classes/Mibew/Plugin/PluginManager.php b/src/mibew/libs/classes/Mibew/Plugin/PluginManager.php
index 97ec956c..07aea7df 100644
--- a/src/mibew/libs/classes/Mibew/Plugin/PluginManager.php
+++ b/src/mibew/libs/classes/Mibew/Plugin/PluginManager.php
@@ -19,6 +19,9 @@
namespace Mibew\Plugin;
+use Mibew\Plugin\Utils as PluginUtils;
+use Mibew\Maintenance\Utils as MaintenanceUtils;
+
/**
* Manage plugins.
*
@@ -102,7 +105,7 @@ class PluginManager
// Builds Dependency graph with available plugins.
$graph = new DependencyGraph();
foreach (State::loadAllEnabled() as $plugin_state) {
- if (!Utils::pluginExists($plugin_state->pluginName)) {
+ if (!PluginUtils::pluginExists($plugin_state->pluginName)) {
trigger_error(
sprintf(
'Plugin "%s" exists in database base but is not found in file system!',
@@ -266,4 +269,74 @@ class PluginManager
return true;
}
+
+ /**
+ * Tries to update a plugin.
+ *
+ * @param string $plugin_name Name of the plugin to update.
+ * @return boolean Indicates if the plugin has been updated or not.
+ */
+ public function update($plugin_name)
+ {
+ $plugin = new PluginInfo($plugin_name);
+
+ if (!$plugin->needsUpdate()) {
+ // There is no need to update the plugin
+ return true;
+ }
+
+ if (!$plugin->canBeUpdated()) {
+ // The plugin cannot be updated.
+ return false;
+ }
+
+ try {
+ // Perform incremental updates
+ $updates = MaintenanceUtils::getUpdates($plugin->getClass());
+ foreach ($updates as $version => $method) {
+ // Skip updates to lower versions.
+ if (version_compare($version, $plugin->getInstalledVersion()) <= 0) {
+ continue;
+ }
+
+ // Skip updates to versions that greater then the current plugin
+ // version.
+ if (version_compare($version, $plugin->getVersion()) > 0) {
+ break;
+ }
+
+ // Run the update
+ if (!$method()) {
+ // By some reasons we cannot update to the next version.
+ // Stop the process here.
+ return false;
+ }
+
+ // Store new version number in the database. With this info
+ // we can rerun the updating process if one of pending
+ // updates fails.
+ $plugin->getState()->version = $version;
+ $plugin->getState()->save();
+ }
+ } catch (\Exception $e) {
+ // Something went wrong
+ trigger_error(
+ sprintf(
+ 'Update of "%s" plugin failed: %s',
+ $plugin->getName(),
+ $e->getMessage()
+ ),
+ E_USER_WARNING
+ );
+
+ return false;
+ }
+
+ // All updates are done. Make sure the state of the plugin contains
+ // current plugin version.
+ $plugin->getState()->version = $plugin->getVersion();
+ $plugin->getState()->save();
+
+ return true;
+ }
}
diff --git a/src/mibew/styles/pages/default/templates_src/server_side/plugins.handlebars b/src/mibew/styles/pages/default/templates_src/server_side/plugins.handlebars
index bd3cd857..97552964 100644
--- a/src/mibew/styles/pages/default/templates_src/server_side/plugins.handlebars
+++ b/src/mibew/styles/pages/default/templates_src/server_side/plugins.handlebars
@@ -48,6 +48,13 @@
{{/if}}
{{/if}}
{{/if}}
+ {{#if needsUpdate}}
+ {{#if canBeUpdated}}
+ {{l10n "update"}}
+ {{else}}
+ {{l10n "update"}}
+ {{/if}}
+ {{/if}}
{{else}}