Compare commits

..

No commits in common. "master" and "v3.3" have entirely different histories.
master ... v3.3

29 changed files with 855 additions and 831 deletions

View File

@ -1,16 +1,3 @@
activityReport 3.4.1 - 2023.10.24
===========================================================
* Require Dotclear 2.28
* Require PHP 8.1
* Code review
activityReport 3.4 - 2023.10.18
===========================================================
* Require Dotclear 2.28
* Require PHP 8.1
* Upgrade to Dotclear 2.28
* Use namespace to load modules activities
activityReport 3.3 - 2023.08.17
===========================================================
* Require Dotclear 2.27

View File

@ -1,24 +1,28 @@
# README
[![Release](https://img.shields.io/badge/release-3.4.1-a2cbe9.svg)](https://git.dotclear.watch/JcDenis/activityReport/releases)
![Date](https://img.shields.io/badge/date-2023.10.24-c44d58.svg)
[![Dotclear](https://img.shields.io/badge/dotclear-v2.28-137bbb.svg)](https://fr.dotclear.org/download)
[![Release](https://img.shields.io/badge/release-3.3-a2cbe9.svg)](https://git.dotclear.watch/JcDenis/activityReport/releases)
[![Date](https://img.shields.io/badge/date-2023.08.17-c44d58.svg)](https://git.dotclear.watch/JcDenis/activityReport/releases)
[![Dotclear](https://img.shields.io/badge/dotclear-v2.27-137bbb.svg)](https://fr.dotclear.org/download)
[![Dotaddict](https://img.shields.io/badge/dotaddict-official-9ac123.svg)](https://plugins.dotaddict.org/dc2/details/activityReport)
[![License](https://img.shields.io/badge/license-GPL--2.0-ececec.svg)](https://git.dotclear.watch/JcDenis/activityReport/src/branch/master/LICENSE)
[![License](https://img.shields.io/github/license/JcDenis/activityReport)](https://git.dotclear.watch/JcDenis/activityReport/blob/master/LICENSE)
## ABOUT
## WHAT IS ACTIVITYREPORT ?
_activityReport_ is a plugin for the open-source web publishing software called [Dotclear](https://www.dotclear.org).
_activityReport_ is a plugin for the open-source
web publishing software called Dotclear.
> Log and receive your blog activity by email, feed, or on dashboard
It help super admin to create and manage packages of
themes and plugins from Dotclear administration pages.
## REQUIREMENTS
* Dotclear 2.28
_activityReport_ requires:
* Permissions admin
* Dotclear 2.27
* PHP 8.1+
* System permissions to add table on database
* System permissions to send email (optionnal)
* Dotclear admin permissions
* permission to add table on database
* permission to send email
## USAGE
@ -31,13 +35,11 @@ go to ''configure plugin'', fill in form.
Once it's done you can manage your reports from menu
''Activity report'' on sidebar or you can add dashboard icon.
## LINKS
## MORE
* [License](https://git.dotclear.watch/JcDenis/activityReport/src/branch/master/LICENSE)
* [Packages & details](https://git.dotclear.watch/JcDenis/activityReport/releases) (or on [Dotaddict](https://plugins.dotaddict.org/dc2/details/activityReport))
* [Sources & contributions](https://git.dotclear.watch/JcDenis/activityReport) (or on [GitHub](https://github.com/JcDenis/activityReport))
* [Issues & security](https://git.dotclear.watch/JcDenis/activityReport/issues) (or on [GitHub](https://github.com/JcDenis/activityReport/issues))
* [Discuss & help](https://forum.dotclear.org/viewtopic.php?id=40504)
* License : [GNU GPL v2](https://www.gnu.org/licenses/old-licenses/lgpl-2.0.html)
* Source & contribution : [Gitea Page](https://git.dotclear.watch/JcDenis/activityReport) or [GitHub Page](https://github.com/JcDenis/activityReport)
* Packages & details: [Gitea Page](https://git.dotclear.watch/JcDenis/activityReport/releases) or [Dotaddict Page](https://plugins.dotaddict.org/dc2/details/activityReport)
## CONTRIBUTORS

View File

@ -1,26 +1,34 @@
<?php
/**
* @file
* @brief The plugin activityReport definition
* @ingroup activityReport
* @brief activityReport, a plugin for Dotclear 2
*
* @defgroup activityReport Plugin activityReport.
* @package Dotclear
* @subpackage Plugin
*
* Log and receive your blog activity by email, feed, or on dashboard.
* @author Jean-Christian Denis and contributors
*
* @author Jean-Christian Denis (author)
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
if (!defined('DC_RC_PATH')) {
return null;
}
$this->registerModule(
'Activity log',
'Log and receive your blog activity by email, feed, or on dashboard',
'Jean-Christian Denis and contributors',
'3.4.1',
'3.3',
[
'requires' => [['core', '2.28']],
'permissions' => 'My',
'requires' => [
['php', '8.1'],
['core', '2.27'],
],
'permissions' => dcCore::app()->auth->makePermissions([
dcCore::app()->auth::PERMISSION_USAGE,
dcCore::app()->auth::PERMISSION_CONTENT_ADMIN,
dcCore::app()->auth::PERMISSION_ADMIN,
]),
'priority' => 2,
'type' => 'plugin',
'support' => 'https://git.dotclear.watch/JcDenis/' . basename(__DIR__) . '/issues',

20
_init.php 100644
View File

@ -0,0 +1,20 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return null;
}
class initActivityReport
{
public const ACTIVITY_TABLE_NAME = 'activity';
}

View File

@ -2,11 +2,11 @@
<modules xmlns:da="http://dotaddict.org/da/">
<module id="activityReport">
<name>Activity log</name>
<version>3.4.1</version>
<version>3.3</version>
<author>Jean-Christian Denis and contributors</author>
<desc>Log and receive your blog activity by email, feed, or on dashboard</desc>
<file>https://git.dotclear.watch/JcDenis/activityReport/releases/download/v3.4.1/plugin-activityReport.zip</file>
<da:dcmin>2.28</da:dcmin>
<file>https://git.dotclear.watch/JcDenis/activityReport/releases/download/v3.3/plugin-activityReport.zip</file>
<da:dcmin>2.27</da:dcmin>
<da:details>https://git.dotclear.watch/JcDenis/activityReport/src/branch/master/README.md</da:details>
<da:support>https://git.dotclear.watch/JcDenis/activityReport/issues</da:support>
</module>

View File

@ -1,17 +1,23 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use Dotclear\App;
use dcCore;
/**
* @brief activityReport action descriptor class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Action descriptor.
*/
class Action
{
@ -33,7 +39,7 @@ class Action
) {
// fake action has no behavior
if (!is_null($function)) {
App::behavior()->addBehavior($behavior, $function);
dcCore::app()->addBehavior($behavior, $function);
}
}
}

View File

@ -1,13 +1,23 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use ArrayObject;
use Dotclear\App;
use Dotclear\Core\Process;
use Dotclear\Interface\Core\BlogInterface;
use dcBlog;
use dcCore;
use dcUtils;
use Dotclear\Database\{
Cursor,
MetaRecord
@ -15,27 +25,12 @@ use Dotclear\Database\{
use Dotclear\Helper\Network\Http;
/**
* @brief activityReport register class.
* @ingroup activityReport
*
* Register default activities and export mail formats.
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
class ActivityReportAction extends Process
class ActivityBehaviors
{
public static function init(): bool
public static function register(): void
{
return self::status(true);
}
public static function process(): bool
{
if (!self::status()) {
return false;
}
$my = new Group(My::id(), __('ActivityReport messages'));
$blog = new Group('blog', __('Actions on blog'));
$post = new Group('post', __('Actions on posts'));
@ -50,7 +45,7 @@ class ActivityReportAction extends Process
__('Special messages'),
__('%s'),
'messageActivityReport',
self::messageActivityReport(...)
[self::class, 'messageActivityReport']
));
// Not use as it is global : BEHAVIOR adminAfterBlogCreate in admin/blog.php
@ -61,7 +56,7 @@ class ActivityReportAction extends Process
__('updating blog'),
__('Blog was updated by "%s"'),
'adminAfterBlogUpdate',
self::blogUpdate(...)
[self::class, 'blogUpdate']
));
// from BEHAVIOR publicHeadContent in template
@ -70,7 +65,7 @@ class ActivityReportAction extends Process
__('404 error'),
__('New 404 error page at "%s"'),
'publicHeadContent',
self::blogP404(...)
[self::class, 'blogP404']
));
// from BEHAVIOR coreAfterPostCreate in inc/core/class.dc.blog.php (DC 2.2)
@ -81,7 +76,7 @@ class ActivityReportAction extends Process
__('post creation'),
__('A new post called "%s" was created by "%s" at %s'),
'adminAfterPostCreate',
self::postCreate(...)
[self::class, 'postCreate']
));
// Plugin contribute
@ -91,7 +86,7 @@ class ActivityReportAction extends Process
__('post creation'),
__('A new post called "%s" was created by "%s" at %s'),
'publicAfterPostCreate',
self::postCreate(...)
[self::class, 'postCreate']
));
// from BEHAVIOR coreAfterPostUpdate in inc/core/class.dc.blog.php (DC2.2)
@ -101,7 +96,7 @@ class ActivityReportAction extends Process
__('updating post'),
__('Post called "%s" has been updated by "%s" at %s'),
'adminAfterPostUpdate',
self::postUpdate(...)
[self::class, 'postUpdate']
));
// from BEHAVIOR adminBeforePostDelete in admin/post.php
@ -110,7 +105,7 @@ class ActivityReportAction extends Process
__('post deletion'),
__('Post called "%s" has been deleted by "%s"'),
'adminBeforePostDelete',
self::postDelete(...)
[self::class, 'postDelete']
));
// Wrong attempt on passworded enrty
@ -120,7 +115,7 @@ class ActivityReportAction extends Process
__('Post protection'),
__('An attempt failed on a passworded post with password "%s" at "%s"'),
'urlHandlerServeDocument',
self::postPasswordAttempt(...)
[self::class, 'postPasswordAttempt']
));
// from BEHAVIOR coreAfterCommentCreate in inc/core/class.dc.blog.php
@ -131,7 +126,7 @@ class ActivityReportAction extends Process
__('comment creation'),
__('A new comment was created by "%s" on post "%s" at %s'),
'coreAfterCommentCreate',
self::commentCreate(...)
[self::class, 'commentCreate']
));
// from BEHAVIOR coreAfterCommentUpdate in inc/core/class.dc.blog.php
@ -141,7 +136,7 @@ class ActivityReportAction extends Process
__('updating comment'),
__('Comment has been updated by "%s" at %s'),
'coreAfterCommentUpdate',
self::commentUpdate(...)
[self::class, 'commentUpdate']
));
// Missing coreBeforeCommentDelete in inc/core/class.dc.blog.php
@ -154,7 +149,7 @@ class ActivityReportAction extends Process
__('trackback creation'),
__('A new trackback to "%" at "%s" was created on post "%s" at %s'),
'coreAfterCommentCreate',
self::trackbackCreate(...)
[self::class, 'trackbackCreate']
));
// from BEHAVIOR adminAfterCategoryCreate in admin/category.php
@ -163,7 +158,7 @@ class ActivityReportAction extends Process
__('category creation'),
__('A new category called "%s" was created by "%s" at %s'),
'adminAfterCategoryCreate',
self::categoryCreate(...)
[self::class, 'categoryCreate']
));
// from BEHAVIOR adminAfterCategoryUpdate in admin/category.php
@ -172,7 +167,7 @@ class ActivityReportAction extends Process
__('updating category'),
__('Category called "%s" has been updated by "%s" at %s'),
'adminAfterCategoryUpdate',
self::categoryUpdate(...)
[self::class, 'categoryUpdate']
));
// Missing adminBeforeCategoryDelete in admin/category.php
@ -183,7 +178,7 @@ class ActivityReportAction extends Process
__('user creation'),
__('A new user named "%s" was created by "%s"'),
'adminAfterUserCreate',
self::userCreate(...)
[self::class, 'userCreate']
));
// from BEHAVIOR adminAfterUserUpdated in admin/user.php
@ -192,7 +187,7 @@ class ActivityReportAction extends Process
__('updating user'),
__('User named "%s" has been updated by "%s"'),
'adminAfterUserUpdate',
self::userUpdate(...)
[self::class, 'userUpdate']
));
// from BEHAVIOR adminAfterUserProfileUpdate in admin/preferences.php
@ -201,21 +196,21 @@ class ActivityReportAction extends Process
__('updating user preference'),
__('"%s" user preference has been updated'),
'adminAfterUserProfileUpdate',
self::userPreference(...)
[self::class, 'userPreference']
));
$user->add(new Action(
'preference',
__('updating user preference'),
__('"%s" user preference has been updated'),
'adminAfterUserOptionsUpdate',
self::userPreference(...)
[self::class, 'userPreference']
));
$user->add(new Action(
'preference',
__('updating user preference'),
__('"%s" user preference has been updated'),
'adminAfterDashboardOptionsUpdate',
self::userOption(...)
[self::class, 'userOption']
));
// from BEHAVIOR adminBeforeUserDelete in admin/users.php
@ -224,7 +219,7 @@ class ActivityReportAction extends Process
__('user deletion'),
__('User named "%s" has been deleted by "%"'),
'adminBeforeUserDelete',
self::userDelete(...)
[self::class, 'userDelete']
));
ActivityReport::instance()->groups
@ -269,8 +264,6 @@ class ActivityReportAction extends Process
'<div class="foot"><p>Powered by <a href="https://github.com/JcDenis/activityReport">activityReport</a></p></div>' .
'</body></html>',
]));
return true;
}
public static function messageActivityReport(string $message): void
@ -281,26 +274,26 @@ class ActivityReportAction extends Process
public static function blogUpdate(Cursor $cur, string $blog_id): void
{
$logs = [self::str(App::auth()->getInfo('user_cn'))];
$logs = [self::str(dcCore::app()->auth->getInfo('user_cn'))];
ActivityReport::instance()->addLog('blog', 'update', $logs);
}
public static function blogP404(): void
{
if (App::url()->type != '404') {
if (dcCore::app()->url->type != '404') {
return;
}
$logs = [self::str(App::blog()->url()) . $_SERVER['QUERY_STRING']];
$logs = [self::str(dcCore::app()->blog?->url) . $_SERVER['QUERY_STRING']];
ActivityReport::instance()->addLog('blog', 'p404', $logs);
}
public static function postCreate(Cursor $cur, int $post_id): void
{
$post_url = App::blog()->getPostURL('', self::str($cur->getField('post_dt')), self::str($cur->getField('post_title')), $post_id);
$post_url = dcCore::app()->blog?->getPostURL('', self::str($cur->getField('post_dt')), self::str($cur->getField('post_title')), $post_id);
$logs = [
self::str($cur->getField('post_title')),
self::str(App::auth()->getInfo('user_cn')),
self::str(App::blog()->url()) . App::url()->getBase(self::str($cur->getField('post_type'))) . '/' . $post_url,
self::str(dcCore::app()->auth->getInfo('user_cn')),
self::str(dcCore::app()->blog?->url) . dcCore::app()->url->getBase(self::str($cur->getField('post_type'))) . '/' . $post_url,
];
ActivityReport::instance()->addLog('post', 'create', $logs);
}
@ -308,31 +301,28 @@ class ActivityReportAction extends Process
public static function postUpdate(Cursor $cur, int|string $post_id): void
{
$post_id = is_numeric($post_id) ? (int) $post_id : 0;
$post_url = App::blog()->getPostURL('', self::str($cur->getField('post_dt')), self::str($cur->getField('post_title')), $post_id);
$post_url = dcCore::app()->blog?->getPostURL('', self::str($cur->getField('post_dt')), self::str($cur->getField('post_title')), $post_id);
$logs = [
self::str($cur->getField('post_title')),
self::str(App::auth()->getInfo('user_cn')),
self::str(App::blog()->url()) . App::url()->getBase(self::str($cur->getField('post_type'))) . '/' . $post_url,
self::str(dcCore::app()->auth->getInfo('user_cn')),
self::str(dcCore::app()->blog?->url) . dcCore::app()->url->getBase(self::str($cur->getField('post_type'))) . '/' . $post_url,
];
ActivityReport::instance()->addLog('post', 'update', $logs);
}
public static function postDelete(int $post_id): void
{
$posts = App::blog()->getPosts(['post_id' => $post_id, 'limit' => 1]);
if ($posts->isEmpty()) {
$posts = dcCore::app()->blog?->getPosts(['post_id' => $post_id, 'limit' => 1]);
if (!$posts || $posts->isEmpty()) {
return;
}
$logs = [
self::str($posts->f('post_title')),
self::str(App::auth()->getInfo('user_cn')),
self::str(dcCore::app()->auth->getInfo('user_cn')),
];
ActivityReport::instance()->addLog('post', 'delete', $logs);
}
/**
* @param ArrayObject<string, mixed> $result
*/
public static function postPasswordAttempt(ArrayObject $result): void
{
if ($result['tpl'] != 'password-form.html' || empty($_POST['password'])) {
@ -345,55 +335,55 @@ class ActivityReportAction extends Process
ActivityReport::instance()->addLog('post', 'protection', $logs);
}
public static function commentCreate(BlogInterface $blog, Cursor $cur): void
public static function commentCreate(dcBlog $blog, Cursor $cur): void
{
if ($cur->getField('comment_trackback')) {
return;
}
$posts = App::blog()->getPosts(
$posts = dcCore::app()->blog?->getPosts(
['post_id' => $cur->getField('post_id'), 'limit' => 1, 'post_type' => '']
);
if ($posts->isEmpty()) {
if (!$posts || $posts->isEmpty()) {
return;
}
$logs = [
self::str($cur->getField('comment_author')),
self::str($posts->f('post_title')),
self::str(App::blog()->url()) . App::url()->getBase(self::str($posts->f('post_type'))) .
self::str(dcCore::app()->blog?->url) . dcCore::app()->url->getBase(self::str($posts->f('post_type'))) .
'/' . self::str($posts->f('post_url')) . '#c' . self::str($cur->getField('comment_id')),
];
ActivityReport::instance()->addLog('comment', 'create', $logs);
}
public static function commentUpdate(BlogInterface $blog, Cursor $cur, MetaRecord $old): void
public static function commentUpdate(dcBlog $blog, Cursor $cur, MetaRecord $old): void
{
$posts = App::blog()->getPosts(
$posts = dcCore::app()->blog?->getPosts(
['post_id' => $old->f('post_id'), 'limit' => 1]
);
if ($posts->isEmpty()) {
if (!$posts || $posts->isEmpty()) {
return;
}
$logs = [
self::str(App::auth()->getInfo('user_cn')),
self::str(dcCore::app()->auth->getInfo('user_cn')),
self::str($posts->f('post_title')),
self::str(App::blog()->url()) . App::url()->getBase(self::str($posts->f('post_type'))) .
self::str(dcCore::app()->blog?->url) . dcCore::app()->url->getBase(self::str($posts->f('post_type'))) .
'/' . self::str($posts->f('post_url')) . '#c' . self::str($old->f('comment_id')),
];
ActivityReport::instance()->addLog('comment', 'update', $logs);
}
public static function trackbackCreate(BlogInterface $blog, Cursor $cur): void
public static function trackbackCreate(dcBlog $blog, Cursor $cur): void
{
if (!$cur->getField('comment_trackback')) {
return;
}
$posts = App::blog()->getPosts(
$posts = dcCore::app()->blog?->getPosts(
['post_id' => $cur->getField('post_id'), 'no_content' => true, 'limit' => 1]
);
if ($posts->isEmpty()) {
if (!$posts || $posts->isEmpty()) {
return;
}
@ -401,7 +391,7 @@ class ActivityReportAction extends Process
self::str($cur->getField('comment_author')),
self::str($cur->getField('comment_site')),
self::str($posts->f('post_title')),
self::str(App::blog()->url()) . App::url()->getBase(self::str($posts->f('post_type'))) .
self::str(dcCore::app()->blog?->url) . dcCore::app()->url->getBase(self::str($posts->f('post_type'))) .
'/' . self::str($posts->f('post_url')),
];
ActivityReport::instance()->addLog('comment', 'trackback', $logs);
@ -411,8 +401,8 @@ class ActivityReportAction extends Process
{
$logs = [
self::str($cur->getField('cat_title')),
self::str(App::auth()->getInfo('user_cn')),
self::str(App::blog()->url()) . App::url()->getBase('category') . '/' . self::str($cur->getField('cat_url')),
self::str(dcCore::app()->auth->getInfo('user_cn')),
self::str(dcCore::app()->blog?->url) . dcCore::app()->url->getBase('category') . '/' . self::str($cur->getField('cat_url')),
];
ActivityReport::instance()->addLog('category', 'create', $logs);
}
@ -421,15 +411,15 @@ class ActivityReportAction extends Process
{
$logs = [
self::str($cur->getField('cat_title')),
self::str(App::auth()->getInfo('user_cn')),
self::str(App::blog()->url()) . App::url()->getBase('category') . '/' . self::str($cur->getField('cat_url')),
self::str(dcCore::app()->auth->getInfo('user_cn')),
self::str(dcCore::app()->blog?->url) . dcCore::app()->url->getBase('category') . '/' . self::str($cur->getField('cat_url')),
];
ActivityReport::instance()->addLog('category', 'update', $logs);
}
public static function userCreate(Cursor $cur, string $user_id): void
{
$user_cn = App::users()->getUserCN(
$user_cn = dcUtils::getUserCN(
self::str($cur->getField('user_id')),
self::str($cur->getField('user_name')),
self::str($cur->getField('user_firstname')),
@ -437,14 +427,14 @@ class ActivityReportAction extends Process
);
$logs = [
self::str($user_cn),
self::str(App::auth()->getInfo('user_cn')),
self::str(dcCore::app()->auth->getInfo('user_cn')),
];
ActivityReport::instance()->addLog('user', 'create', $logs);
}
public static function userUpdate(Cursor $cur, string $user_id): void
{
$user_cn = App::users()->getUserCN(
$user_cn = dcUtils::getUserCN(
self::str($cur->getField('user_id')),
self::str($cur->getField('user_name')),
self::str($cur->getField('user_firstname')),
@ -452,7 +442,7 @@ class ActivityReportAction extends Process
);
$logs = [
self::str($user_cn),
self::str(App::auth()->getInfo('user_cn')),
self::str(dcCore::app()->auth->getInfo('user_cn')),
];
ActivityReport::instance()->addLog('user', 'update', $logs);
}
@ -464,11 +454,11 @@ class ActivityReportAction extends Process
public static function userOption(string $user_id): void
{
$user = App::users()->getUser($user_id);
$user = dcCore::app()->getUser($user_id);
if ($user->isEmpty()) {
return;
}
$user_cn = App::users()->getUserCN(
$user_cn = dcUtils::getUserCN(
self::str($user->f('user_id')),
self::str($user->f('user_name')),
self::str($user->f('user_firstname')),
@ -482,8 +472,8 @@ class ActivityReportAction extends Process
public static function userDelete(string $user_id): void
{
$users = App::users()->getUser($user_id);
$user_cn = App::users()->getUserCN(
$users = dcCore::app()->getUser($user_id);
$user_cn = dcUtils::getUserCN(
self::str($users->f('user_id')),
self::str($users->f('user_name')),
self::str($users->f('user_firstname')),
@ -491,7 +481,7 @@ class ActivityReportAction extends Process
);
$logs = [
self::str($user_cn),
self::str(App::auth()->getInfo('user_cn')),
self::str(dcCore::app()->auth->getInfo('user_cn')),
];
ActivityReport::instance()->addLog('user', 'delete', $logs);
}

View File

@ -1,12 +1,23 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use ArrayObject;
use Dotclear\App;
use Dotclear\Core\Process;
use dcAuth;
use dcBlog;
use dcCore;
use Dotclear\Database\MetaRecord;
use Dotclear\Database\Statement\{
DeleteStatement,
@ -23,78 +34,34 @@ use Dotclear\Helper\File\{
use Dotclear\Helper\Network\Mail\Mail;
use Dotclear\Helper\Text;
use Exception;
use Throwable;
/**
* @brief activityReport main class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Activity report main class.
*/
class ActivityReport
{
/**
* Third party plugins ActivityReport class name.
*
* @var string ACTIVITYREPORT_CLASS_NAME
*/
public const ACTIVITYREPORT_CLASS_NAME = 'ActivityReportAction';
/**
* Activity marked as pending mail report.
*
* @var int STATUS_PENDING
*/
/** @var int activity marked as pending mail report */
public const STATUS_PENDING = 0;
/**
* Activity marked as reported by mail.
*
* @var int STATUS_REPORTED
*/
/** @var int activity marked as reported by mail */
public const STATUS_REPORTED = 1;
/**
* Activity report type (by default activityReport).
*
* @var string $type
*/
/** @var string $type Activity report type (by default activityReport) */
public readonly string $type;
/**
* Activity report settings for current blog.
*
* @var Settings $settings
*/
/** @var Settings $settings Activity report settings for current blog */
public readonly Settings $settings;
/**
* Groups of actions.
*
* @var Groups $groups
*/
/** @var Groups $groups Groups of actions */
public readonly Groups $groups;
/**
* Export available formats.
*
* @var Formats $formats
*/
/** @var Formats $formats Export available formats */
public readonly Formats $formats;
/**
* ActivityReport instance.
*
* @var ActivityReport $instance
*/
/** @var ActivityReport $instance ActivityReport instance */
private static $instance;
/**
* File lock for update.
*
* @var null|string $lock
*/
/** @var null|string $lock File lock for update */
private static $lock = null;
/**
@ -113,23 +80,6 @@ class ActivityReport
$this->obsoleteLogs();
}
public static function init(): void
{
foreach (App::plugins()->getDefines() as $module) {
$class = $module->get('namespace') . '\\' . self::ACTIVITYREPORT_CLASS_NAME;
try {
if (is_a($class, Process::class, true)) {
// check class prerequiretics
if ($class::init()) {
$class::process();
}
}
} catch (Throwable $e) {
}
}
}
/**
* Get singleton instance.
*
@ -147,13 +97,13 @@ class ActivityReport
/**
* Get logs record.
*
* @param null|ArrayObject<string, mixed> $params The query params
* @param null|ArrayObject $params The query params
* @param bool $count_only Count only
* @param null|SelectStatement $ext_sql The sql select statement
*
* @return MetaRecord The logs record
* @return null|MetaRecord The logs record
*/
public function getLogs(ArrayObject $params = null, bool $count_only = false, ?SelectStatement $ext_sql = null): MetaRecord
public function getLogs(ArrayObject $params = null, bool $count_only = false, ?SelectStatement $ext_sql = null): ?MetaRecord
{
if (is_null($params)) {
$params = new ArrayObject();
@ -184,11 +134,11 @@ class ActivityReport
]);
}
$sql
->from($sql->as(App::con()->prefix() . My::ACTIVITY_TABLE_NAME, 'E'), false, true)
->from($sql->as(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME, 'E'), false, true)
->join(
(new JoinStatement())
->left()
->from($sql->as(App::con()->prefix() . App::blog()::BLOG_TABLE_NAME, 'B'))
->from($sql->as(dcCore::app()->prefix . dcBlog::BLOG_TABLE_NAME, 'B'))
->on('E.blog_id = B.blog_id')
->statement()
);
@ -211,7 +161,7 @@ class ActivityReport
$sql->where('E.activity_type = ' . $sql->quote($this->type));
}
if (isset($params['blog_id']) && $params['blog_id'] != '') {
if (isset($params['blog_id']) && is_null($params['blog_id'])) {
$sql->and('E.blog_id IS NOT NULL');
} elseif (!empty($params['blog_id'])) {
if (!is_array($params['blog_id'])) {
@ -219,7 +169,7 @@ class ActivityReport
}
$sql->and('E.blog_id' . $sql->in($params['blog_id']));
} else {
$sql->and('E.blog_id = ' . $sql->quote(App::blog()->id()));
$sql->and('E.blog_id = ' . $sql->quote((string) dcCore::app()->blog?->id));
}
if (isset($params['activity_status']) && is_numeric($params['activity_status'])) {
@ -251,8 +201,8 @@ class ActivityReport
if (!empty($params['requests'])) {
$or = [];
foreach ($this->settings->requests as $group => $actions) {
foreach (array_keys($actions) as $action) {
$or[] = $sql->andGroup(['activity_group = ' . $sql->quote((string) $group), 'activity_action = ' . $sql->quote((string) $action)]);
foreach ($actions as $action) {
$or[] = $sql->andGroup(['activity_group = ' . $sql->quote($group), 'activity_action = ' . $sql->quote($action)]);
}
}
if (!empty($or)) {
@ -275,10 +225,9 @@ class ActivityReport
if (!$count_only && !empty($params['limit'])) {
$sql->limit($params['limit']);
}
$rs = $sql->select();
return is_null($rs) ? MetaRecord::newFromArray([]) : $rs;
return $sql->select();
}
/**
@ -286,17 +235,17 @@ class ActivityReport
*
* @param string $group The group
* @param string $action The action
* @param array<int, string> $logs The logs values
* @param array<int,string> $logs The logs values
*/
public function addLog(string $group, string $action, array $logs): void
{
try {
$cur = App::con()->openCursor(App::con()->prefix() . My::ACTIVITY_TABLE_NAME);
App::con()->writeLock(App::con()->prefix() . My::ACTIVITY_TABLE_NAME);
$cur = dcCore::app()->con->openCursor(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME);
dcCore::app()->con->writeLock(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME);
$cur->setField('activity_id', $this->getNextId());
$cur->setField('activity_type', $this->type);
$cur->setField('blog_id', App::blog()->id());
$cur->setField('blog_id', (string) dcCore::app()->blog?->id);
$cur->setField('activity_group', $group);
$cur->setField('activity_action', $action);
$cur->setField('activity_logs', json_encode($logs));
@ -304,13 +253,13 @@ class ActivityReport
$cur->setField('activity_status', self::STATUS_PENDING);
$cur->insert();
App::con()->unlock();
dcCore::app()->con->unlock();
# --BEHAVIOR-- coreAfterCategoryCreate -- ActivityReport, cursor
App::behavior()->callBehavior('activityReportAfteAddLog', $this, $cur);
dcCore::app()->callBehavior('activityReportAfteAddLog', $this, $cur);
} catch (Exception $e) {
App::con()->unlock();
App::error()->add($e->getMessage());
dcCore::app()->con->unlock();
dcCore::app()->error->add($e->getMessage());
}
// Test if email report is needed
@ -352,7 +301,7 @@ class ActivityReport
$from = time();
$to = 0;
$res = $blog_name = $blog_url = $group = '';
$tz = App::blog()->settings()->get('system')->get('blog_timezone');
$tz = dcCore::app()->blog?->settings->get('system')->get('blog_timezone');
$tz = is_string($tz) ? $tz : 'UTC';
$dt = empty($this->settings->dateformat) ? '%Y-%m-%d %H:%M:%S' : $this->settings->dateformat;
$format = $this->formats->get($this->formats->has($this->settings->mailformat) ? $this->settings->mailformat : 'plain');
@ -445,7 +394,7 @@ class ActivityReport
{
// Get blogs and logs count
$sql = new SelectStatement();
$sql->from(App::con()->prefix() . My::ACTIVITY_TABLE_NAME)
$sql->from(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME)
->column('blog_id')
->where('activity_type =' . $sql->quote($this->type))
->group('blog_id');
@ -461,16 +410,16 @@ class ActivityReport
$obs = Date::str('%Y-%m-%d %H:%M:%S', $ts - (int) $this->settings->obsolete);
if (is_string($rs->f('blog_id'))) {
$sql = new DeleteStatement();
$sql->from(App::con()->prefix() . My::ACTIVITY_TABLE_NAME)
$sql->from(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME)
->where('activity_type =' . $sql->quote($this->type))
->and('activity_dt < TIMESTAMP ' . $sql->quote($obs))
->and('blog_id = ' . $sql->quote($rs->f('blog_id')))
->delete();
if (App::con()->changes()) {
if (dcCore::app()->con->changes()) {
try {
$cur = App::con()->openCursor(App::con()->prefix() . My::ACTIVITY_TABLE_NAME);
App::con()->writeLock(App::con()->prefix() . My::ACTIVITY_TABLE_NAME);
$cur = dcCore::app()->con->openCursor(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME);
dcCore::app()->con->writeLock(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME);
$cur->setField('activity_id', $this->getNextId());
$cur->setField('activity_type', $this->type);
@ -482,10 +431,10 @@ class ActivityReport
$cur->setField('activity_status', self::STATUS_PENDING);
$cur->insert();
App::con()->unlock();
dcCore::app()->con->unlock();
} catch (Exception $e) {
App::con()->unlock();
App::error()->add($e->getMessage());
dcCore::app()->con->unlock();
dcCore::app()->error->add($e->getMessage());
}
}
}
@ -507,7 +456,7 @@ class ActivityReport
$sql->and('activity_status = ' . self::STATUS_REPORTED);
}
return $sql->from(App::con()->prefix() . My::ACTIVITY_TABLE_NAME)
return $sql->from(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME)
->where('activity_type = ' . $sql->quote($this->type))
->delete();
}
@ -521,10 +470,10 @@ class ActivityReport
private function updateStatus(int $from_date_ts, int $to_date_ts): void
{
$sql = new UpdateStatement();
$sql->from(App::con()->prefix() . My::ACTIVITY_TABLE_NAME)
$sql->from(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME)
->column('activity_status')
->set((string) self::STATUS_REPORTED)
->where('blog_id = ' . $sql->quote(App::blog()->id()))
->where('blog_id = ' . $sql->quote((string) dcCore::app()->blog?->id))
->and('activity_type =' . $sql->quote($this->type))
->and('activity_dt >= TIMESTAMP ' . $sql->quote(date('Y-m-d H:i:s', $from_date_ts)))
->and('activity_dt < TIMESTAMP ' . $sql->quote(date('Y-m-d H:i:s', $to_date_ts)))
@ -539,7 +488,7 @@ class ActivityReport
public function getNextId(): int
{
$sql = new SelectStatement();
$sql->from(App::con()->prefix() . My::ACTIVITY_TABLE_NAME)
$sql->from(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME)
->column($sql->max('activity_id'));
return (int) $sql->select()?->f(0) + 1;
@ -554,14 +503,14 @@ class ActivityReport
{
try {
# Cache writable ?
if (!is_writable(App::config()->cacheRoot())) {
if (!is_writable(DC_TPL_CACHE)) {
throw new Exception("Can't write in cache fodler");
}
# Set file path
$f_md5 = md5(App::blog()->id());
$f_md5 = md5((string) dcCore::app()->blog?->id);
$file = sprintf(
'%s/%s/%s/%s/%s.txt',
App::config()->cacheRoot(),
DC_TPL_CACHE,
My::id(),
substr($f_md5, 0, 2),
substr($f_md5, 2, 2),
@ -642,14 +591,14 @@ class ActivityReport
$params = new ArrayObject([
'from_date_ts' => $lastreport,
'to_date_ts' => $now,
'blog_id' => App::blog()->id(),
'blog_id' => dcCore::app()->blog?->id,
'activity_status' => self::STATUS_PENDING,
'requests' => true,
'order' => 'activity_group ASC, activity_action ASC, activity_dt ASC ',
]);
$logs = $this->getLogs($params);
if (!$logs->isEmpty()) {
if (!is_null($logs) && !$logs->isEmpty()) {
// Datas to readable text
$content = $this->parseLogs($logs);
if (!empty($content)) {
@ -663,7 +612,7 @@ class ActivityReport
$this->updateStatus($lastreport, $now);
$this->settings->set('lastreport', $now);
App::behavior()->callBehavior('messageActivityReport', 'Activity report has been successfully send by mail.');
dcCore::app()->callBehavior('messageActivityReport', 'Activity report has been successfully send by mail.');
}
}
@ -709,20 +658,20 @@ class ActivityReport
# Sending mails
try {
$subject = mb_encode_mimeheader(
sprintf(__('Blog "%s" activity report'), App::blog()->name()),
sprintf(__('Blog "%s" activity report'), dcCore::app()->blog?->name),
'UTF-8',
'B'
);
$headers = [];
$headers[] = 'From: ' . (defined('DC_ADMIN_MAILFROM') && str_contains(App::config()->adminMailFrom(), '@') ? App::config()->adminMailFrom() : 'dotclear@local');
$headers[] = 'From: ' . (defined('DC_ADMIN_MAILFROM') && str_contains(DC_ADMIN_MAILFROM, '@') ? DC_ADMIN_MAILFROM : 'dotclear@local');
$headers[] = 'Content-Type: text/' . $mailformat . '; charset=UTF-8;';
//$headers[] = 'MIME-Version: 1.0';
//$headers[] = 'X-Originating-IP: ' . mb_encode_mimeheader(Http::realIP(), 'UTF-8', 'B');
//$headers[] = 'X-Mailer: Dotclear';
//$headers[] = 'X-Blog-Id: ' . mb_encode_mimeheader(App::blog()->id()), 'UTF-8', 'B');
//$headers[] = 'X-Blog-Name: ' . mb_encode_mimeheader(App::blog()->name()), 'UTF-8', 'B');
//$headers[] = 'X-Blog-Url: ' . mb_encode_mimeheader(App::>blog()->url()), 'UTF-8', 'B');
//$headers[] = 'X-Blog-Id: ' . mb_encode_mimeheader(dcCore::app()->blog->id), 'UTF-8', 'B');
//$headers[] = 'X-Blog-Name: ' . mb_encode_mimeheader(dcCore::app()->blog->name), 'UTF-8', 'B');
//$headers[] = 'X-Blog-Url: ' . mb_encode_mimeheader(dcCore::app()->blog->url), 'UTF-8', 'B');
$done = true;
foreach ($recipients as $email) {
@ -744,9 +693,9 @@ class ActivityReport
*/
public function getUserCode(): string
{
$id = is_string(App::auth()->userID()) ? App::auth()->userID() : '';
$pw = is_string(App::auth()->getInfo('user_pwd')) ? App::auth()->getInfo('user_pwd') : '';
$code = pack('a32', $id) . pack('H*', Crypt::hmac(App::config()->masterKey(), $pw));
$id = is_string(dcCore::app()->auth->userID()) ? dcCore::app()->auth->userID() : '';
$pw = is_string(dcCore::app()->auth->getInfo('user_pwd')) ? dcCore::app()->auth->getInfo('user_pwd') : '';
$code = pack('a32', $id) . pack('H*', Crypt::hmac(DC_MASTER_KEY, $pw));
return bin2hex($code);
}
@ -772,7 +721,7 @@ class ActivityReport
$pwd = $pwd['hex'];
$sql = new SelectStatement();
$sql->from(App::con()->prefix() . App::auth()::USER_TABLE_NAME)
$sql->from(dcCore::app()->prefix . dcAuth::USER_TABLE_NAME)
->columns(['user_id', 'user_pwd'])
->where('user_id =' . $sql->quote($user_id));
@ -782,7 +731,7 @@ class ActivityReport
return false;
}
if (Crypt::hmac(App::config()->masterKey(), $rs->f('user_pwd')) != $pwd) {
if (Crypt::hmac(DC_MASTER_KEY, $rs->f('user_pwd')) != $pwd) {
return false;
}

View File

@ -1,5 +1,15 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
@ -7,11 +17,7 @@ namespace Dotclear\Plugin\activityReport;
use Dotclear\Database\MetaRecord;
/**
* @brief activityReport activity record row descriptor class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Activity record row type hinting.
*/
class ActivityRow
{

View File

@ -1,18 +1,35 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use Dotclear\App;
use ArrayObject;
use dcAuth;
use dcCore;
use Dotclear\Core\Backend\Favorites;
use Dotclear\Core\Process;
use Dotclear\Helper\Date;
use Dotclear\Helper\Html\Form\{
Div,
Label,
Para,
Select,
Text
};
/**
* @brief activityReport backend class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Backend process
*/
class Backend extends Process
{
@ -23,22 +40,139 @@ class Backend extends Process
public static function process(): bool
{
if (self::status()) {
// be sure to init report
ActivityReport::init();
if (!self::status()) {
return false;
}
My::addBackendMenuItem();
App::behavior()->addBehaviors([
'adminDashboardFavoritesV2' => BackendBehaviors::adminDashboardFavoritesV2(...),
'adminDashboardContentsV2' => BackendBehaviors::adminDashboardContentsV2(...),
'adminDashboardOptionsFormV2' => BackendBehaviors::adminDashboardOptionsFormV2(...),
'adminAfterDashboardOptionsUpdate' => BackendBehaviors::adminAfterDashboardOptionsUpdate(...),
'adminFiltersListsV2' => BackendBehaviors::adminFiltersListsV2(...),
'adminColumnsListsV2' => BackendBehaviors::adminColumnsListsV2(...),
dcCore::app()->addBehaviors([
// dashboard favorites icon
'adminDashboardFavoritesV2' => function (Favorites $favs): void {
$favs->register(My::id(), [
'title' => My::name(),
'url' => My::manageUrl(),
'small-icon' => My::icons(),
'large-icon' => My::icons(),
'permissions' => dcCore::app()->auth->makePermissions([
dcAuth::PERMISSION_ADMIN,
]),
]);
},
// dashboard content display
'adminDashboardContentsV2' => function (ArrayObject $items): void {
$db = dcCore::app()->auth->user_prefs?->get(My::id())->get('dashboard_item');
$limit = abs(is_numeric($db) ? (int) $db : 0);
if (!$limit) {
return ;
}
return self::status();
$params = new ArrayObject([
'limit' => $limit,
'requests' => true,
]);
$rs = ActivityReport::instance()->getLogs($params);
if (!$rs || $rs->isEmpty()) {
return;
}
$lines = [];
$groups = ActivityReport::instance()->groups;
while ($rs->fetch()) {
$row = new ActivityRow($rs);
if (!$groups->has($row->group)) {
continue;
}
$group = $groups->get($row->group);
$lines[] = '<dt title="' . __($group->title) . '">' .
'<strong>' . __($group->get($row->action)->title) . '</strong>' .
'<br />' . Date::str(
dcCore::app()->blog?->settings->get('system')->get('date_format') . ', ' . dcCore::app()->blog?->settings->get('system')->get('time_format'),
(int) strtotime($row->dt),
is_string(dcCore::app()->auth->getInfo('user_tz')) ? dcCore::app()->auth->getInfo('user_tz') : 'UTC'
) . '<dt>' .
'<dd><p>' .
'<em>' . ActivityReport::parseMessage(__($group->get($row->action)->message), $row->logs) . '</em></p></dd>';
}
if (empty($lines)) {
return ;
}
$items[] = new ArrayObject([
'<div id="activity-report-logs" class="box medium">' .
'<h3>' . My::name() . '</h3>' .
'<dl id="reports">' . implode('', $lines) . '</dl>' .
'<p class="modules"><a class="module-details" href="' .
My::manageUrl() . '">' .
__('View all logs') . '</a> - <a class="module-config" href="' .
dcCore::app()->admin->url->get('admin.plugins', [
'module' => My::id(),
'conf' => 1,
'redir' => dcCore::app()->admin->url->get('admin.home'),
]) . '">' .
__('Configure plugin') . '</a></p>' .
'</div>',
]);
},
// dashboard content user preference form
'adminDashboardOptionsFormV2' => function (): void {
$db = dcCore::app()->auth->user_prefs?->get(My::id())->get('dashboard_item');
echo
(new Div())->class('fieldset')->items([
(new Text('h4', My::name())),
(new Para())->items([
(new Label(__('Number of activities to show on dashboard:'), Label::OUTSIDE_LABEL_BEFORE))->for(My::id() . '_dashboard_item'),
(new Select(My::id() . '_dashboard_item'))->default(is_string($db) ? $db : '')->items([
__('Do not show activity report') => 0,
5 => 5,
10 => 10,
15 => 15,
20 => 20,
50 => 50,
100 => 100,
]),
]),
])->render();
},
// save dashboard content user preference
'adminAfterDashboardOptionsUpdate' => function (?string $user_id = null): void {
if (!is_null($user_id)) {
dcCore::app()->auth->user_prefs?->get(My::id())->put(
'dashboard_item',
(int) $_POST[My::id() . '_dashboard_item'],
'integer'
);
}
},
// list filters
'adminFiltersListsV2' => function (ArrayObject $sorts): void {
$sorts[My::id()] = [
My::name(),
[
__('Group') => 'activity_group',
__('Action') => 'activity_action',
__('Date') => 'activity_dt',
__('Status') => 'activity_status',
],
'activity_dt',
'desc',
[__('logs per page'), 30],
];
},
// list columns user preference
'adminColumnsListsV2' => function (ArrayObject $cols): void {
$cols[My::id()] = [
My::name(),
[
'activity_group' => [true, __('Group')],
'activity_action' => [true, __('Action')],
'activity_dt' => [true, __('Date')],
'activity_status' => [false, __('Status')],
],
];
},
]);
return true;
}
}

View File

@ -1,181 +0,0 @@
<?php
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use ArrayObject;
use Dotclear\App;
use Dotclear\Core\Backend\Favorites;
use Dotclear\Helper\Date;
use Dotclear\Helper\Html\Form\{
Div,
Label,
Para,
Select,
Text
};
/**
* @brief activityReport backend behaviors class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
class BackendBehaviors
{
/**
* Dashboard favorites icon.
*/
public static function adminDashboardFavoritesV2(Favorites $favs): void
{
$favs->register(My::id(), [
'title' => My::name(),
'url' => My::manageUrl(),
'small-icon' => My::icons(),
'large-icon' => My::icons(),
'permissions' => App::auth()->makePermissions([
App::auth()::PERMISSION_ADMIN,
]),
]);
}
/**
* Dashboard content display.
*
* @param ArrayObject<int, mixed> $items
*/
public static function adminDashboardContentsV2(ArrayObject $items): void
{
$db = App::auth()->prefs()->get(My::id())->get('dashboard_item');
$limit = abs(is_numeric($db) ? (int) $db : 0);
if (!$limit) {
return ;
}
$params = new ArrayObject([
'limit' => $limit,
'requests' => true,
]);
$rs = ActivityReport::instance()->getLogs($params);
if ($rs->isEmpty()) {
return;
}
$lines = [];
$groups = ActivityReport::instance()->groups;
while ($rs->fetch()) {
$row = new ActivityRow($rs);
if (!$groups->has($row->group)) {
continue;
}
$group = $groups->get($row->group);
$lines[] = '<dt title="' . __($group->title) . '">' .
'<strong>' . __($group->get($row->action)->title) . '</strong>' .
'<br />' . Date::str(
App::blog()->settings()->get('system')->get('date_format') . ', ' . App::blog()->settings()->get('system')->get('time_format'),
(int) strtotime($row->dt),
is_string(App::auth()->getInfo('user_tz')) ? App::auth()->getInfo('user_tz') : 'UTC'
) . '<dt>' .
'<dd><p>' .
'<em>' . ActivityReport::parseMessage(__($group->get($row->action)->message), $row->logs) . '</em></p></dd>';
}
if (empty($lines)) {
return ;
}
$items[] = new ArrayObject([
'<div id="activity-report-logs" class="box medium">' .
'<h3>' . My::name() . '</h3>' .
'<dl id="reports">' . implode('', $lines) . '</dl>' .
'<p class="modules"><a class="module-details" href="' .
My::manageUrl() . '">' .
__('View all logs') . '</a> - <a class="module-config" href="' .
App::backend()->url()->get('admin.plugins', [
'module' => My::id(),
'conf' => 1,
'redir' => App::backend()->url()->get('admin.home'),
]) . '">' .
__('Configure plugin') . '</a></p>' .
'</div>',
]);
}
/**
* Dashboard content user preference form.
*/
public static function adminDashboardOptionsFormV2(): void
{
$db = App::auth()->prefs()->get(My::id())->get('dashboard_item');
echo
(new Div())->class('fieldset')->items([
(new Text('h4', My::name())),
(new Para())->items([
(new Label(__('Number of activities to show on dashboard:'), Label::OUTSIDE_LABEL_BEFORE))->for(My::id() . '_dashboard_item'),
(new Select(My::id() . '_dashboard_item'))->default(is_string($db) ? $db : '')->items([
__('Do not show activity report') => 0,
5 => 5,
10 => 10,
15 => 15,
20 => 20,
50 => 50,
100 => 100,
]),
]),
])->render();
}
/**
* Save dashboard content user preference.
*/
public static function adminAfterDashboardOptionsUpdate(?string $user_id = null): void
{
if (!is_null($user_id)) {
App::auth()->prefs()->get(My::id())->put(
'dashboard_item',
(int) $_POST[My::id() . '_dashboard_item'],
'integer'
);
}
}
/**
* List filters.
*
* @param ArrayObject<string, mixed> $sorts
*/
public static function adminFiltersListsV2(ArrayObject $sorts): void
{
$sorts[My::id()] = [
My::name(),
[
__('Group') => 'activity_group',
__('Action') => 'activity_action',
__('Date') => 'activity_dt',
__('Status') => 'activity_status',
],
'activity_dt',
'desc',
[__('logs per page'), 30],
];
}
/**
* List columns user preference.
*
* @param ArrayObject<string, mixed> $cols
*/
public static function adminColumnsListsV2(ArrayObject $cols): void
{
$cols[My::id()] = [
My::name(),
[
'activity_group' => [true, __('Group')],
'activity_action' => [true, __('Action')],
'activity_dt' => [true, __('Date')],
'activity_status' => [false, __('Status')],
],
];
}
}

View File

@ -1,15 +1,21 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
/**
* @brief activityReport combo class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Combo helper.
*/
class Combo
{

View File

@ -1,10 +1,20 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use Dotclear\App;
use dcCore;
use Dotclear\Core\Backend\Notices;
use Dotclear\Core\Process;
use Dotclear\Helper\Date;
@ -22,11 +32,7 @@ use Dotclear\Helper\Html\Form\{
use Exception;
/**
* @brief activityReport config class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Config process.
*/
class Config extends Process
{
@ -79,12 +85,12 @@ class Config extends Process
);
}
App::backend()->url()->redirect('admin.plugins', [
dcCore::app()->admin->url->redirect('admin.plugins', [
'module' => My::id(),
'conf' => 1,
]);
} catch (Exception $e) {
App::error()->add($e->getMessage());
dcCore::app()->error->add($e->getMessage());
}
return true;
@ -97,19 +103,19 @@ class Config extends Process
}
$s = ActivityReport::instance()->settings;
$tz = is_string(App::auth()->getInfo('user_tz')) ? App::auth()->getInfo('user_tz') : 'UTC';
$tz = is_string(dcCore::app()->auth->getInfo('user_tz')) ? dcCore::app()->auth->getInfo('user_tz') : 'UTC';
if (!$s->lastreport) {
$last_report = __('never');
$next_report = __('on new activity');
} else {
$last_report = Date::str(
App::blog()->settings()->get('system')->get('date_format') . ', ' . App::blog()->settings()->get('system')->get('time_format'),
dcCore::app()->blog?->settings->get('system')->get('date_format') . ', ' . dcCore::app()->blog?->settings->get('system')->get('time_format'),
$s->lastreport,
$tz
);
$next_report = Date::str(
App::blog()->settings()->get('system')->get('date_format') . ', ' . App::blog()->settings()->get('system')->get('time_format'),
dcCore::app()->blog?->settings->get('system')->get('date_format') . ', ' . dcCore::app()->blog?->settings->get('system')->get('time_format'),
$s->interval + $s->lastreport,
$tz
);
@ -131,13 +137,13 @@ class Config extends Process
]),
(new Para())->items([
(new Label(__('Recipients:'), Label::OUTSIDE_LABEL_BEFORE))->for('mailinglist'),
(new Input('mailinglist'))->size(60)->maxlength(255)->value(implode(';', $s->mailinglist)),
(new Input('mailinglist'))->size(60)->maxlenght(255)->value(implode(';', $s->mailinglist)),
]),
(new Note())->class('form-note')->text(__('Separate multiple email addresses with a semicolon ";"')),
(new Note())->class('form-note')->text(__('Leave it empty to disable mail report.')),
(new Para())->items([
(new Label(__('Date format:'), Label::OUTSIDE_LABEL_BEFORE))->for('dateformat'),
(new Input('dateformat'))->size(60)->maxlength(255)->value($s->dateformat),
(new Input('dateformat'))->size(60)->maxlenght(255)->value($s->dateformat),
]),
(new Note())->class('form-note')->text(__('Use Dotclear date formaters. ex: %B %d at %H:%M')),
(new Para())->items([
@ -160,11 +166,11 @@ class Config extends Process
'ul',
'<li><img alt="' . __('RSS feed') . '" src="' . My::fileURL('img/feed.png') . '" /> ' .
'<a title="' . __('RSS feed') . '" href="' .
App::blog()->url() . App::url()->getBase(My::id()) . '/rss2/' . ActivityReport::instance()->getUserCode() . '">' .
dcCore::app()->blog?->url . dcCore::app()->url->getBase(My::id()) . '/rss2/' . ActivityReport::instance()->getUserCode() . '">' .
__('Rss2 activities feed') . '</a></li>' .
'<li><img alt="' . __('Atom feed') . '" src="' . My::fileURL('img/feed.png') . '" /> ' .
'<a title="' . __('Atom feed') . '" href="' .
App::blog()->url() . App::url()->getBase(My::id()) . '/atom/' . ActivityReport::instance()->getUserCode() . '">' .
dcCore::app()->blog?->url . dcCore::app()->url->getBase(My::id()) . '/atom/' . ActivityReport::instance()->getUserCode() . '">' .
__('Atom activities feed') . '</a></li>'
)),
]),

View File

@ -1,20 +1,26 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use Dotclear\App;
use dcCore;
use Dotclear\Database\MetaRecord;
/**
* @brief activityReport frontend context class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Template helper.
*/
class FrontendContext
class Context
{
/**
* Parse title.
@ -23,14 +29,15 @@ class FrontendContext
*/
public static function parseTitle(): string
{
if (!App::frontend()->context()->exists('activityreports')
|| !(App::frontend()->context()->__get('activityreports') instanceof MetaRecord)
if (!dcCore::app()->ctx
|| !dcCore::app()->ctx->exists('activityreports')
|| !(dcCore::app()->ctx->__get('activityreports') instanceof MetaRecord)
) {
return '';
}
$group = App::frontend()->context()->__get('activityreports')->f('activity_group');
$action = App::frontend()->context()->__get('activityreports')->f('activity_action');
$group = dcCore::app()->ctx->__get('activityreports')->f('activity_group');
$action = dcCore::app()->ctx->__get('activityreports')->f('activity_action');
if (!is_string($group)
|| !is_string($action)
@ -49,15 +56,16 @@ class FrontendContext
*/
public static function parseContent(): string
{
if (!App::frontend()->context()->exists('activityreports')
|| !(App::frontend()->context()->__get('activityreports') instanceof MetaRecord)
if (!dcCore::app()->ctx
|| !dcCore::app()->ctx->exists('activityreports')
|| !(dcCore::app()->ctx->__get('activityreports') instanceof MetaRecord)
) {
return '';
}
$group = App::frontend()->context()->__get('activityreports')->f('activity_group');
$action = App::frontend()->context()->__get('activityreports')->f('activity_action');
$logs = App::frontend()->context()->__get('activityreports')->f('activity_logs');
$group = dcCore::app()->ctx->__get('activityreports')->f('activity_group');
$action = dcCore::app()->ctx->__get('activityreports')->f('activity_action');
$logs = dcCore::app()->ctx->__get('activityreports')->f('activity_logs');
$logs = json_decode(is_string($logs) ? $logs : '', true);
if (!is_string($group)
@ -68,9 +76,9 @@ class FrontendContext
return '';
}
App::filter()->initWikiComment();
dcCore::app()->initWikiComment();
return App::filter()->wikiTransform(vsprintf(
return dcCore::app()->wikiTransform(vsprintf(
__(ActivityReport::instance()->groups->get($group)->get($action)->message),
$logs
));

View File

@ -1,15 +1,21 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
/**
* @brief activityReport format decriptor.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Report format descriptor.
*/
class Format
{

View File

@ -1,15 +1,21 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
/**
* @brief activityReport formats stack.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Email report formats stack.
*/
class Formats
{

View File

@ -1,18 +1,24 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use Dotclear\App;
use dcCore;
use Dotclear\Core\Process;
/**
* @brief activityReport frontend class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Front end process.
*/
class Frontend extends Process
{
@ -23,19 +29,18 @@ class Frontend extends Process
public static function process(): bool
{
if (self::status()) {
// be sure to init report
ActivityReport::init();
$tpl = App::frontend()->template();
$tpl->setPath($tpl->getPath(), implode(DIRECTORY_SEPARATOR, [My::path(), 'default-templates', 'tpl']));
$tpl->addBlock('activityReports', FrontendTemplate::activityReports(...));
$tpl->addValue('activityReportFeedID', FrontendTemplate::activityReportFeedID(...));
$tpl->addValue('activityReportTitle', FrontendTemplate::activityReportTitle(...));
$tpl->addValue('activityReportDate', FrontendTemplate::activityReportDate(...));
$tpl->addValue('activityReportContent', FrontendTemplate::activityReportContent(...));
if (!self::status()) {
return false;
}
return self::status();
dcCore::app()->tpl->setPath(dcCore::app()->tpl->getPath(), implode(DIRECTORY_SEPARATOR, [My::path(), 'default-templates', 'tpl']));
dcCore::app()->tpl->addBlock('activityReports', [Template::class, 'activityReports']);
dcCore::app()->tpl->addValue('activityReportFeedID', [Template::class, 'activityReportFeedID']);
dcCore::app()->tpl->addValue('activityReportTitle', [Template::class, 'activityReportTitle']);
dcCore::app()->tpl->addValue('activityReportDate', [Template::class, 'activityReportDate']);
dcCore::app()->tpl->addValue('activityReportContent', [Template::class, 'activityReportContent']);
return true;
}
}

View File

@ -1,55 +0,0 @@
<?php
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use Dotclear\App;
use Dotclear\Core\Frontend\Url;
/**
* @brief activityReport frontend URL handler class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
class FrontendUrl extends Url
{
/**
* Get activity logs feed.
*
* @param null|string $args The URL arguments
*/
public static function feed(?string $args): void
{
// no context or wrong URL args or module no loaded or report unactive
if (!preg_match('/^(atom|rss2)\/(.+)$/', (string) $args, $m)
|| !ActivityReport::instance()->settings->feed_active
) {
self::p404();
}
/*
if (!is_array($m) || count($m) < 2 || !is_string($m[1]) || !is_string($m[2])) {
self::p404();
}
*/
// get type of feed
$mime = $m[1] == 'atom' ? 'application/atom+xml' : 'application/xml';
if (false === ActivityReport::instance()->checkUserCode($m[2])) {
self::p404();
}
// feed limits
$nb = App::blog()->settings()->get('system')->get('nb_post_per_feed');
//$it = App::blog()->settings()->get('system')->get('short_feed_items');
$rb = App::blog()->settings()->get('system')->get('robots_policy');
App::frontend()->context()->__set('nb_entry_per_page', is_numeric($nb) ? (int) $nb : 20);
// App::frontend->context()->__set('short_feed_items', is_numerci($it) ? (int) $it : 1);
// serve activity feed template
header('X-Robots-Tag: ' . App::frontend()->context()::robotsPolicy(is_string($rb) ? $rb : '', ''));
self::serveDocument('activityreport-' . $m[1] . '.xml', $mime);
}
}

View File

@ -1,15 +1,21 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
/**
* @brief activityReport actions stack class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Actions stack.
*/
class Group
{

View File

@ -1,23 +1,25 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
/**
* @brief activityReport actions groups stack class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Actions groups stack.
*/
class Groups
{
/**
* The actions groups stack.
*
* @var array<string, Group> $stack
*/
/** @var array<string, Group> $stack The actions groups stack */
private array $stack = [];
/**
@ -35,8 +37,6 @@ class Groups
/**
* Add a group.
*
* Existing group will be overwritten.
*
* @param Group $group The group object
*
* @return Groups The groups instance

View File

@ -1,10 +1,20 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use Dotclear\App;
use dcCore;
use Dotclear\Core\Process;
use Dotclear\Database\Structure;
use Dotclear\Database\Statement\{
@ -14,11 +24,7 @@ use Dotclear\Database\Statement\{
use Exception;
/**
* @brief activityReport install class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Install process.
*/
class Install extends Process
{
@ -36,7 +42,7 @@ class Install extends Process
try {
self::beforeGrowUp();
$s = new Structure(App::con(), App::con()->prefix());
$s = new Structure(dcCore::app()->con, dcCore::app()->prefix);
$s->__get(My::ACTIVITY_TABLE_NAME)
->field('activity_id', 'bigint', 0, false)
->field('activity_type', 'varchar', 32, false, "'" . My::id() . "'")
@ -53,11 +59,11 @@ class Install extends Process
->index('idx_activity_action', 'btree', 'activity_group', 'activity_action')
->index('idx_activity_status', 'btree', 'activity_status');
(new Structure(App::con(), App::con()->prefix()))->synchronize($s);
(new Structure(dcCore::app()->con, dcCore::app()->prefix))->synchronize($s);
return true;
} catch (Exception $e) {
App::error()->add($e->getMessage());
dcCore::app()->error->add($e->getMessage());
return false;
}
@ -69,17 +75,17 @@ class Install extends Process
private static function beforeGrowUp(): void
{
// sorry not sorry we restart from scratch
if (is_string(App::version()->getVersion('activityReport'))
&& version_compare(App::version()->getVersion('activityReport'), '3.0', '<')
if (is_string(dcCore::app()->getVersion('activityReport'))
&& version_compare(dcCore::app()->getVersion('activityReport'), '3.0', '<')
) {
$struct = new Structure(App::con(), App::con()->prefix());
$struct = new Structure(dcCore::app()->con, dcCore::app()->prefix);
if ($struct->tableExists('activity')) {
(new TruncateStatement())->from(App::con()->prefix() . 'activity')->truncate();
(new TruncateStatement())->from(dcCore::app()->prefix . 'activity')->truncate();
}
if ($struct->tableExists('activity_settings')) {
(new TruncateStatement())->from(App::con()->prefix() . 'activity_settings')->truncate();
(new DropStatement())->from(App::con()->prefix() . 'activity_settings')->drop();
(new TruncateStatement())->from(dcCore::app()->prefix . 'activity_settings')->truncate();
(new DropStatement())->from(dcCore::app()->prefix . 'activity_settings')->drop();
}
}
}

View File

@ -1,15 +1,22 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use ArrayObject;
use Dotclear\App;
use Dotclear\Core\Backend\Filter\{
Filters,
FiltersLibrary
};
use dcCore;
use Dotclear\Core\Backend\Filter\Filters;
use Dotclear\Core\Backend\{
Notices,
Page
@ -25,11 +32,7 @@ use Dotclear\Helper\Html\Form\{
use Exception;
/**
* @brief activityReport manage class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Manage process (admin logs list).
*/
class Manage extends Process
{
@ -50,7 +53,7 @@ class Manage extends Process
Notices::addSuccessNotice(__('Logs successfully deleted'));
My::redirect();
} catch (Exception $e) {
App::error()->add($e->getMessage());
dcCore::app()->error->add($e->getMessage());
}
}
@ -65,15 +68,16 @@ class Manage extends Process
$logs = $counter = $list = null;
$filter = new Filters(My::id());
$filter->add(FiltersLibrary::getPageFilter());
$params = new ArrayObject($filter->params());
try {
$logs = ActivityReport::instance()->getLogs($params);
$counter = ActivityReport::instance()->getLogs($params, true);
if (!is_null($logs) && !is_null($counter)) {
$list = new ManageList($logs, $counter->f(0));
}
} catch (Exception $e) {
App::error()->add($e->getMessage());
dcCore::app()->error->add($e->getMessage());
}
Page::openModule(
@ -83,7 +87,7 @@ class Manage extends Process
My::jsLoad('backend') .
# --BEHAVIOR-- activityReportListHeader --
App::behavior()->callBehavior('activityReportListHeader')
dcCore::app()->callBehavior('activityReportListHeader')
);
echo
@ -100,7 +104,7 @@ class Manage extends Process
if (!is_null($logs) && !$logs->isEmpty()) {
echo
(new Form('form-logs'))->method('post')->action(App::backend()->getPageURL())->fields([
(new Form('form-logs'))->method('post')->action(dcCore::app()->admin->getPageURL())->fields([
(new Para())->class('right')->separator(' ')->items([
(new Submit('delete_all_logs'))->class('delete')->value(__('Delete all aticivity logs')),
(new Submit('delete_reported_logs'))->class('delete')->value(__('Delete all allready reported logs')),

View File

@ -1,107 +1,80 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use ArrayObject;
use Dotclear\App;
use dcCore;
use Dotclear\Core\Backend\Filter\Filters;
use Dotclear\Core\Backend\Listing\{
Listing,
Pager
};
use Dotclear\Helper\Date;
use Dotclear\Helper\Html\Form\{
Caption,
Div,
Note,
Table,
Tbody,
Td,
Text,
Th,
Tr
};
use Dotclear\Helper\Html\Html;
/**
* @brief activityReport manage logs list class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Logs admin list helper.
*/
class ManageList extends Listing
{
public function logsDisplay(Filters $filter, string $enclose_block = ''): void
{
if ($this->rs->isEmpty()) {
echo (new Note())
->text($filter->show() ? __('No log matches the filter') : __('No log'))
->class('info')
->render();
return;
if (!$this->rs || $this->rs->isEmpty()) {
if ($filter->show()) {
echo '<p><strong>' . __('No log matches the filter') . '</strong></p>';
} else {
echo '<p><strong>' . __('No log') . '</strong></p>';
}
$page = is_numeric($filter->value('page')) ? (int) $filter->value('page') : 1;
$nbpp = is_numeric($filter->value('nb')) ? (int) $filter->value('nb') : 20;
$count = (int) $this->rs_count;
$pager = new Pager($page, $count, $nbpp, 10);
} else {
$page = $filter->value('page');
$nbpp = $filter->value('nb');
$pager = new Pager(is_numeric($page) ? (int) $page : 1, (int) $this->rs_count, is_numeric($nbpp) ? (int) $nbpp : 20, 10);
$pager->var_page = 'page';
$html_block = '<div class="table-outer"><table><caption>' . (
$filter->show() ?
sprintf(__('List of %s logs matching the filter.'), $this->rs_count) :
sprintf(__('List of %s logs.'), $this->rs_count)
) . '</caption>';
$cols = new ArrayObject([
'activity_group' => (new Th())
->text(__('Group'))
->scope('col'),
'activity_action' => (new Th())
->text(__('Action'))
->scope('col'),
'activity_logs' => (new Th())
->text(__('Message'))
->scope('col'),
'activity_date' => (new Th())
->text(__('Date'))
->scope('col'),
'activity_status' => (new Th())
->text(__('Status'))
->scope('col'),
'activity_group' => '<th scope="col" class="nowrap">' . __('Group') . '</th>',
'activity_action' => '<th scope="col" class="nowrap">' . __('Action') . '</th>',
'activity_logs' => '<th scope="col" class="nowrap">' . __('Message') . '</th>',
'activity_date' => '<th scope="col" class="nowrap">' . __('Date') . '</th>',
'activity_status' => '<th scope="col" class="nowrap">' . __('Status') . '</th>',
]);
$this->userColumns(My::id(), $cols);
$lines = [];
$html_block .= '<tr>' . implode(iterator_to_array($cols)) . '</tr>%s</table>%s</div>';
if ($enclose_block) {
$html_block = sprintf($enclose_block, $html_block);
}
$blocks = explode('%s', $html_block);
echo $pager->getLinks() . $blocks[0];
while ($this->rs->fetch()) {
$lines[] = $this->line();
echo $this->logsLine();
}
echo
$pager->getLinks() .
sprintf(
$enclose_block,
(new Div())
->class('table-outer')
->items([
(new Table())
->items([
(new Caption(
$filter->show() ?
sprintf(__('List of %s logs matching the filter.'), $count) :
sprintf(__('List of logs. (%s)'), $count)
)),
(new Tr())
->items(iterator_to_array($cols)),
(new Tbody())
->items($lines),
]),
])
->render()
) .
$pager->getLinks();
echo $blocks[1] . $blocks[2] . $pager->getLinks();
}
}
private function line(): Tr
private function logsLine(): string
{
$row = new ActivityRow($this->rs);
@ -110,34 +83,25 @@ class ManageList extends Listing
$action = $group->get($row->action);
$message = ActivityReport::parseMessage(__($action->message), $row->logs);
$date = Date::str(
App::blog()->settings()->get('system')->get('date_format') . ', ' . App::blog()->settings()->get('system')->get('time_format'),
dcCore::app()->blog?->settings->get('system')->get('date_format') . ', ' . dcCore::app()->blog?->settings->get('system')->get('time_format'),
(int) strtotime($row->dt),
is_string(App::auth()->getInfo('user_tz')) ? App::auth()->getInfo('user_tz') : 'UTC'
is_string(dcCore::app()->auth->getInfo('user_tz')) ? dcCore::app()->auth->getInfo('user_tz') : 'UTC'
);
$status = $row->status == ActivityReport::STATUS_PENDING ? __('pending') : __('reported');
$cols = new ArrayObject([
'activity_group' => (new Td())
->text(Html::escapeHTML(__($group->title)))
->class('nowrap'),
'activity_action' => (new Td())
->text(Html::escapeHTML(__($action->title)))
->class('nowrap'),
'activity_logs' => (new Td())
->text(Html::escapeHTML($message))
->class('maximal'),
'activity_date' => (new Td())
->text(Html::escapeHTML($date))
->class('nowrap'),
'activity_status' => (new Td())
->text(Html::escapeHTML($status))
->class('nowrap'),
'activity_group' => '<td class="nowrap">' . __($group->title) . '</td>',
'activity_action' => '<td class="nowrap">' . __($action->title) . '</td>',
'activity_logs' => '<td class="maximal">' . $message . '</td>',
'activity_date' => '<td class="nowrap">' . $date . '</td>',
'activity_status' => '<td class="nowrap">' . $status . '</td>',
]);
$this->userColumns(My::id(), $cols);
return (new Tr('l' . $row->id))
->class('line' . $offline)
->items(iterator_to_array($cols));
return
'<tr class="line ' . $offline . '" id="l' . $row->id . '">' .
implode(iterator_to_array($cols)) .
'</tr>';
}
}

View File

@ -1,26 +1,59 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use dcCore;
use Dotclear\Module\MyPlugin;
/**
* @brief activityReport My helper.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* This module definitions.
*/
class My extends MyPlugin
{
/**
* Activity database table name.
*
* @var string ACTIVITY_TABLE_NAME
*/
/** @var string Activity database table name */
public const ACTIVITY_TABLE_NAME = 'activity';
// Use default permissions
/** @var int Incremental version by breaking changes */
public const COMPATIBILITY_VERSION = 3;
public static function checkCustomContext(int $context): ?bool
{
switch($context) {
case My::FRONTEND:
return defined('ACTIVITY_REPORT') && My::isInstalled();
case My::BACKEND:
return defined('DC_CONTEXT_ADMIN') && defined('ACTIVITY_REPORT') && My::isInstalled();
case My::CONFIG:
case My::MANAGE:
return defined('DC_CONTEXT_ADMIN')
&& defined('ACTIVITY_REPORT')
&& dcCore::app()->auth->check(dcCore::app()->auth->makePermissions([
dcCore::app()->auth::PERMISSION_ADMIN,
]), dcCore::app()->blog?->id);
default:
return null;
}
}
/**
* Check is module is trully installed.
*
* Required as table structrue has changed
*/
public static function isInstalled(): bool
{
return dcCore::app()->getVersion(self::id()) == (string) dcCore::app()->plugins->getDefine(self::id())->get('version');
}
}

View File

@ -1,18 +1,25 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use Dotclear\App;
use dcCore;
use Dotclear\Core\Process;
use Exception;
/**
* @brief activityReport prepend class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Prepend process.
*/
class Prepend extends Process
{
@ -27,14 +34,30 @@ class Prepend extends Process
return false;
}
if (defined('ACTIVITY_REPORT')) {
return true;
}
try {
// launch once activity report stuff
ActivityReport::instance();
// regirster activity feed URL
App::url()->register(
dcCore::app()->url->register(
My::id(),
'reports',
'^reports/((atom|rss2)/(.+))$',
FrontendUrl::feed(...)
[UrlHandler::class, 'feed']
);
// declare report open
define('ACTIVITY_REPORT', My::COMPATIBILITY_VERSION);
// register predefined activities scan
ActivityBehaviors::register();
} catch (Exception $e) {
}
return true;
}
}

View File

@ -1,15 +1,21 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
/**
* @brief activityReport settings helper class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Module settings helper.
*/
class Settings
{

View File

@ -1,21 +1,27 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use ArrayObject;
use Dotclear\App;
use dcCore;
use Dotclear\Helper\Date;
/**
* @brief activityReport frontend template class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Template blocs and values.
*/
class FrontendTemplate
class Template
{
/**
* tpl:activityReports [attributes] : Activity report logs (tpl block)
@ -25,7 +31,7 @@ class FrontendTemplate
* - lastn integer Limit to last n logs
* - ingnore_pagination 1|0 Ignore pagination paramaters
*
* @param ArrayObject<string, mixed> $attr The attributes
* @param ArrayObject $attr The attributes
* @param string $content The content
*
* @return string The code
@ -37,16 +43,16 @@ class FrontendTemplate
$lastn = abs((int) $attr['lastn']) + 0;
}
$p = '$page_number = App::frontend()->getPageNumber(); if ($page_number < 1) { $page_number = 1; }' . "\n\$params = new ArrayObject();\n";
$p = '$_page_number = dcCore::app()->public->getPageNumber(); if ($_page_number < 1) { $_page_number = 1; }' . "\n\$params = new ArrayObject();\n";
if ($lastn > 0) {
$p .= "\$params['limit'] = " . $lastn . ";\n";
} else {
$p .= "\$params['limit'] = App::frontend()->context()->nb_entry_per_page;\n";
$p .= "\$params['limit'] = dcCore::app()->ctx->nb_entry_per_page;\n";
}
if (!isset($attr['ignore_pagination']) || $attr['ignore_pagination'] == '0') {
$p .= "\$params['limit'] = array(((\$page_number-1)*\$params['limit']),\$params['limit']);\n";
$p .= "\$params['limit'] = array(((\$_page_number-1)*\$params['limit']),\$params['limit']);\n";
} else {
$p .= "\$params['limit'] = array(0, \$params['limit']);\n";
}
@ -54,10 +60,10 @@ class FrontendTemplate
return
"<?php \n" .
$p .
'App::frontend()->context()->activityreport_params = $params; ' . "\n" .
'App::frontend()->context()->activityreports = ' . ActivityReport::class . '::instance()->getLogs($params); unset($params); ' . "\n" .
'while (App::frontend()->context()->activityreports->fetch()) : ?>' . $content . '<?php endwhile; ' .
'App::frontend()->context()->pop("activityreports"); App::frontend()->context()->pop("activityreport_params"); ' . "\n" .
'dcCore::app()->ctx->activityreport_params = $params; ' . "\n" .
'dcCore::app()->ctx->activityreports = ' . ActivityReport::class . '::instance()->getLogs($params); unset($params); ' . "\n" .
'while (dcCore::app()->ctx->activityreports->fetch()) : ?>' . $content . '<?php endwhile; ' .
'dcCore::app()->ctx->pop("activityreports"); dcCore::app()->ctx->pop("activityreport_params"); ' . "\n" .
'?>';
}
@ -68,15 +74,15 @@ class FrontendTemplate
*
* - any filters See self::getFilters()
*
* @param ArrayObject<string, mixed> $attr The attributes
* @param ArrayObject $attr The attributes
*
* @return string The code
*/
public static function activityReportFeedID(ArrayObject $attr): string
{
return
'urn:md5:<?php echo md5(App::frontend()->context()->activityreports->blog_id.' .
'App::frontend()->context()->activityreports->activity_id.App::frontend()->context()->activityreports->activity_dt); ' .
'urn:md5:<?php echo md5(dcCore::app()->ctx->activityreports->blog_id.' .
'dcCore::app()->ctx->activityreports->activity_id.dcCore::app()->ctx->activityreports->activity_dt); ' .
'?>';
}
@ -87,15 +93,15 @@ class FrontendTemplate
*
* - any filters See self::getFilters()
*
* @param ArrayObject<string, mixed> $attr The attributes
* @param ArrayObject $attr The attributes
*
* @return string The code
*/
public static function activityReportTitle(ArrayObject $attr): string
{
$f = App::frontend()->template()->getFilters($attr);
$f = dcCore::app()->tpl->getFilters($attr);
return '<?php echo ' . sprintf($f, FrontendContext::class . '::parseTitle()') . '; ?>';
return '<?php echo ' . sprintf($f, Context::class . '::parseTitle()') . '; ?>';
}
/**
@ -105,15 +111,15 @@ class FrontendTemplate
*
* - any filters See self::getFilters()
*
* @param ArrayObject<string, mixed> $attr The attributes
* @param ArrayObject $attr The attributes
*
* @return string The code
*/
public static function activityReportContent(ArrayObject $attr): string
{
$f = App::frontend()->template()->getFilters($attr);
$f = dcCore::app()->tpl->getFilters($attr);
return '<?php echo ' . sprintf($f, FrontendContext::class . '::parseContent()') . '; ?>';
return '<?php echo ' . sprintf($f, Context::class . '::parseContent()') . '; ?>';
}
/**
@ -126,7 +132,7 @@ class FrontendTemplate
* - rfc822 (1|0) Use Date::rfc822()
* - any filters See self::getFilters()
*
* @param ArrayObject<string, mixed> $attr The attributes
* @param ArrayObject $attr The attributes
*
* @return string The code
*/
@ -140,16 +146,16 @@ class FrontendTemplate
$iso8601 = !empty($attr['iso8601']);
$rfc822 = !empty($attr['rfc822']);
$f = App::frontend()->template()->getFilters($attr);
$f = dcCore::app()->tpl->getFilters($attr);
if ($rfc822) {
return '<?php echo ' . sprintf($f, Date::class . '::rfc822(strtotime(App::frontend()->context()->activityreports->activity_dt),App::blog()->settings()->system->blog_timezone)') . '; ?>';
return '<?php echo ' . sprintf($f, Date::class . '::rfc822(strtotime(dcCore::app()->ctx->activityreports->activity_dt),dcCore::app()->blog->settings->system->blog_timezone)') . '; ?>';
} elseif ($iso8601) {
return '<?php echo ' . sprintf($f, Date::class . '::iso8601(strtotime(App::frontend()->context()->activityreports->activity_dt),App::blog()->settings()->system->blog_timezone)') . '; ?>';
return '<?php echo ' . sprintf($f, Date::class . '::iso8601(strtotime(dcCore::app()->ctx->activityreports->activity_dt),dcCore::app()->blog->settings->system->blog_timezone)') . '; ?>';
} elseif (!empty($format)) {
return '<?php echo ' . sprintf($f, Date::class . "::dt2str('" . $format . "',App::frontend()->context()->activityreports->activity_dt)") . '; ?>';
return '<?php echo ' . sprintf($f, Date::class . "::dt2str('" . $format . "',dcCore::app()->ctx->activityreports->activity_dt)") . '; ?>';
}
return '<?php echo ' . sprintf($f, Date::class . '::dt2str(App::blog()->settings()->system->date_format,App::frontend()->context()->activityreports->activity_dt)') . '; ?>';
return '<?php echo ' . sprintf($f, Date::class . '::dt2str(dcCore::app()->blog->settings->system->date_format,dcCore::app()->ctx->activityreports->activity_dt)') . '; ?>';
}
}

View File

@ -1,18 +1,27 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use dcCore;
use Dotclear\Core\Process;
use Dotclear\Plugin\Uninstaller\Uninstaller;
/**
* @brief activityReport uninstall class.
* @ingroup activityReport
* Uninstall process.
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* Using plugin Uninstaller
*/
class Uninstall extends Process
{
@ -23,7 +32,7 @@ class Uninstall extends Process
public static function process(): bool
{
if (!self::status()) {
if (!self::status() || !dcCore::app()->plugins->moduleExists('Uninstaller')) {
return false;
}

64
src/UrlHandler.php 100644
View File

@ -0,0 +1,64 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use context;
use dcCore;
use dcUrlHandlers;
/**
* Frontend URL handler.
*/
class UrlHandler extends dcUrlHandlers
{
/**
* Get activity logs feed.
*
* @param null|string $args The URL arguments
*/
public static function feed(?string $args): void
{
// no context or wrong URL args or module no loaded or report unactive
if (!dcCore::app()->ctx
|| !preg_match('/^(atom|rss2)\/(.+)$/', (string) $args, $m)
|| !defined('ACTIVITY_REPORT')
|| !ActivityReport::instance()->settings->feed_active
) {
self::p404();
}
/*
if (!is_array($m) || count($m) < 2 || !is_string($m[1]) || !is_string($m[2])) {
self::p404();
}
*/
// get type of feed
$mime = $m[1] == 'atom' ? 'application/atom+xml' : 'application/xml';
if (false === ActivityReport::instance()->checkUserCode($m[2])) {
self::p404();
}
// feed limits
$nb = dcCore::app()->blog?->settings->get('system')->get('nb_post_per_feed');
//$it = dcCore::app()->blog?->settings->get('system')->get('short_feed_items');
$rb = dcCore::app()->blog?->settings->get('system')->get('robots_policy');
dcCore::app()->ctx->__set('nb_entry_per_page', is_numeric($nb) ? (int) $nb : 20);
// dcCore::app()->ctx->__set('short_feed_items', is_numerci($it) ? (int) $it : 1);
// serve activity feed template
header('X-Robots-Tag: ' . context::robotsPolicy(is_string($rb) ? $rb : '', ''));
self::serveDocument('activityreport-' . $m[1] . '.xml', $mime);
}
}