add top writer to dashboard

This commit is contained in:
Jean-Christian Paul Denis 2021-11-06 21:15:51 +01:00
parent f5f2a9636a
commit acce51b890
Signed by: JcDenis
GPG Key ID: 1B5B8C5B90B6C951
8 changed files with 486 additions and 250 deletions

View File

@ -1,6 +1,8 @@
0.8.2 - dev
dev
- [ ] literal rank of writer depending of num of com
- [ ] add dashboard module
- add dashboard module
- update translations
- update to PSR12
0.8.1 - 2021.09.17
- fix plurals and better translation

View File

@ -10,9 +10,193 @@
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_CONTEXT_ADMIN')) {
return null;
}
require dirname(__FILE__) . '/_widgets.php';
# Dashboard item and user preference
$core->addBehavior(
'adminDashboardItems',
['topWriterAdmin', 'adminDashboardItems']
);
$core->addBehavior(
'adminDashboardOptionsForm',
['topWriterAdmin', 'adminDashboardOptionsForm']
);
$core->addBehavior(
'adminAfterDashboardOptionsUpdate',
['topWriterAdmin', 'adminAfterDashboardOptionsUpdate']
);
/**
* @ingroup DC_PLUGIN_TOPWRITER
* @brief Display most active users - admin methods.
* @since 2.6
*/
class topWriterAdmin
{
public static function adminDashboardItems(dcCore $core, $__dashboard_items)
{
$pref = self::setDefaultPref($core);
# top posts
if ($pref['topWriterPostsItems']) {
$lines = topWriter::posts($core, $pref['topWriterPostsPeriod'], $pref['topWriterPostsLimit']);
if (empty($lines)) {
return null;
}
$li = [];
foreach ($lines as $k => $line) {
$li[] = sprintf('<li><strong>%s</strong> %s (%s)</li>', $k, $line['author'], $line['count']);
}
# Display
$__dashboard_items[0][] = '<div class="box small" id="topWriterPostsItems">' .
'<h3>' . html::escapeHTML(__('Top writer: entries')) . '</h3>' .
'<ul>' . implode('', $li) . '</ul>' .
'</div>';
}
# top comments
if ($pref['topWriterCommentsItems']) {
$lines = topWriter::comments($core, $pref['topWriterCommentsPeriod'], $pref['topWriterCommentsLimit']);
if (empty($lines)) {
return null;
}
$li = [];
foreach ($lines as $k => $line) {
$li[] = sprintf('<li><strong>%s</strong> %s (%s)</li>', $k, $line['author'], $line['count']);
}
# Display
$__dashboard_items[0][] = '<div class="box small" id="topWriterCommentsItems">' .
'<h3>' . html::escapeHTML(__('Top writer: comments')) . '</h3>' .
'<ul>' . implode('', $li) . '</ul>' .
'</div>';
}
}
public static function adminDashboardOptionsForm(dcCore $core)
{
$pref = self::setDefaultPref($core);
echo
'<div class="fieldset">' .
'<h4>' . __('Top writer: entries') . '</h4>' .
'<p><label class="classic" for="topWriterPostsItems">' .
form::checkbox('topWriterPostsItems', 1, $pref['topWriterPostsItems']) . ' ' .
__('Show') . '</label></p>' .
'<p><label class="classic" for="topWriterPostsPeriod">' . __('Period:') . ' </label>' .
form::combo('topWriterPostsPeriod', topWriter::periods(), $pref['topWriterPostsPeriod']) . '</p>' .
'<p><label class="classic" for="topWriterPostsLimit">' . __('Limit:') . ' </label>' .
form::number('topWriterPostsLimit', ['min' => 1, 'max' => 20, 'default' => $pref['topWriterPostsLimit']]) . '</p>' .
'</div>' .
'<div class="fieldset">' .
'<h4>' . __('Top writer: comments') . '</h4>' .
'<p><label class="classic" for="topWriterCommentsItems">' .
form::checkbox('topWriterCommentsItems', 1, $pref['topWriterCommentsItems']) . ' ' .
__('Show') . '</label></p>' .
'<p><label class="classic" for="topWriterCommentsPeriod">' . __('Period:') . ' </label>' .
form::combo('topWriterCommentsPeriod', topWriter::periods(), $pref['topWriterCommentsPeriod']) . '</p>' .
'<p><label class="classic" for="topWriterCommentsLimit">' . __('Limit:') . ' </label>' .
form::number('topWriterCommentsLimit', ['min' => 1, 'max' => 20, 'default' => $pref['topWriterCommentsLimit']]) . '</p>' .
'</div>';
}
public static function adminAfterDashboardOptionsUpdate($user_id)
{
global $core;
$core->auth->user_prefs->dashboard->put(
'topWriterPostsItems',
!empty($_POST['topWriterPostsItems']),
'boolean'
);
$core->auth->user_prefs->dashboard->put(
'topWriterPostsPeriod',
(string) $_POST['topWriterPostsPeriod'],
'string'
);
$core->auth->user_prefs->dashboard->put(
'topWriterPostsLimit',
(int) $_POST['topWriterPostsLimit'],
'integer'
);
$core->auth->user_prefs->dashboard->put(
'topWriterCommentsItems',
!empty($_POST['topWriterCommentsItems']),
'boolean'
);
$core->auth->user_prefs->dashboard->put(
'topWriterCommentsPeriod',
(string) $_POST['topWriterCommentsPeriod'],
'string'
);
$core->auth->user_prefs->dashboard->put(
'topWriterCommentsLimit',
(int) $_POST['topWriterCommentsLimit'],
'integer'
);
}
private static function setDefaultPref($core)
{
if (!$core->auth->user_prefs->dashboard->prefExists('topWriterPostsItems')) {
$core->auth->user_prefs->dashboard->put(
'topWriterPostsItems',
false,
'boolean'
);
}
if (!$core->auth->user_prefs->dashboard->prefExists('topWriterPostsPeriod')) {
$core->auth->user_prefs->dashboard->put(
'topWriterPostsPeriod',
'month',
'string'
);
}
if (!$core->auth->user_prefs->dashboard->prefExists('topWriterPostsLimit')) {
$core->auth->user_prefs->dashboard->put(
'topWriterPostsLimit',
10,
'integer'
);
}
if (!$core->auth->user_prefs->dashboard->prefExists('topWriterCommentsItems')) {
$core->auth->user_prefs->dashboard->put(
'topWriterCommentsItems',
false,
'boolean'
);
}
if (!$core->auth->user_prefs->dashboard->prefExists('topWriterCommentsPeriod')) {
$core->auth->user_prefs->dashboard->put(
'topWriterCommentsPeriod',
'month',
'string'
);
}
if (!$core->auth->user_prefs->dashboard->prefExists('topWriterCommentsLimit')) {
$core->auth->user_prefs->dashboard->put(
'topWriterCommentsLimit',
10,
'integer'
);
}
return [
'topWriterPostsItems' => $core->auth->user_prefs->dashboard->get('topWriterPostsItems'),
'topWriterPostsPeriod' => $core->auth->user_prefs->dashboard->get('topWriterPostsPeriod'),
'topWriterPostsLimit' => $core->auth->user_prefs->dashboard->get('topWriterPostsLimit') ?? 10,
'topWriterCommentsItems' => $core->auth->user_prefs->dashboard->get('topWriterCommentsItems'),
'topWriterCommentsPeriod' => $core->auth->user_prefs->dashboard->get('topWriterCommentsPeriod'),
'topWriterCommentsLimit' => $core->auth->user_prefs->dashboard->get('topWriterCommentsLimit') ?? 10
];
}
}

View File

@ -10,7 +10,6 @@
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return null;
}

17
_prepend.php Normal file
View File

@ -0,0 +1,17 @@
<?php
/**
* @brief topWriter, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis, Pierre Van Glabeke
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return;
}
$__autoload['topWriter'] = dirname(__FILE__) . '/inc/class.topwriter.php';

View File

@ -10,7 +10,6 @@
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return null;
}

View File

@ -10,7 +10,6 @@
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return null;
}
@ -21,7 +20,7 @@ class topWriterWidget
{
public static function init($w)
{
#Top comments widget
#Top comments widget
$w
->create(
'topcom',
@ -33,7 +32,7 @@ class topWriterWidget
->addTitle(__('Top comments'))
->setting(
'text',
__('Text:'),
__('Text:') . ' (%rank%, %author%, %count%)',
'%author% (%count%)',
'text'
)
@ -42,13 +41,7 @@ class topWriterWidget
__('Period:'),
'year',
'combo',
[
__('day') => 'day',
__('week') => 'week',
__('month') => 'month',
__('year') => 'year',
__('from begining') => ''
]
topWriter::periods()
)
->setting(
'sort',
@ -56,8 +49,8 @@ class topWriterWidget
'desc',
'combo',
[
__('Ascending') => 'asc',
__('Descending') => 'desc'
__('Ascending') => 'asc',
__('Descending') => 'desc'
]
)
->setting(
@ -89,7 +82,7 @@ class topWriterWidget
->addTitle(__('Top entries'))
->setting(
'text',
__('Text:'),
__('Text:') . ' (%rank%, %author%, %count%)',
'%author% (%count%)',
'text'
)
@ -98,21 +91,16 @@ class topWriterWidget
__('Period:'),
'year',
'combo',
[
__('day') => 'day',
__('week') => 'week',
__('month') => 'month',
__('year') => 'year',
__('from begining') => ''
]
topWriter::periods()
)
->setting(
'sort',
__('Sort:'),'desc',
__('Sort:'),
'desc',
'combo',
[
__('Ascending') => 'asc',
__('Descending') => 'desc'
__('Ascending') => 'asc',
__('Descending') => 'desc'
]
)
->setting(
@ -131,85 +119,15 @@ class topWriterWidget
{
global $core;
if ($w->offline) {
if ($w->offline
|| ($w->homeonly == 1 && !$core->url->isHome($core->url->type))
|| ($w->homeonly == 2 && $core->url->isHome($core->url->type))
) {
return null;
}
if (($w->homeonly == 1 && !$core->url->isHome($core->url->type))
|| ($w->homeonly == 2 && $core->url->isHome($core->url->type))) {
return null;
}
$req =
'SELECT COUNT(*) AS count, comment_email ' .
"FROM " . $core->prefix . "post P, " . $core->prefix . "comment C " .
'WHERE P.post_id=C.post_id ' .
"AND blog_id='" . $core->con->escape($core->blog->id) . "' " .
'AND post_status=1 AND comment_status=1 ' .
self::period('comment_dt', $w->period);
if ($w->exclude) {
$req .=
'AND comment_email NOT IN (' .
' SELECT U.user_email ' .
' FROM ' . $core->prefix . 'user U' .
' INNER JOIN ' . $core->prefix . 'post P ON P.user_id = U.user_id ' .
" WHERE blog_id='" . $core->con->escape($core->blog->id) . "' " .
' GROUP BY U.user_email) ';
}
$req .=
'GROUP BY comment_email ' .
'ORDER BY count ' . ($w->sort == 'asc' ? 'ASC' : 'DESC') . ' ' .
$core->con->limit(abs((integer) $w->limit));
$rs = $core->con->select($req);
if ($rs->isEmpty()) {
return null;
}
$content = '';
$i = 0;
while($rs->fetch()) {
$user = $core->con->select(
"SELECT * FROM " . $core->prefix . "comment " .
"WHERE comment_email='" . $rs->comment_email . "' " .
'ORDER BY comment_dt DESC'
);
if (!$user->comment_author) {
continue;
}
$i++;
$rank = '<span class="topcomments-rank">' . $i . '</span>';
if ($user->comment_site) {
$author = '<a href="' . $user->comment_site . '" title="' .
__('Author link') . '">' . $user->comment_author . '</a>';
} else {
$author = $user->comment_author;
}
$author = '<span class="topcomments-author">' . $author . '</span>';
if ($rs->count == 0) {
$count = __('no comments');
} else {
$count = sprintf(__('one comment', '%s comments', $rs->count), $rs->count);
}
$content .= sprintf(
'<li>%s</li>',
str_replace(
['%rank%', '%author%', '%count%'],
[$rank, $author, $count],
$w->text
)
);
}
if ($i < 1) {
$lines = topWriter::comments($core, $w->period, $w->limit, $w->sort == 'desc', $w->exclude);
if (empty($lines)) {
return null;
}
@ -218,7 +136,7 @@ class topWriterWidget
'topcomments ' . $w->class,
'',
($w->title ? $w->renderTitle(html::escapeHTML($w->title)) : '') .
sprintf('<ul>%s</ul>', $content)
sprintf('<ul>%s</ul>', implode('', self::lines($lines, 'comments', $w->text)))
);
}
@ -226,80 +144,15 @@ class topWriterWidget
{
global $core;
if ($w->offline) {
if ($w->offline
|| ($w->homeonly == 1 && !$core->url->isHome($core->url->type))
|| ($w->homeonly == 2 && $core->url->isHome($core->url->type))
) {
return null;
}
if (($w->homeonly == 1 && !$core->url->isHome($core->url->type))
|| ($w->homeonly == 2 && $core->url->isHome($core->url->type))) {
return null;
}
$rs = $core->con->select(
'SELECT COUNT(*) AS count, U.user_id ' .
"FROM " . $core->prefix . "post P " .
'INNER JOIN ' . $core->prefix . 'user U ON U.user_id = P.user_id ' .
"WHERE blog_id='" . $core->con->escape($core->blog->id) . "' " .
'AND post_status=1 AND user_status=1 ' .
self::period('post_dt', $w->period) .
'GROUP BY U.user_id ' .
'ORDER BY count ' . ($w->sort == 'asc' ? 'ASC' : 'DESC') . ', U.user_id ASC ' .
$core->con->limit(abs((integer) $w->limit)));
if ($rs->isEmpty()) {
return null;
}
$content = '';
$i = 0;
while($rs->fetch()) {
$user = $core->con->select(
"SELECT * FROM " . $core->prefix . "user WHERE user_id='" . $rs->user_id . "' "
);
$author = dcUtils::getUserCN(
$user->user_id,
$user->user_name,
$user->user_firstname,
$user->user_displayname
);
if (empty($author)) {
continue;
}
$i++;
$rank = '<span class="topentries-rank">' . $i . '</span>';
$core->blog->settings->addNamespace('authormode');
if ($core->blog->settings->authormode->authormode_active) {
$author = '<a href="' .
$core->blog->url . $core->url->getBase("author") . '/' . $user->user_id . '" ' .
'title="' . __('Author posts') . '">' . $author . '</a>';
}
elseif ($user->user_url) {
$author = '<a href="' . $user->user_url . '" title="' .
__('Author link') . '">' . $author . '</a>';
}
$author = '<span class="topentries-author">' . $author . '</span>';
if ($rs->count == 0) {
$count = __('no entries');
} else {
$count = sprintf(__('one entry', '%s entries', $rs->count), $rs->count);
}
$content .= sprintf(
'<li>%s</li>',
str_replace(
['%rank%', '%author%', '%count%'],
[$rank, $author, $count],
$w->text
)
);
}
if ($i < 1) {
$lines = topWriter::posts($core, $w->period, $w->limit, $w->sort == 'desc');
if (empty($lines)) {
return null;
}
@ -308,35 +161,26 @@ class topWriterWidget
'topentries ' . $w->class,
'',
($w->title ? $w->renderTitle(html::escapeHTML($w->title)) : '') .
sprintf('<ul>%s</ul>', $content)
sprintf('<ul>%s</ul>', implode('', self::lines($lines, 'posts', $w->text)))
);
}
private static function period($t, $p)
private static function lines($lines, $id, $text)
{
$pat = '%Y-%m-%d %H:%M:%S';
switch($p) {
case 'day':
return
"AND $t > TIMESTAMP '" . dt::str($pat, time() - 3600*24) . "' ";
break;
case 'week':
return
"AND $t > TIMESTAMP '" . dt::str($pat, time() - 3600*24*7) . "' ";
break;
case 'month':
return
"AND $t > TIMESTAMP '" . dt::str($pat, time() - 3600*24*30) . "' ";
break;
case 'year':
return
"AND $t > TIMESTAMP '" . dt::str($pat, time() - 3600*24*30*12) . "' ";
break;
$li = [];
foreach ($lines as $k => $line) {
$rank = '<span class="top' . $id . '-rank">' . $k . '</span>';
$author = '<span class="top' . $id . '-author">' . (empty($line['author_link']) ? $line['author'] : $line['author_link']) . '</span>';
$li[] = sprintf(
'<li>%s</li>',
str_replace(
['%rank%', '%author%', '%count%'],
[$rank, $author, $line['count']],
$text
)
);
}
return '';
return $li;
}
}

189
inc/class.topwriter.php Normal file
View File

@ -0,0 +1,189 @@
<?php
/**
* @brief topWriter, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis, Pierre Van Glabeke
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return;
}
class topWriter
{
public static function posts(dcCore $core, string $period, int $limit, bool $sort_desc = true)
{
$req = 'SELECT COUNT(*) AS count, U.user_id ' .
'FROM ' . $core->prefix . 'post P ' .
'INNER JOIN ' . $core->prefix . 'user U ON U.user_id = P.user_id ' .
"WHERE blog_id='" . $core->con->escape($core->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 ' .
$core->con->limit(abs((int) $limit));
$rs = $core->con->select($req);
if ($rs->isEmpty()) {
return null;
}
$core->blog->settings->addNamespace('authormode');
$res = [];
$i = 0;
while ($rs->fetch()) {
$user = $core->con->select(
'SELECT * FROM ' . $core->prefix . "user WHERE user_id='" . $rs->user_id . "' "
);
if ($user->isEmpty()) {
continue;
}
$author = dcUtils::getUserCN(
$user->user_id,
$user->user_name,
$user->user_firstname,
$user->user_displayname
);
if (empty($author)) {
continue;
}
$i++;
if ($core->blog->settings->authormode->authormode_active) {
$res[$i]['author_link'] = '<a href="' .
$core->blog->url . $core->url->getBase('author') . '/' . $user->user_id . '" ' .
'title="' . __('Author posts') . '">' . $author . '</a>';
} elseif ($user->user_url) {
$res[$i]['author_link'] = '<a href="' . $user->user_url . '" title="' .
__('Author link') . '">' . $author . '</a>';
}
$res[$i]['author'] = $author;
if ($rs->count == 0) {
$res[$i]['count'] = __('no entries');
} else {
$res[$i]['count'] = sprintf(__('one entry', '%s entries', $rs->count), $rs->count);
}
}
if (!$i) {
return null;
}
return $res;
}
public static function comments(dcCore $core, string $period, int $limit, bool $sort_desc = true, $exclude = false)
{
$req = 'SELECT COUNT(*) AS count, comment_email ' .
'FROM ' . $core->prefix . 'post P, ' . $core->prefix . 'comment C ' .
'WHERE P.post_id=C.post_id ' .
"AND blog_id='" . $core->con->escape($core->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 ' . $core->prefix . 'user U' .
' INNER JOIN ' . $core->prefix . 'post P ON P.user_id = U.user_id ' .
" WHERE blog_id='" . $core->con->escape($core->blog->id) . "' " .
' GROUP BY U.user_email) ';
}
$req .= 'GROUP BY comment_email ' .
'ORDER BY count ' . ($sort_desc ? 'DESC' : 'ASC') . ' ' .
$core->con->limit(abs((int) $limit));
$rs = $core->con->select($req);
if ($rs->isEmpty()) {
return null;
}
$res = [];
$i = 0;
while ($rs->fetch()) {
$user = $core->con->select(
'SELECT * FROM ' . $core->prefix . 'comment ' .
"WHERE comment_email='" . $rs->comment_email . "' " .
'ORDER BY comment_dt DESC'
);
if (!$user->comment_author) {
continue;
}
$i++;
if ($user->comment_site) {
$res[$i]['author_link'] = '<a href="' . $user->comment_site . '" title="' .
__('Author link') . '">' . $user->comment_author . '</a>';
}
$res[$i]['author'] = $user->comment_author;
if ($rs->count == 0) {
$res[$i]['count'] = __('no comments');
} else {
$res[$i]['count'] = sprintf(__('one comment', '%s comments', $rs->count), $rs->count);
}
}
if (!$i) {
return null;
}
return $res;
}
private static function period(string $field, string $period): string
{
$pattern = '%Y-%m-%d %H:%M:%S';
$time = 0;
switch ($period) {
case 'day':
$time = 3600 * 24;
break;
case 'week':
$time = 3600 * 24 * 7;
break;
case 'month':
$time = 3600 * 24 * 30;
break;
case 'year':
$time = 3600 * 24 * 30 * 12;
break;
default:
return '';
break;
}
return "AND $field > TIMESTAMP '" . dt::str($pattern, time() - $time) . "' ";
}
public static function periods()
{
return [
__('last day') => 'day',
__('last week') => 'week',
__('last month') => 'month',
__('last year') => 'year',
__('from begining') => ''
];
}
}

View File

@ -1,69 +1,48 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: topWriter 0.8.1\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: 2021-09-25T16:39:29+00:00\n"
"PO-Revision-Date: 2021-11-06T20:12:30+00:00\n"
"Last-Translator: Jean-Christian Denis\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
msgid "Top writer: entries"
msgstr "Top rédacteur : billets"
msgid "Top writer: comments"
msgstr "Top rédacteur : commentaires"
msgid "Period:"
msgstr "Période :"
msgid "Limit:"
msgstr "Limite :"
msgid "List users who write more comments"
msgstr "Liste les utilisateurs qui ont écrit le plus de commentaires"
msgid "Top comments"
msgstr "Top commentaires"
msgid "Period:"
msgstr "Période :"
msgid "day"
msgstr "jour"
msgid "week"
msgstr "semaine"
msgid "month"
msgstr "mois"
msgid "year"
msgstr "année"
msgid "from begining"
msgstr "depuis le début"
msgid "Limit:"
msgstr "Limite :"
msgid "Exclude post writer from list"
msgstr "Exclure de la liste les auteurs de billets"
msgid "Top writer: entries"
msgstr "Top rédacteur : billets"
msgid "List users who write more posts"
msgstr "Liste les utilisateurs qui ont écrit le plus de billets"
msgid "Top entries"
msgstr "Top billets"
msgid "Author link"
msgstr "Lien vers l'auteur"
msgid "one comment"
msgid_plural "%s comments"
msgstr[0] "un commentaire"
msgstr[1] "%s commentaires"
msgid "Author posts"
msgstr "Billets de l'auteur"
msgid "Author link"
msgstr "Lien vers l'auteur"
msgid "no entries"
msgstr "aucun billet"
@ -72,6 +51,29 @@ msgid_plural "%s entries"
msgstr[0] "une publication"
msgstr[1] "%s publications"
msgid "no comments"
msgstr "aucun commentaire"
msgid "one comment"
msgid_plural "%s comments"
msgstr[0] "un commentaire"
msgstr[1] "%s commentaires"
msgid "last day"
msgstr "dernier jour"
msgid "last week"
msgstr "dernière semaine"
msgid "last month"
msgstr "dernier mois"
msgid "last year"
msgstr "dernière année"
msgid "from begining"
msgstr "depuis le début"
msgid "Ranking of the most prolific writers and/or commentators"
msgstr "Classement des plus prolifiques rédacteurs et/ou commentateurs"