From 08bc886d1b54c44b08f7c578a20136b6148719ea Mon Sep 17 00:00:00 2001 From: Sam Georges Date: Wed, 21 May 2014 16:36:05 +1000 Subject: [PATCH] Plugins with missing dependancies are disabled by the system. Plugins can now be disabled manually by config (see config cms.disablePlugins). --- CHANGELOG.md | 4 +- app/config/cms.php | 10 ++ modules/system/classes/PluginBase.php | 9 ++ modules/system/classes/PluginManager.php | 145 ++++++++++++++++++++++- modules/system/console/CacheClear.php | 5 + 5 files changed, 170 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4f5cd89e..21160df1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -* **Build 86** (2014-05-21) +* **Build 87** (2014-05-21) + - Plugins can now be disabled manually by config (see config cms.disablePlugins). + - Plugins with missing dependancies are disabled by the system. - Fixes an issue where paid plugins could not be downloaded. * **Build 84** (2014-05-20) diff --git a/app/config/cms.php b/app/config/cms.php index 3eaf6a08e..239b96556 100644 --- a/app/config/cms.php +++ b/app/config/cms.php @@ -23,6 +23,16 @@ return array( */ 'loadModules' => ['System', 'Backend', 'Cms'], + /* + |-------------------------------------------------------------------------- + | Sepcific plugins to disable + |-------------------------------------------------------------------------- + | + | Specify plugin codes which will always be disabled in the application. + | + */ + 'disablePlugins' => [], + /* |-------------------------------------------------------------------------- | Back-end URI prefix diff --git a/modules/system/classes/PluginBase.php b/modules/system/classes/PluginBase.php index b3dc7c018..698a33534 100644 --- a/modules/system/classes/PluginBase.php +++ b/modules/system/classes/PluginBase.php @@ -10,6 +10,15 @@ use Illuminate\Support\ServiceProvider as ServiceProviderBase; */ abstract class PluginBase extends ServiceProviderBase { + /** + * @var array Plugin dependencies + */ + public $require = []; + + /** + * @var boolean Determine if this plugin should be loaded (false) or not (true). + */ + public $disabled = false; /** * Returns information about this plugin, including plugin name and developer name. diff --git a/modules/system/classes/PluginManager.php b/modules/system/classes/PluginManager.php index e69ae7955..0d96c14a2 100644 --- a/modules/system/classes/PluginManager.php +++ b/modules/system/classes/PluginManager.php @@ -45,13 +45,54 @@ class PluginManager */ protected $booted = false; + /** + * @var string Path to the disarm file. + */ + protected $metaPath; + + /** + * @var array Collection of disabled plugins + */ + protected $disabledPlugins = []; + /** * Initializes the plugin manager */ protected function init() { $this->app = App::make('app'); + $this->metaPath = Config::get('app.manifest'); + $this->loadDisabled(); $this->loadPlugins(); + $this->loadDependencies(); + } + + /** + * 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); + } } /** @@ -78,6 +119,13 @@ class PluginManager $classObj = new $pluginClassName($this->app); $classId = $this->getIdentifier($classObj); + + /* + * Check for disabled plugins + */ + if ($this->isDisabled($classId)) + $classObj->disabled = true; + $this->plugins[$classId] = $classObj; $this->pathMap[$classId] = $classPath; } @@ -85,6 +133,92 @@ class PluginManager return $this->plugins; } + /** + * Loads all disabled plugins from the meta file. + */ + protected function loadDisabled() + { + $path = $this->metaPath.'/disabled.json'; + + if (($configDisabled = Config::get('cms.disablePlugins')) && is_array($configDisabled)) { + foreach ($configDisabled as $disabled) + $this->disabledPlugins[$disabled] = true; + } + + if (File::exists($path)) { + $disabled = json_decode(File::get($path), true); + $this->disabledPlugins = array_merge($this->disabledPlugins, $disabled); + } + else { + $this->writeDisabled(); + } + } + + /** + * Determines if a plugin is disabled by looking at the meta information + * or the application configuration. + * @return boolean + */ + public function isDisabled($id) + { + $code = $this->getIdentifier($id); + if (array_key_exists($code, $this->disabledPlugins)) + return true; + } + + /** + * Write the disabled plugins to a meta file. + */ + protected function writeDisabled() + { + $path = $this->metaPath.'/disabled.json'; + File::put($path, json_encode($this->disabledPlugins)); + } + + /** + * Disables a single plugin in the system. + * @param string $id Plugin code/namespace + * @param bool $user Set to true if disabled by the user + */ + public function disablePlugin($id, $isUser = false) + { + $code = $this->getIdentifier($id); + if (array_key_exists($code, $this->disabledPlugins)) + return false; + + $this->disabledPlugins[$code] = $isUser; + $this->writeDisabled(); + + if ($pluginObj = $this->findByIdentifier($code)) + $pluginObj->disabled = true; + + return true; + } + + /** + * Enables a single plugin in the system. + * @param string $id Plugin code/namespace + * @param bool $user Set to true if enabled by the user + */ + public function enablePlugin($id, $isUser = false) + { + $code = $this->getIdentifier($id); + if (!array_key_exists($code, $this->disabledPlugins)) + return false; + + // Prevent system from enabling plugins disabled by the user + if (!$isUser && $this->disabledPlugins[$code] === true) + return false; + + unset($this->disabledPlugins[$code]); + $this->writeDisabled(); + + if ($pluginObj = $this->findByIdentifier($code)) + $pluginObj->disabled = false; + + return true; + } + /** * Runs the register() method on all plugins. Can only be called once. */ @@ -94,6 +228,9 @@ class PluginManager return; foreach ($this->plugins as $pluginId => $plugin) { + if ($plugin->disabled) + continue; + $plugin->register(); $pluginPath = $this->getPluginPath($plugin); $pluginNamespace = strtolower($pluginId); @@ -145,8 +282,12 @@ class PluginManager if ($this->booted) return; - foreach ($this->plugins as $plugin) + foreach ($this->plugins as $plugin) { + if ($plugin->disabled) + continue; + $plugin->boot(); + } $this->booted = true; } @@ -178,7 +319,7 @@ class PluginManager */ public function getPlugins() { - return $this->plugins; + return array_diff_key($this->plugins, $this->disabledPlugins); } /** diff --git a/modules/system/console/CacheClear.php b/modules/system/console/CacheClear.php index 57939fa18..cd81f03ce 100644 --- a/modules/system/console/CacheClear.php +++ b/modules/system/console/CacheClear.php @@ -37,6 +37,11 @@ class CacheClear extends ClearCommand $this->info('Twig cache cleared!'); } + /* + * Meta + */ + $this->files->delete($this->laravel['config']['app.manifest'].'/disabled.json'); + parent::fire(); }