Compare commits
10 Commits
8c276b9c65
...
71b546d652
Author | SHA1 | Date |
---|---|---|
Jean-Christian Paul Denis | 71b546d652 | |
Jean-Christian Paul Denis | 4d307f01cd | |
Jean-Christian Paul Denis | e509604aee | |
Jean-Christian Paul Denis | 0ab23445e3 | |
Jean-Christian Paul Denis | 496cc3c30a | |
Jean-Christian Paul Denis | e41cacdb70 | |
Jean-Christian Paul Denis | a23fb10211 | |
Jean-Christian Paul Denis | 12851f6c77 | |
Jean-Christian Paul Denis | 67d328cae7 | |
Jean-Christian Paul Denis | b24f5715b5 |
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,3 +1,15 @@
|
|||
1.2 - 2023.05.13
|
||||
- require dotclear 2.26
|
||||
- fix nullsafe warnings
|
||||
- release for dotclear 2.26 stable
|
||||
|
||||
1.1 - 2023.04.20
|
||||
- require dotclear 2.26
|
||||
- use sql statement
|
||||
- use dotclear helpers
|
||||
- use plugin Uninstaller
|
||||
- various cosmetic fix
|
||||
|
||||
1.0 - 2023.03.11
|
||||
- update to dotclear 2.25
|
||||
- use namespace
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
[![Release](https://img.shields.io/github/v/release/JcDenis/dcFilterDuplicate)](https://github.com/JcDenis/dcFilterDuplicate/releases)
|
||||
[![Date](https://img.shields.io/github/release-date/JcDenis/dcFilterDuplicate)](https://github.com/JcDenis/dcFilterDuplicate/releases)
|
||||
[![Issues](https://img.shields.io/github/issues/JcDenis/dcFilterDuplicate)](https://github.com/JcDenis/dcFilterDuplicate/issues)
|
||||
[![Dotclear](https://img.shields.io/badge/dotclear-v2.25-blue.svg)](https://fr.dotclear.org/download)
|
||||
[![Dotclear](https://img.shields.io/badge/dotclear-v2.26-blue.svg)](https://fr.dotclear.org/download)
|
||||
[![Dotaddict](https://img.shields.io/badge/dotaddict-official-green.svg)](https://plugins.dotaddict.org/dc2/details/dcFilterDuplicate)
|
||||
[![License](https://img.shields.io/github/license/JcDenis/dcFilterDuplicate)](https://github.com/JcDenis/dcFilterDuplicate/blob/master/LICENSE)
|
||||
|
||||
|
@ -19,7 +19,7 @@ Filter as spam same (duplicate) comments from multiple blogs of a Dotclear's ins
|
|||
dcFilterDuplicate requires:
|
||||
|
||||
* permissions to manage antispam
|
||||
* Dotclear 2.25
|
||||
* Dotclear 2.26
|
||||
|
||||
## USAGE
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
* @copyright Jean-Christian Denis
|
||||
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
|
||||
*/
|
||||
if (!defined('DC_RC_PATH')) {
|
||||
if (!defined('DC_RC_PATH') || is_null(dcCore::app()->auth)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -18,11 +18,11 @@ $this->registerModule(
|
|||
'Duplicate filter',
|
||||
'Antispam for duplicate comments on multiblog',
|
||||
'Jean-Christian Denis, Pierre Van Glabeke',
|
||||
'1.0',
|
||||
'1.2',
|
||||
[
|
||||
'requires' => [['core', '2.25']],
|
||||
'requires' => [['core', '2.26']],
|
||||
'permissions' => dcCore::app()->auth->makePermissions([
|
||||
dcAuth::PERMISSION_ADMIN,
|
||||
dcCore::app()->auth::PERMISSION_ADMIN,
|
||||
]),
|
||||
'priority' => 200,
|
||||
'type' => 'plugin',
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
<modules xmlns:da="http://dotaddict.org/da/">
|
||||
<module id="dcFilterDuplicate">
|
||||
<name>Duplicate filter</name>
|
||||
<version>1.0</version>
|
||||
<version>1.2</version>
|
||||
<author>Jean-Christian Denis, Pierre Van Glabeke</author>
|
||||
<desc>Antispam for duplicate comments on multiblog</desc>
|
||||
<file>https://github.com/JcDenis/dcFilterDuplicate/releases/download/v1.0/plugin-dcFilterDuplicate.zip</file>
|
||||
<da:dcmin>2.25</da:dcmin>
|
||||
<file>https://github.com/JcDenis/dcFilterDuplicate/releases/download/v1.2/plugin-dcFilterDuplicate.zip</file>
|
||||
<da:dcmin>2.26</da:dcmin>
|
||||
<da:details>http://plugins.dotaddict.org/dc2/details/dcFilterDuplicate</da:details>
|
||||
<da:support>http://forum.dotclear.org/viewtopic.php?pid=332947#p332947</da:support>
|
||||
</module>
|
||||
|
|
|
@ -9,9 +9,11 @@
|
|||
# DOT NOT MODIFY THIS FILE !
|
||||
#
|
||||
|
||||
l10n::$locales['Duplicate'] = 'Doublons';
|
||||
l10n::$locales['Same comments on others blogs of a multiblog'] = 'Commentaires identiques sur les autres blogs du multiblog';
|
||||
l10n::$locales['Minimum content length before check for duplicate:'] = 'Longueur minimum du contenu pour faire une vérification :';
|
||||
l10n::$locales['Super administrator set the minimum length of comment content to %d chars.'] = 'Un super administrateur a régler la longueur minimum d\'un commentaire à surveiller à %d caractères.';
|
||||
l10n::$locales['Antispam for duplicate comments on multiblog'] = 'Antispam contre les doublons de commentaires sur un multiblog';
|
||||
l10n::$locales['Duplicate filter'] = 'Filtre de doublons';
|
||||
use Dotclear\Helper\L10n;
|
||||
|
||||
L10n::$locales['Duplicate'] = 'Doublons';
|
||||
L10n::$locales['Same comments on others blogs of a multiblog'] = 'Commentaires identiques sur les autres blogs du multiblog';
|
||||
L10n::$locales['Minimum content length before check for duplicate:'] = 'Longueur minimum du contenu pour faire une vérification :';
|
||||
L10n::$locales['Super administrator set the minimum length of comment content to %d chars.'] = 'Un super administrateur a régler la longueur minimum d\'un commentaire à surveiller à %d caractères.';
|
||||
L10n::$locales['Antispam for duplicate comments on multiblog'] = 'Antispam contre les doublons de commentaires sur un multiblog';
|
||||
L10n::$locales['Duplicate filter'] = 'Filtre de doublons';
|
||||
|
|
|
@ -16,19 +16,29 @@ namespace Dotclear\Plugin\dcFilterDuplicate;
|
|||
|
||||
use dcBlog;
|
||||
use dcCore;
|
||||
use dcPage;
|
||||
use dcSpamFilter;
|
||||
use Dotclear\Core\Backend\Notices;
|
||||
use Dotclear\Database\Statement\{
|
||||
JoinStatement,
|
||||
SelectStatement
|
||||
};
|
||||
use Dotclear\Helper\Html\Form\{
|
||||
Form,
|
||||
Input,
|
||||
Label,
|
||||
Para,
|
||||
Submit
|
||||
};
|
||||
use Dotclear\Helper\Html\Html;
|
||||
use Dotclear\Helper\Network\Http;
|
||||
use Dotclear\Plugin\antispam\SpamFilter;
|
||||
use Exception;
|
||||
use form;
|
||||
use html;
|
||||
use http;
|
||||
|
||||
/**
|
||||
* @ingroup DC_PLUGIN_DCFILTERDUPLICATE
|
||||
* @brief Filter duplicate comments on multiblogs.
|
||||
* @since 2.6
|
||||
*/
|
||||
class FilterDuplicate extends dcSpamFilter
|
||||
class FilterDuplicate extends SpamFilter
|
||||
{
|
||||
public $name = 'Duplicate filter';
|
||||
public $has_gui = true;
|
||||
|
@ -39,9 +49,9 @@ class FilterDuplicate extends dcSpamFilter
|
|||
$this->description = __('Same comments on others blogs of a multiblog');
|
||||
}
|
||||
|
||||
public function isSpam($type, $author, $email, $site, $ip, $content, $post_id, &$status): ?bool
|
||||
public function isSpam(string $type, ?string $author, ?string $email, ?string $site, ?string $ip, ?string $content, ?int $post_id, string &$status): ?bool
|
||||
{
|
||||
if ($type != 'comment') {
|
||||
if ($type != 'comment' || is_null($content) || is_null($ip)) {
|
||||
return null;
|
||||
}
|
||||
if (strlen($content) < $this->getMinLength()) {
|
||||
|
@ -62,30 +72,40 @@ class FilterDuplicate extends dcSpamFilter
|
|||
}
|
||||
}
|
||||
|
||||
public function isDuplicate($content, $ip): bool
|
||||
public function isDuplicate(string $content, string $ip): bool
|
||||
{
|
||||
$rs = dcCore::app()->con->select(
|
||||
'SELECT C.comment_id ' .
|
||||
'FROM ' . dcCore::app()->prefix . dcBlog::COMMENT_TABLE_NAME . ' C ' .
|
||||
'LEFT JOIN ' . dcCore::app()->prefix . 'post P ON C.post_id=P.post_id ' .
|
||||
"WHERE P.blog_id != '" . dcCore::app()->blog->id . "' " .
|
||||
"AND C.comment_content='" . dcCore::app()->con->escape($content) . "' " .
|
||||
"AND C.comment_ip='" . $ip . "' "
|
||||
);
|
||||
// nullsafe PHP < 8.0
|
||||
if (is_null(dcCore::app()->blog)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !$rs->isEmpty();
|
||||
$sql = new SelectStatement();
|
||||
$rs = $sql->from($sql->as(dcCore::app()->prefix . dcBlog::COMMENT_TABLE_NAME, 'C'))
|
||||
->join(
|
||||
(new JoinStatement())
|
||||
->left()
|
||||
->from($sql->as(dcCore::app()->prefix . dcBlog::POST_TABLE_NAME, 'P'))
|
||||
->on('C.post_id = P.post_id')
|
||||
->statement()
|
||||
)
|
||||
->where('P.blog_id != ' . $sql->quote(dcCore::app()->blog->id))
|
||||
->and('C.comment_content = ' . $sql->quote($content))
|
||||
->and('C.comment_ip=' . $sql->quote($ip))
|
||||
->select();
|
||||
|
||||
return !is_null($rs) && !$rs->isEmpty();
|
||||
}
|
||||
|
||||
public function markDuplicate($content, $ip): void
|
||||
public function markDuplicate(string $content, string $ip): void
|
||||
{
|
||||
$cur = dcCore::app()->con->openCursor(dcCore::app()->prefix . dcBlog::COMMENT_TABLE_NAME);
|
||||
dcCore::app()->con->writeLock(dcCore::app()->prefix . dcBlog::COMMENT_TABLE_NAME);
|
||||
|
||||
$cur->comment_status = -2;
|
||||
$cur->comment_spam_status = 'Duplicate on other blog';
|
||||
$cur->comment_spam_filter = 'dcFilterDuplicate';
|
||||
$cur->setField('comment_status', -2);
|
||||
$cur->setField('comment_spam_status', 'Duplicate on other blog');
|
||||
$cur->setField('comment_spam_filter', My::id());
|
||||
$cur->update(
|
||||
"WHERE comment_content='" . dcCore::app()->con->escape($content) . "' " .
|
||||
"WHERE comment_content='" . dcCore::app()->con->escapeStr($content) . "' " .
|
||||
"AND comment_ip='" . $ip . "' "
|
||||
);
|
||||
dcCore::app()->con->unlock();
|
||||
|
@ -94,33 +114,37 @@ class FilterDuplicate extends dcSpamFilter
|
|||
|
||||
public function gui(string $url): string
|
||||
{
|
||||
// nullsafe PHP < 8.0
|
||||
if (is_null(dcCore::app()->blog)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (dcCore::app()->auth->isSuperAdmin()) {
|
||||
dcCore::app()->blog->settings->get(My::id())->drop('dcfilterduplicate_minlen');
|
||||
if (isset($_POST['dcfilterduplicate_minlen'])) {
|
||||
dcCore::app()->blog->settings->get(My::id())->put(
|
||||
'dcfilterduplicate_minlen',
|
||||
abs((int) $_POST['dcfilterduplicate_minlen']),
|
||||
My::settings()->drop(My::SETTING_PREFIX . 'minlen');
|
||||
if (isset($_POST[My::SETTING_PREFIX . 'minlen'])) {
|
||||
My::settings()->put(
|
||||
My::SETTING_PREFIX . 'minlen',
|
||||
abs((int) $_POST[My::SETTING_PREFIX . 'minlen']),
|
||||
'integer',
|
||||
'Minimum lenght of comment to filter',
|
||||
true,
|
||||
true
|
||||
);
|
||||
dcPage::addSuccessNotice(__('Configuration successfully updated.'));
|
||||
http::redirect($url);
|
||||
Notices::addSuccessNotice(__('Configuration successfully updated.'));
|
||||
Http::redirect($url);
|
||||
}
|
||||
|
||||
return
|
||||
'<form action="' . html::escapeURL($url) . '" method="post">' .
|
||||
'<p><label class="classic">' . __('Minimum content length before check for duplicate:') . '<br />' .
|
||||
form::field(
|
||||
['dcfilterduplicate_minlen'],
|
||||
65,
|
||||
255,
|
||||
$this->getMinlength(),
|
||||
) . '</label></p>' .
|
||||
'<p><input type="submit" name="save" value="' . __('Save') . '" />' .
|
||||
dcCore::app()->formNonce() . '</p>' .
|
||||
'</form>';
|
||||
(new Form(My::id() . '_gui'))->method('post')->action(Html::escapeURL($url))->fields([
|
||||
(new Para())->items([
|
||||
(new Label(__('Minimum content length before check for duplicate:')))->for(My::SETTING_PREFIX . 'minlen'),
|
||||
(new Input(My::SETTING_PREFIX . 'minlen'))->size(65)->maxlenght(255)->value($this->getMinlength()),
|
||||
]),
|
||||
(new Para())->items([
|
||||
(new Submit('save'))->value(__('Save')),
|
||||
dcCore::app()->formNonce(false),
|
||||
]),
|
||||
])->render();
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -132,18 +156,33 @@ class FilterDuplicate extends dcSpamFilter
|
|||
|
||||
private function getMinLength(): int
|
||||
{
|
||||
return abs((int) dcCore::app()->blog->settings->get('dcFilterDuplicate')->getGlobal('dcfilterduplicate_minlen'));
|
||||
// nullsafe PHP < 8.0
|
||||
if (is_null(dcCore::app()->blog)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return abs((int) My::settings()->getGlobal(My::SETTING_PREFIX . 'minlen'));
|
||||
}
|
||||
|
||||
public function triggerOtherBlogs($content, $ip): void
|
||||
public function triggerOtherBlogs(string $content, string $ip): void
|
||||
{
|
||||
$rs = dcCore::app()->con->select(
|
||||
'SELECT P.blog_id ' .
|
||||
'FROM ' . dcCore::app()->prefix . dcBlog::COMMENT_TABLE_NAME . ' C ' .
|
||||
'LEFT JOIN ' . dcCore::app()->prefix . 'post P ON C.post_id=P.post_id ' .
|
||||
"WHERE C.comment_content='" . dcCore::app()->con->escape($content) . "' " .
|
||||
"AND C.comment_ip='" . $ip . "' "
|
||||
);
|
||||
$sql = new SelectStatement();
|
||||
$rs = $sql->from($sql->as(dcCore::app()->prefix . dcBlog::COMMENT_TABLE_NAME, 'C'))
|
||||
->column('P.blog_id')
|
||||
->join(
|
||||
(new JoinStatement())
|
||||
->left()
|
||||
->from($sql->as(dcCore::app()->prefix . dcBlog::POST_TABLE_NAME, 'P'))
|
||||
->on('C.post_id = P.post_id')
|
||||
->statement()
|
||||
)
|
||||
->where('C.comment_content = ' . $sql->quote($content))
|
||||
->and('C.comment_ip=' . $sql->quote($ip))
|
||||
->select();
|
||||
|
||||
if (is_null($rs) || $rs->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
while ($rs->fetch()) {
|
||||
$b = new dcBlog($rs->f('blog_id'));
|
||||
|
|
|
@ -15,44 +15,32 @@ declare(strict_types=1);
|
|||
namespace Dotclear\Plugin\dcFilterDuplicate;
|
||||
|
||||
use dcCore;
|
||||
use dcNsProcess;
|
||||
use Dotclear\Core\Process;
|
||||
use Exception;
|
||||
|
||||
class Install extends dcNsProcess
|
||||
class Install extends Process
|
||||
{
|
||||
# -- Module specs --
|
||||
private static $mod_conf = [[
|
||||
'dcfilterduplicate_minlen',
|
||||
'Minimum lenght of comment to filter',
|
||||
30,
|
||||
'integer',
|
||||
]];
|
||||
|
||||
public static function init(): bool
|
||||
{
|
||||
static::$init = defined('DC_CONTEXT_ADMIN') && dcCore::app()->newVersion(My::id(), dcCore::app()->plugins->moduleInfo(My::id(), 'version'));
|
||||
|
||||
return static::$init;
|
||||
return self::status(My::checkContext(My::INSTALL));
|
||||
}
|
||||
|
||||
public static function process(): bool
|
||||
{
|
||||
if (!static::$init) {
|
||||
if (!self::status()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
# Set module settings
|
||||
foreach (self::$mod_conf as $v) {
|
||||
dcCore::app()->blog->settings->get(My::id())->put(
|
||||
$v[0],
|
||||
$v[2],
|
||||
$v[3],
|
||||
$v[1],
|
||||
false,
|
||||
true
|
||||
);
|
||||
}
|
||||
My::settings()->put(
|
||||
My::SETTING_PREFIX . 'minlen',
|
||||
30,
|
||||
'integer',
|
||||
'Minimum lenght of comment to filter',
|
||||
false,
|
||||
true
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
|
|
23
src/My.php
23
src/My.php
|
@ -14,26 +14,13 @@ declare(strict_types=1);
|
|||
|
||||
namespace Dotclear\Plugin\dcFilterDuplicate;
|
||||
|
||||
use dcCore;
|
||||
use Dotclear\Module\MyPlugin;
|
||||
|
||||
/**
|
||||
* Plugin definitions
|
||||
* This module definitions.
|
||||
*/
|
||||
class My
|
||||
class My extends MyPlugin
|
||||
{
|
||||
/**
|
||||
* This module id
|
||||
*/
|
||||
public static function id(): string
|
||||
{
|
||||
return basename(dirname(__DIR__));
|
||||
}
|
||||
|
||||
/**
|
||||
* This module name
|
||||
*/
|
||||
public static function name(): string
|
||||
{
|
||||
return __((string) dcCore::app()->plugins->moduleInfo(self::id(), 'name'));
|
||||
}
|
||||
/** @var string Plugin setting prefix */
|
||||
public const SETTING_PREFIX = 'dcfilterduplicate_';
|
||||
}
|
||||
|
|
|
@ -15,20 +15,18 @@ declare(strict_types=1);
|
|||
namespace Dotclear\Plugin\dcFilterDuplicate;
|
||||
|
||||
use dcCore;
|
||||
use dcNsProcess;
|
||||
use Dotclear\Core\Process;
|
||||
|
||||
class Prepend extends dcNsProcess
|
||||
class Prepend extends Process
|
||||
{
|
||||
public static function init(): bool
|
||||
{
|
||||
static::$init = true;
|
||||
|
||||
return static::$init;
|
||||
return self::status(My::checkContext(My::PREPEND));
|
||||
}
|
||||
|
||||
public static function process(): bool
|
||||
{
|
||||
if (!static::$init) {
|
||||
if (!self::status()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,89 +14,57 @@ declare(strict_types=1);
|
|||
|
||||
namespace Dotclear\Plugin\dcFilterDuplicate;
|
||||
|
||||
class Uninstall
|
||||
{
|
||||
protected static $init = false;
|
||||
use dcCore;
|
||||
use Dotclear\Core\Process;
|
||||
use Dotclear\Plugin\Uninstaller\Uninstaller;
|
||||
|
||||
class Uninstall extends Process
|
||||
{
|
||||
public static function init(): bool
|
||||
{
|
||||
static::$init = defined('DC_RC_PATH');
|
||||
|
||||
return static::$init;
|
||||
return self::status(My::checkContext(My::UNINSTALL));
|
||||
}
|
||||
|
||||
public static function process($uninstaller): ?bool
|
||||
public static function process(): bool
|
||||
{
|
||||
if (!static::$init) {
|
||||
if (!self::status() || !dcCore::app()->plugins->moduleExists('Uninstaller')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$uninstaller->addUserAction(
|
||||
/* type */
|
||||
'settings',
|
||||
/* action */
|
||||
'delete_all',
|
||||
/* ns */
|
||||
My::id(),
|
||||
/* desc */
|
||||
__('delete all settings')
|
||||
);
|
||||
Uninstaller::instance()
|
||||
->addUserAction(
|
||||
'settings',
|
||||
'delete_all',
|
||||
My::id()
|
||||
)
|
||||
->addUserAction(
|
||||
'plugins',
|
||||
'delete',
|
||||
My::id()
|
||||
)
|
||||
->addUserAction(
|
||||
'versions',
|
||||
'delete',
|
||||
My::id()
|
||||
)
|
||||
->addDirectAction(
|
||||
'settings',
|
||||
'delete_all',
|
||||
My::id()
|
||||
)
|
||||
->addDirectAction(
|
||||
'versions',
|
||||
'delete',
|
||||
My::id()
|
||||
)
|
||||
->addDirectAction(
|
||||
'plugins',
|
||||
'delete',
|
||||
My::id()
|
||||
)
|
||||
;
|
||||
|
||||
$uninstaller->addUserAction(
|
||||
/* type */
|
||||
'plugins',
|
||||
/* action */
|
||||
'delete',
|
||||
/* ns */
|
||||
My::id(),
|
||||
/* desc */
|
||||
__('delete plugin files')
|
||||
);
|
||||
|
||||
$uninstaller->addUserAction(
|
||||
/* type */
|
||||
'versions',
|
||||
/* action */
|
||||
'delete',
|
||||
/* ns */
|
||||
My::id(),
|
||||
/* desc */
|
||||
__('delete the version number')
|
||||
);
|
||||
|
||||
$uninstaller->addDirectAction(
|
||||
/* type */
|
||||
'settings',
|
||||
/* action */
|
||||
'delete_all',
|
||||
/* ns */
|
||||
My::id(),
|
||||
/* desc */
|
||||
sprintf(__('delete all %s settings'), My::id())
|
||||
);
|
||||
|
||||
$uninstaller->addDirectAction(
|
||||
/* type */
|
||||
'versions',
|
||||
/* action */
|
||||
'delete',
|
||||
/* ns */
|
||||
My::id(),
|
||||
/* desc */
|
||||
sprintf(__('delete %s version number'), My::id())
|
||||
);
|
||||
|
||||
$uninstaller->addDirectAction(
|
||||
/* type */
|
||||
'plugins',
|
||||
/* action */
|
||||
'delete',
|
||||
/* ns */
|
||||
My::id(),
|
||||
/* desc */
|
||||
sprintf(__('delete %s plugin files'), My::id())
|
||||
);
|
||||
|
||||
return true;
|
||||
// no custom action
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue