feat(plugins): add settings page for podcast and episode if defined in the plugin's manifest
- rename options to settings
This commit is contained in:
parent
3be613452b
commit
5dd94ee6e0
|
@ -109,7 +109,7 @@ class Autoload extends AutoloadConfig
|
||||||
*
|
*
|
||||||
* @var list<string>
|
* @var list<string>
|
||||||
*/
|
*/
|
||||||
public $helpers = ['auth', 'setting', 'icons'];
|
public $helpers = ['auth', 'setting', 'icons', 'plugins'];
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,4 +20,5 @@ return [
|
||||||
'video-clips-create' => 'New video clip',
|
'video-clips-create' => 'New video clip',
|
||||||
'soundbites-list' => 'Soundbites',
|
'soundbites-list' => 'Soundbites',
|
||||||
'soundbites-create' => 'New soundbite',
|
'soundbites-create' => 'New soundbite',
|
||||||
|
'plugins' => 'Plugins',
|
||||||
];
|
];
|
||||||
|
|
|
@ -20,6 +20,7 @@ return [
|
||||||
'episodes' => 'Episodes',
|
'episodes' => 'Episodes',
|
||||||
'episode-list' => 'All episodes',
|
'episode-list' => 'All episodes',
|
||||||
'episode-create' => 'New episode',
|
'episode-create' => 'New episode',
|
||||||
|
'plugins' => 'Plugins',
|
||||||
'analytics' => 'Analytics',
|
'analytics' => 'Analytics',
|
||||||
'podcast-analytics' => 'Audience overview',
|
'podcast-analytics' => 'Audience overview',
|
||||||
'podcast-analytics-webpages' => 'Web pages visits',
|
'podcast-analytics-webpages' => 'Web pages visits',
|
||||||
|
|
|
@ -22,7 +22,7 @@ use RuntimeException;
|
||||||
* @property string[] $keywords
|
* @property string[] $keywords
|
||||||
* @property string[] $hooks
|
* @property string[] $hooks
|
||||||
* @property string $iconSrc
|
* @property string $iconSrc
|
||||||
* @property array{settings:array{key:string,name:string,description:string}[],podcast:array{key:string,name:string,description:string}[],episode:array{key:string,name:string,description:string}[]} $options
|
* @property array{general:array{key:string,name:string,description:string}[],podcast:array{key:string,name:string,description:string}[],episode:array{key:string,name:string,description:string}[]} $settings
|
||||||
*/
|
*/
|
||||||
abstract class BasePlugin implements PluginInterface
|
abstract class BasePlugin implements PluginInterface
|
||||||
{
|
{
|
||||||
|
@ -147,24 +147,24 @@ abstract class BasePlugin implements PluginInterface
|
||||||
|
|
||||||
$validation = service('validation');
|
$validation = service('validation');
|
||||||
|
|
||||||
if (array_key_exists('options', $manifest)) {
|
if (array_key_exists('settings', $manifest)) {
|
||||||
$optionRules = [
|
$fieldRules = [
|
||||||
'key' => 'required|alpha_numeric',
|
'key' => 'required|alpha_numeric',
|
||||||
'name' => 'required|max_length[32]',
|
'name' => 'required|max_length[32]',
|
||||||
'description' => 'permit_empty|max_length[128]',
|
'description' => 'permit_empty|max_length[128]',
|
||||||
];
|
];
|
||||||
$defaultOption = [
|
$defaultField = [
|
||||||
'key' => '',
|
'key' => '',
|
||||||
'name' => '',
|
'name' => '',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
];
|
];
|
||||||
$validation->setRules($optionRules);
|
$validation->setRules($fieldRules);
|
||||||
foreach ($manifest['options'] as $key => $options) {
|
foreach ($manifest['settings'] as $key => $settings) {
|
||||||
foreach ($options as $key2 => $option) {
|
foreach ($settings as $key2 => $fields) {
|
||||||
$manifest['options'][$key][$key2] = array_merge($defaultOption, $option);
|
$manifest['settings'][$key][$key2] = array_merge($defaultField, $fields);
|
||||||
|
|
||||||
if (! $validation->run($manifest['options'][$key][$key2])) {
|
if (! $validation->run($manifest['settings'][$key][$key2])) {
|
||||||
dd($this->key, $manifest['options'][$key][$key2], $validation->getErrors());
|
dd($this->key, $manifest['settings'][$key][$key2], $validation->getErrors());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,7 @@ abstract class BasePlugin implements PluginInterface
|
||||||
'website' => 'valid_url_strict',
|
'website' => 'valid_url_strict',
|
||||||
'keywords.*' => 'permit_empty|in_list[seo,podcasting20,analytics]',
|
'keywords.*' => 'permit_empty|in_list[seo,podcasting20,analytics]',
|
||||||
'hooks.*' => 'permit_empty|in_list[' . implode(',', Plugins::HOOKS) . ']',
|
'hooks.*' => 'permit_empty|in_list[' . implode(',', Plugins::HOOKS) . ']',
|
||||||
'options' => 'permit_empty',
|
'settings' => 'permit_empty',
|
||||||
];
|
];
|
||||||
|
|
||||||
$validation->setRules($rules);
|
$validation->setRules($rules);
|
||||||
|
@ -200,10 +200,10 @@ abstract class BasePlugin implements PluginInterface
|
||||||
'website' => '',
|
'website' => '',
|
||||||
'hooks' => [],
|
'hooks' => [],
|
||||||
'keywords' => [],
|
'keywords' => [],
|
||||||
'options' => [
|
'settings' => [
|
||||||
'settings' => [],
|
'general' => [],
|
||||||
'podcast' => [],
|
'podcast' => [],
|
||||||
'episode' => [],
|
'episode' => [],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -13,26 +13,46 @@ $routes->group(
|
||||||
],
|
],
|
||||||
static function ($routes): void {
|
static function ($routes): void {
|
||||||
$routes->group('plugins', static function ($routes): void {
|
$routes->group('plugins', static function ($routes): void {
|
||||||
$routes->get('/', 'PluginsController::installed', [
|
$routes->get('/', 'PluginController::installed', [
|
||||||
'as' => 'plugins-installed',
|
'as' => 'plugins-installed',
|
||||||
'filter' => 'permission:plugins.manage',
|
'filter' => 'permission:plugins.manage',
|
||||||
]);
|
]);
|
||||||
$routes->get('(:segment)', 'PluginsController::settings/$1', [
|
$routes->get('(:segment)', 'PluginController::generalSettings/$1', [
|
||||||
'as' => 'plugins-settings',
|
'as' => 'plugins-general-settings',
|
||||||
'filter' => 'permission:plugins.manage',
|
'filter' => 'permission:plugins.manage',
|
||||||
]);
|
]);
|
||||||
$routes->post('(:segment)', 'PluginsController::settingsAction/$1', [
|
$routes->post('(:segment)', 'PluginController::generalSettingsAction/$1', [
|
||||||
'as' => 'plugins-settings-action',
|
'as' => 'plugins-general-settings-action',
|
||||||
'filter' => 'permission:plugins.manage',
|
'filter' => 'permission:plugins.manage',
|
||||||
]);
|
]);
|
||||||
$routes->post('activate/(:segment)', 'PluginsController::activate/$1', [
|
$routes->post('activate/(:segment)', 'PluginController::activate/$1', [
|
||||||
'as' => 'plugins-activate',
|
'as' => 'plugins-activate',
|
||||||
'filter' => 'permission:plugins.manage',
|
'filter' => 'permission:plugins.manage',
|
||||||
]);
|
]);
|
||||||
$routes->post('deactivate/(:segment)', 'PluginsController::deactivate/$1', [
|
$routes->post('deactivate/(:segment)', 'PluginController::deactivate/$1', [
|
||||||
'as' => 'plugins-deactivate',
|
'as' => 'plugins-deactivate',
|
||||||
'filter' => 'permission:plugins.manage',
|
'filter' => 'permission:plugins.manage',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
$routes->group('podcasts/(:num)/plugins', static function ($routes): void {
|
||||||
|
$routes->get('(:segment)', 'PluginController::podcastSettings/$1/$2', [
|
||||||
|
'as' => 'plugins-podcast-settings',
|
||||||
|
'filter' => 'permission:podcast#.edit',
|
||||||
|
]);
|
||||||
|
$routes->post('(:segment)', 'PluginController::podcastSettingsAction/$1/$2', [
|
||||||
|
'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', [
|
||||||
|
'as' => 'plugins-episode-settings',
|
||||||
|
'filter' => 'permission:podcast#.edit',
|
||||||
|
]);
|
||||||
|
$routes->post('(:segment)', 'PluginController::episodeSettingsAction/$1/$2/$3', [
|
||||||
|
'as' => 'plugins-episode-settings-action',
|
||||||
|
'filter' => 'permission:podcast#.edit',
|
||||||
|
]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,193 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Modules\Plugins\Controllers;
|
||||||
|
|
||||||
|
use App\Entities\Episode;
|
||||||
|
use App\Entities\Podcast;
|
||||||
|
use App\Models\EpisodeModel;
|
||||||
|
use App\Models\PodcastModel;
|
||||||
|
use CodeIgniter\Exceptions\PageNotFoundException;
|
||||||
|
use CodeIgniter\HTTP\RedirectResponse;
|
||||||
|
use Modules\Admin\Controllers\BaseController;
|
||||||
|
use Modules\Plugins\Plugins;
|
||||||
|
|
||||||
|
class PluginController extends BaseController
|
||||||
|
{
|
||||||
|
public function installed(): string
|
||||||
|
{
|
||||||
|
/** @var Plugins $plugins */
|
||||||
|
$plugins = service('plugins');
|
||||||
|
|
||||||
|
$pager = service('pager');
|
||||||
|
|
||||||
|
$page = (int) ($this->request->getGet('page') ?? 1);
|
||||||
|
$perPage = 10;
|
||||||
|
$total = $plugins->getInstalledCount();
|
||||||
|
|
||||||
|
$pager_links = $pager->makeLinks($page, $perPage, $total);
|
||||||
|
|
||||||
|
return view('plugins/installed', [
|
||||||
|
'total' => $total,
|
||||||
|
'plugins' => $plugins->getPlugins($page, $perPage),
|
||||||
|
'pager_links' => $pager_links,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generalSettings(string $pluginKey): string
|
||||||
|
{
|
||||||
|
/** @var Plugins $plugins */
|
||||||
|
$plugins = service('plugins');
|
||||||
|
|
||||||
|
$plugin = $plugins->getPlugin($pluginKey);
|
||||||
|
|
||||||
|
if ($plugin === null) {
|
||||||
|
throw PageNotFoundException::forPageNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
helper('form');
|
||||||
|
return view('plugins/settings_general', [
|
||||||
|
'plugin' => $plugin,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generalSettingsAction(string $pluginKey): RedirectResponse
|
||||||
|
{
|
||||||
|
/** @var Plugins $plugins */
|
||||||
|
$plugins = service('plugins');
|
||||||
|
|
||||||
|
$plugin = $plugins->getPlugin($pluginKey);
|
||||||
|
|
||||||
|
if ($plugin === null) {
|
||||||
|
throw PageNotFoundException::forPageNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($plugin->settings['general'] as $option) {
|
||||||
|
$optionKey = $option['key'];
|
||||||
|
$optionValue = $this->request->getPost($optionKey);
|
||||||
|
$plugins->setOption($pluginKey, $optionKey, $optionValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->back()
|
||||||
|
->with('message', lang('Plugins.messages.saveSettingsSuccess', [
|
||||||
|
'pluginName' => $plugin->getName(),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function podcastSettings(string $podcastId, string $pluginKey): string
|
||||||
|
{
|
||||||
|
$podcast = (new PodcastModel())->getPodcastById((int) $podcastId);
|
||||||
|
|
||||||
|
if (! $podcast instanceof Podcast) {
|
||||||
|
throw PageNotFoundException::forPageNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var Plugins $plugins */
|
||||||
|
$plugins = service('plugins');
|
||||||
|
|
||||||
|
$plugin = $plugins->getPlugin($pluginKey);
|
||||||
|
|
||||||
|
if ($plugin === null) {
|
||||||
|
throw PageNotFoundException::forPageNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
helper('form');
|
||||||
|
replace_breadcrumb_params([
|
||||||
|
0 => $podcast->handle,
|
||||||
|
]);
|
||||||
|
return view('plugins/settings_podcast', [
|
||||||
|
'podcast' => $podcast,
|
||||||
|
'plugin' => $plugin,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function podcastSettingsAction(string $podcastId, string $pluginKey): RedirectResponse
|
||||||
|
{
|
||||||
|
/** @var Plugins $plugins */
|
||||||
|
$plugins = service('plugins');
|
||||||
|
|
||||||
|
$plugin = $plugins->getPlugin($pluginKey);
|
||||||
|
|
||||||
|
if ($plugin === null) {
|
||||||
|
throw PageNotFoundException::forPageNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($plugin->settings['podcast'] as $setting) {
|
||||||
|
$settingKey = $setting['key'];
|
||||||
|
$settingValue = $this->request->getPost($settingKey);
|
||||||
|
$plugins->setOption($pluginKey, $settingKey, $settingValue, ['podcast', (int) $podcastId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->back()
|
||||||
|
->with('message', lang('Plugins.messages.saveSettingsSuccess', [
|
||||||
|
'pluginName' => $plugin->getName(),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function episodeSettings(string $podcastId, string $episodeId, string $pluginKey): string
|
||||||
|
{
|
||||||
|
$episode = (new EpisodeModel())->getEpisodeById((int) $episodeId);
|
||||||
|
|
||||||
|
if (! $episode instanceof Episode) {
|
||||||
|
throw PageNotFoundException::forPageNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var Plugins $plugins */
|
||||||
|
$plugins = service('plugins');
|
||||||
|
|
||||||
|
$plugin = $plugins->getPlugin($pluginKey);
|
||||||
|
|
||||||
|
if ($plugin === null) {
|
||||||
|
throw PageNotFoundException::forPageNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
helper('form');
|
||||||
|
replace_breadcrumb_params([
|
||||||
|
0 => $episode->podcast->handle,
|
||||||
|
1 => $episode->title,
|
||||||
|
]);
|
||||||
|
return view('plugins/settings_episode', [
|
||||||
|
'podcast' => $episode->podcast,
|
||||||
|
'episode' => $episode,
|
||||||
|
'plugin' => $plugin,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function episodeSettingsAction(string $podcastId, string $episodeId, string $pluginKey): RedirectResponse
|
||||||
|
{
|
||||||
|
/** @var Plugins $plugins */
|
||||||
|
$plugins = service('plugins');
|
||||||
|
|
||||||
|
$plugin = $plugins->getPlugin($pluginKey);
|
||||||
|
|
||||||
|
if ($plugin === null) {
|
||||||
|
throw PageNotFoundException::forPageNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($plugin->settings['episode'] as $setting) {
|
||||||
|
$settingKey = $setting['key'];
|
||||||
|
$settingValue = $this->request->getPost($settingKey);
|
||||||
|
$plugins->setOption($pluginKey, $settingKey, $settingValue, ['episode', (int) $episodeId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->back()
|
||||||
|
->with('message', lang('Plugins.messages.saveSettingsSuccess', [
|
||||||
|
'pluginName' => $plugin->getName(),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function activate(string $pluginKey): RedirectResponse
|
||||||
|
{
|
||||||
|
service('plugins')->activate($pluginKey);
|
||||||
|
|
||||||
|
return redirect()->back();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deactivate(string $pluginKey): RedirectResponse
|
||||||
|
{
|
||||||
|
service('plugins')->deactivate($pluginKey);
|
||||||
|
|
||||||
|
return redirect()->back();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,84 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Modules\Plugins\Controllers;
|
|
||||||
|
|
||||||
use CodeIgniter\Exceptions\PageNotFoundException;
|
|
||||||
use CodeIgniter\HTTP\RedirectResponse;
|
|
||||||
use Modules\Admin\Controllers\BaseController;
|
|
||||||
use Modules\Plugins\Plugins;
|
|
||||||
|
|
||||||
class PluginsController extends BaseController
|
|
||||||
{
|
|
||||||
public function installed(): string
|
|
||||||
{
|
|
||||||
/** @var Plugins $plugins */
|
|
||||||
$plugins = service('plugins');
|
|
||||||
|
|
||||||
$pager = service('pager');
|
|
||||||
|
|
||||||
$page = (int) ($this->request->getGet('page') ?? 1);
|
|
||||||
$perPage = 10;
|
|
||||||
$total = $plugins->getInstalledCount();
|
|
||||||
|
|
||||||
$pager_links = $pager->makeLinks($page, $perPage, $total);
|
|
||||||
|
|
||||||
return view('plugins/installed', [
|
|
||||||
'total' => $total,
|
|
||||||
'plugins' => $plugins->getPlugins($page, $perPage),
|
|
||||||
'pager_links' => $pager_links,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function settings(string $pluginKey): string
|
|
||||||
{
|
|
||||||
/** @var Plugins $plugins */
|
|
||||||
$plugins = service('plugins');
|
|
||||||
|
|
||||||
$plugin = $plugins->getPlugin($pluginKey);
|
|
||||||
|
|
||||||
if ($plugin === null) {
|
|
||||||
throw PageNotFoundException::forPageNotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
helper('form');
|
|
||||||
return view('plugins/settings', [
|
|
||||||
'plugin' => $plugin,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function settingsAction(string $pluginKey): RedirectResponse
|
|
||||||
{
|
|
||||||
/** @var Plugins $plugins */
|
|
||||||
$plugins = service('plugins');
|
|
||||||
|
|
||||||
$plugin = $plugins->getPlugin($pluginKey);
|
|
||||||
|
|
||||||
if ($plugin === null) {
|
|
||||||
throw PageNotFoundException::forPageNotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($plugin->options['settings'] as $option) {
|
|
||||||
$optionKey = $option['key'];
|
|
||||||
$optionValue = $this->request->getPost($optionKey);
|
|
||||||
$plugins->setOption($pluginKey, $optionKey, $optionValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()->back();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function activate(string $pluginKey): RedirectResponse
|
|
||||||
{
|
|
||||||
service('plugins')->activate($pluginKey);
|
|
||||||
|
|
||||||
return redirect()->back();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deactivate(string $pluginKey): RedirectResponse
|
|
||||||
{
|
|
||||||
service('plugins')->deactivate($pluginKey);
|
|
||||||
|
|
||||||
return redirect()->back();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,22 +2,49 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Modules\Plugins\Plugins;
|
||||||
|
|
||||||
|
if (! function_exists('plugins')) {
|
||||||
|
function plugins(): Plugins
|
||||||
|
{
|
||||||
|
return service('plugins');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (! function_exists('get_plugin_option')) {
|
if (! function_exists('get_plugin_option')) {
|
||||||
function get_plugin_option(string $pluginKey, string $option): mixed
|
/**
|
||||||
|
* @param ?array{'podcast'|'episode',int} $additionalContext
|
||||||
|
*/
|
||||||
|
function get_plugin_option(string $pluginKey, string $option, array $additionalContext = null): mixed
|
||||||
{
|
{
|
||||||
$key = sprintf('Plugins.%s', $option);
|
$key = sprintf('Plugins.%s', $option);
|
||||||
$context = sprintf('plugin:%s', $pluginKey);
|
$context = sprintf('plugin:%s', $pluginKey);
|
||||||
|
|
||||||
|
if ($additionalContext !== null) {
|
||||||
|
$context .= sprintf('+%s:%d', ...$additionalContext);
|
||||||
|
}
|
||||||
|
|
||||||
return setting()->get($key, $context);
|
return setting()->get($key, $context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! function_exists('set_plugin_option')) {
|
if (! function_exists('set_plugin_option')) {
|
||||||
function set_plugin_option(string $pluginKey, string $option, mixed $value = null): void
|
/**
|
||||||
{
|
* @param ?array{'podcast'|'episode',int} $additionalContext
|
||||||
|
*/
|
||||||
|
function set_plugin_option(
|
||||||
|
string $pluginKey,
|
||||||
|
string $option,
|
||||||
|
mixed $value = null,
|
||||||
|
array $additionalContext = null
|
||||||
|
): void {
|
||||||
$key = sprintf('Plugins.%s', $option);
|
$key = sprintf('Plugins.%s', $option);
|
||||||
$context = sprintf('plugin:%s', $pluginKey);
|
$context = sprintf('plugin:%s', $pluginKey);
|
||||||
|
|
||||||
|
if ($additionalContext !== null) {
|
||||||
|
$context .= sprintf('+%s:%d', ...$additionalContext);
|
||||||
|
}
|
||||||
|
|
||||||
setting()
|
setting()
|
||||||
->set($key, $value, $context);
|
->set($key, $value, $context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,14 +9,18 @@ declare(strict_types=1);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return [
|
return [
|
||||||
"installed" => "Installed plugins ({count})",
|
'installed' => 'Installed plugins ({count})',
|
||||||
"website" => "Website",
|
'website' => 'Website',
|
||||||
"activate" => "Activate",
|
'settings' => '{pluginName} settings',
|
||||||
"deactivate" => "Deactivate",
|
'activate' => 'Activate',
|
||||||
"keywords" => [
|
'deactivate' => 'Deactivate',
|
||||||
|
'keywords' => [
|
||||||
'podcasting20' => 'Podcasting 2.0',
|
'podcasting20' => 'Podcasting 2.0',
|
||||||
'seo' => 'SEO',
|
'seo' => 'SEO',
|
||||||
'analytics' => 'Analytics',
|
'analytics' => 'Analytics',
|
||||||
'accessibility' => 'Accessibility',
|
'accessibility' => 'Accessibility',
|
||||||
],
|
],
|
||||||
|
'messages' => [
|
||||||
|
'saveSettingsSuccess' => '{pluginName} settings were successfully saved!',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
@ -57,6 +57,48 @@ class Plugins
|
||||||
return array_slice(static::$plugins, (($page - 1) * $perPage), $perPage);
|
return array_slice(static::$plugins, (($page - 1) * $perPage), $perPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<BasePlugin>
|
||||||
|
*/
|
||||||
|
public function getPluginsWithPodcastSettings(): array
|
||||||
|
{
|
||||||
|
$pluginsWithPodcastSettings = [];
|
||||||
|
foreach (static::$plugins as $plugin) {
|
||||||
|
if (! $plugin->isActive()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($plugin->settings['podcast'] === []) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pluginsWithPodcastSettings[] = $plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pluginsWithPodcastSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<BasePlugin>
|
||||||
|
*/
|
||||||
|
public function getPluginsWithEpisodeSettings(): array
|
||||||
|
{
|
||||||
|
$pluginsWithEpisodeSettings = [];
|
||||||
|
foreach (static::$plugins as $plugin) {
|
||||||
|
if (! $plugin->isActive()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($plugin->settings['episode'] === []) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pluginsWithEpisodeSettings[] = $plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pluginsWithEpisodeSettings;
|
||||||
|
}
|
||||||
|
|
||||||
public function getPlugin(string $key): ?BasePlugin
|
public function getPlugin(string $key): ?BasePlugin
|
||||||
{
|
{
|
||||||
foreach (static::$plugins as $plugin) {
|
foreach (static::$plugins as $plugin) {
|
||||||
|
@ -98,9 +140,12 @@ class Plugins
|
||||||
set_plugin_option($pluginKey, 'active', false);
|
set_plugin_option($pluginKey, 'active', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setOption(string $pluginKey, string $name, string $value): void
|
/**
|
||||||
|
* @param ?array{'podcast'|'episode',int} $additionalContext
|
||||||
|
*/
|
||||||
|
public function setOption(string $pluginKey, string $name, mixed $value, array $additionalContext = null): void
|
||||||
{
|
{
|
||||||
set_plugin_option($pluginKey, $name, $value);
|
set_plugin_option($pluginKey, $name, $value, $additionalContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getInstalledCount(): int
|
public function getInstalledCount(): int
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
<nav class="flex flex-col flex-1 py-4 overflow-y-auto">
|
<nav class="flex flex-col flex-1 py-4 overflow-y-auto">
|
||||||
<?php foreach ($navigation as $section => $data):
|
<?php foreach ($navigation as $section => $data):
|
||||||
|
if ($data['items'] === []) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$isSectionActive = false;
|
$isSectionActive = false;
|
||||||
$activeItem = '';
|
$activeItem = '';
|
||||||
foreach ($data['items'] as $item) {
|
foreach ($data['items'] as $item) {
|
||||||
if (url_is(route_to($item, $podcastId ?? null, $episodeId ?? null))) {
|
$href = str_starts_with($item, '/') ? $item : route_to($item, $podcastId ?? null, $episodeId ?? null);
|
||||||
|
if (url_is($href)) {
|
||||||
$activeItem = $item;
|
$activeItem = $item;
|
||||||
$isSectionActive = true;
|
$isSectionActive = true;
|
||||||
}
|
}
|
||||||
|
@ -27,8 +32,10 @@
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</summary>
|
</summary>
|
||||||
<ul class="flex flex-col pb-4">
|
<ul class="flex flex-col pb-4">
|
||||||
<?php foreach ($data['items'] as $item):
|
<?php foreach ($data['items'] as $key => $item):
|
||||||
$isActive = $item === $activeItem;
|
$isActive = $item === $activeItem;
|
||||||
|
$label = array_key_exists('items-labels', $data) ? $data['items-labels'][$key] : lang($langKey . '.' . $item);
|
||||||
|
$href = str_starts_with($item, '/') ? $item : route_to($item, $podcastId ?? null, $episodeId ?? null);
|
||||||
|
|
||||||
$isAllowed = true;
|
$isAllowed = true;
|
||||||
if (array_key_exists('items-permissions', $data) && array_key_exists($item, $data['items-permissions'])) {
|
if (array_key_exists('items-permissions', $data) && array_key_exists($item, $data['items-permissions'])) {
|
||||||
|
@ -43,13 +50,9 @@
|
||||||
<?php if ($isAllowed): ?>
|
<?php if ($isAllowed): ?>
|
||||||
<a class="relative w-full py-3 pl-14 pr-2 text-sm hover:opacity-100 before:content-chevronRightIcon before:absolute before:-ml-5 before:opacity-0 before:w-5 before:h-5 hover:bg-navigation-active focus:ring-inset focus:ring-accent<?= $isActive
|
<a class="relative w-full py-3 pl-14 pr-2 text-sm hover:opacity-100 before:content-chevronRightIcon before:absolute before:-ml-5 before:opacity-0 before:w-5 before:h-5 hover:bg-navigation-active focus:ring-inset focus:ring-accent<?= $isActive
|
||||||
? ' before:opacity-100 font-semibold inline-flex items-center'
|
? ' before:opacity-100 font-semibold inline-flex items-center'
|
||||||
: ' hover:before:opacity-60 focus:before:opacity-60' ?>" href="<?= route_to($item, $podcastId ?? null, $episodeId ?? null) ?>"><?= lang(
|
: ' hover:before:opacity-60 focus:before:opacity-60' ?>" href="<?= $href ?>"><?= $label ?></a>
|
||||||
$langKey . '.' . $item,
|
|
||||||
) ?></a>
|
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<span data-tooltip="right" title="<?= lang('Navigation.not-authorized') ?>" class="relative w-full py-3 pr-2 text-sm cursor-not-allowed before:inset-y-0 before:my-auto pl-14 hover:opacity-100 before:absolute before:content-prohibitedIcon before:-ml-5 before:opacity-60 before:w-4 before:h-4 hover:bg-navigation-active focus:ring-inset focus:ring-accent"><?= lang(
|
<span data-tooltip="right" title="<?= lang('Navigation.not-authorized') ?>" class="relative w-full py-3 pr-2 text-sm cursor-not-allowed before:inset-y-0 before:my-auto pl-14 hover:opacity-100 before:absolute before:content-prohibitedIcon before:-ml-5 before:opacity-60 before:w-4 before:h-4 hover:bg-navigation-active focus:ring-inset focus:ring-accent"><?= $label ?></span>
|
||||||
$langKey . '.' . $item,
|
|
||||||
) ?></span>
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</li>
|
</li>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|
|
@ -24,7 +24,22 @@ $episodeNavigation = [
|
||||||
'count-route' => 'video-clips-list',
|
'count-route' => 'video-clips-list',
|
||||||
'add-cta' => 'video-clips-create',
|
'add-cta' => 'video-clips-create',
|
||||||
],
|
],
|
||||||
]; ?>
|
'plugins' => [
|
||||||
|
'icon' => 'puzzle-fill', // @icon('puzzle-fill')
|
||||||
|
'items' => [],
|
||||||
|
'items-labels' => [],
|
||||||
|
'items-permissions' => [],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach (plugins()->getPluginsWithEpisodeSettings() as $plugin) {
|
||||||
|
$route = route_to('plugins-episode-settings', $podcast->id, $episode->id, $plugin->getKey());
|
||||||
|
$episodeNavigation['plugins']['items'][] = $route;
|
||||||
|
$episodeNavigation['plugins']['items-labels'][] = $plugin->getName();
|
||||||
|
$episodeNavigation['plugins']['items-permissions'][$route] = 'episodes.edit';
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
<a href="<?= route_to('podcast-view', $podcast->id) ?>" class="flex items-center px-4 py-2 focus:ring-inset focus:ring-accent">
|
<a href="<?= route_to('podcast-view', $podcast->id) ?>" class="flex items-center px-4 py-2 focus:ring-inset focus:ring-accent">
|
||||||
<?= icon('arrow-left-line', [
|
<?= icon('arrow-left-line', [
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
<article class="flex flex-col p-4 rounded-xl bg-elevated border-3 <?= $plugin->isActive() ? 'border-accent-base' : 'border-subtle' ?>">
|
<article class="flex flex-col p-4 rounded-xl relative bg-elevated border-3 <?= $plugin->isActive() ? 'border-accent-base' : 'border-subtle' ?>">
|
||||||
|
<?php if ($plugin->settings['general'] !== []): ?>
|
||||||
|
<?php // @icon('equalizer-fill')?>
|
||||||
|
<IconButton class="absolute top-0 right-0 mt-4 mr-4" uri="<?= route_to('plugins-general-settings', $plugin->getKey()) ?>" glyph="equalizer-fill"><?= lang('Plugins.settings', [
|
||||||
|
'pluginName' => $plugin->getName(),
|
||||||
|
]) ?></IconButton>
|
||||||
|
<?php endif; ?>
|
||||||
<img class="rounded-full min-w-16 max-w-16 aspect-square" src="<?= $plugin->iconSrc ?>">
|
<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 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>
|
<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>
|
||||||
|
@ -8,10 +14,7 @@
|
||||||
<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', [
|
<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', [
|
||||||
'class' => 'text-gray-500',
|
'class' => 'text-gray-500',
|
||||||
]) . lang('Plugins.website') ?></a>
|
]) . lang('Plugins.website') ?></a>
|
||||||
<?php if ($plugin->options['settings'] !== []): ?>
|
|
||||||
<?php // @icon('equalizer-fill')?>
|
|
||||||
<IconButton uri="<?= route_to('plugins-settings', $plugin->getKey()) ?>" glyph="equalizer-fill">Settings</IconButton>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if($plugin->isActive()): ?>
|
<?php if($plugin->isActive()): ?>
|
||||||
<form class="flex justify-end" method="POST" action="<?= route_to('plugins-deactivate', $plugin->getKey()) ?>">
|
<form class="flex justify-end" method="POST" action="<?= route_to('plugins-deactivate', $plugin->getKey()) ?>">
|
||||||
<?= csrf_field() ?>
|
<?= csrf_field() ?>
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
<form method="POST" action="<?= $action ?>" class="flex flex-col max-w-sm gap-4" >
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
<?php foreach ($plugin->settings[$type] as $field): ?>
|
||||||
|
<Forms.Field
|
||||||
|
name="<?= $field['key'] ?>"
|
||||||
|
label="<?= $field['name'] ?>"
|
||||||
|
hint="<?= $field['description'] ?>"
|
||||||
|
value="<?= get_plugin_option($plugin->getKey(), $field['key'], $context) ?>"
|
||||||
|
/>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<Button class="self-end mt-4" variant="primary" type="submit"><?= lang('Common.forms.save') ?></Button>
|
||||||
|
</form>
|
|
@ -1,15 +0,0 @@
|
||||||
<?= $this->extend('_layout') ?>
|
|
||||||
|
|
||||||
<?= $this->section('content') ?>
|
|
||||||
<form method="POST" action="<?= route_to('plugins-settings-action', $plugin->getKey()) ?>" class="flex flex-col max-w-sm gap-4" >
|
|
||||||
<?= csrf_field() ?>
|
|
||||||
<?php foreach ($plugin->options['settings'] as $option): ?>
|
|
||||||
<Forms.Field
|
|
||||||
name="<?= $option['key'] ?>"
|
|
||||||
label="<?= $option['name'] ?>"
|
|
||||||
hint="<?= $option['description'] ?>"
|
|
||||||
/>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<Button class="self-end mt-4" variant="primary" type="submit"><?= lang('Plugins.form.save') ?></Button>
|
|
||||||
</form>
|
|
||||||
<?= $this->endSection() ?>
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?= $this->extend('_layout') ?>
|
||||||
|
|
||||||
|
<?= $this->section('title') ?>
|
||||||
|
<?= lang('Plugins.settings', [
|
||||||
|
'pluginName' => $plugin->getName(),
|
||||||
|
]) ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
<?= $this->section('pageTitle') ?>
|
||||||
|
<?= lang('Plugins.settings', [
|
||||||
|
'pluginName' => $plugin->getName(),
|
||||||
|
]) ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
<?= $this->section('content') ?>
|
||||||
|
<?= view('plugins/_settings', [
|
||||||
|
'plugin' => $plugin,
|
||||||
|
'action' => route_to('plugins-episode-settings-action', $podcast->id, $episode->id, $plugin->getKey()),
|
||||||
|
'type' => 'episode',
|
||||||
|
'context' => ['episode', $episode->id],
|
||||||
|
]) ?>
|
||||||
|
<?= $this->endSection() ?>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?= $this->extend('_layout') ?>
|
||||||
|
|
||||||
|
<?= $this->section('title') ?>
|
||||||
|
<?= lang('Plugins.settings', [
|
||||||
|
'pluginName' => $plugin->getName(),
|
||||||
|
]) ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
<?= $this->section('pageTitle') ?>
|
||||||
|
<?= lang('Plugins.settings', [
|
||||||
|
'pluginName' => $plugin->getName(),
|
||||||
|
]) ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
<?= $this->section('content') ?>
|
||||||
|
<?= view('plugins/_settings', [
|
||||||
|
'plugin' => $plugin,
|
||||||
|
'action' => route_to('plugins-general-settings-action', $plugin->getKey()),
|
||||||
|
'type' => 'general',
|
||||||
|
'context' => null,
|
||||||
|
]) ?>
|
||||||
|
<?= $this->endSection() ?>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?= $this->extend('_layout') ?>
|
||||||
|
|
||||||
|
<?= $this->section('title') ?>
|
||||||
|
<?= lang('Plugins.settings', [
|
||||||
|
'pluginName' => $plugin->getName(),
|
||||||
|
]) ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
<?= $this->section('pageTitle') ?>
|
||||||
|
<?= lang('Plugins.settings', [
|
||||||
|
'pluginName' => $plugin->getName(),
|
||||||
|
]) ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
<?= $this->section('content') ?>
|
||||||
|
<?= view('plugins/_settings', [
|
||||||
|
'plugin' => $plugin,
|
||||||
|
'action' => route_to('plugins-podcast-settings-action', $podcast->id, $plugin->getKey()),
|
||||||
|
'type' => 'podcast',
|
||||||
|
'context' => ['podcast', $podcast->id],
|
||||||
|
]) ?>
|
||||||
|
<?= $this->endSection() ?>
|
|
@ -23,6 +23,12 @@ $podcastNavigation = [
|
||||||
'count' => $podcast->getEpisodesCount(),
|
'count' => $podcast->getEpisodesCount(),
|
||||||
'count-route' => 'episode-list',
|
'count-route' => 'episode-list',
|
||||||
],
|
],
|
||||||
|
'plugins' => [
|
||||||
|
'icon' => 'puzzle-fill', // @icon('puzzle-fill')
|
||||||
|
'items' => [],
|
||||||
|
'items-labels' => [],
|
||||||
|
'items-permissions' => [],
|
||||||
|
],
|
||||||
'analytics' => [
|
'analytics' => [
|
||||||
'icon' => 'line-chart-fill', // @icon('line-chart-fill')
|
'icon' => 'line-chart-fill', // @icon('line-chart-fill')
|
||||||
'items' => [
|
'items' => [
|
||||||
|
@ -83,6 +89,13 @@ $podcastNavigation = [
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
foreach (plugins()->getPluginsWithPodcastSettings() as $plugin) {
|
||||||
|
$route = route_to('plugins-podcast-settings', $podcast->id, $plugin->getKey());
|
||||||
|
$podcastNavigation['plugins']['items'][] = $route;
|
||||||
|
$podcastNavigation['plugins']['items-labels'][] = $plugin->getName();
|
||||||
|
$podcastNavigation['plugins']['items-permissions'][$route] = 'edit';
|
||||||
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="flex gap-2 px-2 py-2 border-b border-navigation">
|
<div class="flex gap-2 px-2 py-2 border-b border-navigation">
|
||||||
|
|
Loading…
Reference in New Issue