Offer the possibility to define version compatibility from plugins
This commit is contained in:
@@ -14,6 +14,7 @@ Improvements:
|
|||||||
* Display project analytics in modal box
|
* Display project analytics in modal box
|
||||||
* Display project exports in modal box
|
* Display project exports in modal box
|
||||||
* Improve accordion component
|
* Improve accordion component
|
||||||
|
* Offer the possibility to define version compatibility from plugins
|
||||||
|
|
||||||
Version 1.0.36 (Dec 30, 2016)
|
Version 1.0.36 (Dec 30, 2016)
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class PluginController extends BaseController
|
|||||||
{
|
{
|
||||||
$this->response->html($this->helper->layout->plugin('plugin/show', array(
|
$this->response->html($this->helper->layout->plugin('plugin/show', array(
|
||||||
'plugins' => $this->pluginLoader->getPlugins(),
|
'plugins' => $this->pluginLoader->getPlugins(),
|
||||||
|
'incompatible_plugins' => $this->pluginLoader->getIncompatiblePlugins(),
|
||||||
'title' => t('Installed Plugins'),
|
'title' => t('Installed Plugins'),
|
||||||
'is_configured' => Installer::isConfigured(),
|
'is_configured' => Installer::isConfigured(),
|
||||||
)));
|
)));
|
||||||
|
|||||||
@@ -131,4 +131,17 @@ abstract class Base extends \Kanboard\Core\Base
|
|||||||
{
|
{
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get application compatibility version
|
||||||
|
*
|
||||||
|
* Examples: >=1.0.36, 1.0.37, APP_VERSION
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getCompatibleVersion()
|
||||||
|
{
|
||||||
|
return APP_VERSION;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,18 +36,7 @@ class Directory extends BaseCore
|
|||||||
*/
|
*/
|
||||||
public function isCompatible(array $plugin, $appVersion = APP_VERSION)
|
public function isCompatible(array $plugin, $appVersion = APP_VERSION)
|
||||||
{
|
{
|
||||||
if (strpos($appVersion, 'master') !== false) {
|
return Version::isCompatible($plugin['compatible_version'], $appVersion);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace Kanboard\Core\Plugin;
|
|||||||
|
|
||||||
use Composer\Autoload\ClassLoader;
|
use Composer\Autoload\ClassLoader;
|
||||||
use DirectoryIterator;
|
use DirectoryIterator;
|
||||||
|
use Exception;
|
||||||
use LogicException;
|
use LogicException;
|
||||||
use Kanboard\Core\Tool;
|
use Kanboard\Core\Tool;
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ class Loader extends \Kanboard\Core\Base
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $plugins = array();
|
protected $plugins = array();
|
||||||
|
protected $incompatiblePlugins = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get list of loaded plugins
|
* Get list of loaded plugins
|
||||||
@@ -34,6 +36,17 @@ class Loader extends \Kanboard\Core\Base
|
|||||||
return $this->plugins;
|
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
|
* Scan plugin folder and load plugins
|
||||||
*
|
*
|
||||||
@@ -51,8 +64,7 @@ class Loader extends \Kanboard\Core\Base
|
|||||||
foreach ($dir as $fileInfo) {
|
foreach ($dir as $fileInfo) {
|
||||||
if ($fileInfo->isDir() && substr($fileInfo->getFilename(), 0, 1) !== '.') {
|
if ($fileInfo->isDir() && substr($fileInfo->getFilename(), 0, 1) !== '.') {
|
||||||
$pluginName = $fileInfo->getFilename();
|
$pluginName = $fileInfo->getFilename();
|
||||||
$this->loadSchema($pluginName);
|
$this->initializePlugin($pluginName);
|
||||||
$this->initializePlugin($pluginName, $this->loadPlugin($pluginName));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,7 +97,7 @@ class Loader extends \Kanboard\Core\Base
|
|||||||
$className = '\Kanboard\Plugin\\'.$pluginName.'\\Plugin';
|
$className = '\Kanboard\Plugin\\'.$pluginName.'\\Plugin';
|
||||||
|
|
||||||
if (! class_exists($className)) {
|
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);
|
return new $className($this->container);
|
||||||
@@ -96,18 +108,30 @@ class Loader extends \Kanboard\Core\Base
|
|||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @param string $pluginName
|
* @param string $pluginName
|
||||||
* @param Base $plugin
|
|
||||||
*/
|
*/
|
||||||
public function initializePlugin($pluginName, Base $plugin)
|
public function initializePlugin($pluginName)
|
||||||
{
|
{
|
||||||
if (method_exists($plugin, 'onStartup')) {
|
try {
|
||||||
$this->dispatcher->addListener('app.bootstrap', array($plugin, 'onStartup'));
|
$plugin = $this->loadPlugin($pluginName);
|
||||||
|
|
||||||
|
if (Version::isCompatible($plugin->getCompatibleVersion(), APP_VERSION)) {
|
||||||
|
$this->loadSchema($pluginName);
|
||||||
|
|
||||||
|
if (method_exists($plugin, 'onStartup')) {
|
||||||
|
$this->dispatcher->addListener('app.bootstrap', array($plugin, 'onStartup'));
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
Tool::buildDIC($this->container, $plugin->getClasses());
|
|
||||||
Tool::buildDICHelpers($this->container, $plugin->getHelpers());
|
|
||||||
|
|
||||||
$plugin->initialize();
|
|
||||||
$this->plugins[$pluginName] = $plugin;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
app/Core/Plugin/PluginException.php
Normal file
15
app/Core/Plugin/PluginException.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kanboard\Core\Plugin;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class PluginException
|
||||||
|
*
|
||||||
|
* @package Kanboard\Core\Plugin
|
||||||
|
* @author Frederic Guillot
|
||||||
|
*/
|
||||||
|
class PluginException extends Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -2,14 +2,12 @@
|
|||||||
|
|
||||||
namespace Kanboard\Core\Plugin;
|
namespace Kanboard\Core\Plugin;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class PluginInstallerException
|
* Class PluginInstallerException
|
||||||
*
|
*
|
||||||
* @package Kanboard\Core\Plugin
|
* @package Kanboard\Core\Plugin
|
||||||
* @author Frederic Guillot
|
* @author Frederic Guillot
|
||||||
*/
|
*/
|
||||||
class PluginInstallerException extends Exception
|
class PluginInstallerException extends PluginException
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
38
app/Core/Plugin/Version.php
Normal file
38
app/Core/Plugin/Version.php
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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">
|
<div class="page-header">
|
||||||
<h2><?= t('Installed Plugins') ?></h2>
|
<h2><?= t('Installed Plugins') ?></h2>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -71,6 +71,15 @@ class Plugin extends Base
|
|||||||
{
|
{
|
||||||
$this->template->hook->attach('template:layout:head', 'theme:layout/head');
|
$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)
|
- `getPluginHomepage()`: Should return plugin Homepage (link)
|
||||||
- `setContentSecurityPolicy(array $rules)`: Override default HTTP CSP rules
|
- `setContentSecurityPolicy(array $rules)`: Override default HTTP CSP rules
|
||||||
- `onStartup()`: If present, this method is executed automatically when the event "app.bootstrap" is triggered
|
- `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.
|
Your plugin registration class can also inherit from Kanboard\Core\Base, that way you can access all classes and methods of Kanboard easily.
|
||||||
|
|
||||||
|
|||||||
29
tests/units/Core/Plugin/VersionTest.php
Normal file
29
tests/units/Core/Plugin/VersionTest.php
Normal file
@@ -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'));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user