Compare commits

..

10 Commits

10 changed files with 181 additions and 187 deletions

View File

@ -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

View File

@ -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

View File

@ -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',

View File

@ -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>

View File

@ -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';

View File

@ -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'));

View File

@ -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) {

View File

@ -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_';
}

View File

@ -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;
}

View File

@ -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;
}
}