mirror of
https://code.castopod.org/adaures/castopod
synced 2025-05-29 13:32:02 +00:00
feat(plugins): load README.md file to view plugin's instructions in UI
This commit is contained in:
parent
95088196ce
commit
bb03798aff
@ -25,11 +25,15 @@ $routes->group(
|
|||||||
'filter' => 'permission:plugins.manage',
|
'filter' => 'permission:plugins.manage',
|
||||||
]);
|
]);
|
||||||
$routes->group('(:pluginKey)', static function ($routes): void {
|
$routes->group('(:pluginKey)', static function ($routes): void {
|
||||||
$routes->get('/', 'PluginController::generalSettings/$1/$2', [
|
$routes->get('/', 'PluginController::view/$1/$2', [
|
||||||
|
'as' => 'plugins-view',
|
||||||
|
'filter' => 'permission:plugins.manage',
|
||||||
|
]);
|
||||||
|
$routes->get('settings', 'PluginController::generalSettings/$1/$2', [
|
||||||
'as' => 'plugins-general-settings',
|
'as' => 'plugins-general-settings',
|
||||||
'filter' => 'permission:plugins.manage',
|
'filter' => 'permission:plugins.manage',
|
||||||
]);
|
]);
|
||||||
$routes->post('/', 'PluginController::generalSettingsAction/$1/$2', [
|
$routes->post('settings', 'PluginController::generalSettingsAction/$1/$2', [
|
||||||
'as' => 'plugins-general-settings-action',
|
'as' => 'plugins-general-settings-action',
|
||||||
'filter' => 'permission:plugins.manage',
|
'filter' => 'permission:plugins.manage',
|
||||||
]);
|
]);
|
||||||
|
@ -48,6 +48,22 @@ class PluginController extends BaseController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function view(string $vendor, string $package): string
|
||||||
|
{
|
||||||
|
/** @var Plugins $plugins */
|
||||||
|
$plugins = service('plugins');
|
||||||
|
|
||||||
|
$plugin = $plugins->getPlugin($vendor, $package);
|
||||||
|
|
||||||
|
if ($plugin === null) {
|
||||||
|
throw PageNotFoundException::forPageNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('plugins/view', [
|
||||||
|
'plugin' => $plugin,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function generalSettings(string $vendor, string $package): string
|
public function generalSettings(string $vendor, string $package): string
|
||||||
{
|
{
|
||||||
/** @var Plugins $plugins */
|
/** @var Plugins $plugins */
|
||||||
|
@ -8,6 +8,14 @@ use App\Entities\Episode;
|
|||||||
use App\Entities\Podcast;
|
use App\Entities\Podcast;
|
||||||
use App\Libraries\SimpleRSSElement;
|
use App\Libraries\SimpleRSSElement;
|
||||||
use CodeIgniter\HTTP\URI;
|
use CodeIgniter\HTTP\URI;
|
||||||
|
use League\CommonMark\Environment\Environment;
|
||||||
|
use League\CommonMark\Event\DocumentParsedEvent;
|
||||||
|
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
|
||||||
|
use League\CommonMark\Extension\GithubFlavoredMarkdownExtension;
|
||||||
|
use League\CommonMark\Extension\SmartPunct\SmartPunctExtension;
|
||||||
|
use League\CommonMark\MarkdownConverter;
|
||||||
|
use Modules\Plugins\ExternalImageProcessor;
|
||||||
|
use Modules\Plugins\ExternalLinkProcessor;
|
||||||
use Modules\Plugins\Manifest\Manifest;
|
use Modules\Plugins\Manifest\Manifest;
|
||||||
use Modules\Plugins\Manifest\Settings;
|
use Modules\Plugins\Manifest\Settings;
|
||||||
use Modules\Plugins\Manifest\SettingsField;
|
use Modules\Plugins\Manifest\SettingsField;
|
||||||
@ -27,6 +35,8 @@ abstract class BasePlugin implements PluginInterface
|
|||||||
|
|
||||||
protected Manifest $manifest;
|
protected Manifest $manifest;
|
||||||
|
|
||||||
|
protected string $readmeHTML;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected string $vendor,
|
protected string $vendor,
|
||||||
protected string $package,
|
protected string $package,
|
||||||
@ -55,6 +65,8 @@ abstract class BasePlugin implements PluginInterface
|
|||||||
$this->active = get_plugin_option($this->key, 'active') ?? false;
|
$this->active = get_plugin_option($this->key, 'active') ?? false;
|
||||||
|
|
||||||
$this->iconSrc = $this->loadIcon($directory . '/icon.svg');
|
$this->iconSrc = $this->loadIcon($directory . '/icon.svg');
|
||||||
|
|
||||||
|
$this->readmeHTML = $this->loadReadme($directory . '/README.md');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -182,6 +194,11 @@ abstract class BasePlugin implements PluginInterface
|
|||||||
return $description;
|
return $description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public function getReadmeHTML(): string
|
||||||
|
{
|
||||||
|
return $this->readmeHTML;
|
||||||
|
}
|
||||||
|
|
||||||
final protected function getOption(string $option): mixed
|
final protected function getOption(string $option): mixed
|
||||||
{
|
{
|
||||||
return get_plugin_option($this->key, $option);
|
return get_plugin_option($this->key, $option);
|
||||||
@ -208,4 +225,38 @@ abstract class BasePlugin implements PluginInterface
|
|||||||
$encodedIcon
|
$encodedIcon
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function loadReadme(string $path): ?string
|
||||||
|
{
|
||||||
|
// TODO: cache readme
|
||||||
|
$readmeMD = @file_get_contents($path);
|
||||||
|
|
||||||
|
if (! $readmeMD) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$environment = new Environment([
|
||||||
|
'html_input' => 'escape',
|
||||||
|
'allow_unsafe_links' => false,
|
||||||
|
'host' => 'hello',
|
||||||
|
]);
|
||||||
|
$environment->addExtension(new CommonMarkCoreExtension());
|
||||||
|
|
||||||
|
$environment->addExtension(new GithubFlavoredMarkdownExtension());
|
||||||
|
$environment->addExtension(new SmartPunctExtension());
|
||||||
|
|
||||||
|
$environment->addEventListener(
|
||||||
|
DocumentParsedEvent::class,
|
||||||
|
[new ExternalLinkProcessor($environment), 'onDocumentParsed']
|
||||||
|
);
|
||||||
|
$environment->addEventListener(
|
||||||
|
DocumentParsedEvent::class,
|
||||||
|
[new ExternalImageProcessor($environment), 'onDocumentParsed']
|
||||||
|
);
|
||||||
|
|
||||||
|
$converter = new MarkdownConverter($environment);
|
||||||
|
|
||||||
|
return $converter->convert($readmeMD)
|
||||||
|
->getContent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
53
modules/Plugins/ExternalImageProcessor.php
Normal file
53
modules/Plugins/ExternalImageProcessor.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Modules\Plugins;
|
||||||
|
|
||||||
|
use CodeIgniter\HTTP\URI;
|
||||||
|
use League\CommonMark\Environment\EnvironmentInterface;
|
||||||
|
use League\CommonMark\Event\DocumentParsedEvent;
|
||||||
|
use League\CommonMark\Extension\CommonMark\Node\Inline\Image;
|
||||||
|
|
||||||
|
class ExternalImageProcessor
|
||||||
|
{
|
||||||
|
private EnvironmentInterface $environment;
|
||||||
|
|
||||||
|
public function __construct(EnvironmentInterface $environment)
|
||||||
|
{
|
||||||
|
$this->environment = $environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onDocumentParsed(DocumentParsedEvent $event): void
|
||||||
|
{
|
||||||
|
$document = $event->getDocument();
|
||||||
|
$walker = $document->walker();
|
||||||
|
while ($event = $walker->next()) {
|
||||||
|
$node = $event->getNode();
|
||||||
|
|
||||||
|
// Only stop at Link nodes when we first encounter them
|
||||||
|
if (! ($node instanceof Image) || ! $event->isEntering()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $node->getUrl();
|
||||||
|
if ($this->isUrlExternal($url)) {
|
||||||
|
$node->detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isUrlExternal(string $url): bool
|
||||||
|
{
|
||||||
|
// Only look at http and https URLs
|
||||||
|
if (! preg_match('/^https?:\/\//', $url)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = parse_url($url, PHP_URL_HOST);
|
||||||
|
|
||||||
|
// TODO: load from environment's config
|
||||||
|
// return $host != $this->environment->getConfiguration()->get('host');
|
||||||
|
return $host !== (new URI(base_url()))->getHost();
|
||||||
|
}
|
||||||
|
}
|
54
modules/Plugins/ExternalLinkProcessor.php
Normal file
54
modules/Plugins/ExternalLinkProcessor.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Modules\Plugins;
|
||||||
|
|
||||||
|
use CodeIgniter\HTTP\URI;
|
||||||
|
use League\CommonMark\Environment\EnvironmentInterface;
|
||||||
|
use League\CommonMark\Event\DocumentParsedEvent;
|
||||||
|
use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
|
||||||
|
|
||||||
|
class ExternalLinkProcessor
|
||||||
|
{
|
||||||
|
private EnvironmentInterface $environment;
|
||||||
|
|
||||||
|
public function __construct(EnvironmentInterface $environment)
|
||||||
|
{
|
||||||
|
$this->environment = $environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onDocumentParsed(DocumentParsedEvent $event): void
|
||||||
|
{
|
||||||
|
$document = $event->getDocument();
|
||||||
|
$walker = $document->walker();
|
||||||
|
while ($event = $walker->next()) {
|
||||||
|
$node = $event->getNode();
|
||||||
|
|
||||||
|
// Only stop at Link nodes when we first encounter them
|
||||||
|
if (! ($node instanceof Link) || ! $event->isEntering()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $node->getUrl();
|
||||||
|
if ($this->isUrlExternal($url)) {
|
||||||
|
$node->data->append('attributes/target', '_blank');
|
||||||
|
$node->data->append('attributes/rel', 'noopener noreferrer');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isUrlExternal(string $url): bool
|
||||||
|
{
|
||||||
|
// Only look at http and https URLs
|
||||||
|
if (! preg_match('/^https?:\/\//', $url)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = parse_url($url, PHP_URL_HOST);
|
||||||
|
|
||||||
|
// TODO: load from environment's config
|
||||||
|
// return $host != $this->environment->getConfiguration()->get('host');
|
||||||
|
return $host !== (new URI(base_url()))->getHost();
|
||||||
|
}
|
||||||
|
}
|
16
themes/cp_admin/plugins/view.php
Normal file
16
themes/cp_admin/plugins/view.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?= $this->extend('_layout') ?>
|
||||||
|
|
||||||
|
<?= $this->section('title') ?>
|
||||||
|
<?= lang('Plugins.view') ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
<?= $this->section('pageTitle') ?>
|
||||||
|
<?= lang('Plugins.view') ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
<?= $this->section('content') ?>
|
||||||
|
<section class="prose">
|
||||||
|
|
||||||
|
<?= $plugin->getReadmeHTML() ?>
|
||||||
|
</section>
|
||||||
|
<?= $this->endSection() ?>
|
Loading…
x
Reference in New Issue
Block a user