Commit 8ec79097 authored by Yassine Doghri's avatar Yassine Doghri
Browse files

feat(plugins): display errors when plugin is invalid instead of crashing

parent 45ac2a4b
Loading
Loading
Loading
Loading
+31 −22
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ use Modules\Plugins\Manifest\Manifest;
use Modules\Plugins\Manifest\Person;
use Modules\Plugins\Manifest\Repository;
use Modules\Plugins\Manifest\Settings;
use RuntimeException;

/**
 * @property string $key
@@ -33,7 +32,12 @@ abstract class BasePlugin implements PluginInterface

    protected string $iconSrc;

    protected bool $active;
    /**
     * @var array<string,string>
     */
    protected array $errors = [];

    protected PluginStatus $status;

    protected Manifest $manifest;

@@ -51,20 +55,30 @@ abstract class BasePlugin implements PluginInterface
        $manifestContents = file_get_contents($manifestPath);

        if (! $manifestContents) {
            throw new RuntimeException(sprintf('Plugin manifest "%s" is missing!', $manifestPath));
            $manifestContents = '{}';
            $this->errors['manifest'] = lang('Plugins.errors.manifestMissing', [
                'manifestPath' => $manifestPath,
            ]);
        }

        /** @var array<mixed>|null $manifestData */
        $manifestData = json_decode($manifestContents, true);

        if ($manifestData === null) {
            throw new RuntimeException(sprintf('Plugin manifest "%s" is not a valid JSON', $manifestPath), 1);
            $manifestData = [];
            $this->errors['manifest'] = lang('Plugins.errors.manifestJsonInvalid', [
                'manifestPath' => $manifestPath,
            ]);
        }

        $this->manifest = new Manifest($manifestData);
        $this->manifest = new Manifest($this->key, $manifestData);
        $this->errors = [...$this->errors, ...Manifest::getPluginErrors($this->key)];

        // check that plugin is active
        $this->active = get_plugin_option($this->key, 'active') ?? false;
        if ($this->errors !== []) {
            $this->status = PluginStatus::INVALID;
        } else {
            $this->status = get_plugin_option($this->key, 'active') ? PluginStatus::ACTIVE : PluginStatus::INACTIVE;
        }

        $this->iconSrc = $this->loadIcon($directory . '/icon.svg');

@@ -98,9 +112,17 @@ abstract class BasePlugin implements PluginInterface
    {
    }

    final public function isActive(): bool
    final public function getStatus(): PluginStatus
    {
        return $this->active;
        return $this->status;
    }

    /**
     * @return array<string,string>
     */
    final public function getErrors(): array
    {
        return $this->errors;
    }

    final public function isHookDeclared(string $name): bool
@@ -144,19 +166,6 @@ abstract class BasePlugin implements PluginInterface
        return $this->iconSrc;
    }

    final public function doesManifestHaveErrors(): bool
    {
        return $this->getManifestErrors() !== [];
    }

    /**
     * @return array<string,string>
     */
    final public function getManifestErrors(): array
    {
        return $this->manifest::$errors;
    }

    /**
     * @return Field[]
     */
+12 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace Modules\Plugins\Core;

enum PluginStatus: string
{
    case INVALID = 'invalid';
    case INACTIVE = 'inactive';
    case ACTIVE = 'active';
}
+5 −5
Original line number Diff line number Diff line
@@ -96,7 +96,7 @@ class Plugins
    {
        $activePlugins = [];
        foreach (static::$plugins as $plugin) {
            if ($plugin->isActive()) {
            if ($plugin->getStatus() === PluginStatus::ACTIVE) {
                $activePlugins[] = $plugin;
            }
        }
@@ -111,7 +111,7 @@ class Plugins
    {
        $pluginsWithPodcastSettings = [];
        foreach (static::$plugins as $plugin) {
            if (! $plugin->isActive()) {
            if ($plugin->getStatus() !== PluginStatus::ACTIVE) {
                continue;
            }

@@ -132,7 +132,7 @@ class Plugins
    {
        $pluginsWithEpisodeSettings = [];
        foreach (static::$plugins as $plugin) {
            if (! $plugin->isActive()) {
            if ($plugin->getStatus() !== PluginStatus::ACTIVE) {
                continue;
            }

@@ -183,7 +183,7 @@ class Plugins
    {
        foreach (static::$plugins as $plugin) {
            // only run hook on active plugins
            if (! $plugin->isActive()) {
            if ($plugin->getStatus() !== PluginStatus::ACTIVE) {
                continue;
            }

@@ -282,7 +282,7 @@ class Plugins
            static::$pluginsByVendor[$vendor][] = $plugin;
            ++static::$installedCount;

            if ($plugin->isActive()) {
            if ($plugin->getStatus() === PluginStatus::ACTIVE) {
                ++static::$activeCount;
            }
        }
+6 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ return [
    'deactivate' => 'Deactivate',
    'active' => 'Active',
    'inactive' => 'Inactive',
    'invalid' => 'Invalid',
    'uninstall' => 'Uninstall',
    'keywords' => [
        'podcasting20' => 'Podcasting 2.0',
@@ -40,4 +41,9 @@ return [
    'messages' => [
        'saveSettingsSuccess' => '{pluginName} settings were successfully saved!',
    ],
    'errors' => [
        'manifestError' => 'Plugin manifest has errors',
        'manifestMissing' => 'Plugin manifest "{manifestPath}" is missing.',
        'manifestJsonInvalid' => 'Plugin manifest "{manifestPath}" is not a valid JSON.',
    ],
];
+2 −2
Original line number Diff line number Diff line
@@ -46,9 +46,9 @@ class Manifest extends ManifestObject
        'repository' => Repository::class,
    ];

    protected string $name;
    protected ?string $name = '???';

    protected string $version;
    protected ?string $version = 'X.Y.Z';

    protected ?string $description = null;

Loading