diff --git a/modules/backend/controllers/Auth.php b/modules/backend/controllers/Auth.php index 105f2a6cf..8015d892f 100644 --- a/modules/backend/controllers/Auth.php +++ b/modules/backend/controllers/Auth.php @@ -9,7 +9,7 @@ use BackendAuth; use Backend\Models\User; use Backend\Models\AccessLog; use Backend\Classes\Controller; -use System\Classes\VersionManager; +use System\Classes\UpdateManager; use System\Classes\ApplicationException; use October\Rain\Support\ValidationException; use Exception; @@ -75,7 +75,7 @@ class Auth extends Controller ], true); // Load version updates - VersionManager::instance()->updateAll(); + UpdateManager::instance()->update(); // Log the sign in event AccessLog::add($user); diff --git a/modules/system/classes/PluginManager.php b/modules/system/classes/PluginManager.php index 3d33ae706..7c8efe11f 100644 --- a/modules/system/classes/PluginManager.php +++ b/modules/system/classes/PluginManager.php @@ -9,6 +9,7 @@ use Config; use RecursiveIteratorIterator; use RecursiveDirectoryIterator; use Illuminate\Container\Container; +use ApplicationException; /** * Plugin manager @@ -105,34 +106,6 @@ class PluginManager return $this->plugins; } - /** - * Cross checks all plugins and their dependancies, if not met plugins - * are disabled and vice versa. - */ - protected function loadDependencies() - { - foreach ($this->plugins as $id => $plugin) { - - if (!isset($plugin->require) || !$plugin->require) - continue; - - $required = is_array($plugin->require) ? $plugin->require : [$plugin->require]; - $disable = false; - foreach ($required as $require) { - if (!$this->hasPlugin($require)) - $disable = true; - - elseif (($pluginObj = $this->findByIdentifier($require)) && $pluginObj->disabled) - $disable = true; - } - - if ($disable) - $this->disablePlugin($id); - else - $this->enablePlugin($id); - } - } - /** * Runs the register() method on all plugins. Can only be called once. */ @@ -227,6 +200,18 @@ class PluginManager return $this->pathMap[$classId]; } + /** + * Check if a plugin exists and is enabled. + * @param string $id Plugin identifier, eg: Namespace.PluginName + * @return boolean + */ + public function exists($id) + { + return (!$this->findByIdentifier($id) || $this->isDisabled($id)) + ? false + : true; + } + /** * Returns an array with all registered plugins * The index is the plugin namespace, the value is the plugin information object. @@ -446,17 +431,108 @@ class PluginManager return true; } + // + // Dependencies + // + /** - * Check if a plugin exists and is enabled. - * @param string $id Plugin identifier, eg: Namespace.PluginName - * @return boolean + * Cross checks all plugins and their dependancies, if not met plugins + * are disabled and vice versa. */ - public static function pluginExists($id) + protected function loadDependencies() { - $instance = static::instance(); - return (!$instance->findByIdentifier($id) || $instance->isDisabled($id)) - ? false - : true; + foreach ($this->plugins as $id => $plugin) { + if (!$required = $this->getDependencies($plugin)) + continue; + + $disable = false; + foreach ($required as $require) { + if (!$this->hasPlugin($require)) + $disable = true; + + elseif (($pluginObj = $this->findByIdentifier($require)) && $pluginObj->disabled) + $disable = true; + } + + if ($disable) + $this->disablePlugin($id); + else + $this->enablePlugin($id); + } + } + + /** + * Returns the plugin identifiers that are required by the supplied plugin. + * @param string $plugin Plugin identifier, object or class + * @return array + */ + public function getDependencies($plugin) + { + if (is_string($plugin) && (!$plugin = $this->findByIdentifier($identifer))) + return false; + + if (!isset($plugin->require) || !$plugin->require) + return null; + + return is_array($plugin->require) ? $plugin->require : [$plugin->require]; + } + + /** + * Sorts a collection of plugins, in the order that they should be actioned, + * according to their given dependencies. Least dependent come first. + * @param array $plugins Object collection to sort, or null to sort all. + * @return array Collection of sorted plugin identifiers + */ + public function sortByDependencies($plugins = null) + { + if (!is_array($plugins)) + $plugins = $this->getPlugins(); + + $result = []; + $checklist = $plugins; + + $loopCount = 0; + while (count($checklist)) { + + if (++$loopCount > 999) + throw new ApplicationException('Too much recursion'); + + foreach ($checklist as $code => $plugin) { + + /* + * Get dependencies and remove any aliens + */ + $depends = $this->getDependencies($plugin) ?: []; + $depends = array_filter($depends, function($pluginCode) use ($plugins) { + return isset($plugins[$pluginCode]); + }); + + /* + * No dependencies + */ + if (!$depends) { + array_push($result, $code); + unset($checklist[$code]); + continue; + } + + /* + * Find dependencies that have not been checked + */ + $depends = array_diff($depends, $result); + if (count($depends) > 0) + continue; + + /* + * All dependencies are checked + */ + array_push($result, $code); + unset($checklist[$code]); + } + + } + + return $result; } } \ No newline at end of file diff --git a/modules/system/classes/UpdateManager.php b/modules/system/classes/UpdateManager.php index 04e8dcd2e..7ac9dc4ab 100644 --- a/modules/system/classes/UpdateManager.php +++ b/modules/system/classes/UpdateManager.php @@ -104,9 +104,9 @@ class UpdateManager /* * Update plugins */ - $plugins = $this->pluginManager->getPlugins(); - foreach ($plugins as $name => $plugin) { - $this->updatePlugin($name); + $plugins = $this->pluginManager->sortByDependencies(); + foreach ($plugins as $plugin) { + $this->updatePlugin($plugin); } Parameters::set('system::update.count', 0); diff --git a/modules/system/classes/VersionManager.php b/modules/system/classes/VersionManager.php index aad27e31a..1a0d6c641 100644 --- a/modules/system/classes/VersionManager.php +++ b/modules/system/classes/VersionManager.php @@ -67,21 +67,6 @@ class VersionManager $this->pluginManager = PluginManager::instance(); } - /** - * Performs the update for all plugins - */ - public function updateAll() - { - $plugins = $this->pluginManager->getPlugins(); - - foreach ($plugins as $code => $plugin) { - if (!$this->hasVersionFile($code)) - continue; - - $this->updatePlugin($code); - } - } - /** * Updates a single plugin by its code or object with it's latest changes. */