diff --git a/app/Providers/PluginServiceProvider.php b/app/Providers/PluginServiceProvider.php index e4e96709..b2531572 100644 --- a/app/Providers/PluginServiceProvider.php +++ b/app/Providers/PluginServiceProvider.php @@ -17,6 +17,9 @@ class PluginServiceProvider extends ServiceProvider */ public function boot(PluginManager $plugins) { + // Disable plugins which has unsatisfied dependencies + $this->disableRequirementsUnsatisfiedPlugins($plugins); + // Store paths of class files of plugins $src_paths = []; @@ -52,6 +55,15 @@ class PluginServiceProvider extends ServiceProvider } } + protected function disableRequirementsUnsatisfiedPlugins(PluginManager $manager) + { + foreach ($manager->getEnabledPlugins() as $plugin) { + if (! $manager->isRequirementsSatisfied($plugin->name)) { + $manager->disable($plugin->name); + } + } + } + protected function registerPluginCallbackListener() { Event::listen([ diff --git a/app/Services/Plugin.php b/app/Services/Plugin.php index da5e4d2e..3c7a9a10 100644 --- a/app/Services/Plugin.php +++ b/app/Services/Plugin.php @@ -193,6 +193,25 @@ class Plugin implements Arrayable, ArrayAccess return $this->version; } + /** + * @param array $require + * @return Plugin + */ + public function setRequirements($require) + { + $this->require = $require; + + return $this; + } + + /** + * @return array + */ + public function getRequirements() + { + return (array) $this->require; + } + /** * @param bool $enabled * @return Plugin diff --git a/app/Services/PluginManager.php b/app/Services/PluginManager.php index 426c6fc1..2e1064e1 100644 --- a/app/Services/PluginManager.php +++ b/app/Services/PluginManager.php @@ -3,6 +3,7 @@ namespace App\Services; use App\Events; +use Composer\Semver\Semver; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use App\Events\PluginWasUninstalled; @@ -265,12 +266,78 @@ class PluginManager /** * Whether the plugin is enabled. * - * @param $plugin + * @param string $pluginName * @return bool */ - public function isEnabled($plugin) + public function isEnabled($pluginName) { - return in_array($plugin, $this->getEnabled()); + return in_array($pluginName, $this->getEnabled()); + } + + /** + * Get the unsatisfied requirements of plugin. + * + * @param string $pluginName + * @return array + */ + public function getUnsatisfiedRequirements($pluginName) + { + $plugin = $this->getPlugin($pluginName); + + if (! $plugin) { + throw new \InvalidArgumentException('Plugin with given name does not exist.'); + } + + $requirements = $plugin->getRequirements(); + + $unsatisfied = []; + + foreach ($requirements as $name => $versionConstraint) { + // Version requirement for the main application + if ($name == 'blessing-skin-server') { + if (! Semver::satisfies(config('app.version'), $versionConstraint)) { + $unsatisfied['blessing-skin-server'] = [ + 'version' => config('app.version'), + 'constraint' => $versionConstraint + ]; + } + + continue; + } + + $requiredPlugin = $this->getPlugin($name); + + if (!$requiredPlugin || !$requiredPlugin->isEnabled()) { + $unsatisfied[$name] = [ + 'version' => null, + 'constraint' => $versionConstraint + ]; + + continue; + } + + if (! Semver::satisfies($requiredPlugin->getVersion(), $versionConstraint)) { + $unsatisfied[$name] = [ + 'version' => $requiredPlugin->getVersion(), + 'constraint' => $versionConstraint + ]; + + continue; + } + } + + return $unsatisfied; + } + + /** + * Whether the plugin's requirements are satisfied. + * + * @param string $pluginName + * @return bool + */ + public function isRequirementsSatisfied($pluginName) + { + return empty($this->getUnsatisfiedRequirements($pluginName)); } /** diff --git a/composer.json b/composer.json index b636f186..8385685e 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,8 @@ "doctrine/inflector": "1.1.0", "laravel/framework": "5.2.*", "devitek/yaml-translation": "^2.0", - "printempw/laravel-datatables-lite": "^1.0" + "printempw/laravel-datatables-lite": "^1.0", + "composer/semver": "^1.4" }, "require-dev": { "fzaninotto/faker": "~1.4", diff --git a/composer.lock b/composer.lock index 8f5d6b71..8221f935 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "7cc7aede94ac363755668bb4198c9ca4", + "content-hash": "e6e453416d7f35a9441a2184964d5177", "packages": [ { "name": "classpreloader/classpreloader", @@ -60,6 +60,68 @@ ], "time": "2016-09-16T12:50:15+00:00" }, + { + "name": "composer/semver", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573" + }, + "dist": { + "type": "zip", + "url": "https://files.phpcomposer.com/files/composer/semver/c7cb9a2095a074d131b65a8a0cd294479d785573.zip", + "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.5 || ^5.0.5", + "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "time": "2016-08-30T16:08:34+00:00" + }, { "name": "devitek/yaml-translation", "version": "2.0.1",