use object for filters stack

This commit is contained in:
Jean-Christian Paul Denis 2023-04-21 15:46:40 +02:00
parent d1ea771013
commit dd0e90cd5e
Signed by: JcDenis
GPG Key ID: 1B5B8C5B90B6C951
9 changed files with 185 additions and 164 deletions

View File

@ -35,8 +35,8 @@ class Epc
{
public const FLAGGER = 'ççççç%sççççç';
protected static array $default_filters = [];
public static array $epcFilterLimit = [];
private static EpcFilters $filters;
public static array $epcFilterLimit = [];
#
# Default definition
@ -110,31 +110,29 @@ class Epc
return is_array($rs) ? $rs : self::defaultAllowedPubPages();
}
public static function getFilters(): array
/**
* Get filters.
*
* On first call, we load once filters from behavior.
*
* @return EpcFilters The fitlers instacne
*/
public static function getFilters(): EpcFilters
{
if (empty(self::$default_filters)) {
$final = $sort = [];
/** @var ArrayObject<string,EpcFilter> $filters The filters stack */
$filters = new ArrayObject();
if (empty(self::$filters)) {
$filters = new EpcFilters();
try {
# --BEHAVIOR-- enhancePostContentFilters : ArrayObject
# --BEHAVIOR-- enhancePostContentFilters : EpcFilters
dcCore::app()->callBehavior('enhancePostContentFilters', $filters);
foreach ($filters as $filter) {
if (!isset($final[$filter->id()]) && ($filter instanceof EpcFilter)) {
$sort[$filter->id()] = $filter->priority;
$final[$filter->id()] = $filter;
}
}
} catch (Exception $e) {
dcCore::app()->error->add($e->getMessage());
}
array_multisort($sort, $final);
self::$default_filters = $final;
self::$filters = $filters->sort();
}
return self::$default_filters;
return self::$filters;
}
public static function testContext(string $tag, array $args, EpcFilter $filter): bool
@ -243,7 +241,7 @@ class Epc
public static function removeTags(array $m): string
{
return $m[1] . preg_replace('#' . sprintf(self::FLAGGER, '(?!') .')#s', '$1', $m[3]) . $m[4];
return $m[1] . preg_replace('#' . sprintf(self::FLAGGER, '(?!') . ')#s', '$1', $m[3]) . $m[4];
}
public static function decodeTags(string $t): array

89
src/EpcFilters.php Normal file
View File

@ -0,0 +1,89 @@
<?php
/**
* @brief enhancePostContent, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and Contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\enhancePostContent;
/**
* Filters stack.
*
* Use Epc::getFilters() to get loaded stack
*/
class EpcFilters
{
/** @var array<int,EpcFilter> $satck The filters stack */
private array $stack = [];
/**
* Add a filter to the stack.
*
* @return EpcFilters The filters instance
*/
public function add(EpcFilter $filter): EpcFilters
{
$this->stack[$filter->id()] = $filter;
return $this;
}
/**
* Get all filters.
*
* @return array<string,EpcFilter> The filters stack
*/
public function dump(): array
{
return $this->stack;
}
/**
* Get a filter.
*
* @param string $id The filter ID
*
* @return null|EpcFilter The filter
*/
public function get(string $id): ?EpcFilter
{
return $this->stack[$id] ?? null;
}
/**
* Get filters name / id pair.
*
* @return array The nid pairs
*/
public function nid(bool $exclude_widget = false): array
{
$nid = [];
foreach ($this->stack as $filter) {
if ($filter->widget != '') {
$nid[$filter->name] = $filter->id();
}
}
return $nid;
}
/**
* Sort filters stack by filter name.
*
* @return EpcFilters The filters instance
*/
public function sort(): EpcFilters
{
uasort($this->stack, fn ($a, $b) => $a->name <=> $b->name);
return $this;
}
}

View File

@ -44,7 +44,7 @@ class Frontend extends dcNsProcess
},
// Filter template blocks content
'publicBeforeContentFilterV2' => function (string $tag, array $args): void {
foreach (Epc::getFilters() as $id => $filter) {
foreach (Epc::getFilters()->dump() as $filter) {
if (!Epc::testContext($tag, $args, $filter)) {
continue;
}

View File

@ -75,7 +75,7 @@ class Install extends dcNsProcess
$s->put('allowedpubpages', json_encode(Epc::defaultAllowedPubPages()), 'string', 'List of allowed template pages', false, true);
// Filters settings
foreach (Epc::getFilters() as $id => $filter) {
foreach (Epc::getFilters()->dump() as $filter) {
// Only editable options
$opt = [
'nocase' => $filter->nocase,
@ -85,7 +85,7 @@ class Install extends dcNsProcess
'tplValues' => $filter->tplValues,
'pubPages' => $filter->pubPages,
];
$s->put($id, json_encode($opt), 'string', 'Settings for ' . $id, false, true);
$s->put($filter->id(), json_encode($opt), 'string', 'Settings for ' . $filter->id(), false, true);
}
return true;
@ -178,13 +178,13 @@ class Install extends dcNsProcess
private static function upTo20221120(): void
{
// list of settings using serialize values to move to json
$ids = [
'allowedtplvalues',
'allowedpubpages',
];
foreach (Epc::getFilters() as $id => $f) {
$ids[] = $id;
}
$ids = array_merge(
[
'allowedtplvalues',
'allowedpubpages',
],
array_values(Epc::getFilters()->nid())
);
// get all enhancePostContent settings
$record = dcCore::app()->con->select(

View File

@ -61,20 +61,24 @@ class Manage extends dcNsProcess
return false;
}
$current = ManageVars::init();
$action = $_POST['action'] ?? '';
$filter = Epc::getFilters()->get($_REQUEST['part'] ?? '');
if (is_null($filter)) {
return true;
}
if (dcCore::app()->error->flag()) {
return true;
}
if (!empty($current->action)) {
if (!empty($action)) {
# --BEHAVIOR-- enhancePostContentAdminSave
dcCore::app()->callBehavior('enhancePostContentAdminSave');
}
try {
# Update filter settings
if ($current->action == 'savefiltersetting') {
if ($action == 'savefiltersetting') {
# Parse filters options
$f = [
'nocase' => !empty($_POST['filter_nocase']),
@ -86,7 +90,7 @@ class Manage extends dcNsProcess
'pubPages' => (array) $_POST['filter_pubPages'],
];
dcCore::app()->blog->settings->get(My::id())->put($current->filter->id(), json_encode($f));
dcCore::app()->blog->settings->get(My::id())->put($filter->id(), json_encode($f));
dcCore::app()->blog->triggerBlog();
@ -96,18 +100,18 @@ class Manage extends dcNsProcess
dcCore::app()->adminurl->redirect(
'admin.plugin.' . My::id(),
['part' => $current->part],
['part' => $filter->id()],
'#settings'
);
}
# Add new filter record
if ($current->action == 'savenewrecord'
if ($action == 'savenewrecord'
&& !empty($_POST['new_key'])
&& !empty($_POST['new_value'])
) {
$cur = EpcRecord::openCursor();
$cur->setField('epc_filter', $current->filter->id());
$cur->setField('epc_filter', $filter->id());
$cur->setField('epc_key', Html::escapeHTML($_POST['new_key']));
$cur->setField('epc_value', Html::escapeHTML($_POST['new_value']));
@ -124,14 +128,14 @@ class Manage extends dcNsProcess
}
dcCore::app()->adminurl->redirect(
'admin.plugin.' . My::id(),
['part' => $current->part],
['part' => $filter->id()],
'#record'
);
}
# Update filter records
if ($current->action == 'deleterecords'
&& $current->filter->has_list
if ($action == 'deleterecords'
&& $filter->has_list
&& !empty($_POST['epc_id'])
&& is_array($_POST['epc_id'])
) {
@ -150,7 +154,7 @@ class Manage extends dcNsProcess
} else {
dcCore::app()->adminurl->redirect(
'admin.plugin.' . My::id(),
['part' => $current->part],
['part' => $filter->id()],
'#record'
);
}
@ -172,17 +176,21 @@ class Manage extends dcNsProcess
return;
}
$current = ManageVars::init();
$filters = Epc::getFilters();
$filter = $filters->get($_REQUEST['part'] ?? 'link');
if (is_null($filter)) {
return;
}
# -- Prepare page --
$header = '';
if ($current->filter->has_list) {
if ($filter->has_list) {
$sorts = new adminGenericFilterV2('epc');
$sorts->add(dcAdminFilters::getPageFilter());
$sorts->add('part', $current->part);
$sorts->add('part', $filter->id());
$params = $sorts->params();
$params['epc_filter'] = $current->filter->id();
$params['epc_filter'] = $filter->id();
try {
$list = EpcRecord::getRecords($params);
@ -192,7 +200,7 @@ class Manage extends dcNsProcess
dcCore::app()->error->add($e->getMessage());
}
$header = $sorts->js(dcCore::app()->adminurl->get('admin.plugin.' . My::id(), ['part' => $current->part], '&') . '#record');
$header = $sorts->js(dcCore::app()->adminurl->get('admin.plugin.' . My::id(), ['part' => $filter->id()], '&') . '#record');
}
# Page headers
@ -209,9 +217,9 @@ class Manage extends dcNsProcess
# Page title
echo
dcPage::breadcrumb([
__('Plugins') => '',
My::name() => '',
$current->filter->name => '',
__('Plugins') => '',
My::name() => '',
$filter->name => '',
]) .
dcPage::notices();
@ -219,7 +227,7 @@ class Manage extends dcNsProcess
echo
(new Form('filters_menu'))->method('get')->action(dcCore::app()->adminurl->get('admin.plugin.' . My::id()))->fields([
(new Para())->class('anchor-nav')->items([
(new Select('part'))->items($current->combo)->default($current->part),
(new Select('part'))->items($filters->nid())->default($filter->id()),
(new Submit(['do']))->value(__('Ok')),
(new Hidden(['p'], My::id())),
]),
@ -227,14 +235,14 @@ class Manage extends dcNsProcess
# Filter title and description
echo
'<h3>' . $current->filter->name . '</h3>' .
'<p>' . $current->filter->help . '</p>';
'<h3>' . $filter->name . '</h3>' .
'<p>' . $filter->help . '</p>';
# Filter settings
$form_pages = [(new Text('h4', __('Pages to be filtered')))];
foreach (Epc::blogAllowedPubPages() as $k => $v) {
$form_pages[] = (new Para())->items([
(new Checkbox(['filter_pubPages[]', 'filter_pubPages' . $v], in_array($v, $current->filter->pubPages)))->value($v),
(new Checkbox(['filter_pubPages[]', 'filter_pubPages' . $v], in_array($v, $filter->pubPages)))->value($v),
(new Label(__($k), Label::OUTSIDE_LABEL_AFTER))->for('filter_pubPages' . $v)->class('classic'),
]);
}
@ -242,16 +250,16 @@ class Manage extends dcNsProcess
$form_values = [(new Text('h4', __('Contents to be filtered')))];
foreach (Epc::blogAllowedTplValues() as $k => $v) {
$form_values[] = (new Para())->items([
(new Checkbox(['filter_tplValues[]', 'filter_tplValues' . $v], in_array($v, $current->filter->tplValues)))->value($v),
(new Checkbox(['filter_tplValues[]', 'filter_tplValues' . $v], in_array($v, $filter->tplValues)))->value($v),
(new Label(__($k), Label::OUTSIDE_LABEL_AFTER))->for('filter_tplValues' . $v)->class('classic'),
]);
}
$form_styles = [(new Text('h4', __('Style')))];
foreach ($current->filter->class as $k => $v) {
foreach ($filter->class as $k => $v) {
$form_styles[] = (new Para())->items([
(new Label(sprintf(__('Class "%s":'), $v), Label::OUTSIDE_LABEL_BEFORE))->for('filter_style' . $k),
(new Input(['filter_style[]', 'filter_style' . $k]))->size(60)->maxlenght(255)->value(Html::escapeHTML($current->filter->style[$k])),
(new Input(['filter_style[]', 'filter_style' . $k]))->size(60)->maxlenght(255)->value(Html::escapeHTML($filter->style[$k])),
]);
}
@ -262,46 +270,46 @@ class Manage extends dcNsProcess
(new Div())->class('two-boxes odd')->items([
(new Text('h4', __('Filtering'))),
(new Para())->items([
(new Checkbox('filter_nocase', $current->filter->nocase))->value(1),
(new Checkbox('filter_nocase', $filter->nocase))->value(1),
(new Label(__('Case insensitive'), Label::OUTSIDE_LABEL_AFTER))->for('filter_nocase')->class('classic'),
]),
(new Para())->items([
(new Checkbox('filter_plural', $current->filter->plural))->value(1),
(new Checkbox('filter_plural', $filter->plural))->value(1),
(new Label(__('Also use the plural'), Label::OUTSIDE_LABEL_AFTER))->for('filter_plural')->class('classic'),
]),
(new Para())->items([
(new Label(__('Limit the number of replacement to:'), Label::OUTSIDE_LABEL_BEFORE))->for('filter_limit'),
(new Number('filter_limit'))->min(0)->max(99)->value((int) $current->filter->limit),
(new Number('filter_limit'))->min(0)->max(99)->value((int) $filter->limit),
]),
(new Note())->class('form-note')->text(__('Leave it blank or set it to 0 for no limit')),
]),
(new Div())->class('two-boxes even')->items($form_values),
(new Div())->class('two-boxes odd')->items(array_merge($form_styles, [
(new Note())->class('form-note')->text(sprintf(__('The inserted HTML tag looks like: %s'), Html::escapeHTML(str_replace('%s', '...', $current->filter->replace)))),
(new Note())->class('form-note')->text(sprintf(__('The inserted HTML tag looks like: %s'), Html::escapeHTML(str_replace('%s', '...', $filter->replace)))),
(new Para())->items([
(new Label(__('Ignore HTML tags:'), Label::OUTSIDE_LABEL_BEFORE))->for('filter_notag'),
(new Input('filter_notag'))->size(60)->maxlenght(255)->value(Html::escapeHTML($current->filter->notag)),
(new Input('filter_notag'))->size(60)->maxlenght(255)->value(Html::escapeHTML($filter->notag)),
]),
(new Note())->class('form-note')->text(__('This is the list of HTML tags where content will be ignored.') . ' ' . ('' != $current->filter->htmltag ? '' : sprintf(__('Tag "%s" always be ignored.'), $current->filter->htmltag))),
(new Note())->class('form-note')->text(__('This is the list of HTML tags where content will be ignored.') . ' ' . ('' != $filter->htmltag ? '' : sprintf(__('Tag "%s" always be ignored.'), $filter->htmltag))),
])),
(new Div())->class('clear')->items([
dcCore::app()->formNonce(false),
(new Hidden(['action'], 'savefiltersetting')),
(new Hidden(['part'], $current->part)),
(new Hidden(['part'], $filter->id())),
(new Submit(['save']))->value(__('Save')),
]),
]),
])->render();
# Filter records list
if ($current->filter->has_list && isset($pager)) {
if ($filter->has_list && isset($pager)) {
$pager_url = dcCore::app()->adminurl->get('admin.plugin.' . My::id(), array_diff_key($sorts->values(true), ['page' => ''])) . '&page=%s#record';
echo '
<div class="multi-part" id="record" title="' . __('Records') . '">';
$sorts->display(['admin.plugin.' . My::id(), '#record'], (new Hidden('p', My::id()))->render() . (new Hidden('part', $current->part))->render());
$sorts->display(['admin.plugin.' . My::id(), '#record'], (new Hidden('p', My::id()))->render() . (new Hidden('part', $filter->id()))->render());
$pager->display(
$sorts,
@ -342,7 +350,7 @@ class Manage extends dcNsProcess
(new Para())->class('clear')->items([
dcCore::app()->formNonce(false),
(new Hidden(['action'], 'savenewrecord')),
(new Hidden(['part'], $current->part)),
(new Hidden(['part'], $filter->id())),
(new Submit(['save', 'new-action']))->value(__('Save')),
]),
]),

View File

@ -1,62 +0,0 @@
<?php
/**
* @brief enhancePostContent, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and Contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\enhancePostContent;
use Exception;
class ManageVars
{
/**
* @var ManageVars self instance
*/
private static $container;
public readonly EpcFilter $filter;
public readonly string $action;
public readonly string $part;
public readonly array $combo;
protected function __construct()
{
$_filters = Epc::getFilters();
$filters_id = $filters_combo = [];
if (!empty($_filters)) {
foreach ($_filters as $id => $filter) {
$filters_id[$id] = $filter->name;
$filters_combo[$filter->name] = $id;
}
}
$part = $_REQUEST['part'] ?? key($filters_id);
if (!isset($_filters[$part])) {
throw new Exception(__('no filters'));
}
$this->action = $_POST['action'] ?? '';
$this->part = $part;
$this->filter = $_filters[$part];
$this->combo = $filters_combo;
}
public static function init(): ManageVars
{
if (!(self::$container instanceof self)) {
self::$container = new self();
}
return self::$container;
}
}

View File

@ -29,16 +29,16 @@ class My
/** @var array Distributed filters */
public const DEFAULT_FILTERS = [
'Tag',
'Search',
'Acronym',
'Abbreviation',
'Definition',
'Citation',
'Link',
'Replace',
'Update',
'Twitter',
Filter\EpcFilterTag::class,
Filter\EpcFilterSearch::class,
Filter\EpcFilterAcronym::class,
Filter\EpcFilterAbbreviation::class,
Filter\EpcFilterDefinition::class,
Filter\EpcFilterCitation::class,
Filter\EpcFilterLink::class,
Filter\EpcFilterReplace::class,
Filter\EpcFilterUpdate::class,
Filter\EpcFilterTwitter::class,
];
/**

View File

@ -33,27 +33,21 @@ class Prepend extends dcNsProcess
return false;
}
$dir = __DIR__ . DIRECTORY_SEPARATOR . 'Filter' . DIRECTORY_SEPARATOR;
$ns = __NAMESPACE__ . '\\Filter\\';
dcCore::app()->autoload->addNamespace($ns, $dir);
foreach (My::DEFAULT_FILTERS as $f) {
dcCore::app()->addBehavior('enhancePostContentFilters', [$ns . 'EpcFilter' . $f, 'create']);
}
// register epc filters
dcCore::app()->addBehavior('enhancePostContentFilters', function (EpcFilters $stack): void {
foreach (My::DEFAULT_FILTERS as $class) {
$stack->add(new $class());
}
});
// register epc filters frontend css
dcCore::app()->url->register(
'epccss',
'epc.css',
'^epc\.css',
function (string $args): void {
$css = [];
$filters = Epc::getFilters();
if (empty($filters)) {
return;
}
foreach ($filters as $id => $filter) {
$css = [];
foreach (Epc::getFilters()->dump() as $filter) {
if ('' == $filter->class || '' == $filter->style) {
continue;
}
@ -68,7 +62,7 @@ class Prepend extends dcNsProcess
}
if (!empty($res)) {
$css[] = '/* CSS for enhancePostContent ' . $id . " */ \n" . $res . "\n";
$css[] = '/* CSS for enhancePostContent ' . $filter->id() . " */ \n" . $res . "\n";
}
}

View File

@ -51,18 +51,12 @@ class Widgets
'text'
);
# Type
$types = [];
foreach (Epc::getFilters() as $id => $filter) {
if ($filter->widget != '') {
$types[$filter->name] = $id;
}
}
$w->epclist->setting(
'type',
__('Type:'),
'Definition',
'combo',
$types
Epc::getFilters()->nid(true)
);
# Content
foreach (Epc::defaultAllowedWidgetValues() as $k => $v) {
@ -122,11 +116,11 @@ class Widgets
}
# Filter
$list = new ArrayObject();
$filters = Epc::getFilters();
$list = new ArrayObject();
$filter = Epc::getFilters()->get($w->type);
if (isset($filters[$w->type])) {
$filters[$w->type]->widgetList($content, $w, $list);
if (!is_null($filter)) {
$filter->widgetList($content, $w, $list);
}
if (!count($list)) {