Compare commits

..

No commits in common. "71b546d652aab51b4b55581da945078ee43f4e6b" and "8c276b9c6587fd70ae4f491d95cd60ec45318e61" have entirely different histories.

10 changed files with 187 additions and 181 deletions

View File

@ -1,15 +1,3 @@
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.26-blue.svg)](https://fr.dotclear.org/download)
[![Dotclear](https://img.shields.io/badge/dotclear-v2.25-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.26
* Dotclear 2.25
## 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') || is_null(dcCore::app()->auth)) {
if (!defined('DC_RC_PATH')) {
return null;
}
@ -18,11 +18,11 @@ $this->registerModule(
'Duplicate filter',
'Antispam for duplicate comments on multiblog',
'Jean-Christian Denis, Pierre Van Glabeke',
'1.2',
'1.0',
[
'requires' => [['core', '2.26']],
'requires' => [['core', '2.25']],
'permissions' => dcCore::app()->auth->makePermissions([
dcCore::app()->auth::PERMISSION_ADMIN,
dcAuth::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.2</version>
<version>1.0</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.2/plugin-dcFilterDuplicate.zip</file>
<da:dcmin>2.26</da:dcmin>
<file>https://github.com/JcDenis/dcFilterDuplicate/releases/download/v1.0/plugin-dcFilterDuplicate.zip</file>
<da:dcmin>2.25</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,11 +9,9 @@
# DOT NOT MODIFY THIS FILE !
#
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';
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,29 +16,19 @@ namespace Dotclear\Plugin\dcFilterDuplicate;
use dcBlog;
use dcCore;
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 dcPage;
use dcSpamFilter;
use Exception;
use form;
use html;
use http;
/**
* @ingroup DC_PLUGIN_DCFILTERDUPLICATE
* @brief Filter duplicate comments on multiblogs.
* @since 2.6
*/
class FilterDuplicate extends SpamFilter
class FilterDuplicate extends dcSpamFilter
{
public $name = 'Duplicate filter';
public $has_gui = true;
@ -49,9 +39,9 @@ class FilterDuplicate extends SpamFilter
$this->description = __('Same comments on others blogs of a multiblog');
}
public function isSpam(string $type, ?string $author, ?string $email, ?string $site, ?string $ip, ?string $content, ?int $post_id, string &$status): ?bool
public function isSpam($type, $author, $email, $site, $ip, $content, $post_id, &$status): ?bool
{
if ($type != 'comment' || is_null($content) || is_null($ip)) {
if ($type != 'comment') {
return null;
}
if (strlen($content) < $this->getMinLength()) {
@ -72,40 +62,30 @@ class FilterDuplicate extends SpamFilter
}
}
public function isDuplicate(string $content, string $ip): bool
public function isDuplicate($content, $ip): bool
{
// nullsafe PHP < 8.0
if (is_null(dcCore::app()->blog)) {
return false;
}
$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 . "' "
);
$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();
return !$rs->isEmpty();
}
public function markDuplicate(string $content, string $ip): void
public function markDuplicate($content, $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->setField('comment_status', -2);
$cur->setField('comment_spam_status', 'Duplicate on other blog');
$cur->setField('comment_spam_filter', My::id());
$cur->comment_status = -2;
$cur->comment_spam_status = 'Duplicate on other blog';
$cur->comment_spam_filter = 'dcFilterDuplicate';
$cur->update(
"WHERE comment_content='" . dcCore::app()->con->escapeStr($content) . "' " .
"WHERE comment_content='" . dcCore::app()->con->escape($content) . "' " .
"AND comment_ip='" . $ip . "' "
);
dcCore::app()->con->unlock();
@ -114,37 +94,33 @@ class FilterDuplicate extends SpamFilter
public function gui(string $url): string
{
// nullsafe PHP < 8.0
if (is_null(dcCore::app()->blog)) {
return '';
}
if (dcCore::app()->auth->isSuperAdmin()) {
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']),
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']),
'integer',
'Minimum lenght of comment to filter',
true,
true
);
Notices::addSuccessNotice(__('Configuration successfully updated.'));
Http::redirect($url);
dcPage::addSuccessNotice(__('Configuration successfully updated.'));
http::redirect($url);
}
return
(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();
'<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>';
}
return
@ -156,33 +132,18 @@ class FilterDuplicate extends SpamFilter
private function getMinLength(): int
{
// nullsafe PHP < 8.0
if (is_null(dcCore::app()->blog)) {
return 0;
}
return abs((int) My::settings()->getGlobal(My::SETTING_PREFIX . 'minlen'));
return abs((int) dcCore::app()->blog->settings->get('dcFilterDuplicate')->getGlobal('dcfilterduplicate_minlen'));
}
public function triggerOtherBlogs(string $content, string $ip): void
public function triggerOtherBlogs($content, $ip): void
{
$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;
}
$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 . "' "
);
while ($rs->fetch()) {
$b = new dcBlog($rs->f('blog_id'));

View File

@ -15,32 +15,44 @@ declare(strict_types=1);
namespace Dotclear\Plugin\dcFilterDuplicate;
use dcCore;
use Dotclear\Core\Process;
use dcNsProcess;
use Exception;
class Install extends Process
class Install extends dcNsProcess
{
# -- Module specs --
private static $mod_conf = [[
'dcfilterduplicate_minlen',
'Minimum lenght of comment to filter',
30,
'integer',
]];
public static function init(): bool
{
return self::status(My::checkContext(My::INSTALL));
static::$init = defined('DC_CONTEXT_ADMIN') && dcCore::app()->newVersion(My::id(), dcCore::app()->plugins->moduleInfo(My::id(), 'version'));
return static::$init;
}
public static function process(): bool
{
if (!self::status()) {
if (!static::$init) {
return false;
}
try {
# Set module settings
My::settings()->put(
My::SETTING_PREFIX . 'minlen',
30,
'integer',
'Minimum lenght of comment to filter',
false,
true
);
foreach (self::$mod_conf as $v) {
dcCore::app()->blog->settings->get(My::id())->put(
$v[0],
$v[2],
$v[3],
$v[1],
false,
true
);
}
return true;
} catch (Exception $e) {

View File

@ -14,13 +14,26 @@ declare(strict_types=1);
namespace Dotclear\Plugin\dcFilterDuplicate;
use Dotclear\Module\MyPlugin;
use dcCore;
/**
* This module definitions.
* Plugin definitions
*/
class My extends MyPlugin
class My
{
/** @var string Plugin setting prefix */
public const SETTING_PREFIX = 'dcfilterduplicate_';
/**
* 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'));
}
}

View File

@ -15,18 +15,20 @@ declare(strict_types=1);
namespace Dotclear\Plugin\dcFilterDuplicate;
use dcCore;
use Dotclear\Core\Process;
use dcNsProcess;
class Prepend extends Process
class Prepend extends dcNsProcess
{
public static function init(): bool
{
return self::status(My::checkContext(My::PREPEND));
static::$init = true;
return static::$init;
}
public static function process(): bool
{
if (!self::status()) {
if (!static::$init) {
return false;
}

View File

@ -14,57 +14,89 @@ declare(strict_types=1);
namespace Dotclear\Plugin\dcFilterDuplicate;
use dcCore;
use Dotclear\Core\Process;
use Dotclear\Plugin\Uninstaller\Uninstaller;
class Uninstall extends Process
class Uninstall
{
protected static $init = false;
public static function init(): bool
{
return self::status(My::checkContext(My::UNINSTALL));
static::$init = defined('DC_RC_PATH');
return static::$init;
}
public static function process(): bool
public static function process($uninstaller): ?bool
{
if (!self::status() || !dcCore::app()->plugins->moduleExists('Uninstaller')) {
if (!static::$init) {
return false;
}
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 */
'settings',
/* action */
'delete_all',
/* ns */
My::id(),
/* desc */
__('delete all settings')
);
// no custom action
return false;
$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;
}
}