mirror of
https://code.castopod.org/adaures/castopod
synced 2025-04-19 13:01:19 +00:00
refactor(plugins): redefine plugins folder structure to vendor/package
This commit is contained in:
parent
fe48bbbda5
commit
ea9685faad
@ -114,14 +114,28 @@ class Autoload extends AutoloadConfig
|
||||
public function __construct()
|
||||
{
|
||||
// load plugins namespaces
|
||||
$pluginsPaths = glob(PLUGINS_PATH . '*', GLOB_ONLYDIR | GLOB_NOSORT);
|
||||
$pluginsPaths = glob(PLUGINS_PATH . '*/*', GLOB_ONLYDIR | GLOB_NOSORT);
|
||||
|
||||
if (! $pluginsPaths) {
|
||||
$pluginsPaths = [];
|
||||
}
|
||||
|
||||
foreach ($pluginsPaths as $pluginPath) {
|
||||
$this->psr4[sprintf('Plugins\%s', basename($pluginPath))] = $pluginPath;
|
||||
$vendor = basename(dirname($pluginPath));
|
||||
$package = basename($pluginPath);
|
||||
|
||||
// validate plugin pattern
|
||||
if (preg_match('~' . PLUGINS_KEY_PATTERN . '~', $vendor . '/' . $package) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$pluginNamespace = 'Plugins\\' . str_replace(
|
||||
' ',
|
||||
'',
|
||||
ucwords(str_replace(['-', '_', '.'], ' ', $vendor . '\\' . $package))
|
||||
);
|
||||
|
||||
$this->psr4[$pluginNamespace] = $pluginPath;
|
||||
}
|
||||
|
||||
parent::__construct();
|
||||
|
@ -38,6 +38,9 @@ defined('APP_NAMESPACE') || define('APP_NAMESPACE', 'App');
|
||||
defined('PLUGINS_PATH') ||
|
||||
define('PLUGINS_PATH', ROOTPATH . 'plugins' . DIRECTORY_SEPARATOR);
|
||||
|
||||
defined('PLUGINS_KEY_PATTERN') ||
|
||||
define('PLUGINS_KEY_PATTERN', '[a-z0-9]([_.-]?[a-z0-9]+)*\/[a-z0-9]([_.-]?[a-z0-9]+)*');
|
||||
|
||||
/*
|
||||
| --------------------------------------------------------------------------
|
||||
| Composer Path
|
||||
|
@ -11,6 +11,7 @@ use CodeIgniter\I18n\Time;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @property string $key
|
||||
* @property string $name
|
||||
* @property string $description
|
||||
* @property string $version
|
||||
@ -29,12 +30,13 @@ abstract class BasePlugin implements PluginInterface
|
||||
protected bool $active;
|
||||
|
||||
public function __construct(
|
||||
protected string $key,
|
||||
protected string $filePath
|
||||
protected string $vendor,
|
||||
protected string $package,
|
||||
protected string $directory
|
||||
) {
|
||||
$pluginDirectory = dirname($filePath);
|
||||
$this->key = sprintf('%s/%s', $vendor, $package);
|
||||
|
||||
$manifest = $this->loadManifest($pluginDirectory . '/manifest.json');
|
||||
$manifest = $this->loadManifest($directory . '/manifest.json');
|
||||
|
||||
foreach ($manifest as $key => $value) {
|
||||
$this->{$key} = $value;
|
||||
@ -43,7 +45,7 @@ abstract class BasePlugin implements PluginInterface
|
||||
// check that plugin is active
|
||||
$this->active = get_plugin_option($this->key, 'active') ?? false;
|
||||
|
||||
$this->iconSrc = $this->loadIcon($pluginDirectory . '/icon.svg');
|
||||
$this->iconSrc = $this->loadIcon($directory . '/icon.svg');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,6 +90,16 @@ abstract class BasePlugin implements PluginInterface
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
final public function getVendor(): string
|
||||
{
|
||||
return $this->vendor;
|
||||
}
|
||||
|
||||
final public function getPackage(): string
|
||||
{
|
||||
return $this->package;
|
||||
}
|
||||
|
||||
final public function getName(): string
|
||||
{
|
||||
$key = sprintf('Plugin.%s.name', $this->key);
|
||||
|
@ -6,6 +6,7 @@ namespace Modules\Plugins\Commands;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use Modules\Plugins\Plugins;
|
||||
|
||||
class UninstallPlugin extends BaseCommand
|
||||
{
|
||||
@ -51,18 +52,20 @@ class UninstallPlugin extends BaseCommand
|
||||
*/
|
||||
public function run(array $pluginKeys): int
|
||||
{
|
||||
$validation = service('validation');
|
||||
/** @var Plugins $plugins */
|
||||
$plugins = service('plugins');
|
||||
|
||||
/** @var list<string> $errors */
|
||||
$errors = [];
|
||||
foreach ($pluginKeys as $pluginKey) {
|
||||
// TODO: change validation of pluginKey
|
||||
if (! $validation->check($pluginKey, 'required')) {
|
||||
$errors = [...$errors, ...$validation->getErrors()];
|
||||
$plugin = $plugins->getPluginByKey($pluginKey);
|
||||
|
||||
if ($plugin === null) {
|
||||
$errors[] = sprintf('Plugin %s was not found.', $pluginKey);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! service('plugins')->uninstall($pluginKey)) {
|
||||
if (! $plugins->uninstall($plugin)) {
|
||||
$errors[] = sprintf('Something happened when removing %s', $pluginKey);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ declare(strict_types=1);
|
||||
use CodeIgniter\Router\RouteCollection;
|
||||
|
||||
/** @var RouteCollection $routes */
|
||||
$routes->addPlaceholder('pluginVendor', '[a-z0-9]([_.-]?[a-z0-9]+)*');
|
||||
$routes->addPlaceholder('pluginKey', PLUGINS_KEY_PATTERN);
|
||||
|
||||
$routes->group(
|
||||
config('Admin')
|
||||
->gateway,
|
||||
@ -17,44 +20,50 @@ $routes->group(
|
||||
'as' => 'plugins-installed',
|
||||
'filter' => 'permission:plugins.manage',
|
||||
]);
|
||||
$routes->get('(:segment)', 'PluginController::generalSettings/$1', [
|
||||
'as' => 'plugins-general-settings',
|
||||
'filter' => 'permission:plugins.manage',
|
||||
]);
|
||||
$routes->post('(:segment)', 'PluginController::generalSettingsAction/$1', [
|
||||
'as' => 'plugins-general-settings-action',
|
||||
'filter' => 'permission:plugins.manage',
|
||||
]);
|
||||
$routes->post('(:segment)/activate', 'PluginController::activate/$1', [
|
||||
'as' => 'plugins-activate',
|
||||
'filter' => 'permission:plugins.manage',
|
||||
]);
|
||||
$routes->post('(:segment)/deactivate', 'PluginController::deactivate/$1', [
|
||||
'as' => 'plugins-deactivate',
|
||||
'filter' => 'permission:plugins.manage',
|
||||
]);
|
||||
// TODO: change to delete
|
||||
$routes->get('(:segment)/uninstall', 'PluginController::uninstall/$1', [
|
||||
'as' => 'plugins-uninstall',
|
||||
$routes->get('(:pluginVendor)', 'PluginController::vendor/$1', [
|
||||
'as' => 'plugins-vendor',
|
||||
'filter' => 'permission:plugins.manage',
|
||||
]);
|
||||
$routes->group('(:pluginKey)', static function ($routes): void {
|
||||
$routes->get('/', 'PluginController::generalSettings/$1/$2', [
|
||||
'as' => 'plugins-general-settings',
|
||||
'filter' => 'permission:plugins.manage',
|
||||
]);
|
||||
$routes->post('/', 'PluginController::generalSettingsAction/$1/$2', [
|
||||
'as' => 'plugins-general-settings-action',
|
||||
'filter' => 'permission:plugins.manage',
|
||||
]);
|
||||
$routes->post('activate', 'PluginController::activate/$1/$2', [
|
||||
'as' => 'plugins-activate',
|
||||
'filter' => 'permission:plugins.manage',
|
||||
]);
|
||||
$routes->post('deactivate', 'PluginController::deactivate/$1/$2', [
|
||||
'as' => 'plugins-deactivate',
|
||||
'filter' => 'permission:plugins.manage',
|
||||
]);
|
||||
// TODO: change to delete
|
||||
$routes->get('uninstall', 'PluginController::uninstall/$1/$2', [
|
||||
'as' => 'plugins-uninstall',
|
||||
'filter' => 'permission:plugins.manage',
|
||||
]);
|
||||
});
|
||||
});
|
||||
$routes->group('podcasts/(:num)/plugins', static function ($routes): void {
|
||||
$routes->get('(:segment)', 'PluginController::podcastSettings/$1/$2', [
|
||||
$routes->get('(:pluginKey)', 'PluginController::podcastSettings/$1/$2/$3', [
|
||||
'as' => 'plugins-podcast-settings',
|
||||
'filter' => 'permission:podcast#.edit',
|
||||
]);
|
||||
$routes->post('(:segment)', 'PluginController::podcastSettingsAction/$1/$2', [
|
||||
$routes->post('(:pluginKey)', 'PluginController::podcastSettingsAction/$1/$2/$3', [
|
||||
'as' => 'plugins-podcast-settings-action',
|
||||
'filter' => 'permission:podcast#.edit',
|
||||
]);
|
||||
});
|
||||
$routes->group('podcasts/(:num)/episodes/(:num)/plugins', static function ($routes): void {
|
||||
$routes->get('(:segment)', 'PluginController::episodeSettings/$1/$2/$3', [
|
||||
$routes->get('(:pluginKey)', 'PluginController::episodeSettings/$1/$2/$3/$4', [
|
||||
'as' => 'plugins-episode-settings',
|
||||
'filter' => 'permission:podcast#.edit',
|
||||
]);
|
||||
$routes->post('(:segment)', 'PluginController::episodeSettingsAction/$1/$2/$3', [
|
||||
$routes->post('(:pluginKey)', 'PluginController::episodeSettingsAction/$1/$2/$3/$4', [
|
||||
'as' => 'plugins-episode-settings-action',
|
||||
'filter' => 'permission:podcast#.edit',
|
||||
]);
|
||||
|
@ -35,12 +35,25 @@ class PluginController extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
public function generalSettings(string $pluginKey): string
|
||||
public function vendor(string $vendor): string
|
||||
{
|
||||
/** @var Plugins $plugins */
|
||||
$plugins = service('plugins');
|
||||
|
||||
$plugin = $plugins->getPlugin($pluginKey);
|
||||
$vendorPlugins = $plugins->getVendorPlugins($vendor);
|
||||
return view('plugins/installed', [
|
||||
'total' => count($vendorPlugins),
|
||||
'plugins' => $vendorPlugins,
|
||||
'pager_links' => '',
|
||||
]);
|
||||
}
|
||||
|
||||
public function generalSettings(string $vendor, string $package): string
|
||||
{
|
||||
/** @var Plugins $plugins */
|
||||
$plugins = service('plugins');
|
||||
|
||||
$plugin = $plugins->getPlugin($vendor, $package);
|
||||
|
||||
if ($plugin === null) {
|
||||
throw PageNotFoundException::forPageNotFound();
|
||||
@ -52,12 +65,12 @@ class PluginController extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
public function generalSettingsAction(string $pluginKey): RedirectResponse
|
||||
public function generalSettingsAction(string $vendor, string $package): RedirectResponse
|
||||
{
|
||||
/** @var Plugins $plugins */
|
||||
$plugins = service('plugins');
|
||||
|
||||
$plugin = $plugins->getPlugin($pluginKey);
|
||||
$plugin = $plugins->getPlugin($vendor, $package);
|
||||
|
||||
if ($plugin === null) {
|
||||
throw PageNotFoundException::forPageNotFound();
|
||||
@ -66,7 +79,7 @@ class PluginController extends BaseController
|
||||
foreach ($plugin->settings['general'] as $option) {
|
||||
$optionKey = $option['key'];
|
||||
$optionValue = $this->request->getPost($optionKey);
|
||||
$plugins->setOption($pluginKey, $optionKey, $optionValue);
|
||||
$plugins->setOption($plugin, $optionKey, $optionValue);
|
||||
}
|
||||
|
||||
return redirect()->back()
|
||||
@ -75,7 +88,7 @@ class PluginController extends BaseController
|
||||
]));
|
||||
}
|
||||
|
||||
public function podcastSettings(string $podcastId, string $pluginKey): string
|
||||
public function podcastSettings(string $podcastId, string $vendor, string $package): string
|
||||
{
|
||||
$podcast = (new PodcastModel())->getPodcastById((int) $podcastId);
|
||||
|
||||
@ -86,7 +99,7 @@ class PluginController extends BaseController
|
||||
/** @var Plugins $plugins */
|
||||
$plugins = service('plugins');
|
||||
|
||||
$plugin = $plugins->getPlugin($pluginKey);
|
||||
$plugin = $plugins->getPlugin($vendor, $package);
|
||||
|
||||
if ($plugin === null) {
|
||||
throw PageNotFoundException::forPageNotFound();
|
||||
@ -102,12 +115,12 @@ class PluginController extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
public function podcastSettingsAction(string $podcastId, string $pluginKey): RedirectResponse
|
||||
public function podcastSettingsAction(string $podcastId, string $vendor, string $package): RedirectResponse
|
||||
{
|
||||
/** @var Plugins $plugins */
|
||||
$plugins = service('plugins');
|
||||
|
||||
$plugin = $plugins->getPlugin($pluginKey);
|
||||
$plugin = $plugins->getPlugin($vendor, $package);
|
||||
|
||||
if ($plugin === null) {
|
||||
throw PageNotFoundException::forPageNotFound();
|
||||
@ -116,7 +129,7 @@ class PluginController extends BaseController
|
||||
foreach ($plugin->settings['podcast'] as $setting) {
|
||||
$settingKey = $setting['key'];
|
||||
$settingValue = $this->request->getPost($settingKey);
|
||||
$plugins->setOption($pluginKey, $settingKey, $settingValue, ['podcast', (int) $podcastId]);
|
||||
$plugins->setOption($plugin, $settingKey, $settingValue, ['podcast', (int) $podcastId]);
|
||||
}
|
||||
|
||||
return redirect()->back()
|
||||
@ -125,7 +138,7 @@ class PluginController extends BaseController
|
||||
]));
|
||||
}
|
||||
|
||||
public function episodeSettings(string $podcastId, string $episodeId, string $pluginKey): string
|
||||
public function episodeSettings(string $podcastId, string $episodeId, string $vendor, string $package): string
|
||||
{
|
||||
$episode = (new EpisodeModel())->getEpisodeById((int) $episodeId);
|
||||
|
||||
@ -136,7 +149,7 @@ class PluginController extends BaseController
|
||||
/** @var Plugins $plugins */
|
||||
$plugins = service('plugins');
|
||||
|
||||
$plugin = $plugins->getPlugin($pluginKey);
|
||||
$plugin = $plugins->getPlugin($vendor, $package);
|
||||
|
||||
if ($plugin === null) {
|
||||
throw PageNotFoundException::forPageNotFound();
|
||||
@ -154,12 +167,16 @@ class PluginController extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
public function episodeSettingsAction(string $podcastId, string $episodeId, string $pluginKey): RedirectResponse
|
||||
{
|
||||
public function episodeSettingsAction(
|
||||
string $podcastId,
|
||||
string $episodeId,
|
||||
string $vendor,
|
||||
string $package
|
||||
): RedirectResponse {
|
||||
/** @var Plugins $plugins */
|
||||
$plugins = service('plugins');
|
||||
|
||||
$plugin = $plugins->getPlugin($pluginKey);
|
||||
$plugin = $plugins->getPlugin($vendor, $package);
|
||||
|
||||
if ($plugin === null) {
|
||||
throw PageNotFoundException::forPageNotFound();
|
||||
@ -168,7 +185,7 @@ class PluginController extends BaseController
|
||||
foreach ($plugin->settings['episode'] as $setting) {
|
||||
$settingKey = $setting['key'];
|
||||
$settingValue = $this->request->getPost($settingKey);
|
||||
$plugins->setOption($pluginKey, $settingKey, $settingValue, ['episode', (int) $episodeId]);
|
||||
$plugins->setOption($plugin, $settingKey, $settingValue, ['episode', (int) $episodeId]);
|
||||
}
|
||||
|
||||
return redirect()->back()
|
||||
@ -177,23 +194,50 @@ class PluginController extends BaseController
|
||||
]));
|
||||
}
|
||||
|
||||
public function activate(string $pluginKey): RedirectResponse
|
||||
public function activate(string $vendor, string $package): RedirectResponse
|
||||
{
|
||||
service('plugins')->activate($pluginKey);
|
||||
/** @var Plugins $plugins */
|
||||
$plugins = service('plugins');
|
||||
|
||||
$plugin = $plugins->getPlugin($vendor, $package);
|
||||
|
||||
if ($plugin === null) {
|
||||
throw PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
|
||||
$plugins->activate($plugin);
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function deactivate(string $pluginKey): RedirectResponse
|
||||
public function deactivate(string $vendor, string $package): RedirectResponse
|
||||
{
|
||||
service('plugins')->deactivate($pluginKey);
|
||||
/** @var Plugins $plugins */
|
||||
$plugins = service('plugins');
|
||||
|
||||
$plugin = $plugins->getPlugin($vendor, $package);
|
||||
|
||||
if ($plugin === null) {
|
||||
throw PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
|
||||
$plugins->deactivate($plugin);
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function uninstall(string $pluginKey): RedirectResponse
|
||||
public function uninstall(string $vendor, string $package): RedirectResponse
|
||||
{
|
||||
service('plugins')->uninstall($pluginKey);
|
||||
/** @var Plugins $plugins */
|
||||
$plugins = service('plugins');
|
||||
|
||||
$plugin = $plugins->getPlugin($vendor, $package);
|
||||
|
||||
if ($plugin === null) {
|
||||
throw PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
|
||||
$plugins->uninstall($plugin);
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
@ -28,6 +28,11 @@ class Plugins
|
||||
*/
|
||||
protected static array $plugins = [];
|
||||
|
||||
/**
|
||||
* @var array<string,BasePlugin[]>
|
||||
*/
|
||||
protected static array $pluginsByVendor = [];
|
||||
|
||||
protected static int $installedCount = 0;
|
||||
|
||||
public function __construct()
|
||||
@ -100,10 +105,18 @@ class Plugins
|
||||
return $pluginsWithEpisodeSettings;
|
||||
}
|
||||
|
||||
public function getPlugin(string $key): ?BasePlugin
|
||||
/**
|
||||
* @return array<BasePlugin>
|
||||
*/
|
||||
public function getVendorPlugins(string $vendor): array
|
||||
{
|
||||
foreach (static::$plugins as $plugin) {
|
||||
if ($plugin->getKey() === $key) {
|
||||
return static::$pluginsByVendor[$vendor] ?? [];
|
||||
}
|
||||
|
||||
public function getPlugin(string $vendor, string $package): ?BasePlugin
|
||||
{
|
||||
foreach ($this->getVendorPlugins($vendor) as $plugin) {
|
||||
if ($plugin->getKey() === $vendor . '/' . $package) {
|
||||
return $plugin;
|
||||
}
|
||||
}
|
||||
@ -111,6 +124,16 @@ class Plugins
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getPluginByKey(string $key): ?BasePlugin
|
||||
{
|
||||
if (! str_contains('/', $key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$keyArray = explode('/', $key);
|
||||
return $this->getPlugin($keyArray[0], $keyArray[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value-of<static::HOOKS> $name
|
||||
* @param array<mixed> $arguments
|
||||
@ -131,22 +154,22 @@ class Plugins
|
||||
}
|
||||
}
|
||||
|
||||
public function activate(string $pluginKey): void
|
||||
public function activate(BasePlugin $plugin): void
|
||||
{
|
||||
set_plugin_option($pluginKey, 'active', true);
|
||||
set_plugin_option($plugin->getKey(), 'active', true);
|
||||
}
|
||||
|
||||
public function deactivate(string $pluginKey): void
|
||||
public function deactivate(BasePlugin $plugin): void
|
||||
{
|
||||
set_plugin_option($pluginKey, 'active', false);
|
||||
set_plugin_option($plugin->getKey(), 'active', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ?array{'podcast'|'episode',int} $additionalContext
|
||||
*/
|
||||
public function setOption(string $pluginKey, string $name, mixed $value, array $additionalContext = null): void
|
||||
public function setOption(BasePlugin $plugin, string $name, mixed $value, array $additionalContext = null): void
|
||||
{
|
||||
set_plugin_option($pluginKey, $name, $value, $additionalContext);
|
||||
set_plugin_option($plugin->getKey(), $name, $value, $additionalContext);
|
||||
}
|
||||
|
||||
public function getInstalledCount(): int
|
||||
@ -154,7 +177,7 @@ class Plugins
|
||||
return static::$installedCount;
|
||||
}
|
||||
|
||||
public function uninstall(string $pluginKey): bool
|
||||
public function uninstall(BasePlugin $plugin): bool
|
||||
{
|
||||
// remove all settings data
|
||||
$db = Database::connect();
|
||||
@ -162,7 +185,7 @@ class Plugins
|
||||
|
||||
$db->transStart();
|
||||
$builder->where('class', self::class);
|
||||
$builder->like('context', sprintf('plugin:%s', $pluginKey . '%'));
|
||||
$builder->like('context', sprintf('plugin:%s', $plugin->getKey() . '%'));
|
||||
|
||||
if (! $builder->delete()) {
|
||||
$db->transRollback();
|
||||
@ -170,7 +193,7 @@ class Plugins
|
||||
}
|
||||
|
||||
// delete plugin folder from PLUGINS_PATH
|
||||
$pluginFolder = PLUGINS_PATH . $pluginKey;
|
||||
$pluginFolder = PLUGINS_PATH . $plugin->getKey();
|
||||
$rmdirResult = $this->rrmdir($pluginFolder);
|
||||
|
||||
$transResult = $db->transCommit();
|
||||
@ -181,27 +204,36 @@ class Plugins
|
||||
protected function registerPlugins(): void
|
||||
{
|
||||
// search for plugins in plugins folder
|
||||
// TODO: only get directories? Should be organized as author/repo?
|
||||
$pluginsFiles = glob(PLUGINS_PATH . '**/Plugin.php');
|
||||
$pluginsDirectories = glob(PLUGINS_PATH . '*/*', GLOB_ONLYDIR);
|
||||
|
||||
if (! $pluginsFiles) {
|
||||
if ($pluginsDirectories === false || $pluginsDirectories === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$locator = service('locator');
|
||||
foreach ($pluginsFiles as $file) {
|
||||
$className = $locator->findQualifiedNameFromPath($file);
|
||||
foreach ($pluginsDirectories as $pluginDirectory) {
|
||||
$vendor = basename(dirname($pluginDirectory));
|
||||
$package = basename($pluginDirectory);
|
||||
|
||||
if (preg_match('~' . PLUGINS_KEY_PATTERN . '~', $vendor . '/' . $package) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$pluginFile = $pluginDirectory . DIRECTORY_SEPARATOR . 'Plugin.php';
|
||||
|
||||
$className = $locator->findQualifiedNameFromPath($pluginFile);
|
||||
|
||||
if ($className === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$plugin = new $className(basename(dirname($file)), $file);
|
||||
$plugin = new $className($vendor, $package, $pluginDirectory);
|
||||
if (! $plugin instanceof BasePlugin) {
|
||||
continue;
|
||||
}
|
||||
|
||||
static::$plugins[] = $plugin;
|
||||
static::$pluginsByVendor[$vendor][] = $plugin;
|
||||
++static::$installedCount;
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,10 @@
|
||||
]) ?></IconButton>
|
||||
<?php endif; ?>
|
||||
<img class="rounded-full min-w-16 max-w-16 aspect-square" src="<?= $plugin->iconSrc ?>">
|
||||
<div class="flex flex-col mt-2">
|
||||
<div class="flex flex-col items-start mt-2">
|
||||
<h2 class="flex items-center text-xl font-bold font-display gap-x-2"><?= $plugin->getName() ?><span class="px-1 font-mono text-xs rounded-full bg-subtle"><?= $plugin->version ?></span></h2>
|
||||
<p class="text-gray-600"><?= $plugin->getDescription() ?></p>
|
||||
<p class="font-mono text-xs tracking-wide bg-gray-100"><a href="<?= route_to('plugins-vendor', $plugin->getVendor()) ?>" class="underline underline-offset-2 decoration-2 decoration-dotted hover:decoration-solid decoration-accent"><?= $plugin->getVendor() ?></a>/<?= $plugin->getPackage() ?></p>
|
||||
<p class="mt-2 text-gray-600"><?= $plugin->getDescription() ?></p>
|
||||
</div>
|
||||
<footer class="flex items-center justify-between mt-4">
|
||||
<a href="<?= $plugin->website ?>" class="inline-flex items-center text-sm font-semibold underline hover:no-underline gap-x-1" target="_blank" rel="noopener noreferrer"><?= icon('link', [
|
||||
|
Loading…
x
Reference in New Issue
Block a user