diff --git a/docs/src/content/docs/en/plugins/manifest.mdx b/docs/src/content/docs/en/plugins/manifest.mdx index 8941bce9..53798f92 100644 --- a/docs/src/content/docs/en/plugins/manifest.mdx +++ b/docs/src/content/docs/en/plugins/manifest.mdx @@ -62,6 +62,10 @@ directories should refuse to publish the plugin. Array of strings to help your plugin get discovered when listed in repositories. +### minCastopodVersion + +The minimal version of Castopod with which the plugin is compatible. + ### hooks List of hooks used by the plugin. If the hook is not specified, Castopod will diff --git a/modules/Plugins/Core/BasePlugin.php b/modules/Plugins/Core/BasePlugin.php index 1335b749..b0ed8f67 100644 --- a/modules/Plugins/Core/BasePlugin.php +++ b/modules/Plugins/Core/BasePlugin.php @@ -54,7 +54,16 @@ abstract class BasePlugin implements PluginInterface $this->manifest = new Manifest($this->key); $this->manifest->loadFromFile($manifestPath); - $this->status = get_plugin_setting($this->key, 'active') ? PluginStatus::ACTIVE : PluginStatus::INACTIVE; + // check compatibility with Castopod version + if ($this->manifest->minCastopodVersion !== null && version_compare( + CP_VERSION, + $this->manifest->minCastopodVersion, + '<' + )) { + $this->status = PluginStatus::INCOMPATIBLE; + } else { + $this->status = get_plugin_setting($this->key, 'active') ? PluginStatus::ACTIVE : PluginStatus::INACTIVE; + } $this->iconSrc = $this->loadIcon($directory . '/icon.svg'); @@ -214,6 +223,11 @@ abstract class BasePlugin implements PluginInterface return $settings->{$type}; } + final public function getMinCastopodVersion(): string + { + return $this->manifest->minCastopodVersion ?? ''; + } + /** * @return list */ diff --git a/modules/Plugins/Core/PluginStatus.php b/modules/Plugins/Core/PluginStatus.php index 8057b0f0..8583b123 100644 --- a/modules/Plugins/Core/PluginStatus.php +++ b/modules/Plugins/Core/PluginStatus.php @@ -7,6 +7,7 @@ namespace Modules\Plugins\Core; enum PluginStatus: string { case INVALID = 'invalid'; + case INCOMPATIBLE = 'incompatible'; case INACTIVE = 'inactive'; case ACTIVE = 'active'; } diff --git a/modules/Plugins/Core/Plugins.php b/modules/Plugins/Core/Plugins.php index 37062797..3ff1b263 100644 --- a/modules/Plugins/Core/Plugins.php +++ b/modules/Plugins/Core/Plugins.php @@ -19,8 +19,6 @@ use Modules\Plugins\Config\Plugins as PluginsConfig; */ class Plugins { - public const API_VERSION = '1.0'; - /** * @var list */ diff --git a/modules/Plugins/Language/en/Plugins.php b/modules/Plugins/Language/en/Plugins.php index 2bd33360..04d5927d 100644 --- a/modules/Plugins/Language/en/Plugins.php +++ b/modules/Plugins/Language/en/Plugins.php @@ -29,6 +29,8 @@ return [ 'active' => 'Active', 'inactive' => 'Inactive', 'invalid' => 'Invalid', + 'incompatible' => 'Incompatible', + 'incompatible_hint' => 'Plugin requires Castopod v{minCastopodVersion} minimum.', 'uninstall' => 'Uninstall', 'keywords' => [ 'podcasting20' => 'Podcasting 2.0', diff --git a/modules/Plugins/Manifest/Manifest.php b/modules/Plugins/Manifest/Manifest.php index f5d0ce3c..d183176b 100644 --- a/modules/Plugins/Manifest/Manifest.php +++ b/modules/Plugins/Manifest/Manifest.php @@ -16,6 +16,7 @@ use CodeIgniter\HTTP\URI; * @property ?string $license * @property bool $private * @property list $keywords + * @property ?string $minCastopodVersion * @property list $hooks * @property ?Settings $settings * @property ?Repository $repository @@ -26,17 +27,18 @@ class Manifest extends ManifestObject * @var array */ public const VALIDATION_RULES = [ - 'name' => 'required|max_length[128]|regex_match[/^[a-z0-9]([_.-]?[a-z0-9]+)*\/[a-z0-9]([_.-]?[a-z0-9]+)*$/]', - 'version' => 'required|regex_match[/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/]', - 'description' => 'permit_empty|max_length[256]', - 'authors' => 'permit_empty|is_list', - 'homepage' => 'permit_empty|valid_url_strict', - 'license' => 'permit_empty|string', - 'private' => 'permit_empty|is_boolean', - 'keywords.*' => 'permit_empty', - 'hooks.*' => 'permit_empty|in_list[rssBeforeChannel,rssAfterChannel,rssBeforeItem,rssAfterItem,siteHead]', - 'settings' => 'permit_empty|is_list', - 'repository' => 'permit_empty|is_list', + 'name' => 'required|max_length[128]|regex_match[/^[a-z0-9]([_.-]?[a-z0-9]+)*\/[a-z0-9]([_.-]?[a-z0-9]+)*$/]', + 'version' => 'required|regex_match[/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/]', + 'description' => 'permit_empty|max_length[256]', + 'authors' => 'permit_empty|is_list', + 'homepage' => 'permit_empty|valid_url_strict', + 'license' => 'permit_empty|string', + 'private' => 'permit_empty|is_boolean', + 'keywords.*' => 'permit_empty', + 'minCastopodVersion' => 'permit_empty|regex_match[/^(0|[1-9]\d*)\.(0|[1-9]\d*)(\.(0|[1-9]\d*))?(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/]', + 'hooks.*' => 'permit_empty|in_list[rssBeforeChannel,rssAfterChannel,rssBeforeItem,rssAfterItem,siteHead]', + 'settings' => 'permit_empty|is_list', + 'repository' => 'permit_empty|is_list', ]; protected const CASTS = [ @@ -68,6 +70,8 @@ class Manifest extends ManifestObject */ protected array $keywords = []; + protected ?string $minCastopodVersion = null; + /** * @var list */ diff --git a/modules/Plugins/Manifest/manifest.schema.json b/modules/Plugins/Manifest/manifest.schema.json index e1263789..4744e083 100644 --- a/modules/Plugins/Manifest/manifest.schema.json +++ b/modules/Plugins/Manifest/manifest.schema.json @@ -83,6 +83,12 @@ }, "uniqueItems": true }, + "minCastopodVersion": { + "description": "The minimal version of Castopod for which the plugin is compatible with.", + "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(\\.(0|[1-9]\\d*))?(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "examples": ["2.0"] + }, "hooks": { "description": "The hooks used by the plugin.", "type": "array", diff --git a/themes/cp_admin/plugins/_plugin.php b/themes/cp_admin/plugins/_plugin.php index 225871cc..8be4724d 100644 --- a/themes/cp_admin/plugins/_plugin.php +++ b/themes/cp_admin/plugins/_plugin.php @@ -14,6 +14,11 @@ use Modules\Plugins\Core\PluginStatus; getStatus() === PluginStatus::INVALID): ?> + getStatus() === PluginStatus::INCOMPATIBLE): ?> + +