Compare commits

...

10 Commits

8 changed files with 220 additions and 123 deletions

View File

@ -1,6 +1,17 @@
dev dev
- [ ] literal rank of writer depending of num of com - [ ] literal rank of writer depending of num of com
1.2 - 2023.04.23
- require dotclear 2.26
- use latest dotclear namespace
- use sql statement
- fix plural translations
- fix nullsafe warnings
1.1 - 2023.03.21
- require Dotclear 2.25
- use namespace
1.0 - 2022.12.23 1.0 - 2022.12.23
- use dotclear methods for widgets - use dotclear methods for widgets
- fix permissions - fix permissions

View File

@ -3,7 +3,7 @@
[![Release](https://img.shields.io/github/v/release/JcDenis/topWriter)](https://github.com/JcDenis/topWriter/releases) [![Release](https://img.shields.io/github/v/release/JcDenis/topWriter)](https://github.com/JcDenis/topWriter/releases)
[![Date](https://img.shields.io/github/release-date/JcDenis/topWriter)](https://github.com/JcDenis/topWriter/releases) [![Date](https://img.shields.io/github/release-date/JcDenis/topWriter)](https://github.com/JcDenis/topWriter/releases)
[![Issues](https://img.shields.io/github/issues/JcDenis/topWriter)](https://github.com/JcDenis/topWriter/issues) [![Issues](https://img.shields.io/github/issues/JcDenis/topWriter)](https://github.com/JcDenis/topWriter/issues)
[![Dotclear](https://img.shields.io/badge/dotclear-v2.24-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/topWriter) [![Dotaddict](https://img.shields.io/badge/dotaddict-official-green.svg)](https://plugins.dotaddict.org/dc2/details/topWriter)
[![License](https://img.shields.io/github/license/JcDenis/topWriter)](https://github.com/JcDenis/topWriter/blob/master/LICENSE) [![License](https://img.shields.io/github/license/JcDenis/topWriter)](https://github.com/JcDenis/topWriter/blob/master/LICENSE)
@ -19,7 +19,7 @@ Show most active contributor on a widget.
_topWriter_ requires: _topWriter_ requires:
* permissions to manage widgets * permissions to manage widgets
* Dotclear 2.24 * Dotclear 2.26
## USAGE ## USAGE

View File

@ -10,7 +10,7 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @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; return null;
} }
@ -18,15 +18,15 @@ $this->registerModule(
'Top writer', 'Top writer',
'Ranking of the most prolific writers and/or commentators', 'Ranking of the most prolific writers and/or commentators',
'Jean-Christian Denis, Pierre Van Glabeke', 'Jean-Christian Denis, Pierre Van Glabeke',
'1.0', '1.2',
[ [
'requires' => [['core', '2.24']], 'requires' => [['core', '2.26']],
'permissions' => dcCore::app()->auth->makePermissions([ 'permissions' => dcCore::app()->auth->makePermissions([
dcAuth::PERMISSION_CONTENT_ADMIN, dcCore::app()->auth::PERMISSION_CONTENT_ADMIN,
]), ]),
'type' => 'plugin', 'type' => 'plugin',
'support' => 'http://forum.dotclear.org/viewtopic.php?pid=333002#p333002', 'support' => 'http://forum.dotclear.org/viewtopic.php?pid=333002#p333002',
'details' => 'http://plugins.dotaddict.org/dc2/details/topWriter', 'details' => 'http://plugins.dotaddict.org/dc2/details/topWriter',
'repository' => 'https://raw.githubusercontent.com/JcDenis/topWriter/master/dcstore.xml', 'repository' => 'https://raw.githubusercontent.com/JcDenis/topWriter/master/dcstore.xml',
] ]
); );

View File

@ -2,11 +2,11 @@
<modules xmlns:da="http://dotaddict.org/da/"> <modules xmlns:da="http://dotaddict.org/da/">
<module id="topWriter"> <module id="topWriter">
<name>Top writer</name> <name>Top writer</name>
<version>1.0</version> <version>1.2</version>
<author>Jean-Christian Denis, Pierre Van Glabeke</author> <author>Jean-Christian Denis, Pierre Van Glabeke</author>
<desc>Ranking of the most prolific writers and/or commentators</desc> <desc>Ranking of the most prolific writers and/or commentators</desc>
<file>https://github.com/JcDenis/topWriter/releases/download/v1.0/plugin-topWriter.zip</file> <file>https://github.com/JcDenis/topWriter/releases/download/v1.2/plugin-topWriter.zip</file>
<da:dcmin>2.24</da:dcmin> <da:dcmin>2.26</da:dcmin>
<da:details>http://plugins.dotaddict.org/dc2/details/topWriter</da:details> <da:details>http://plugins.dotaddict.org/dc2/details/topWriter</da:details>
<da:support>http://forum.dotclear.org/viewtopic.php?pid=333002#p333002</da:support> <da:support>http://forum.dotclear.org/viewtopic.php?pid=333002#p333002</da:support>
</module> </module>

View File

@ -9,26 +9,32 @@
# DOT NOT MODIFY THIS FILE ! # DOT NOT MODIFY THIS FILE !
# #
l10n::$locales['Top writer: entries'] = 'Top rédacteur : billets'; use Dotclear\Helper\L10n;
l10n::$locales['Top writer: comments'] = 'Top rédacteur : commentaires';
l10n::$locales['Period:'] = 'Période :'; L10n::$locales['Top writer: entries'] = 'Top rédacteur : billets';
l10n::$locales['Limit:'] = 'Limite :'; L10n::$locales['Top writer: comments'] = 'Top rédacteur : commentaires';
l10n::$locales['List users who write more comments'] = 'Liste les utilisateurs qui ont écrit le plus de commentaires'; L10n::$locales['Period:'] = 'Période :';
l10n::$locales['Top comments'] = 'Top commentaires'; L10n::$locales['Limit:'] = 'Limite :';
l10n::$locales['Exclude post writer from list'] = 'Exclure de la liste les auteurs de billets'; L10n::$locales['List users who write more comments'] = 'Liste les utilisateurs qui ont écrit le plus de commentaires';
l10n::$locales['List users who write more posts'] = 'Liste les utilisateurs qui ont écrit le plus de billets'; L10n::$locales['Top comments'] = 'Top commentaires';
l10n::$locales['Top entries'] = 'Top billets'; L10n::$locales['Exclude post writer from list'] = 'Exclure de la liste les auteurs de billets';
l10n::$locales['Author posts'] = 'Billets de l\'auteur'; L10n::$locales['List users who write more posts'] = 'Liste les utilisateurs qui ont écrit le plus de billets';
l10n::$locales['Author link'] = 'Lien vers l\'auteur'; L10n::$locales['Top entries'] = 'Top billets';
l10n::$locales['no entries'] = 'aucun billet'; L10n::$locales['Author posts'] = 'Billets de l\'auteur';
l10n::$locales['one entry'][0] = 'une publication'; L10n::$locales['Author link'] = 'Lien vers l\'auteur';
l10n::$locales['one entry'][1] = '%s publications'; L10n::$locales['no entries'] = 'aucun billet';
l10n::$locales['no comments'] = 'aucun commentaire'; L10n::$locales['one entry'] = [
l10n::$locales['one comment'][0] = 'un commentaire'; 'une publication',
l10n::$locales['one comment'][1] = '%s commentaires'; '%s publications',
l10n::$locales['last day'] = 'dernier jour'; ];
l10n::$locales['last week'] = 'dernière semaine'; L10n::$locales['no comments'] = 'aucun commentaire';
l10n::$locales['last month'] = 'dernier mois'; L10n::$locales['one comment'] = [
l10n::$locales['last year'] = 'dernière année'; 'un commentaire',
l10n::$locales['from begining'] = 'depuis le début'; '%s commentaires',
l10n::$locales['Ranking of the most prolific writers and/or commentators'] = 'Classement des plus prolifiques rédacteurs et/ou commentateurs'; ];
L10n::$locales['last day'] = 'dernier jour';
L10n::$locales['last week'] = 'dernière semaine';
L10n::$locales['last month'] = 'dernier mois';
L10n::$locales['last year'] = 'dernière année';
L10n::$locales['from begining'] = 'depuis le début';
L10n::$locales['Ranking of the most prolific writers and/or commentators'] = 'Classement des plus prolifiques rédacteurs et/ou commentateurs';

View File

@ -16,8 +16,16 @@ namespace Dotclear\Plugin\topWriter;
use ArrayObject; use ArrayObject;
use dcCore; use dcCore;
use form; use Dotclear\Helper\Html\Form\{
use html; Checkbox,
Div,
Label,
Number,
Para,
Select,
Text
};
use Dotclear\Helper\Html\Html;
/** /**
* @ingroup DC_PLUGIN_TOPWRITER * @ingroup DC_PLUGIN_TOPWRITER
@ -41,7 +49,7 @@ class BackendBehaviors
# Display # Display
$__dashboard_items[0][] = '<div class="box small" id="topWriterPostsItems">' . $__dashboard_items[0][] = '<div class="box small" id="topWriterPostsItems">' .
'<h3>' . html::escapeHTML(__('Top writer: entries')) . '</h3>' . '<h3>' . Html::escapeHTML(__('Top writer: entries')) . '</h3>' .
'<ul>' . implode('', $li) . '</ul>' . '<ul>' . implode('', $li) . '</ul>' .
'</div>'; '</div>';
} }
@ -58,7 +66,7 @@ class BackendBehaviors
# Display # Display
$__dashboard_items[0][] = '<div class="box small" id="topWriterCommentsItems">' . $__dashboard_items[0][] = '<div class="box small" id="topWriterCommentsItems">' .
'<h3>' . html::escapeHTML(__('Top writer: comments')) . '</h3>' . '<h3>' . Html::escapeHTML(__('Top writer: comments')) . '</h3>' .
'<ul>' . implode('', $li) . '</ul>' . '<ul>' . implode('', $li) . '</ul>' .
'</div>'; '</div>';
} }
@ -70,31 +78,47 @@ class BackendBehaviors
$pref = self::setDefaultPref(); $pref = self::setDefaultPref();
echo echo
'<div class="fieldset">' . (new Div())->items([
'<h4>' . __('Top writer: entries') . '</h4>' . (new Div())->class('fieldset')->items([
'<p><label class="classic" for="topWriterPostsItems">' . (new Text('h4', __('Top writer: entries'))),
form::checkbox('topWriterPostsItems', 1, $pref['topWriterPostsItems']) . ' ' . (new Para())->items([
__('Show') . '</label></p>' . (new Checkbox('topWriterPostsItems', $pref['topWriterPostsItems']))->value(1),
'<p><label class="classic" for="topWriterPostsPeriod">' . __('Period:') . ' </label>' . (new Label(__('Show'), Label::OUTSIDE_LABEL_AFTER))->for('topWriterPostsItems')->class('classic'),
form::combo('topWriterPostsPeriod', Utils::periods(), $pref['topWriterPostsPeriod']) . '</p>' . ]),
'<p><label class="classic" for="topWriterPostsLimit">' . __('Limit:') . ' </label>' . (new Para())->class('field')->items([
form::number('topWriterPostsLimit', ['min' => 1, 'max' => 20, 'default' => $pref['topWriterPostsLimit']]) . '</p>' . (new Label(__('Period:'), Label::OUTSIDE_LABEL_BEFORE))->for('topWriterPostsPeriod'),
'</div>' . (new Select('topWriterPostsPeriod'))->default($pref['topWriterPostsPeriod'])->items(Utils::periods()),
]),
'<div class="fieldset">' . (new Para())->class('field')->items([
'<h4>' . __('Top writer: comments') . '</h4>' . (new Label(__('Limit:'), Label::OUTSIDE_LABEL_BEFORE))->for('topWriterPostsLimit'),
'<p><label class="classic" for="topWriterCommentsItems">' . (new Number('topWriterPostsLimit'))->min(1)->max(20)->value($pref['topWriterPostsLimit']),
form::checkbox('topWriterCommentsItems', 1, $pref['topWriterCommentsItems']) . ' ' . ]),
__('Show') . '</label></p>' . ]),
'<p><label class="classic" for="topWriterCommentsPeriod">' . __('Period:') . ' </label>' . (new Div())->class('fieldset')->items([
form::combo('topWriterCommentsPeriod', Utils::periods(), $pref['topWriterCommentsPeriod']) . '</p>' . (new Text('h4', __('Top writer: comments'))),
'<p><label class="classic" for="topWriterCommentsLimit">' . __('Limit:') . ' </label>' . (new Para())->items([
form::number('topWriterCommentsLimit', ['min' => 1, 'max' => 20, 'default' => $pref['topWriterCommentsLimit']]) . '</p>' . (new Checkbox('topWriterCommentsItems', $pref['topWriterCommentsItems']))->value(1),
'</div>'; (new Label(__('Show'), Label::OUTSIDE_LABEL_AFTER))->for('topWriterCommentsItems')->class('classic'),
]),
(new Para())->class('field')->items([
(new Label(__('Period:'), Label::OUTSIDE_LABEL_BEFORE))->for('topWriterCommentsPeriod'),
(new Select('topWriterCommentsPeriod'))->default($pref['topWriterCommentsPeriod'])->items(Utils::periods()),
]),
(new Para())->class('field')->items([
(new Label(__('Limit:'), Label::OUTSIDE_LABEL_BEFORE))->for('topWriterCommentsLimit'),
(new Number('topWriterCommentsLimit'))->min(1)->max(20)->value($pref['topWriterCommentsLimit']),
]),
]),
])->render();
} }
public static function adminAfterDashboardOptionsUpdate(?string $user_id): void public static function adminAfterDashboardOptionsUpdate(?string $user_id): void
{ {
// nullsafe
if (is_null(dcCore::app()->auth) || is_null(dcCore::app()->auth->user_prefs)) {
return;
}
dcCore::app()->auth->user_prefs->get('dashboard')->put( dcCore::app()->auth->user_prefs->get('dashboard')->put(
'topWriterPostsItems', 'topWriterPostsItems',
!empty($_POST['topWriterPostsItems']), !empty($_POST['topWriterPostsItems']),
@ -130,6 +154,11 @@ class BackendBehaviors
private static function setDefaultPref(): array private static function setDefaultPref(): array
{ {
// nullsafe
if (is_null(dcCore::app()->auth) || is_null(dcCore::app()->auth->user_prefs)) {
return [];
}
if (!dcCore::app()->auth->user_prefs->get('dashboard')->prefExists('topWriterPostsItems')) { if (!dcCore::app()->auth->user_prefs->get('dashboard')->prefExists('topWriterPostsItems')) {
dcCore::app()->auth->user_prefs->get('dashboard')->put( dcCore::app()->auth->user_prefs->get('dashboard')->put(
'topWriterPostsItems', 'topWriterPostsItems',

View File

@ -18,42 +18,69 @@ use dcAuth;
use dcBlog; use dcBlog;
use dcCore; use dcCore;
use dcUtils; use dcUtils;
use dt; use Dotclear\Database\Statement\{
JoinStatement,
SelectStatement
};
use Dotclear\Helper\Date;
class Utils class Utils
{ {
public static function posts(string $period, int $limit, bool $sort_desc = true): array public static function posts(string $period, int $limit, bool $sort_desc = true): array
{ {
$req = 'SELECT COUNT(*) AS count, U.user_id ' . // nullsafe
'FROM ' . dcCore::app()->prefix . dcBlog::POST_TABLE_NAME . ' P ' . if (is_null(dcCore::app()->blog)) {
'INNER JOIN ' . dcCore::app()->prefix . dcAuth::USER_TABLE_NAME . ' U ON U.user_id = P.user_id ' . return [];
"WHERE blog_id='" . dcCore::app()->con->escape(dcCore::app()->blog->id) . "' " . }
'AND post_status=1 AND user_status=1 ' .
self::period('post_dt', $period) .
'GROUP BY U.user_id ' .
'ORDER BY count ' . ($sort_desc ? 'DESC' : 'ASC') . ' , U.user_id ASC ' .
dcCore::app()->con->limit(abs((int) $limit));
$rs = dcCore::app()->con->select($req); $sql = new SelectStatement();
if ($rs->isEmpty()) { $sql
->from($sql->as(dcCore::app()->prefix . dcBlog::POST_TABLE_NAME, 'P'))
->columns([
$sql->count('*', 'count'),
'U.user_id',
])
->join(
(new JoinStatement())
->inner()
->from($sql->as(dcCore::app()->prefix . dcAuth::USER_TABLE_NAME, 'U'))
->on('U.user_id = P.user_id')
->statement()
)
->where('blog_id = ' . $sql->quote(dcCore::app()->blog->id))
->and('post_status = ' . dcBlog::POST_PUBLISHED)
->and('user_status = 1')
->group('U.user_id')
->order('count ' . ($sort_desc ? 'DESC' : 'ASC') . ' , U.user_id ASC')
->limit(abs((int) $limit));
self::period($sql, $period, 'post_dt');
$rs = $sql->select();
if (is_null($rs) || $rs->isEmpty()) {
return []; return [];
} }
$res = []; $res = [];
$i = 0; $i = 0;
while ($rs->fetch()) { while ($rs->fetch()) {
$user = dcCore::app()->con->select( $sql = new SelectStatement();
'SELECT * FROM ' . dcCore::app()->prefix . dcAuth::USER_TABLE_NAME . " WHERE user_id='" . $rs->user_id . "' " $user = $sql
); ->from(dcCore::app()->prefix . dcAuth::USER_TABLE_NAME)
if ($user->isEmpty()) { ->column('*')
->where('user_id = ' . $sql->quote($rs->f('user_id')))
->select();
if (is_null($user) || $user->isEmpty()) {
continue; continue;
} }
$author = dcUtils::getUserCN( $author = dcUtils::getUserCN(
$user->user_id, $user->f('user_id'),
$user->user_name, $user->f('user_name'),
$user->user_firstname, $user->f('user_firstname'),
$user->user_displayname $user->f('user_displayname')
); );
if (empty($author)) { if (empty($author)) {
continue; continue;
@ -62,18 +89,18 @@ class Utils
$i++; $i++;
if (dcCore::app()->blog->settings->get('authormode')->get('authormode_active')) { if (dcCore::app()->blog->settings->get('authormode')->get('authormode_active')) {
$res[$i]['author_link'] = '<a href="' . $res[$i]['author_link'] = '<a href="' .
dcCore::app()->blog->url . dcCore::app()->url->getBase('author') . '/' . $user->user_id . '" ' . dcCore::app()->blog->url . dcCore::app()->url->getBase('author') . '/' . $user->f('user_id') . '" ' .
'title="' . __('Author posts') . '">' . $author . '</a>'; 'title="' . __('Author posts') . '">' . $author . '</a>';
} elseif ($user->user_url) { } elseif ($user->f('user_url')) {
$res[$i]['author_link'] = '<a href="' . $user->user_url . '" title="' . $res[$i]['author_link'] = '<a href="' . $user->f('user_url') . '" title="' .
__('Author link') . '">' . $author . '</a>'; __('Author link') . '">' . $author . '</a>';
} }
$res[$i]['author'] = $author; $res[$i]['author'] = $author;
if ($rs->count == 0) { if ((int) $rs->f('count') == 0) {
$res[$i]['count'] = __('no entries'); $res[$i]['count'] = __('no entries');
} else { } else {
$res[$i]['count'] = sprintf(__('one entry', '%s entries', (int) $rs->count), $rs->count); $res[$i]['count'] = sprintf(__('one entry', '%s entries', (int) $rs->f('count')), $rs->f('count'));
} }
} }
@ -82,63 +109,87 @@ class Utils
public static function comments(string $period, int $limit, bool $sort_desc = true, bool $exclude = false): array public static function comments(string $period, int $limit, bool $sort_desc = true, bool $exclude = false): array
{ {
$req = 'SELECT COUNT(*) AS count, comment_email ' . // nullsafe
'FROM ' . dcCore::app()->prefix . dcBlog::POST_TABLE_NAME . ' P, ' . dcCore::app()->prefix . dcBlog::COMMENT_TABLE_NAME . ' C ' . if (is_null(dcCore::app()->blog)) {
'WHERE P.post_id=C.post_id ' . return [];
"AND blog_id='" . dcCore::app()->con->escape(dcCore::app()->blog->id) . "' " .
'AND post_status=1 AND comment_status=1 ' .
self::period('comment_dt', $period);
if ($exclude) {
$req .= 'AND comment_email NOT IN (' .
' SELECT U.user_email ' .
' FROM ' . dcCore::app()->prefix . dcAuth::USER_TABLE_NAME . ' U' .
' INNER JOIN ' . dcCore::app()->prefix . dcBlog::POST_TABLE_NAME . ' P ON P.user_id = U.user_id ' .
" WHERE blog_id='" . dcCore::app()->con->escape(dcCore::app()->blog->id) . "' " .
' GROUP BY U.user_email) ';
} }
$req .= 'GROUP BY comment_email ' . $sql = new SelectStatement();
'ORDER BY count ' . ($sort_desc ? 'DESC' : 'ASC') . ' ' . $sql
dcCore::app()->con->limit(abs((int) $limit)); ->from($sql->as(dcCore::app()->prefix . dcBlog::POST_TABLE_NAME, 'P'))
->from($sql->as(dcCore::app()->prefix . dcBlog::COMMENT_TABLE_NAME, 'C'))
->columns([
$sql->count('*', 'count'),
'comment_email',
])
->where('blog_id = ' . $sql->quote(dcCore::app()->blog->id))
->and('P.post_id = C.post_id')
->and('post_status = ' . dcBlog::POST_PUBLISHED)
->and('comment_status = ' . dcBlog::COMMENT_PUBLISHED)
->group('comment_email')
->order('count ' . ($sort_desc ? 'DESC' : 'ASC'))
->limit(abs((int) $limit))
;
$rs = dcCore::app()->con->select($req); self::period($sql, $period, 'comment_dt');
if ($rs->isEmpty()) {
if ($exclude) {
$sql->and('comment_email NOT IN (' .
(new SelectStatement())
->from($sql->as(dcCore::app()->prefix . dcAuth::USER_TABLE_NAME, 'U'))
->column('U.user_email')
->join(
(new JoinStatement())
->inner()
->from($sql->as(dcCore::app()->prefix . dcBlog::POST_TABLE_NAME, 'P'))
->on('P.user_id = U.user_id')
->statement()
)
->where('blog_id = ' . $sql->quote(dcCore::app()->blog->id))
->group('U.user_email')
->statement() .
')');
}
$rs = $sql->select();
if (is_null($rs) || $rs->isEmpty()) {
return []; return [];
} }
$res = []; $res = [];
$i = 0; $i = 0;
while ($rs->fetch()) { while ($rs->fetch()) {
$user = dcCore::app()->con->select( $sql = new SelectStatement();
'SELECT * FROM ' . dcCore::app()->prefix . dcBlog::COMMENT_TABLE_NAME . ' ' . $user = $sql
"WHERE comment_email='" . $rs->comment_email . "' " . ->from(dcCore::app()->prefix . dcBlog::COMMENT_TABLE_NAME)
'ORDER BY comment_dt DESC' ->column('*')
); ->where('comment_email = ' . $sql->quote($rs->f('comment_email')))
->order('comment_dt DESC')
->select();
if (!$user->comment_author) { if (is_null($user) || !$user->f('comment_author')) {
continue; continue;
} }
$i++; $i++;
if ($user->comment_site) { if ($user->f('comment_site')) {
$res[$i]['author_link'] = '<a href="' . $user->comment_site . '" title="' . $res[$i]['author_link'] = '<a href="' . $user->f('comment_site') . '" title="' .
__('Author link') . '">' . $user->comment_author . '</a>'; __('Author link') . '">' . $user->f('comment_author') . '</a>';
} }
$res[$i]['author'] = $user->comment_author; $res[$i]['author'] = $user->f('comment_author');
if ($rs->count == 0) { if ((int) $rs->f('count') == 0) {
$res[$i]['count'] = __('no comments'); $res[$i]['count'] = __('no comments');
} else { } else {
$res[$i]['count'] = sprintf(__('one comment', '%s comments', (int) $rs->count), $rs->count); $res[$i]['count'] = sprintf(__('one comment', '%s comments', (int) $rs->f('count')), $rs->f('count'));
} }
} }
return $i ? $res : []; return $i ? $res : [];
} }
private static function period(string $field, string $period): string private static function period(SelectStatement $sql, string $period, string $field): void
{ {
$pattern = '%Y-%m-%d %H:%M:%S'; $pattern = '%Y-%m-%d %H:%M:%S';
$time = 0; $time = 0;
@ -164,10 +215,10 @@ class Utils
break; break;
default: default:
return ''; return;
} }
return "AND $field > TIMESTAMP '" . dt::str($pattern, time() - $time) . "' "; $sql->and($field . ' > TIMESTAMP ' . $sql->quote(Date::str($pattern, time() - $time)));
} }
public static function periods(): array public static function periods(): array

View File

@ -15,9 +15,9 @@ declare(strict_types=1);
namespace Dotclear\Plugin\topWriter; namespace Dotclear\Plugin\topWriter;
use dcCore; use dcCore;
use Dotclear\Helper\Html\Html;
use Dotclear\Plugin\widgets\WidgetsStack; use Dotclear\Plugin\widgets\WidgetsStack;
use Dotclear\Plugin\widgets\WidgetsElement; use Dotclear\Plugin\widgets\WidgetsElement;
use html;
class Widgets class Widgets
{ {
@ -133,7 +133,7 @@ class Widgets
(bool) $w->content_only, (bool) $w->content_only,
'topcomments ' . $w->class, 'topcomments ' . $w->class,
'', '',
($w->title ? $w->renderTitle(html::escapeHTML($w->title)) : '') . ($w->title ? $w->renderTitle(Html::escapeHTML($w->title)) : '') .
sprintf('<ul>%s</ul>', implode('', self::lines($lines, 'comments', $w->text))) sprintf('<ul>%s</ul>', implode('', self::lines($lines, 'comments', $w->text)))
); );
} }
@ -153,7 +153,7 @@ class Widgets
(bool) $w->content_only, (bool) $w->content_only,
'topentries ' . $w->class, 'topentries ' . $w->class,
'', '',
($w->title ? $w->renderTitle(html::escapeHTML($w->title)) : '') . ($w->title ? $w->renderTitle(Html::escapeHTML($w->title)) : '') .
sprintf('<ul>%s</ul>', implode('', self::lines($lines, 'posts', $w->text))) sprintf('<ul>%s</ul>', implode('', self::lines($lines, 'posts', $w->text)))
); );
} }