diff options
author | Frederic Guillot <fred@kanboard.net> | 2017-01-08 17:02:31 -0500 |
---|---|---|
committer | Frederic Guillot <fred@kanboard.net> | 2017-01-08 17:02:31 -0500 |
commit | 07f9700179da74b056485375652c3b26d6fbce0d (patch) | |
tree | 027408d8895514daa33297d1fbfdda7f26f3dc1c | |
parent | 17ac414d744155d77c729f150115e8fd9e18f285 (diff) |
Offer the possibility to define version compatibility from plugins
-rw-r--r-- | ChangeLog | 1 | ||||
-rw-r--r-- | app/Controller/PluginController.php | 1 | ||||
-rw-r--r-- | app/Core/Plugin/Base.php | 13 | ||||
-rw-r--r-- | app/Core/Plugin/Directory.php | 13 | ||||
-rw-r--r-- | app/Core/Plugin/Loader.php | 48 | ||||
-rw-r--r-- | app/Core/Plugin/PluginException.php | 15 | ||||
-rw-r--r-- | app/Core/Plugin/PluginInstallerException.php | 4 | ||||
-rw-r--r-- | app/Core/Plugin/Version.php | 38 | ||||
-rw-r--r-- | app/Template/plugin/show.php | 40 | ||||
-rw-r--r-- | doc/plugin-registration.markdown | 10 | ||||
-rw-r--r-- | tests/units/Core/Plugin/VersionTest.php | 29 |
11 files changed, 185 insertions, 27 deletions
@@ -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 @@ -35,6 +37,17 @@ class Loader extends \Kanboard\Core\Base } /** + * Get list of not compatible plugins + * + * @access public + * @return Base[] + */ + public function getIncompatiblePlugins() + { + return $this->incompatiblePlugins; + } + + /** * Scan plugin folder and load plugins * * @access public @@ -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 @@ +<?php + +namespace Kanboard\Core\Plugin; + +use Exception; + +/** + * Class PluginException + * + * @package Kanboard\Core\Plugin + * @author Frederic Guillot + */ +class PluginException extends Exception +{ +} diff --git a/app/Core/Plugin/PluginInstallerException.php b/app/Core/Plugin/PluginInstallerException.php index 7d356c9b..31745f22 100644 --- a/app/Core/Plugin/PluginInstallerException.php +++ b/app/Core/Plugin/PluginInstallerException.php @@ -2,14 +2,12 @@ namespace Kanboard\Core\Plugin; -use Exception; - /** * Class PluginInstallerException * * @package Kanboard\Core\Plugin * @author Frederic Guillot */ -class PluginInstallerException extends Exception +class PluginInstallerException extends PluginException { } diff --git a/app/Core/Plugin/Version.php b/app/Core/Plugin/Version.php new file mode 100644 index 00000000..ba5e0443 --- /dev/null +++ b/app/Core/Plugin/Version.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Core\Plugin; + +/** + * Class Version + * + * @package Kanboard\Core\Plugin + * @author Frederic Guillot + */ +class Version +{ + /** + * Check plugin version compatibility with application version + * + * @param string $pluginCompatibleVersion + * @param string $appVersion + * @return bool + */ + public static function isCompatible($pluginCompatibleVersion, $appVersion = APP_VERSION) + { + if (strpos($appVersion, 'master') !== false) { + return true; + } + + $appVersion = str_replace('v', '', $appVersion); + $pluginCompatibleVersion = str_replace('v', '', $pluginCompatibleVersion); + + foreach (array('>=', '>', '<=', '<') 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 @@ +<?php if (! empty($incompatible_plugins)): ?> + <div class="page-header"> + <h2><?= t('Incompatible Plugins') ?></h2> + </div> + <table> + <tr> + <th class="column-35"><?= t('Name') ?></th> + <th class="column-25"><?= t('Author') ?></th> + <th class="column-10"><?= t('Version') ?></th> + <th class="column-12"><?= t('Compatibility') ?></th> + <?php if ($is_configured): ?> + <th><?= t('Action') ?></th> + <?php endif ?> + </tr> + + <?php foreach ($incompatible_plugins as $pluginFolder => $plugin): ?> + <tr> + <td> + <?php if ($plugin->getPluginHomepage()): ?> + <a href="<?= $plugin->getPluginHomepage() ?>" target="_blank" rel="noreferrer"><?= $this->text->e($plugin->getPluginName()) ?></a> + <?php else: ?> + <?= $this->text->e($plugin->getPluginName()) ?> + <?php endif ?> + </td> + <td><?= $this->text->e($plugin->getPluginAuthor()) ?></td> + <td><?= $this->text->e($plugin->getPluginVersion()) ?></td> + <td><?= $this->text->e($plugin->getCompatibleVersion()) ?></td> + <?php if ($is_configured): ?> + <td> + <?= $this->modal->confirm('trash-o', t('Uninstall'), 'PluginController', 'confirm', array('pluginId' => $pluginFolder)) ?> + </td> + <?php endif ?> + </tr> + <tr> + <td colspan="<?= $is_configured ? 6 : 5 ?>"><?= $this->text->e($plugin->getPluginDescription()) ?></td> + </tr> + <?php endforeach ?> + </table> +<?php endif ?> + <div class="page-header"> <h2><?= t('Installed Plugins') ?></h2> </div> 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 @@ +<?php + +use Kanboard\Core\Plugin\Version; + +require_once __DIR__.'/../../Base.php'; + +class VersionTest extends Base +{ + public function testIsCompatible() + { + $this->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')); + } +} |