code review and doc

master
Jean-Christian Paul Denis 2023-04-22 00:21:30 +02:00
parent dd0e90cd5e
commit 2948be39fd
Signed by: JcDenis
GPG Key ID: 1B5B8C5B90B6C951
20 changed files with 640 additions and 419 deletions

View File

@ -48,7 +48,7 @@ class Backend extends dcNsProcess
return false; return false;
} }
# Admin menu // backend sidebar menu icon
dcCore::app()->menu[dcAdmin::MENU_PLUGINS]->addItem( dcCore::app()->menu[dcAdmin::MENU_PLUGINS]->addItem(
My::name(), My::name(),
dcCore::app()->adminurl?->get('admin.plugin.' . My::id()), dcCore::app()->adminurl?->get('admin.plugin.' . My::id()),
@ -58,7 +58,7 @@ class Backend extends dcNsProcess
); );
dcCore::app()->addBehaviors([ dcCore::app()->addBehaviors([
# Dashboard favorites // backend user dashboard favorites icon
'adminDashboardFavoritesV2' => function (dcFavorites $favs): void { 'adminDashboardFavoritesV2' => function (dcFavorites $favs): void {
$favs->register(My::id(), [ $favs->register(My::id(), [
'title' => My::name(), 'title' => My::name(),
@ -68,11 +68,11 @@ class Backend extends dcNsProcess
'permissions' => dcCore::app()->auth?->makePermissions([dcCore::app()->auth::PERMISSION_CONTENT_ADMIN]), 'permissions' => dcCore::app()->auth?->makePermissions([dcCore::app()->auth::PERMISSION_CONTENT_ADMIN]),
]); ]);
}, },
# Preference form // backend user preference form
'adminBlogPreferencesFormV2' => function (dcSettings $blog_settings): void { 'adminBlogPreferencesFormV2' => function (dcSettings $blog_settings): void {
$active = (bool) $blog_settings->get(My::id())->get('active'); $active = (bool) $blog_settings->get(My::id())->get('active');
$allowedtplvalues = Epc::blogAllowedTplValues(); $allowedtplvalues = Epc::blogAllowedTemplateValue();
$allowedpubpages = Epc::blogAllowedPubPages(); $allowedpubpages = Epc::blogAllowedTemplatePage();
echo echo
'<div class="fieldset"><h4 id="epc_params">' . My::name() . '</h4>' . '<div class="fieldset"><h4 id="epc_params">' . My::name() . '</h4>' .
@ -95,13 +95,13 @@ class Backend extends dcNsProcess
// allowedtplvalues // allowedtplvalues
(new Para())->items([ (new Para())->items([
(new Label(__('Allowed DC template values:'), Label::OUTSIDE_LABEL_BEFORE))->for('epc_allowedtplvalues'), (new Label(__('Allowed DC template values:'), Label::OUTSIDE_LABEL_BEFORE))->for('epc_allowedtplvalues'),
(new Input('epc_allowedtplvalues'))->size(100)->maxlenght(0)->value(Epc::implode($allowedtplvalues)), (new Input('epc_allowedtplvalues'))->size(100)->maxlenght(0)->value(Epc::encodeMulti($allowedtplvalues)),
])->render() . ])->render() .
'<p class="form-note">' . __('Use "readable_name1:template_value1;readable_name2:template_value2;" like "entry content:EntryContent;entry excerpt:EntryExcerpt;".') . '</p>' . '<p class="form-note">' . __('Use "readable_name1:template_value1;readable_name2:template_value2;" like "entry content:EntryContent;entry excerpt:EntryExcerpt;".') . '</p>' .
// allowedpubpages // allowedpubpages
(new Para())->items([ (new Para())->items([
(new Label(__('Allowed public pages:'), Label::OUTSIDE_LABEL_BEFORE))->for('epc_allowedpubpages'), (new Label(__('Allowed public pages:'), Label::OUTSIDE_LABEL_BEFORE))->for('epc_allowedpubpages'),
(new Input('epc_allowedpubpages'))->size(100)->maxlenght(0)->value(Epc::implode($allowedpubpages)), (new Input('epc_allowedpubpages'))->size(100)->maxlenght(0)->value(Epc::encodeMulti($allowedpubpages)),
])->render() . ])->render() .
'<p class="form-note">' . __('Use "readable_name1:template_page1;readable_name2:template_page2;" like "post page:post.html;home page:home.html;".') . '</p>' . '<p class="form-note">' . __('Use "readable_name1:template_page1;readable_name2:template_page2;" like "post page:post.html;home page:home.html;".') . '</p>' .
'</div>' . '</div>' .
@ -109,17 +109,17 @@ class Backend extends dcNsProcess
'<br class="clear" />' . '<br class="clear" />' .
'</div>'; '</div>';
}, },
# Save preference // backend user preference save
'adminBeforeBlogSettingsUpdate' => function (dcSettings $blog_settings): void { 'adminBeforeBlogSettingsUpdate' => function (dcSettings $blog_settings): void {
$active = !empty($_POST['epc_active']); $active = !empty($_POST['epc_active']);
$allowedtplvalues = Epc::explode($_POST['epc_allowedtplvalues']); $allowedtplvalues = Epc::decodeMulti($_POST['epc_allowedtplvalues']);
$allowedpubpages = Epc::explode($_POST['epc_allowedpubpages']); $allowedpubpages = Epc::decodeMulti($_POST['epc_allowedpubpages']);
$blog_settings->get(My::id())->put('active', $active); $blog_settings->get(My::id())->put('active', $active);
$blog_settings->get(My::id())->put('allowedtplvalues', json_encode($allowedtplvalues)); $blog_settings->get(My::id())->put('allowedtplvalues', json_encode($allowedtplvalues));
$blog_settings->get(My::id())->put('allowedpubpages', json_encode($allowedpubpages)); $blog_settings->get(My::id())->put('allowedpubpages', json_encode($allowedpubpages));
}, },
# List filter // backend epc list filter
'adminFiltersListsV2' => function (ArrayObject $sorts): void { 'adminFiltersListsV2' => function (ArrayObject $sorts): void {
$sorts['epc'] = [ $sorts['epc'] = [
My::name(), My::name(),
@ -134,7 +134,7 @@ class Backend extends dcNsProcess
[__('records per page'), 20], [__('records per page'), 20],
]; ];
}, },
# Widgets // widgets registration
'initWidgets' => [Widgets::class, 'initWidgets'], 'initWidgets' => [Widgets::class, 'initWidgets'],
]); ]);

View File

@ -22,57 +22,65 @@ use Dotclear\Helper\Html\Form\Checkbox;
use Dotclear\Helper\Html\Html; use Dotclear\Helper\Html\Html;
/** /**
* @ingroup DC_PLUGIN_PERIODICAL * Backend filters values list.
* @brief Periodical - admin pager methods.
* @since 2.6
*/ */
class BackendList extends adminGenericListV2 class BackendList extends adminGenericListV2
{ {
public function display(adminGenericFilterV2 $filter, string $pager_url, string $enclose_block): void /**
* Display list.
*
* @param adminGenericFilterV2 $filter The filter
* @param string $url The pager URL
* @param string $block The enclose bloc
*/
public function display(adminGenericFilterV2 $filter, string $url, string $block): void
{ {
if ($this->rs->isEmpty()) { if ($this->rs->isEmpty()) {
echo '<p><strong>' . ($filter->show() ? __('No record matches the filter') : __('No record')) . '</strong></p>'; echo '<p><strong>' . ($filter->show() ? __('No record matches the filter') : __('No record')) . '</strong></p>';
} else {
$pager = new dcPager($filter->value('page'), $this->rs_count, $filter->value('nb'), 10);
$pager->base_url = $pager_url;
$epc_id = []; return;
if (isset($_REQUEST['epc_id'])) {
foreach ($_REQUEST['epc_id'] as $v) {
$epc_id[(int) $v] = true;
}
}
$cols = [
'key' => '<th colspan="2" class="first">' . __('Key') . '</th>',
'value' => '<th scope="col">' . __('Value') . '</th>',
'date' => '<th scope="col">' . __('Date') . '</th>',
];
$html_block = '<div class="table-outer"><table><caption>' .
(
$filter->show() ?
sprintf(__('List of %s records matching the filter.'), $this->rs_count) :
sprintf(__('List of %s records.'), $this->rs_count)
) . '</caption>' .
'<tr>' . implode($cols) . '</tr>%s</table>%s</div>';
if ($enclose_block) {
$html_block = sprintf($enclose_block, $html_block);
}
$blocks = explode('%s', $html_block);
echo $pager->getLinks() . $blocks[0];
while ($this->rs->fetch()) {
echo $this->line(isset($epc_id[$this->rs->epc_id]));
}
echo $blocks[1] . $blocks[2] . $pager->getLinks();
} }
$pager = new dcPager($filter->value('page'), $this->rs_count, $filter->value('nb'), 10);
$pager->base_url = $url;
$epc_id = [];
if (isset($_REQUEST['epc_id'])) {
foreach ($_REQUEST['epc_id'] as $v) {
$epc_id[(int) $v] = true;
}
}
$cols = [
'key' => '<th colspan="2" class="first">' . __('Key') . '</th>',
'value' => '<th scope="col">' . __('Value') . '</th>',
'date' => '<th scope="col">' . __('Date') . '</th>',
];
$content = '<div class="table-outer"><table><caption>' . (
$filter->show() ?
sprintf(__('List of %s records matching the filter.'), $this->rs_count) :
sprintf(__('List of %s records.'), $this->rs_count)
) . '</caption>' .
'<tr>' . implode($cols) . '</tr>%s</table>%s</div>';
$blocks = explode('%s', sprintf($block, $content));
echo $pager->getLinks() . $blocks[0];
while ($this->rs->fetch()) {
$this->line(isset($epc_id[$this->rs->epc_id]));
}
echo $blocks[1] . $blocks[2] . $pager->getLinks();
} }
private function line(bool $checked): string /**
* Dispay a list line.
*
* @param bool $checked Checkbox checked
*/
private function line(bool $checked): void
{ {
$cols = [ $cols = [
'check' => '<td class="nowrap">' . (new Checkbox(['epc_id[]'], $checked))->value($this->rs->epc_id)->render() . '</td>', 'check' => '<td class="nowrap">' . (new Checkbox(['epc_id[]'], $checked))->value($this->rs->epc_id)->render() . '</td>',
@ -81,9 +89,9 @@ class BackendList extends adminGenericListV2
'date' => '<td class="nowrap count">' . Date::dt2str(__('%Y-%m-%d %H:%M'), $this->rs->epc_upddt) . '</td>', 'date' => '<td class="nowrap count">' . Date::dt2str(__('%Y-%m-%d %H:%M'), $this->rs->epc_upddt) . '</td>',
]; ];
return echo
'<tr class="line" id="p' . $this->rs->epc_id . '">' . '<tr class="line" id="p' . $this->rs->epc_id . '">' .
implode($cols) . implode($cols) .
'</tr>'; '</tr>';
} }
} }

View File

@ -33,39 +33,54 @@ __('RSS feeds');
class Epc class Epc
{ {
/** @var string The temporary pattern to tag words to replace */
public const FLAGGER = 'ççççç%sççççç'; public const FLAGGER = 'ççççç%sççççç';
/** @var EpcFilters $filters THe filters stack */
private static EpcFilters $filters; private static EpcFilters $filters;
public static array $epcFilterLimit = [];
# /** @var array<string,int> $limits The replacment limit per filtre */
# Default definition private static array $limits = [];
#
public static function defaultAllowedTplValues(): array /**
* Get list of default allowed templates name->tag.
*
* @return array<string,string> The templates name->tag pairs
*/
public static function defaultAllowedTemplateValue(): array
{ {
$rs = new ArrayObject([ $list = new ArrayObject([
'entry excerpt' => 'EntryExcerpt', 'entry excerpt' => 'EntryExcerpt',
'entry content' => 'EntryContent', 'entry content' => 'EntryContent',
'comment content' => 'CommentContent', 'comment content' => 'CommentContent',
]); ]);
# --BEHAVIOR-- enhancePostContentAllowedTplValues : ArrayObject # --BEHAVIOR-- enhancePostContentAllowedTplValues : ArrayObject
dcCore::app()->callBehavior('enhancePostContentAllowedTplValues', $rs); dcCore::app()->callBehavior('enhancePostContentAllowedTplValues', $list);
return iterator_to_array($rs, true); return iterator_to_array($list, true);
} }
public static function blogAllowedTplValues(): array /**
* Get list of allowed templates name->tag set on current blog.
*
* @return array<string,string> The templates name->tag pairs
*/
public static function blogAllowedTemplateValue(): array
{ {
$rs = json_decode((string) dcCore::app()->blog?->settings->get(My::id())->get('allowedtplvalues'), true); $list = json_decode((string) dcCore::app()->blog?->settings->get(My::id())->get('allowedtplvalues'), true);
return is_array($rs) ? $rs : self::defaultAllowedTplValues(); return is_array($list) ? $list : self::defaultAllowedTemplateValue();
} }
public static function defaultAllowedWidgetValues(): array /**
* Get list of allowed templates name->[tag,callback] to list on epc widgets.
*
* @return array The templates name->[id,cb] values
*/
public static function widgetAllowedTemplateValue(): array
{ {
$rs = new ArrayObject([ $list = new ArrayObject([
'entry excerpt' => [ 'entry excerpt' => [
'id' => 'entryexcerpt', 'id' => 'entryexcerpt',
'cb' => [self::class, 'widgetContentEntryExcerpt'], 'cb' => [self::class, 'widgetContentEntryExcerpt'],
@ -81,14 +96,19 @@ class Epc
]); ]);
# --BEHAVIOR-- enhancePostContentAllowedWidgetValues : ArrayObject # --BEHAVIOR-- enhancePostContentAllowedWidgetValues : ArrayObject
dcCore::app()->callBehavior('enhancePostContentAllowedWidgetValues', $rs); dcCore::app()->callBehavior('enhancePostContentAllowedWidgetValues', $list);
return iterator_to_array($rs, true); return iterator_to_array($list, true);
} }
public static function defaultAllowedPubPages(): array /**
* Get list of default allowed templates name->page to list on epc widgets.
*
* @return array<string,string> The templates name->page pairs
*/
public static function defaultAllowedTemplatePage(): array
{ {
$rs = new ArrayObject([ $list = new ArrayObject([
'home page' => 'home.html', 'home page' => 'home.html',
'post page' => 'post.html', 'post page' => 'post.html',
'category page' => 'category.html', 'category page' => 'category.html',
@ -98,16 +118,21 @@ class Epc
]); ]);
# --BEHAVIOR-- enhancePostContentAllowedPubPages : ArrayObject # --BEHAVIOR-- enhancePostContentAllowedPubPages : ArrayObject
dcCore::app()->callBehavior('enhancePostContentAllowedPubPages', $rs); dcCore::app()->callBehavior('enhancePostContentAllowedPubPages', $list);
return iterator_to_array($rs, true); return iterator_to_array($list, true);
} }
public static function blogAllowedPubPages(): array /**
* Get list of allowed templates name->page set on blog to list on epc widgets.
*
* @return array<string,string> The templates name->page pairs
*/
public static function blogAllowedTemplatePage(): array
{ {
$rs = json_decode((string) dcCore::app()->blog?->settings->get(My::id())->get('allowedpubpages'), true); $list = json_decode((string) dcCore::app()->blog?->settings->get(My::id())->get('allowedpubpages'), true);
return is_array($rs) ? $rs : self::defaultAllowedPubPages(); return is_array($list) ? $list : self::defaultAllowedTemplatePage();
} }
/** /**
@ -135,153 +160,193 @@ class Epc
return self::$filters; return self::$filters;
} }
public static function testContext(string $tag, array $args, EpcFilter $filter): bool /**
{ * Apply filter to content.
return in_array((string) dcCore::app()->ctx?->__get('current_tpl'), $filter->pubPages) *
&& in_array($tag, $filter->tplValues) * @param string $search The search
&& $args[0] != '' //content * @param string $replacement The replacement
&& empty($args['encode_xml']) * @param string $content The content
&& empty($args['encode_html']) * @param EpcFilter $filter The filter
&& empty($args['remove_html']) * @param string $before The start limit pattern
&& empty($args['strip_tags']) * @param string $after The end limit pattern
; */
} public static function replaceString(
string $search,
public static function replaceString(string $p, string $r, string $s, EpcFilter $filter, string $before = '\b', string $after = '\b'): string string $replacement,
{ string $content,
# Limit EpcFilter $filter,
string $before = '\b',
string $after = '\b'
): string {
// Limit
if ($filter->limit > 0) { if ($filter->limit > 0) {
$limit = array_key_exists($filter->id() . '_' . $p, self::$epcFilterLimit) ? self::$epcFilterLimit[$filter->id() . '_' . $p] : $filter->limit; // memorize limit between two template values
$limit = array_key_exists($filter->id() . '_' . $search, self::$limits) ? self::$limits[$filter->id() . '_' . $search] : $filter->limit;
if ($limit < 1) { if ($limit < 1) {
return $s; return $content;
} }
} else { } else {
$limit = -1; $limit = -1;
} }
# Case sensitive
$i = $filter->nocase ? 'i' : ''; // Case sensitive
$caseless = $filter->nocase ? 'i' : '';
# Plural # Plural
$x = $filter->plural ? $p . 's|' . $p : $p; $plural = $filter->plural ? 's?' : '';
# Mark words // Mark words
$ret = preg_replace('#(' . $before . ')(' . $x . ')(' . $after . ')#su' . $i, '$1' . sprintf(self::FLAGGER, '$2') . '$3', $s, -1, $count); $ret = preg_replace('#(' . $before . ')(' . $search . $plural . ')(' . $after . ')#su' . $caseless, '$1' . sprintf(self::FLAGGER, '$2') . '$3', $content, -1, $count);
if (is_string($ret)) { if (is_string($ret)) {
$s = $ret; $content = $ret;
} }
# Nothing to parse
// Nothing to parse
if (!$count) { if (!$count) {
return $s; return $content;
} }
# Remove words that are into unwanted html tags // Remove words that are into unwanted html tags
$ignore_tags = array_merge(self::decodeTags($filter->htmltag), self::decodeTags($filter->notag)); $ignore = array_merge(self::decodeSingle($filter->ignore), self::decodeSingle($filter->notag));
if (!empty($ignore_tags)) { if (!empty($ignore)) {
$tags = implode('|', array_unique($ignore_tags)); $ret = preg_replace_callback('#(<(' . implode('|', array_unique($ignore)) . ')[^>]*?>)(.*?)(</\\2>)#s', function (array $m): string {
return $m[1] . preg_replace('#' . sprintf(self::FLAGGER, '(?!') . ')#s', '$1', $m[3]) . $m[4];
$ret = preg_replace_callback('#(<(' . $tags . ')[^>]*?>)(.*?)(</\\2>)#s', [self::class, 'removeTags'], $s); }, $content);
if (is_string($ret)) { if (is_string($ret)) {
$s = $ret; $content = $ret;
} }
} }
# Remove words inside html tag (class, title, alt, href, ...) // Remove words inside html tag (class, title, alt, href, ...)
$ret = preg_replace('#(' . sprintf(self::FLAGGER, '(' . $x . '(s|))') . ')(?=[^<]*>)#s' . $i, '$2$4', $s); $ret = preg_replace('#(' . sprintf(self::FLAGGER, '(' . $search . '(' . $plural . '))') . ')(?=[^<]*>)#s' . $caseless, '$2$4', $content);
if (is_string($ret)) { if (is_string($ret)) {
$s = $ret; $content = $ret;
} }
# Replace words by what you want (with limit) // Replace words by what you want (with limit)
$ret = preg_replace('#' . sprintf(self::FLAGGER, '(' . $p . '(s|))') . '#s' . $i, $r, $s, $limit, $count); $ret = preg_replace('#' . sprintf(self::FLAGGER, '(' . $search . '(' . $plural . '))') . '#s' . $caseless, $replacement, $content, $limit, $count);
if (is_string($ret)) { if (is_string($ret)) {
$s = $ret; $content = $ret;
} }
# update limit // update limit
self::$epcFilterLimit[$filter->id() . '_' . $p] = $limit - $count; self::$limits[$filter->id() . '_' . $search] = $limit - $count;
# Clean rest // Clean rest
$ret = preg_replace('#' . sprintf(self::FLAGGER, '(.*?)') . '#s', '$1', $s); $ret = preg_replace('#' . sprintf(self::FLAGGER, '(.*?)') . '#s', '$1', $content);
if (is_string($ret)) { if (is_string($ret)) {
$s = $ret; $content = $ret;
} }
return $s; return $content;
} }
public static function matchString(string $p, string $r, string $s, EpcFilter $filter, string $before = '\b', string $after = '\b'): array /**
{ * Find filter on content.
# Case sensitive *
$i = $filter->nocase ? 'i' : ''; * @param string $search The search
# Plural * @param string $replacement The replacement
$x = $filter->plural ? $p . 's|' . $p : $p; * @param string $content The content
# Mark words * @param EpcFilter $filter The filter
$t = preg_match_all('#' . $before . '(' . $x . ')' . $after . '#su' . $i, $s, $matches); * @param string $before The start limit pattern
# Nothing to parse * @param string $after The end limit pattern
if (!$t) { */
return ['total' => 0, 'matches' => []]; public static function matchString(
} string $search,
string $replacement,
# Build array string $content,
$m = []; EpcFilter $filter,
$loop = 0; string $before = '\b',
foreach ($matches[1] as $match) { string $after = '\b'
$m[$loop]['key'] = $match; ): array {
$m[$loop]['match'] = preg_replace('#(' . $p . '(s|))#s' . $i, $r, $match, -1, $count); return [
$m[$loop]['num'] = $count; 'total' => (int) preg_match_all('#' . $before . '(' . $search . ($filter->plural ? 's?' : '') . ')' . $after . '#su' . ($filter->nocase ? 'i' : ''), $content),
$loop++; 'search' => $search,
} 'replacement' => preg_replace('#(' . $search . ')#', $replacement, $search),
];
return ['total' => $t, 'matches' => $m];
} }
public static function quote(string $s): string /**
* Quote regular expression according to epc parser.
*
* @param string $string The string
*
* @return string The quoted string
*/
public static function quote(string $string): string
{ {
return preg_quote($s, '#'); return preg_quote($string, '#');
} }
public static function removeTags(array $m): string /**
* Implode simple array into string a,b,c.
*
* @param array|string $values The values
*
* @return string The value
*/
public static function encodeSingle(array|string $values): string
{ {
return $m[1] . preg_replace('#' . sprintf(self::FLAGGER, '(?!') . ')#s', '$1', $m[3]) . $m[4]; return implode(',', self::decodeSingle($values));
} }
public static function decodeTags(string $t): array /**
* Explode string into simple array [a,b,c].
*
* @param array|string $value The value
*
* @return array The values
*/
public static function decodeSingle(array|string $value): array
{ {
return preg_match_all('#([A-Za-z0-9]+)#', (string) $t, $m) ? $m[1] : []; if (is_array($value)) {
$value = implode(',', $value);
}
return preg_match_all('#([A-Za-z0-9]+)#', (string) $value, $matches) ? $matches[1] : [];
} }
public static function implode(array|string $a): string /**
* Implode complexe array into string a:aa:b:bb;c:cc.
*
* @param array|string $values The values
*
* @return string The value
*/
public static function encodeMulti(array|string $values): string
{ {
if (is_string($a)) { if (is_string($values)) {
return $a; return $values;
}
if (!is_array($a)) {
return '';
} }
$r = ''; $string = '';
foreach ($a as $k => $v) { foreach ($values as $key => $value) {
$r .= $k . ':' . $v . ';'; $string .= $key . ':' . $value . ';';
} }
return $r; return $string;
} }
public static function explode(array|string $s): array /**
* Explode string into complexe array [a=>aa,b=>aa,c=>cc].
*
* @param array|string $value The value
*
* @return array The values
*/
public static function decodeMulti(array|string $value): array
{ {
if (is_array($s)) { if (is_array($value)) {
return $s; return $value;
} }
if (!is_string($s)) {
$values = [];
$exp = explode(';', (string) $value);
if (!is_array($exp)) {
return []; return [];
} }
$r = []; foreach ($exp as $cpl) {
$s = explode(';', (string) $s);
if (!is_array($s)) {
return [];
}
foreach ($s as $cpl) {
$cur = explode(':', $cpl); $cur = explode(':', $cpl);
if (!is_array($cur) || !isset($cur[1])) { if (!is_array($cur) || !isset($cur[1])) {
@ -295,59 +360,75 @@ class Epc
continue; continue;
} }
$r[$key] = $val; $values[$key] = $val;
} }
return $r; return $values;
} }
# /**
# Widgets * Send entries excerpts to widget.
# *
* @param WidgetsElement|null $widget The widgets
public static function widgetContentEntryExcerpt(?WidgetsElement $w = null): string *
* @return string The entries exceprts
*/
public static function widgetContentEntryExcerpt(?WidgetsElement $widget = null): string
{ {
if (is_null(dcCore::app()->ctx) || !dcCore::app()->ctx->exists('posts')) { if (is_null(dcCore::app()->ctx) || !dcCore::app()->ctx->exists('posts')) {
return ''; return '';
} }
$res = ''; $content = '';
while (dcCore::app()->ctx->__get('posts')?->fetch()) { while (dcCore::app()->ctx->__get('posts')?->fetch()) {
$res .= dcCore::app()->ctx->__get('posts')->f('post_excerpt'); $content .= dcCore::app()->ctx->__get('posts')->f('post_excerpt');
} }
return $res; return $content;
} }
public static function widgetContentEntryContent(): string /**
* Send entries contents to widget.
*
* @param WidgetsElement|null $widget The widgets
*
* @return string The entries contents
*/
public static function widgetContentEntryContent(?WidgetsElement $widget = null): string
{ {
if (is_null(dcCore::app()->ctx) || !dcCore::app()->ctx->exists('posts')) { if (is_null(dcCore::app()->ctx) || !dcCore::app()->ctx->exists('posts')) {
return ''; return '';
} }
$res = ''; $content = '';
while (dcCore::app()->ctx->__get('posts')?->fetch()) { while (dcCore::app()->ctx->__get('posts')?->fetch()) {
$res .= dcCore::app()->ctx->__get('posts')->f('post_content'); $content .= dcCore::app()->ctx->__get('posts')->f('post_content');
} }
return $res; return $content;
} }
public static function widgetContentCommentContent(): string /**
* Send entries comments to widget.
*
* @param WidgetsElement|null $widget The widgets
*
* @return string The entries comments
*/
public static function widgetContentCommentContent(?WidgetsElement $widget = null): string
{ {
if (is_null(dcCore::app()->ctx) || !dcCore::app()->ctx->exists('posts')) { if (is_null(dcCore::app()->ctx) || !dcCore::app()->ctx->exists('posts')) {
return ''; return '';
} }
$res = ''; $content = '';
$post_ids = [];
while (dcCore::app()->ctx->__get('posts')?->fetch()) { while (dcCore::app()->ctx->__get('posts')?->fetch()) {
$comments = dcCore::app()->blog?->getComments(['post_id' => dcCore::app()->ctx->__get('posts')->f('post_id')]); $comments = dcCore::app()->blog?->getComments(['post_id' => dcCore::app()->ctx->__get('posts')->f('post_id')]);
while ($comments?->fetch()) { while ($comments?->fetch()) {
$res .= $comments->getContent(); $content .= $comments->getContent();
} }
} }
return $res; return $content;
} }
} }

View File

@ -20,31 +20,67 @@ use dcRecord;
use Dotclear\Plugin\widgets\WidgetsElement; use Dotclear\Plugin\widgets\WidgetsElement;
use Exception; use Exception;
/**
* Filter abstract class.
*
* All filter must extends this class.
*/
abstract class EpcFilter abstract class EpcFilter
{ {
/** @var string $id The filter id */
protected string $id = 'undefined'; protected string $id = 'undefined';
/** @var dcRecord $records The filter record if any */
private ?dcRecord $records = null; private ?dcRecord $records = null;
// properties /** @var int $priority The filter priority (property) */
public readonly int $priority; public readonly int $priority;
/** @var string $name The filter name (property) */
public readonly string $name; public readonly string $name;
public readonly string $help;
/** @var string $description The filter description (property) */
public readonly string $description;
/** @var bool $has_list Filter has list of records (property) */
public readonly bool $has_list; public readonly bool $has_list;
public readonly string $htmltag;
/** @var array $ignore The filter disabled html tags (property) */
public readonly array $ignore;
/** @var array $class The css class that apply to filter (property) */
public readonly array $class; public readonly array $class;
/** @var string $replace The filter replacement bloc in content (property) */
public readonly string $replace; public readonly string $replace;
/** @var string $widget The filter replacement bloc in widget (property) */
public readonly string $widget; public readonly string $widget;
// settings /** @var bool $nocase The filter caseless match (settings) */
public readonly bool $nocase; public readonly bool $nocase;
public readonly bool $plural;
public readonly int $limit;
public readonly array $style;
public readonly string $notag;
public readonly array $tplValues;
public readonly array $pubPages;
/** @var bool $plural The filter caseless match (settings) */
public readonly bool $plural;
/** @var bool $plural The replacement limit per filter (settings) */
public readonly int $limit;
/** @var array $style The style applied to filter class (settings) */
public readonly array $style;
/** @var array $notag The filter disabled html tags (settings) */
public readonly array $notag;
/** @var array $template The extra template value to scan (settings) */
public readonly array $template;
/** @var array $page The extra frontend pages to scan (settings) */
public readonly array $page;
/**
* Constructor sets filter properties and settings.
*/
final public function __construct() final public function __construct()
{ {
if ($this->id == 'undefined') { if ($this->id == 'undefined') {
@ -61,55 +97,66 @@ abstract class EpcFilter
$settings = $this->initSettings(); $settings = $this->initSettings();
// from filter defautl properties // from filter defautl properties
$this->priority = isset($properties['priority']) ? abs((int) $properties['priority']) : 500; $this->priority = isset($properties['priority']) ? abs((int) $properties['priority']) : 500;
$this->name = isset($properties['name']) ? (string) $properties['name'] : 'undefined'; $this->name = isset($properties['name']) ? (string) $properties['name'] : 'undefined';
$this->help = isset($properties['help']) ? (string) $properties['help'] : 'undefined'; $this->description = isset($properties['description']) ? (string) $properties['description'] : 'undefined';
$this->has_list = isset($properties['has_list']) ? (bool) $properties['has_list'] : false; $this->has_list = isset($properties['has_list']) ? (bool) $properties['has_list'] : false;
$this->htmltag = isset($properties['htmltag']) ? (string) $properties['htmltag'] : ''; $this->ignore = isset($properties['ignore']) && is_array($properties['ignore']) ? $properties['ignore'] : [];
$this->class = isset($properties['class']) && is_array($properties['class']) ? $properties['class'] : []; $this->class = isset($properties['class']) && is_array($properties['class']) ? $properties['class'] : [];
$this->replace = isset($properties['replace']) ? (string) $properties['replace'] : ''; $this->replace = isset($properties['replace']) ? (string) $properties['replace'] : '';
$this->widget = isset($properties['widget']) ? (string) $properties['widget'] : ''; $this->widget = isset($properties['widget']) ? (string) $properties['widget'] : '';
// from filter defautl settings // from filter defautl settings
$nocase = isset($settings['nocase']) ? (bool) $settings['nocase'] : false; $nocase = isset($settings['nocase']) ? (bool) $settings['nocase'] : false;
$plural = isset($settings['plural']) ? (bool) $settings['plural'] : false; $plural = isset($settings['plural']) ? (bool) $settings['plural'] : false;
$limit = isset($settings['limit']) ? abs((int) $settings['limit']) : 0; $limit = isset($settings['limit']) ? abs((int) $settings['limit']) : 0;
$style = isset($settings['style']) && is_array($settings['style']) ? $settings['style'] : []; $style = isset($settings['style']) && is_array($settings['style']) ? $settings['style'] : [];
$notag = isset($settings['notag']) ? (string) $settings['notag'] : ''; $notag = isset($settings['notag']) && is_array($settings['notag']) ? $settings['notag'] : [];
$tplValues = isset($settings['tplValues']) && is_array($settings['tplValues']) ? $settings['tplValues'] : []; $template = isset($settings['template']) && is_array($settings['template']) ? $settings['template'] : [];
$pubPages = isset($settings['pubPages']) && is_array($settings['pubPages']) ? $settings['pubPages'] : []; $page = isset($settings['page']) && is_array($settings['page']) ? $settings['page'] : [];
// from blog settings // from blog settings
$this->nocase = isset($s['nocase']) ? (bool) $s['nocase'] : $nocase; $this->nocase = isset($s['nocase']) ? (bool) $s['nocase'] : $nocase;
$this->plural = isset($s['plural']) ? (bool) $s['plural'] : $plural; $this->plural = isset($s['plural']) ? (bool) $s['plural'] : $plural;
$this->limit = isset($s['limit']) ? abs((int) $s['limit']) : $limit; $this->limit = isset($s['limit']) ? abs((int) $s['limit']) : $limit;
$this->style = isset($s['style']) && is_array($s['style']) ? $s['style'] : $style; $this->style = isset($s['style']) && is_array($s['style']) ? $s['style'] : $style;
$this->notag = isset($s['notag']) ? (string) $s['notag'] : $notag; $this->notag = isset($s['notag']) && is_array($s['notag']) ? $s['notag'] : $notag;
$this->tplValues = isset($s['tplValues']) && is_array($s['tplValues']) ? $s['tplValues'] : $tplValues; $this->template = isset($s['template']) && is_array($s['template']) ? $s['template'] : $template;
$this->pubPages = isset($s['pubPages']) && is_array($s['pubPages']) ? $s['pubPages'] : $pubPages; $this->page = isset($s['page']) && is_array($s['page']) ? $s['page'] : $page;
} }
protected function initProperties(): array /**
{ * Return filter default properties.
return []; *
} * @return array The properties
*/
abstract protected function initProperties(): array;
protected function initSettings(): array /**
{ * Return filter default settings.
return []; *
} * @return array The settings
*/
public static function create(ArrayObject $o): void abstract protected function initSettings(): array;
{
$c = static::class;
$o->append(new $c());
}
/**
* Get fitler ID.
*
* @return string The filter ID
*/
final public function id(): string final public function id(): string
{ {
return $this->id; return $this->id;
} }
/**
* Get fitler record.
*
* Fitler records are usefull to store and retrieve
* list of keyword / replacement etc...
*
* @return dcRecord The filter record instance
*/
final public function records(): dcRecord final public function records(): dcRecord
{ {
if ($this->records === null && $this->has_list) { if ($this->records === null && $this->has_list) {
@ -119,11 +166,27 @@ abstract class EpcFilter
return $this->records ?? dcRecord::newFromArray([]); return $this->records ?? dcRecord::newFromArray([]);
} }
/**
* Filter frontend contents in situ.
*
* @param string $tag The tempale block tag
* @param array $args The template block arguments
*/
public function publicContent(string $tag, array $args): void public function publicContent(string $tag, array $args): void
{ {
} }
public function widgetList(string $content, WidgetsElement $w, ArrayObject $list): void /**
* Filter frontend contents for widgets.
*
* Filter the contents and return matching results infos
* into the list of current widget.
*
* @param string $content The contents
* @param WidgetsElement $widget The widget
* @param ArrayObject $list The list
*/
public function widgetList(string $content, WidgetsElement $widget, ArrayObject $list): void
{ {
} }
} }

View File

@ -67,7 +67,7 @@ class EpcFilters
{ {
$nid = []; $nid = [];
foreach ($this->stack as $filter) { foreach ($this->stack as $filter) {
if ($filter->widget != '') { if (!$exclude_widget || $filter->widget != '') {
$nid[$filter->name] = $filter->id(); $nid[$filter->name] = $filter->id();
} }
} }
@ -76,13 +76,17 @@ class EpcFilters
} }
/** /**
* Sort filters stack by filter name. * Sort filters stack by filter name or priority.
* *
* @return EpcFilters The filters instance * @return EpcFilters The filters instance
*/ */
public function sort(): EpcFilters public function sort(bool $by_name = false): EpcFilters
{ {
uasort($this->stack, fn ($a, $b) => $a->name <=> $b->name); if ($by_name) {
uasort($this->stack, fn ($a, $b) => $a->name <=> $b->name);
} else {
uasort($this->stack, fn ($a, $b) => $a->priority <=> $b->priority);
}
return $this; return $this;
} }

View File

@ -19,8 +19,19 @@ use dcCore;
use dcRecord; use dcRecord;
use Exception; use Exception;
/**
* Filter records.
*/
class EpcRecord class EpcRecord
{ {
/**
* Get records.
*
* @param array $params The query params
* @param bool $count_only Count only
*
* @return dcRecord The records instance
*/
public static function getRecords(array $params, bool $count_only = false): dcRecord public static function getRecords(array $params, bool $count_only = false): dcRecord
{ {
if ($count_only) { if ($count_only) {
@ -99,6 +110,13 @@ class EpcRecord
return new dcRecord(dcCore::app()->con->select($strReq)); return new dcRecord(dcCore::app()->con->select($strReq));
} }
/**
* Add record.
*
* @param cursor $cur The cursor
*
* @return int The record ID
*/
public static function addRecord(cursor $cur): int public static function addRecord(cursor $cur): int
{ {
dcCore::app()->con->writeLock(dcCore::app()->prefix . My::TABLE_NAME); dcCore::app()->con->writeLock(dcCore::app()->prefix . My::TABLE_NAME);
@ -117,7 +135,7 @@ class EpcRecord
throw $e; throw $e;
} }
self::trigger(); dcCore::app()->blog?->triggerBlog();
# --BEHAVIOR-- enhancePostContentAfterAddRecord : cursor # --BEHAVIOR-- enhancePostContentAfterAddRecord : cursor
dcCore::app()->callBehavior('enhancePostContentAfterAddRecord', $cur); dcCore::app()->callBehavior('enhancePostContentAfterAddRecord', $cur);
@ -125,6 +143,12 @@ class EpcRecord
return (int) $cur->getField('epc_id'); return (int) $cur->getField('epc_id');
} }
/**
* Update a record.
*
* @param int $id The record ID
* @param cursor $cur The cursor
*/
public static function updRecord(int $id, cursor $cur): void public static function updRecord(int $id, cursor $cur): void
{ {
if (empty($id)) { if (empty($id)) {
@ -134,12 +158,21 @@ class EpcRecord
$cur->setField('epc_upddt', date('Y-m-d H:i:s')); $cur->setField('epc_upddt', date('Y-m-d H:i:s'));
$cur->update('WHERE epc_id = ' . $id . " AND blog_id = '" . dcCore::app()->con->escapeStr((string) dcCore::app()->blog?->id) . "' "); $cur->update('WHERE epc_id = ' . $id . " AND blog_id = '" . dcCore::app()->con->escapeStr((string) dcCore::app()->blog?->id) . "' ");
self::trigger(); dcCore::app()->blog?->triggerBlog();
# --BEHAVIOR-- enhancePostContentAfterUpdRecord : cursor, int # --BEHAVIOR-- enhancePostContentAfterUpdRecord : cursor, int
dcCore::app()->callBehavior('enhancePostContentAfterUpdRecord', $cur, $id); dcCore::app()->callBehavior('enhancePostContentAfterUpdRecord', $cur, $id);
} }
/**
* Check if a record exists.
*
* @param null|string $filter The filter ID
* @param null|string $key The record key
* @param null|int $not_id Exclude an id
*
* @return bool True if it exists
*/
public static function isRecord(?string $filter, ?string $key, ?int $not_id = null): bool public static function isRecord(?string $filter, ?string $key, ?int $not_id = null): bool
{ {
return 0 < self::getRecords([ return 0 < self::getRecords([
@ -149,6 +182,11 @@ class EpcRecord
], true)->f(0); ], true)->f(0);
} }
/**
* Delete a record.
*
* @param int $id The record ID
*/
public static function delRecord(int $id): void public static function delRecord(int $id): void
{ {
if (empty($id)) { if (empty($id)) {
@ -164,9 +202,14 @@ class EpcRecord
"AND blog_id = '" . dcCore::app()->con->escapeStr((string) dcCore::app()->blog?->id) . "' " "AND blog_id = '" . dcCore::app()->con->escapeStr((string) dcCore::app()->blog?->id) . "' "
); );
self::trigger(); dcCore::app()->blog?->triggerBlog();
} }
/**
* Get next record ID.
*
* @return int The next record ID
*/
private static function getNextId(): int private static function getNextId(): int
{ {
return (int) dcCore::app()->con->select( return (int) dcCore::app()->con->select(
@ -174,11 +217,21 @@ class EpcRecord
)->f(0) + 1; )->f(0) + 1;
} }
/**
* Open filter cursor.
*
* @return cursor The cursor
*/
public static function openCursor(): cursor public static function openCursor(): cursor
{ {
return dcCore::app()->con->openCursor(dcCore::app()->prefix . My::TABLE_NAME); return dcCore::app()->con->openCursor(dcCore::app()->prefix . My::TABLE_NAME);
} }
/**
* Clean up a cursor.
*
* @param cursor $cur The cursor
*/
private static function getCursor(cursor $cur): void private static function getCursor(cursor $cur): void
{ {
if ($cur->getField('epc_key') == '') { if ($cur->getField('epc_key') == '') {
@ -191,9 +244,4 @@ class EpcRecord
throw new Exception(__('No record filter')); throw new Exception(__('No record filter'));
} }
} }
private static function trigger(): void
{
dcCore::app()->blog?->triggerBlog();
}
} }

View File

@ -26,24 +26,24 @@ class EpcFilterAbbreviation extends EpcFilter
protected function initProperties(): array protected function initProperties(): array
{ {
return [ return [
'priority' => 400, 'priority' => 400,
'name' => __('Abbreviation'), 'name' => __('Abbreviation'),
'help' => __('Explain some abbreviation. First term of the list is the abbreviation and second term the explanation.'), 'description' => __('Explain some abbreviation. First term of the list is the abbreviation and second term the explanation.'),
'has_list' => true, 'has_list' => true,
'htmltag' => 'pre,code,a', 'ignore' => ['pre','code','a'],
'class' => ['abbr.epc-abbr'], 'class' => ['abbr.epc-abbr'],
'replace' => '<abbr class="epc-abbr" title="%s">%s</abbr>', 'replace' => '<abbr class="epc-abbr" title="%s">%s</abbr>',
'widget' => '<abbr title="%s">%s</abbr>', 'widget' => '<abbr title="%s">%s</abbr>',
]; ];
} }
protected function initSettings(): array protected function initSettings(): array
{ {
return [ return [
'style' => ['font-weight: bold;'], 'style' => ['font-weight: bold;'],
'notag' => 'a,acronym,abbr,dfn,h1,h2,h3', 'notag' => ['acronym','abbr','dfn','h1','h2','h3'],
'tplValues' => ['EntryContent'], 'template' => ['EntryContent'],
'pubPages' => ['post.html'], 'page' => ['post.html'],
]; ];
} }

View File

@ -26,24 +26,24 @@ class EpcFilterAcronym extends EpcFilter
protected function initProperties(): array protected function initProperties(): array
{ {
return [ return [
'priority' => 700, 'priority' => 700,
'name' => __('Acronym'), 'name' => __('Acronym'),
'help' => __('Explain some acronyms. First term of the list is the acornym and second term the explanation.'), 'description' => __('Explain some acronyms. First term of the list is the acornym and second term the explanation.'),
'has_list' => true, 'has_list' => true,
'htmltag' => 'pre,code,acronym', 'ignore' => ['pre','code','acronym'],
'class' => ['acronym.epc-acronym'], 'class' => ['acronym.epc-acronym'],
'replace' => '<acronym class="epc-acronym" title="%s">%s</acronym>', 'replace' => '<acronym class="epc-acronym" title="%s">%s</acronym>',
'widget' => '<acronym title="%s">%s</acronym>', 'widget' => '<acronym title="%s">%s</acronym>',
]; ];
} }
protected function initSettings(): array protected function initSettings(): array
{ {
return [ return [
'style' => ['font-weight: bold;'], 'style' => ['font-weight: bold;'],
'notag' => 'a,acronym,abbr,dfn,h1,h2,h3', 'notag' => ['a','acronym','abbr','dfn','h1','h2','h3'],
'tplValues' => ['EntryContent'], 'template' => ['EntryContent'],
'pubPages' => ['post.html'], 'page' => ['post.html'],
]; ];
} }

View File

@ -26,25 +26,25 @@ class EpcFilterCitation extends EpcFilter
protected function initProperties(): array protected function initProperties(): array
{ {
return [ return [
'priority' => 600, 'priority' => 600,
'name' => __('Citation'), 'name' => __('Citation'),
'help' => __('Highlight citation of people. First term of the list is the citation and second term the author.'), 'description' => __('Highlight citation of people. First term of the list is the citation and second term the author.'),
'has_list' => true, 'has_list' => true,
'htmltag' => 'pre,code,cite', 'ignore' => ['pre','code','cite'],
'class' => ['cite.epc-cite'], 'class' => ['cite.epc-cite'],
'replace' => '<cite class="epc-cite" title="%s">%s</cite>', 'replace' => '<cite class="epc-cite" title="%s">%s</cite>',
'widget' => '<cite title="%s">%s</cite>', 'widget' => '<cite title="%s">%s</cite>',
]; ];
} }
protected function initSettings(): array protected function initSettings(): array
{ {
return [ return [
'nocase' => true, 'nocase' => true,
'style' => ['font-style: italic;'], 'style' => ['font-style: italic;'],
'notag' => 'a,h1,h2,h3', 'notag' => ['a','h1','h2','h3'],
'tplValues' => ['EntryContent'], 'template' => ['EntryContent'],
'pubPages' => ['post.html'], 'page' => ['post.html'],
]; ];
} }

View File

@ -26,24 +26,24 @@ class EpcFilterDefinition extends EpcFilter
protected function initProperties(): array protected function initProperties(): array
{ {
return [ return [
'priority' => 800, 'priority' => 800,
'name' => __('Definition'), 'name' => __('Definition'),
'help' => __('Explain some definition. First term of the list is the sample to define and second term the explanation.'), 'description' => __('Explain some definition. First term of the list is the sample to define and second term the explanation.'),
'has_list' => true, 'has_list' => true,
'htmltag' => 'pre,code,dfn', 'ignore' => ['pre','code','dfn'],
'class' => ['dfn.epc-dfn'], 'class' => ['dfn.epc-dfn'],
'replace' => '<dfn class="epc-dfn" title="%s">%s</dfn>', 'replace' => '<dfn class="epc-dfn" title="%s">%s</dfn>',
'widget' => '<dfn class="epc-dfn" title="%s">%s</dfn>', 'widget' => '<dfn class="epc-dfn" title="%s">%s</dfn>',
]; ];
} }
protected function initSettings(): array protected function initSettings(): array
{ {
return [ return [
'style' => ['font-weight: bold;'], 'style' => ['font-weight: bold;'],
'notag' => 'a,acronym,abbr,dfn,h1,h2,h3', 'notag' => ['a','acronym','abbr','dfn','h1','h2','h3'],
'tplValues' => ['EntryContent'], 'template' => ['EntryContent'],
'pubPages' => ['post.html'], 'page' => ['post.html'],
]; ];
} }

View File

@ -26,24 +26,24 @@ class EpcFilterLink extends EpcFilter
protected function initProperties(): array protected function initProperties(): array
{ {
return [ return [
'priority' => 500, 'priority' => 500,
'name' => __('Link'), 'name' => __('Link'),
'help' => __('Link some words. First term of the list is the term to link and second term the link.'), 'description' => __('Link some words. First term of the list is the term to link and second term the link.'),
'has_list' => true, 'has_list' => true,
'htmltag' => 'pre,code,a', 'ignore' => ['pre','code','a'],
'class' => ['a.epc-link'], 'class' => ['a.epc-link'],
'replace' => '<a class="epc-link" title="%s" href="%s">%s</a>', 'replace' => '<a class="epc-link" title="%s" href="%s">%s</a>',
'widget' => '<a title="%s" href="%s">%s</a>', 'widget' => '<a title="%s" href="%s">%s</a>',
]; ];
} }
protected function initSettings(): array protected function initSettings(): array
{ {
return [ return [
'style' => ['text-decoration: none; font-style: italic; color: #0000FF;'], 'style' => ['text-decoration: none; font-style: italic; color: #0000FF;'],
'notag' => 'a,h1,h2,h3', 'notag' => ['h1','h2','h3'],
'tplValues' => ['EntryContent'], 'template' => ['EntryContent'],
'pubPages' => ['post.html'], 'page' => ['post.html'],
]; ];
} }

View File

@ -24,25 +24,25 @@ class EpcFilterReplace extends EpcFilter
protected function initProperties(): array protected function initProperties(): array
{ {
return [ return [
'priority' => 200, 'priority' => 200,
'name' => __('Replace'), 'name' => __('Replace'),
'help' => __('Replace some text. First term of the list is the text to replace and second term the replacement.'), 'description' => __('Replace some text. First term of the list is the text to replace and second term the replacement.'),
'has_list' => true, 'has_list' => true,
'htmltag' => 'pre,code', 'ignore' => ['pre','code'],
'class' => ['span.epc-replace'], 'class' => ['span.epc-replace'],
'replace' => '<span class="epc-replace">%s</span>', 'replace' => '<span class="epc-replace">%s</span>',
]; ];
} }
protected function initSettings(): array protected function initSettings(): array
{ {
return [ return [
'nocase' => true, 'nocase' => true,
'plural' => true, 'plural' => true,
'style' => ['font-style: italic;'], 'style' => ['font-style: italic;'],
'notag' => 'h1,h2,h3', 'notag' => ['h1','h2','h3'],
'tplValues' => ['EntryContent'], 'template' => ['EntryContent'],
'pubPages' => ['post.html'], 'page' => ['post.html'],
]; ];
} }

View File

@ -25,12 +25,12 @@ class EpcFilterSearch extends EpcFilter
protected function initProperties(): array protected function initProperties(): array
{ {
return [ return [
'priority' => 100, 'priority' => 100,
'name' => __('Search'), 'name' => __('Search'),
'help' => __('Highlight searched words.'), 'description' => __('Highlight searched words.'),
'htmltag' => '', 'ignore' => [],
'class' => ['span.epc-search'], 'class' => ['span.epc-search'],
'replace' => '<span class="epc-search" title="' . __('Search') . '">%s</span>', 'replace' => '<span class="epc-search" title="' . __('Search') . '">%s</span>',
]; ];
} }
@ -40,9 +40,9 @@ class EpcFilterSearch extends EpcFilter
'nocase' => true, 'nocase' => true,
'plural' => true, 'plural' => true,
'style' => ['color: #FFCC66;'], 'style' => ['color: #FFCC66;'],
'notag' => 'h1,h2,h3', 'notag' => ['h1','h2','h3'],
'tplValues' => ['EntryContent'], 'tplValues' => ['EntryContent'],
'pubPages' => ['search.html'], 'page' => ['search.html'],
]; ];
} }

View File

@ -27,23 +27,23 @@ class EpcFilterTag extends EpcFilter
protected function initProperties(): array protected function initProperties(): array
{ {
return [ return [
'priority' => 900, 'priority' => 900,
'name' => __('Tag'), 'name' => __('Tag'),
'help' => __('Highlight tags of your blog.'), 'description' => __('Highlight tags of your blog.'),
'htmltag' => 'pre,code,a', 'ignore' => ['pre','code','a'],
'class' => ['a.epc-tag'], 'class' => ['a.epc-tag'],
'replace' => '<a class="epc-tag" href="%s" title="' . __('Tag') . '">%s</a>', 'replace' => '<a class="epc-tag" href="%s" title="' . __('Tag') . '">%s</a>',
'widget' => '<a href="%s" title="' . __('Tag') . '">%s</a>', 'widget' => '<a href="%s" title="' . __('Tag') . '">%s</a>',
]; ];
} }
protected function initSettings(): array protected function initSettings(): array
{ {
return [ return [
'style' => ['text-decoration: none; border-bottom: 3px double #CCCCCC;'], 'style' => ['text-decoration: none; border-bottom: 3px double #CCCCCC;'],
'notag' => 'pre,code,a,h1,h2,h3', 'notag' => ['h1','h2','h3'],
'tplValues' => ['EntryContent'], 'template' => ['EntryContent'],
'pubPages' => ['post.html'], 'page' => ['post.html'],
]; ];
} }

View File

@ -24,22 +24,22 @@ class EpcFilterTwitter extends EpcFilter
protected function initProperties(): array protected function initProperties(): array
{ {
return [ return [
'priority' => 1000, 'priority' => 1000,
'name' => __('Twitter'), 'name' => __('Twitter'),
'help' => __('Add link to twitter user page. Every word started with "@" will be considered as twitter user.'), 'description' => __('Add link to twitter user page. Every word started with "@" will be considered as twitter user.'),
'htmltag' => 'pre,code,a', 'ingore' => ['pre','code','a'],
'class' => ['a.epc-twitter'], 'class' => ['a.epc-twitter'],
'replace' => '<a class="epc-twitter" title="' . __("View this user's twitter page") . '" href="%s">%s</a>', 'replace' => '<a class="epc-twitter" title="' . __("View this user's twitter page") . '" href="%s">%s</a>',
]; ];
} }
protected function initSettings(): array protected function initSettings(): array
{ {
return [ return [
'style' => ['text-decoration: none; font-weight: bold; font-style: italic; color: #0000FF;'], 'style' => ['text-decoration: none; font-weight: bold; font-style: italic; color: #0000FF;'],
'notag' => 'a,h1,h2,h3', 'notag' => ['h1','h2','h3'],
'tplValues' => ['EntryContent'], 'template' => ['EntryContent'],
'pubPages' => ['post.html'], 'page' => ['post.html'],
]; ];
} }

View File

@ -24,25 +24,25 @@ class EpcFilterUpdate extends EpcFilter
protected function initProperties(): array protected function initProperties(): array
{ {
return [ return [
'priority' => 300, 'priority' => 300,
'name' => __('Update'), 'name' => __('Update'),
'help' => __('Update and show terms. First term of the list is the term to update and second term the new term.'), 'description' => __('Update and show terms. First term of the list is the term to update and second term the new term.'),
'has_list' => true, 'has_list' => true,
'htmltag' => 'pre,code,del,ins', 'ignore' => ['pre','code','del','ins'],
'class' => ['del.epc-update', 'ins.epc-update'], 'class' => ['del.epc-update', 'ins.epc-update'],
'replace' => '<del class="epc-update">%s</del> <ins class="epc-update">%s</ins>', 'replace' => '<del class="epc-update">%s</del> <ins class="epc-update">%s</ins>',
]; ];
} }
protected function initSettings(): array protected function initSettings(): array
{ {
return [ return [
'nocase' => true, 'nocase' => true,
'plural' => true, 'plural' => true,
'style' => ['text-decoration: line-through;', 'font-style: italic;'], 'style' => ['text-decoration: line-through;', 'font-style: italic;'],
'notag' => 'h1,h2,h3', 'notag' => ['h1','h2','h3'],
'tplValues' => ['EntryContent'], 'template' => ['EntryContent'],
'pubPages' => ['post.html'], 'page' => ['post.html'],
]; ];
} }

View File

@ -38,17 +38,24 @@ class Frontend extends dcNsProcess
} }
dcCore::app()->addBehaviors([ dcCore::app()->addBehaviors([
// add CSS URL to header // Add CSS URL to frontend header
'publicHeadContent' => function (): void { 'publicHeadContent' => function (): void {
echo dcUtils::cssLoad(dcCore::app()->blog?->url . dcCore::app()->url->getURLFor('epccss')); echo dcUtils::cssLoad(dcCore::app()->blog?->url . dcCore::app()->url->getURLFor('epccss'));
}, },
// Filter template blocks content // Filter template blocks content
'publicBeforeContentFilterV2' => function (string $tag, array $args): void { 'publicBeforeContentFilterV2' => function (string $tag, array $args): void {
foreach (Epc::getFilters()->dump() as $filter) { foreach (Epc::getFilters()->dump() as $filter) {
if (!Epc::testContext($tag, $args, $filter)) { // test context
continue; if (in_array((string) dcCore::app()->ctx?->__get('current_tpl'), $filter->page)
&& in_array($tag, $filter->template)
&& $args[0] != '' //content
&& empty($args['encode_xml'])
&& empty($args['encode_html'])
&& empty($args['remove_html'])
&& empty($args['strip_tags'])
) {
$filter->publicContent($tag, $args);
} }
$filter->publicContent($tag, $args);
} }
}, },
// Widgets // Widgets

View File

@ -71,19 +71,19 @@ class Install extends dcNsProcess
$s->put('list_sortby', 'epc_key', 'string', 'Admin records list field order', false, true); $s->put('list_sortby', 'epc_key', 'string', 'Admin records list field order', false, true);
$s->put('list_order', 'desc', 'string', 'Admin records list order', false, true); $s->put('list_order', 'desc', 'string', 'Admin records list order', false, true);
$s->put('list_nb', 20, 'integer', 'Admin records list nb per page', false, true); $s->put('list_nb', 20, 'integer', 'Admin records list nb per page', false, true);
$s->put('allowedtplvalues', json_encode(Epc::defaultAllowedTplValues()), 'string', 'List of allowed template values', false, true); $s->put('allowedtplvalues', json_encode(Epc::defaultAllowedTemplateValue()), 'string', 'List of allowed template values', false, true);
$s->put('allowedpubpages', json_encode(Epc::defaultAllowedPubPages()), 'string', 'List of allowed template pages', false, true); $s->put('allowedpubpages', json_encode(Epc::defaultAllowedTemplatePage()), 'string', 'List of allowed template pages', false, true);
// Filters settings // Filters settings
foreach (Epc::getFilters()->dump() as $filter) { foreach (Epc::getFilters()->dump() as $filter) {
// Only editable options // Only editable options
$opt = [ $opt = [
'nocase' => $filter->nocase, 'nocase' => $filter->nocase,
'plural' => $filter->plural, 'plural' => $filter->plural,
'style' => $filter->style, 'style' => $filter->style,
'notag' => $filter->notag, 'notag' => $filter->notag,
'tplValues' => $filter->tplValues, 'template' => $filter->template,
'pubPages' => $filter->pubPages, 'page' => $filter->page,
]; ];
$s->put($filter->id(), json_encode($opt), 'string', 'Settings for ' . $filter->id(), false, true); $s->put($filter->id(), json_encode($opt), 'string', 'Settings for ' . $filter->id(), false, true);
} }
@ -114,10 +114,12 @@ class Install extends dcNsProcess
if ($current && version_compare($current, '2022.11.20', '<=')) { if ($current && version_compare($current, '2022.11.20', '<=')) {
self::upTo20221120(); self::upTo20221120();
} }
// 2023.04.22: not replaced: tplValues->template and pubPages->page
} }
/** /**
* 0.6.6 * Upgrade from 0.6.6
* *
* - filters move from settings to dedicated table * - filters move from settings to dedicated table
*/ */
@ -150,7 +152,7 @@ class Install extends dcNsProcess
} }
/** /**
* 2021.10.06 * Upgrade from 2021.10.06
* *
* - filters change name to id * - filters change name to id
*/ */
@ -169,7 +171,7 @@ class Install extends dcNsProcess
} }
/** /**
* 2022.11.20 * Upgrade from 2022.11.20
* *
* - setting id changes to shorter one, * - setting id changes to shorter one,
* - setting ns changes to abstract one (no real changes), * - setting ns changes to abstract one (no real changes),

View File

@ -57,37 +57,41 @@ class Manage extends dcNsProcess
return false; return false;
} }
// nullsafe check
if (is_null(dcCore::app()->blog) || is_null(dcCore::app()->adminurl)) { if (is_null(dcCore::app()->blog) || is_null(dcCore::app()->adminurl)) {
return false; return false;
} }
// get filter and post values
$action = $_POST['action'] ?? ''; $action = $_POST['action'] ?? '';
$filter = Epc::getFilters()->get($_REQUEST['part'] ?? ''); $filter = Epc::getFilters()->get($_REQUEST['part'] ?? '');
if (is_null($filter)) { if (is_null($filter)) {
return true; return true;
} }
// check errors
if (dcCore::app()->error->flag()) { if (dcCore::app()->error->flag()) {
return true; return true;
} }
// open save to other plugins
if (!empty($action)) { if (!empty($action)) {
# --BEHAVIOR-- enhancePostContentAdminSave # --BEHAVIOR-- enhancePostContentAdminSave
dcCore::app()->callBehavior('enhancePostContentAdminSave'); dcCore::app()->callBehavior('enhancePostContentAdminSave');
} }
try { try {
# Update filter settings // Update filter settings
if ($action == 'savefiltersetting') { if ($action == 'savefiltersetting') {
# Parse filters options # Parse filters options
$f = [ $f = [
'nocase' => !empty($_POST['filter_nocase']), 'nocase' => !empty($_POST['filter_nocase']),
'plural' => !empty($_POST['filter_plural']), 'plural' => !empty($_POST['filter_plural']),
'limit' => abs((int) $_POST['filter_limit']), 'limit' => abs((int) $_POST['filter_limit']),
'style' => (array) $_POST['filter_style'], 'style' => (array) $_POST['filter_style'],
'notag' => (string) $_POST['filter_notag'], 'notag' => Epc::decodeSingle($_POST['filter_notag']),
'tplValues' => (array) $_POST['filter_tplValues'], 'template' => (array) $_POST['filter_template'],
'pubPages' => (array) $_POST['filter_pubPages'], 'page' => (array) $_POST['filter_page'],
]; ];
dcCore::app()->blog->settings->get(My::id())->put($filter->id(), json_encode($f)); dcCore::app()->blog->settings->get(My::id())->put($filter->id(), json_encode($f));
@ -105,7 +109,7 @@ class Manage extends dcNsProcess
); );
} }
# Add new filter record // Add new filter record
if ($action == 'savenewrecord' if ($action == 'savenewrecord'
&& !empty($_POST['new_key']) && !empty($_POST['new_key'])
&& !empty($_POST['new_value']) && !empty($_POST['new_value'])
@ -133,7 +137,7 @@ class Manage extends dcNsProcess
); );
} }
# Update filter records // Update filter records
if ($action == 'deleterecords' if ($action == 'deleterecords'
&& $filter->has_list && $filter->has_list
&& !empty($_POST['epc_id']) && !empty($_POST['epc_id'])
@ -172,17 +176,22 @@ class Manage extends dcNsProcess
return; return;
} }
// nullsafe check
if (is_null(dcCore::app()->blog) || is_null(dcCore::app()->adminurl)) { if (is_null(dcCore::app()->blog) || is_null(dcCore::app()->adminurl)) {
return; return;
} }
// get filters
$filters = Epc::getFilters(); $filters = Epc::getFilters();
$filter = $filters->get($_REQUEST['part'] ?? 'link'); $filter = $filters->get($_REQUEST['part'] ?? 'link');
if (is_null($filter)) { if (is_null($filter)) {
return; return;
} }
# -- Prepare page -- // sort filters by name on backend
Epc::getFilters()->sort(true);
// Prepare tabs and lists
$header = ''; $header = '';
if ($filter->has_list) { if ($filter->has_list) {
$sorts = new adminGenericFilterV2('epc'); $sorts = new adminGenericFilterV2('epc');
@ -203,7 +212,7 @@ class Manage extends dcNsProcess
$header = $sorts->js(dcCore::app()->adminurl->get('admin.plugin.' . My::id(), ['part' => $filter->id()], '&') . '#record'); $header = $sorts->js(dcCore::app()->adminurl->get('admin.plugin.' . My::id(), ['part' => $filter->id()], '&') . '#record');
} }
# Page headers // display
dcPage::openModule( dcPage::openModule(
My::name(), My::name(),
dcPage::jsPageTabs() . dcPage::jsPageTabs() .
@ -214,7 +223,6 @@ class Manage extends dcNsProcess
dcCore::app()->callBehavior('enhancePostContentAdminHeader') dcCore::app()->callBehavior('enhancePostContentAdminHeader')
); );
# Page title
echo echo
dcPage::breadcrumb([ dcPage::breadcrumb([
__('Plugins') => '', __('Plugins') => '',
@ -223,7 +231,7 @@ class Manage extends dcNsProcess
]) . ]) .
dcPage::notices(); dcPage::notices();
# Filters select menu list // filters select menu
echo echo
(new Form('filters_menu'))->method('get')->action(dcCore::app()->adminurl->get('admin.plugin.' . My::id()))->fields([ (new Form('filters_menu'))->method('get')->action(dcCore::app()->adminurl->get('admin.plugin.' . My::id()))->fields([
(new Para())->class('anchor-nav')->items([ (new Para())->class('anchor-nav')->items([
@ -233,25 +241,25 @@ class Manage extends dcNsProcess
]), ]),
])->render(); ])->render();
# Filter title and description // selected filter
echo echo
'<h3>' . $filter->name . '</h3>' . '<h3>' . $filter->name . '</h3>' .
'<p>' . $filter->help . '</p>'; '<p>' . $filter->description . '</p>';
# Filter settings // Filter settings
$form_pages = [(new Text('h4', __('Pages to be filtered')))]; $form_pages = [(new Text('h4', __('Pages to be filtered')))];
foreach (Epc::blogAllowedPubPages() as $k => $v) { foreach (Epc::blogAllowedTemplatePage() as $k => $v) {
$form_pages[] = (new Para())->items([ $form_pages[] = (new Para())->items([
(new Checkbox(['filter_pubPages[]', 'filter_pubPages' . $v], in_array($v, $filter->pubPages)))->value($v), (new Checkbox(['filter_page[]', 'filter_page' . $v], in_array($v, $filter->page)))->value($v),
(new Label(__($k), Label::OUTSIDE_LABEL_AFTER))->for('filter_pubPages' . $v)->class('classic'), (new Label(__($k), Label::OUTSIDE_LABEL_AFTER))->for('filter_page' . $v)->class('classic'),
]); ]);
} }
$form_values = [(new Text('h4', __('Contents to be filtered')))]; $form_values = [(new Text('h4', __('Contents to be filtered')))];
foreach (Epc::blogAllowedTplValues() as $k => $v) { foreach (Epc::blogAllowedTemplateValue() as $k => $v) {
$form_values[] = (new Para())->items([ $form_values[] = (new Para())->items([
(new Checkbox(['filter_tplValues[]', 'filter_tplValues' . $v], in_array($v, $filter->tplValues)))->value($v), (new Checkbox(['filter_template[]', 'filter_template' . $v], in_array($v, $filter->template)))->value($v),
(new Label(__($k), Label::OUTSIDE_LABEL_AFTER))->for('filter_tplValues' . $v)->class('classic'), (new Label(__($k), Label::OUTSIDE_LABEL_AFTER))->for('filter_template' . $v)->class('classic'),
]); ]);
} }
@ -288,9 +296,9 @@ class Manage extends dcNsProcess
(new Note())->class('form-note')->text(sprintf(__('The inserted HTML tag looks like: %s'), Html::escapeHTML(str_replace('%s', '...', $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 Para())->items([
(new Label(__('Ignore HTML tags:'), Label::OUTSIDE_LABEL_BEFORE))->for('filter_notag'), (new Label(__('Ignore HTML tags:'), Label::OUTSIDE_LABEL_BEFORE))->for('filter_notag'),
(new Input('filter_notag'))->size(60)->maxlenght(255)->value(Html::escapeHTML($filter->notag)), (new Input('filter_notag'))->size(60)->maxlenght(255)->value(Epc::encodeSingle($filter->notag)),
]), ]),
(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 Note())->class('form-note')->text(__('This is the list of HTML tags where content will be ignored.') . '<br />' . (empty($filter->ignore) ? '' : sprintf(__('Tags "%s" will allways be ignored.'), Epc::encodeSingle($filter->ignore)))),
])), ])),
(new Div())->class('clear')->items([ (new Div())->class('clear')->items([
@ -302,7 +310,7 @@ class Manage extends dcNsProcess
]), ]),
])->render(); ])->render();
# Filter records list // Filter records list (if any)
if ($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'; $pager_url = dcCore::app()->adminurl->get('admin.plugin.' . My::id(), array_diff_key($sorts->values(true), ['page' => ''])) . '&page=%s#record';
@ -335,7 +343,7 @@ class Manage extends dcNsProcess
echo '</div>'; echo '</div>';
# New record // New record
echo echo
(new Div('newrecord'))->class('multi-part')->title(__('New record'))->items([ (new Div('newrecord'))->class('multi-part')->title(__('New record'))->items([
(new Form('form-create'))->method('post')->action(dcCore::app()->adminurl->get('admin.plugin.' . My::id()) . '#record')->fields([ (new Form('form-create'))->method('post')->action(dcCore::app()->adminurl->get('admin.plugin.' . My::id()) . '#record')->fields([

View File

@ -59,10 +59,10 @@ class Widgets
Epc::getFilters()->nid(true) Epc::getFilters()->nid(true)
); );
# Content # Content
foreach (Epc::defaultAllowedWidgetValues() as $k => $v) { foreach (Epc::widgetAllowedTemplateValue() as $name => $info) {
$w->epclist->setting( $w->epclist->setting(
'content' . $v['id'], 'content' . $info['id'],
sprintf(__('Enable filter on %s'), __($k)), sprintf(__('Enable filter on %s'), __($name)),
1, 1,
'check' 'check'
); );
@ -101,11 +101,11 @@ class Widgets
# Content # Content
$content = ''; $content = '';
foreach (Epc::defaultAllowedWidgetValues() as $k => $v) { foreach (Epc::widgetAllowedTemplateValue() as $info) {
$ns = 'content' . $v['id']; $ns = 'content' . $info['id'];
if ($w->$ns && is_callable($v['cb'])) { if ($w->$ns && is_callable($info['cb'])) {
$content .= call_user_func( $content .= call_user_func(
$v['cb'], $info['cb'],
$w $w
); );
} }
@ -130,11 +130,11 @@ class Widgets
# Parse result # Parse result
$res = ''; $res = '';
foreach ($list as $line) { foreach ($list as $line) {
if (empty($line['matches'][0]['match'])) { if ((int) $line['total'] == 0) {
continue; continue;
} }
$res .= '<li>' . $line['matches'][0]['match'] . $res .= '<li>' . $line['replacement'] .
($w->show_total ? ' (' . $line['total'] . ')' : '') . ($w->show_total ? ' (' . $line['total'] . ')' : '') .
'</li>'; '</li>';
} }