From 07f9700179da74b056485375652c3b26d6fbce0d Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Sun, 8 Jan 2017 17:02:31 -0500 Subject: Offer the possibility to define version compatibility from plugins --- ChangeLog | 1 + app/Controller/PluginController.php | 1 + app/Core/Plugin/Base.php | 13 ++++++++ app/Core/Plugin/Directory.php | 13 +------- app/Core/Plugin/Loader.php | 48 +++++++++++++++++++++------- app/Core/Plugin/PluginException.php | 15 +++++++++ app/Core/Plugin/PluginInstallerException.php | 4 +-- app/Core/Plugin/Version.php | 38 ++++++++++++++++++++++ app/Template/plugin/show.php | 40 +++++++++++++++++++++++ doc/plugin-registration.markdown | 10 ++++++ tests/units/Core/Plugin/VersionTest.php | 29 +++++++++++++++++ 11 files changed, 185 insertions(+), 27 deletions(-) create mode 100644 app/Core/Plugin/PluginException.php create mode 100644 app/Core/Plugin/Version.php create mode 100644 tests/units/Core/Plugin/VersionTest.php diff --git a/ChangeLog b/ChangeLog index c925b7f6..e833f84c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,7 @@ Improvements: * Display project analytics in modal box * Display project exports in modal box * Improve accordion component +* Offer the possibility to define version compatibility from plugins Version 1.0.36 (Dec 30, 2016) ----------------------------- diff --git a/app/Controller/PluginController.php b/app/Controller/PluginController.php index 7b9d64d9..dbb739d6 100644 --- a/app/Controller/PluginController.php +++ b/app/Controller/PluginController.php @@ -23,6 +23,7 @@ class PluginController extends BaseController { $this->response->html($this->helper->layout->plugin('plugin/show', array( 'plugins' => $this->pluginLoader->getPlugins(), + 'incompatible_plugins' => $this->pluginLoader->getIncompatiblePlugins(), 'title' => t('Installed Plugins'), 'is_configured' => Installer::isConfigured(), ))); diff --git a/app/Core/Plugin/Base.php b/app/Core/Plugin/Base.php index 9d8167a9..e0b5954a 100644 --- a/app/Core/Plugin/Base.php +++ b/app/Core/Plugin/Base.php @@ -131,4 +131,17 @@ abstract class Base extends \Kanboard\Core\Base { return ''; } + + /** + * Get application compatibility version + * + * Examples: >=1.0.36, 1.0.37, APP_VERSION + * + * @access public + * @return string + */ + public function getCompatibleVersion() + { + return APP_VERSION; + } } diff --git a/app/Core/Plugin/Directory.php b/app/Core/Plugin/Directory.php index 27c3514e..dc32e655 100644 --- a/app/Core/Plugin/Directory.php +++ b/app/Core/Plugin/Directory.php @@ -36,18 +36,7 @@ class Directory extends BaseCore */ public function isCompatible(array $plugin, $appVersion = APP_VERSION) { - if (strpos($appVersion, 'master') !== false) { - return true; - } - - foreach (array('>=', '>') as $operator) { - if (strpos($plugin['compatible_version'], $operator) === 0) { - $pluginVersion = substr($plugin['compatible_version'], strlen($operator)); - return version_compare($appVersion, $pluginVersion, $operator); - } - } - - return $plugin['compatible_version'] === $appVersion; + return Version::isCompatible($plugin['compatible_version'], $appVersion); } /** diff --git a/app/Core/Plugin/Loader.php b/app/Core/Plugin/Loader.php index f2f6add7..38f41d39 100644 --- a/app/Core/Plugin/Loader.php +++ b/app/Core/Plugin/Loader.php @@ -4,6 +4,7 @@ namespace Kanboard\Core\Plugin; use Composer\Autoload\ClassLoader; use DirectoryIterator; +use Exception; use LogicException; use Kanboard\Core\Tool; @@ -22,6 +23,7 @@ class Loader extends \Kanboard\Core\Base * @var array */ protected $plugins = array(); + protected $incompatiblePlugins = array(); /** * Get list of loaded plugins @@ -34,6 +36,17 @@ class Loader extends \Kanboard\Core\Base return $this->plugins; } + /** + * Get list of not compatible plugins + * + * @access public + * @return Base[] + */ + public function getIncompatiblePlugins() + { + return $this->incompatiblePlugins; + } + /** * Scan plugin folder and load plugins * @@ -51,8 +64,7 @@ class Loader extends \Kanboard\Core\Base foreach ($dir as $fileInfo) { if ($fileInfo->isDir() && substr($fileInfo->getFilename(), 0, 1) !== '.') { $pluginName = $fileInfo->getFilename(); - $this->loadSchema($pluginName); - $this->initializePlugin($pluginName, $this->loadPlugin($pluginName)); + $this->initializePlugin($pluginName); } } } @@ -85,7 +97,7 @@ class Loader extends \Kanboard\Core\Base $className = '\Kanboard\Plugin\\'.$pluginName.'\\Plugin'; if (! class_exists($className)) { - throw new LogicException('Unable to load this plugin class '.$className); + throw new LogicException('Unable to load this plugin class: '.$className); } return new $className($this->container); @@ -96,18 +108,30 @@ class Loader extends \Kanboard\Core\Base * * @access public * @param string $pluginName - * @param Base $plugin */ - public function initializePlugin($pluginName, Base $plugin) + public function initializePlugin($pluginName) { - if (method_exists($plugin, 'onStartup')) { - $this->dispatcher->addListener('app.bootstrap', array($plugin, 'onStartup')); - } + try { + $plugin = $this->loadPlugin($pluginName); - Tool::buildDIC($this->container, $plugin->getClasses()); - Tool::buildDICHelpers($this->container, $plugin->getHelpers()); + if (Version::isCompatible($plugin->getCompatibleVersion(), APP_VERSION)) { + $this->loadSchema($pluginName); + + if (method_exists($plugin, 'onStartup')) { + $this->dispatcher->addListener('app.bootstrap', array($plugin, 'onStartup')); + } - $plugin->initialize(); - $this->plugins[$pluginName] = $plugin; + Tool::buildDIC($this->container, $plugin->getClasses()); + Tool::buildDICHelpers($this->container, $plugin->getHelpers()); + + $plugin->initialize(); + $this->plugins[$pluginName] = $plugin; + } else { + $this->incompatiblePlugins[$pluginName] = $plugin; + $this->logger->error($pluginName.' is not compatible with this version'); + } + } catch (Exception $e) { + $this->logger->critical($pluginName.': '.$e->getMessage()); + } } } diff --git a/app/Core/Plugin/PluginException.php b/app/Core/Plugin/PluginException.php new file mode 100644 index 00000000..fae7de35 --- /dev/null +++ b/app/Core/Plugin/PluginException.php @@ -0,0 +1,15 @@ +=', '>', '<=', '<') as $operator) { + if (strpos($pluginCompatibleVersion, $operator) === 0) { + $pluginVersion = substr($pluginCompatibleVersion, strlen($operator)); + return version_compare($appVersion, $pluginVersion, $operator); + } + } + + return $pluginCompatibleVersion === $appVersion; + } +} diff --git a/app/Template/plugin/show.php b/app/Template/plugin/show.php index 0e997c55..266568ac 100644 --- a/app/Template/plugin/show.php +++ b/app/Template/plugin/show.php @@ -1,3 +1,43 @@ + + + + + + + + + + + + + + $plugin): ?> + + + + + + + + + + + + + +
+ getPluginHomepage()): ?> + text->e($plugin->getPluginName()) ?> + + text->e($plugin->getPluginName()) ?> + + text->e($plugin->getPluginAuthor()) ?>text->e($plugin->getPluginVersion()) ?>text->e($plugin->getCompatibleVersion()) ?> + modal->confirm('trash-o', t('Uninstall'), 'PluginController', 'confirm', array('pluginId' => $pluginFolder)) ?> +
text->e($plugin->getPluginDescription()) ?>
+ + diff --git a/doc/plugin-registration.markdown b/doc/plugin-registration.markdown index 2c80aab3..5a4a6234 100644 --- a/doc/plugin-registration.markdown +++ b/doc/plugin-registration.markdown @@ -71,6 +71,15 @@ class Plugin extends Base { $this->template->hook->attach('template:layout:head', 'theme:layout/head'); } + + public function getCompatibleVersion() + { + // Examples: + // >=1.0.37 + // <1.0.37 + // <=1.0.37 + return '1.0.37'; + } } ``` @@ -93,6 +102,7 @@ Available methods from `Kanboard\Core\Plugin\Base`: - `getPluginHomepage()`: Should return plugin Homepage (link) - `setContentSecurityPolicy(array $rules)`: Override default HTTP CSP rules - `onStartup()`: If present, this method is executed automatically when the event "app.bootstrap" is triggered +- `getCompatibleVersion()`: You may want to specify the Kanboard version compatible with the plugin Your plugin registration class can also inherit from Kanboard\Core\Base, that way you can access all classes and methods of Kanboard easily. diff --git a/tests/units/Core/Plugin/VersionTest.php b/tests/units/Core/Plugin/VersionTest.php new file mode 100644 index 00000000..78f10d95 --- /dev/null +++ b/tests/units/Core/Plugin/VersionTest.php @@ -0,0 +1,29 @@ +assertFalse(Version::isCompatible('1.0.29', '1.0.28')); + $this->assertTrue(Version::isCompatible('1.0.28', '1.0.28')); + $this->assertTrue(Version::isCompatible('1.0.28', 'master.1234')); + $this->assertTrue(Version::isCompatible('>=1.0.32', 'master')); + $this->assertTrue(Version::isCompatible('>=1.0.32', '1.0.32')); + $this->assertTrue(Version::isCompatible('>=1.0.32', '1.0.33')); + $this->assertTrue(Version::isCompatible('>1.0.32', '1.0.33')); + $this->assertFalse(Version::isCompatible('>1.0.32', '1.0.32')); + $this->assertTrue(Version::isCompatible('1.0.32', 'v1.0.32')); + $this->assertTrue(Version::isCompatible('>=v1.0.32', 'v1.0.32')); + $this->assertTrue(Version::isCompatible('<=v1.0.36', 'v1.0.36')); + $this->assertFalse(Version::isCompatible('<1.0.36', 'v1.0.36')); + $this->assertTrue(Version::isCompatible('<1.0.40', '1.0.36')); + $this->assertTrue(Version::isCompatible('<=1.0.40', '1.0.36')); + $this->assertFalse(Version::isCompatible('<1.0.40', '1.0.40')); + $this->assertFalse(Version::isCompatible('1.0.40', 'v1.0.36')); + $this->assertTrue(Version::isCompatible('<1.1.0', 'v1.0.36')); + } +} -- cgit v1.2.3