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 activityReport 3.3 - 2023.08.17
=========================================================== ===========================================================
* Require Dotclear 2.27 * Require Dotclear 2.27

View File

@ -1,24 +1,28 @@
# README # README
[![Release](https://img.shields.io/badge/release-3.4.1-a2cbe9.svg)](https://git.dotclear.watch/JcDenis/activityReport/releases) [![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.10.24-c44d58.svg) [![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.28-137bbb.svg)](https://fr.dotclear.org/download) [![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) [![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 ## REQUIREMENTS
* Dotclear 2.28 _activityReport_ requires:
* Permissions admin
* Dotclear 2.27
* PHP 8.1+ * PHP 8.1+
* System permissions to add table on database * permission to add table on database
* System permissions to send email (optionnal) * permission to send email
* Dotclear admin permissions
## USAGE ## USAGE
@ -31,13 +35,11 @@ go to ''configure plugin'', fill in form.
Once it's done you can manage your reports from menu Once it's done you can manage your reports from menu
''Activity report'' on sidebar or you can add dashboard icon. ''Activity report'' on sidebar or you can add dashboard icon.
## LINKS ## MORE
* [License](https://git.dotclear.watch/JcDenis/activityReport/src/branch/master/LICENSE) * License : [GNU GPL v2](https://www.gnu.org/licenses/old-licenses/lgpl-2.0.html)
* [Packages & details](https://git.dotclear.watch/JcDenis/activityReport/releases) (or on [Dotaddict](https://plugins.dotaddict.org/dc2/details/activityReport)) * Source & contribution : [Gitea Page](https://git.dotclear.watch/JcDenis/activityReport) or [GitHub Page](https://github.com/JcDenis/activityReport)
* [Sources & contributions](https://git.dotclear.watch/JcDenis/activityReport) (or on [GitHub](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)
* [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)
## CONTRIBUTORS ## CONTRIBUTORS

View File

@ -1,30 +1,38 @@
<?php <?php
/** /**
* @file * @brief activityReport, a plugin for Dotclear 2
* @brief The plugin activityReport definition
* @ingroup activityReport
* *
* @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 * @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( $this->registerModule(
'Activity log', 'Activity log',
'Log and receive your blog activity by email, feed, or on dashboard', 'Log and receive your blog activity by email, feed, or on dashboard',
'Jean-Christian Denis and contributors', 'Jean-Christian Denis and contributors',
'3.4.1', '3.3',
[ [
'requires' => [['core', '2.28']], 'requires' => [
'permissions' => 'My', ['php', '8.1'],
'priority' => 2, ['core', '2.27'],
'type' => 'plugin', ],
'support' => 'https://git.dotclear.watch/JcDenis/' . basename(__DIR__) . '/issues', 'permissions' => dcCore::app()->auth->makePermissions([
'details' => 'https://git.dotclear.watch/JcDenis/' . basename(__DIR__) . '/src/branch/master/README.md', dcCore::app()->auth::PERMISSION_USAGE,
'repository' => 'https://git.dotclear.watch/JcDenis/' . basename(__DIR__) . '/raw/branch/master/dcstore.xml', dcCore::app()->auth::PERMISSION_CONTENT_ADMIN,
dcCore::app()->auth::PERMISSION_ADMIN,
]),
'priority' => 2,
'type' => 'plugin',
'support' => 'https://git.dotclear.watch/JcDenis/' . basename(__DIR__) . '/issues',
'details' => 'https://git.dotclear.watch/JcDenis/' . basename(__DIR__) . '/src/branch/master/README.md',
'repository' => 'https://git.dotclear.watch/JcDenis/' . basename(__DIR__) . '/raw/branch/master/dcstore.xml',
] ]
); );

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

View File

@ -1,27 +1,33 @@
<?php <?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); declare(strict_types=1);
namespace Dotclear\Plugin\activityReport; namespace Dotclear\Plugin\activityReport;
use Dotclear\App; use dcCore;
/** /**
* @brief activityReport action descriptor class. * Action descriptor.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Action class Action
{ {
/** /**
* Constructor sets action description. * Constructor sets action description.
* *
* @param string $id The action ID * @param string $id The action ID
* @param string $title The action title * @param string $title The action title
* @param string $message The action message * @param string $message The action message
* @param string $behavior The behavior name * @param string $behavior The behavior name
* @param null|callable $function The callback function * @param null|callable $function The callback function
*/ */
public function __construct( public function __construct(
@ -33,7 +39,7 @@ class Action
) { ) {
// fake action has no behavior // fake action has no behavior
if (!is_null($function)) { if (!is_null($function)) {
App::behavior()->addBehavior($behavior, $function); dcCore::app()->addBehavior($behavior, $function);
} }
} }
} }

View File

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

View File

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

View File

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

View File

@ -1,18 +1,35 @@
<?php <?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); declare(strict_types=1);
namespace Dotclear\Plugin\activityReport; namespace Dotclear\Plugin\activityReport;
use Dotclear\App; use ArrayObject;
use dcAuth;
use dcCore;
use Dotclear\Core\Backend\Favorites;
use Dotclear\Core\Process; use Dotclear\Core\Process;
use Dotclear\Helper\Date;
use Dotclear\Helper\Html\Form\{
Div,
Label,
Para,
Select,
Text
};
/** /**
* @brief activityReport backend class. * Backend process
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Backend extends Process class Backend extends Process
{ {
@ -23,22 +40,139 @@ class Backend extends Process
public static function process(): bool public static function process(): bool
{ {
if (self::status()) { if (!self::status()) {
// be sure to init report return false;
ActivityReport::init();
My::addBackendMenuItem();
App::behavior()->addBehaviors([
'adminDashboardFavoritesV2' => BackendBehaviors::adminDashboardFavoritesV2(...),
'adminDashboardContentsV2' => BackendBehaviors::adminDashboardContentsV2(...),
'adminDashboardOptionsFormV2' => BackendBehaviors::adminDashboardOptionsFormV2(...),
'adminAfterDashboardOptionsUpdate' => BackendBehaviors::adminAfterDashboardOptionsUpdate(...),
'adminFiltersListsV2' => BackendBehaviors::adminFiltersListsV2(...),
'adminColumnsListsV2' => BackendBehaviors::adminColumnsListsV2(...),
]);
} }
return self::status(); My::addBackendMenuItem();
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 ;
}
$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 <?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); declare(strict_types=1);
namespace Dotclear\Plugin\activityReport; namespace Dotclear\Plugin\activityReport;
/** /**
* @brief activityReport combo class. * Combo helper.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Combo class Combo
{ {

View File

@ -1,10 +1,20 @@
<?php <?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); declare(strict_types=1);
namespace Dotclear\Plugin\activityReport; namespace Dotclear\Plugin\activityReport;
use Dotclear\App; use dcCore;
use Dotclear\Core\Backend\Notices; use Dotclear\Core\Backend\Notices;
use Dotclear\Core\Process; use Dotclear\Core\Process;
use Dotclear\Helper\Date; use Dotclear\Helper\Date;
@ -22,11 +32,7 @@ use Dotclear\Helper\Html\Form\{
use Exception; use Exception;
/** /**
* @brief activityReport config class. * Config process.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Config extends 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(), 'module' => My::id(),
'conf' => 1, 'conf' => 1,
]); ]);
} catch (Exception $e) { } catch (Exception $e) {
App::error()->add($e->getMessage()); dcCore::app()->error->add($e->getMessage());
} }
return true; return true;
@ -97,19 +103,19 @@ class Config extends Process
} }
$s = ActivityReport::instance()->settings; $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) { if (!$s->lastreport) {
$last_report = __('never'); $last_report = __('never');
$next_report = __('on new activity'); $next_report = __('on new activity');
} else { } else {
$last_report = Date::str( $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, $s->lastreport,
$tz $tz
); );
$next_report = Date::str( $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, $s->interval + $s->lastreport,
$tz $tz
); );
@ -131,13 +137,13 @@ class Config extends Process
]), ]),
(new Para())->items([ (new Para())->items([
(new Label(__('Recipients:'), Label::OUTSIDE_LABEL_BEFORE))->for('mailinglist'), (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(__('Separate multiple email addresses with a semicolon ";"')),
(new Note())->class('form-note')->text(__('Leave it empty to disable mail report.')), (new Note())->class('form-note')->text(__('Leave it empty to disable mail report.')),
(new Para())->items([ (new Para())->items([
(new Label(__('Date format:'), Label::OUTSIDE_LABEL_BEFORE))->for('dateformat'), (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 Note())->class('form-note')->text(__('Use Dotclear date formaters. ex: %B %d at %H:%M')),
(new Para())->items([ (new Para())->items([
@ -160,11 +166,11 @@ class Config extends Process
'ul', 'ul',
'<li><img alt="' . __('RSS feed') . '" src="' . My::fileURL('img/feed.png') . '" /> ' . '<li><img alt="' . __('RSS feed') . '" src="' . My::fileURL('img/feed.png') . '" /> ' .
'<a title="' . __('RSS feed') . '" href="' . '<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>' . __('Rss2 activities feed') . '</a></li>' .
'<li><img alt="' . __('Atom feed') . '" src="' . My::fileURL('img/feed.png') . '" /> ' . '<li><img alt="' . __('Atom feed') . '" src="' . My::fileURL('img/feed.png') . '" /> ' .
'<a title="' . __('Atom feed') . '" href="' . '<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>' __('Atom activities feed') . '</a></li>'
)), )),
]), ]),

View File

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

View File

@ -1,15 +1,21 @@
<?php <?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); declare(strict_types=1);
namespace Dotclear\Plugin\activityReport; namespace Dotclear\Plugin\activityReport;
/** /**
* @brief activityReport format decriptor. * Report format descriptor.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Format class Format
{ {

View File

@ -1,15 +1,21 @@
<?php <?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); declare(strict_types=1);
namespace Dotclear\Plugin\activityReport; namespace Dotclear\Plugin\activityReport;
/** /**
* @brief activityReport formats stack. * Email report formats stack.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Formats class Formats
{ {

View File

@ -1,18 +1,24 @@
<?php <?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); declare(strict_types=1);
namespace Dotclear\Plugin\activityReport; namespace Dotclear\Plugin\activityReport;
use Dotclear\App; use dcCore;
use Dotclear\Core\Process; use Dotclear\Core\Process;
/** /**
* @brief activityReport frontend class. * Front end process.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Frontend extends Process class Frontend extends Process
{ {
@ -23,19 +29,18 @@ class Frontend extends Process
public static function process(): bool public static function process(): bool
{ {
if (self::status()) { if (!self::status()) {
// be sure to init report return false;
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(...));
} }
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 <?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); declare(strict_types=1);
namespace Dotclear\Plugin\activityReport; namespace Dotclear\Plugin\activityReport;
/** /**
* @brief activityReport actions stack class. * Actions stack.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Group class Group
{ {

View File

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

View File

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

View File

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

View File

@ -1,107 +1,80 @@
<?php <?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); declare(strict_types=1);
namespace Dotclear\Plugin\activityReport; namespace Dotclear\Plugin\activityReport;
use ArrayObject; use ArrayObject;
use Dotclear\App; use dcCore;
use Dotclear\Core\Backend\Filter\Filters; use Dotclear\Core\Backend\Filter\Filters;
use Dotclear\Core\Backend\Listing\{ use Dotclear\Core\Backend\Listing\{
Listing, Listing,
Pager Pager
}; };
use Dotclear\Helper\Date; 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. * Logs admin list helper.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class ManageList extends Listing class ManageList extends Listing
{ {
public function logsDisplay(Filters $filter, string $enclose_block = ''): void public function logsDisplay(Filters $filter, string $enclose_block = ''): void
{ {
if ($this->rs->isEmpty()) { if (!$this->rs || $this->rs->isEmpty()) {
echo (new Note()) if ($filter->show()) {
->text($filter->show() ? __('No log matches the filter') : __('No log')) echo '<p><strong>' . __('No log matches the filter') . '</strong></p>';
->class('info') } else {
->render(); echo '<p><strong>' . __('No log') . '</strong></p>';
}
} 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';
return; $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' => '<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);
$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()) {
echo $this->logsLine();
}
echo $blocks[1] . $blocks[2] . $pager->getLinks();
} }
$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);
$pager->var_page = 'page';
$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'),
]);
$this->userColumns(My::id(), $cols);
$lines = [];
while ($this->rs->fetch()) {
$lines[] = $this->line();
}
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();
} }
private function line(): Tr private function logsLine(): string
{ {
$row = new ActivityRow($this->rs); $row = new ActivityRow($this->rs);
@ -110,34 +83,25 @@ class ManageList extends Listing
$action = $group->get($row->action); $action = $group->get($row->action);
$message = ActivityReport::parseMessage(__($action->message), $row->logs); $message = ActivityReport::parseMessage(__($action->message), $row->logs);
$date = Date::str( $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), (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'); $status = $row->status == ActivityReport::STATUS_PENDING ? __('pending') : __('reported');
$cols = new ArrayObject([ $cols = new ArrayObject([
'activity_group' => (new Td()) 'activity_group' => '<td class="nowrap">' . __($group->title) . '</td>',
->text(Html::escapeHTML(__($group->title))) 'activity_action' => '<td class="nowrap">' . __($action->title) . '</td>',
->class('nowrap'), 'activity_logs' => '<td class="maximal">' . $message . '</td>',
'activity_action' => (new Td()) 'activity_date' => '<td class="nowrap">' . $date . '</td>',
->text(Html::escapeHTML(__($action->title))) 'activity_status' => '<td class="nowrap">' . $status . '</td>',
->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'),
]); ]);
$this->userColumns(My::id(), $cols); $this->userColumns(My::id(), $cols);
return (new Tr('l' . $row->id)) return
->class('line' . $offline) '<tr class="line ' . $offline . '" id="l' . $row->id . '">' .
->items(iterator_to_array($cols)); implode(iterator_to_array($cols)) .
'</tr>';
} }
} }

View File

@ -1,26 +1,59 @@
<?php <?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); declare(strict_types=1);
namespace Dotclear\Plugin\activityReport; namespace Dotclear\Plugin\activityReport;
use dcCore;
use Dotclear\Module\MyPlugin; use Dotclear\Module\MyPlugin;
/** /**
* @brief activityReport My helper. * This module definitions.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class My extends MyPlugin class My extends MyPlugin
{ {
/** /** @var string Activity database table name */
* Activity database table name.
*
* @var string ACTIVITY_TABLE_NAME
*/
public const ACTIVITY_TABLE_NAME = 'activity'; 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 <?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); declare(strict_types=1);
namespace Dotclear\Plugin\activityReport; namespace Dotclear\Plugin\activityReport;
use Dotclear\App; use dcCore;
use Dotclear\Core\Process; use Dotclear\Core\Process;
use Exception;
/** /**
* @brief activityReport prepend class. * Prepend process.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Prepend extends Process class Prepend extends Process
{ {
@ -27,13 +34,29 @@ class Prepend extends Process
return false; return false;
} }
// regirster activity feed URL if (defined('ACTIVITY_REPORT')) {
App::url()->register( return true;
My::id(), }
'reports',
'^reports/((atom|rss2)/(.+))$', try {
FrontendUrl::feed(...) // launch once activity report stuff
); ActivityReport::instance();
// regirster activity feed URL
dcCore::app()->url->register(
My::id(),
'reports',
'^reports/((atom|rss2)/(.+))$',
[UrlHandler::class, 'feed']
);
// declare report open
define('ACTIVITY_REPORT', My::COMPATIBILITY_VERSION);
// register predefined activities scan
ActivityBehaviors::register();
} catch (Exception $e) {
}
return true; return true;
} }

View File

@ -1,15 +1,21 @@
<?php <?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); declare(strict_types=1);
namespace Dotclear\Plugin\activityReport; namespace Dotclear\Plugin\activityReport;
/** /**
* @brief activityReport settings helper class. * Module settings helper.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Settings class Settings
{ {

View File

@ -1,21 +1,27 @@
<?php <?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); declare(strict_types=1);
namespace Dotclear\Plugin\activityReport; namespace Dotclear\Plugin\activityReport;
use ArrayObject; use ArrayObject;
use Dotclear\App; use dcCore;
use Dotclear\Helper\Date; use Dotclear\Helper\Date;
/** /**
* @brief activityReport frontend template class. * Template blocs and values.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class FrontendTemplate class Template
{ {
/** /**
* tpl:activityReports [attributes] : Activity report logs (tpl block) * tpl:activityReports [attributes] : Activity report logs (tpl block)
@ -25,10 +31,10 @@ class FrontendTemplate
* - lastn integer Limit to last n logs * - lastn integer Limit to last n logs
* - ingnore_pagination 1|0 Ignore pagination paramaters * - ingnore_pagination 1|0 Ignore pagination paramaters
* *
* @param ArrayObject<string, mixed> $attr The attributes * @param ArrayObject $attr The attributes
* @param string $content The content * @param string $content The content
* *
* @return string The code * @return string The code
*/ */
public static function activityReports(ArrayObject $attr, string $content): string public static function activityReports(ArrayObject $attr, string $content): string
{ {
@ -37,16 +43,16 @@ class FrontendTemplate
$lastn = abs((int) $attr['lastn']) + 0; $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) { if ($lastn > 0) {
$p .= "\$params['limit'] = " . $lastn . ";\n"; $p .= "\$params['limit'] = " . $lastn . ";\n";
} else { } 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') { 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 { } else {
$p .= "\$params['limit'] = array(0, \$params['limit']);\n"; $p .= "\$params['limit'] = array(0, \$params['limit']);\n";
} }
@ -54,10 +60,10 @@ class FrontendTemplate
return return
"<?php \n" . "<?php \n" .
$p . $p .
'App::frontend()->context()->activityreport_params = $params; ' . "\n" . 'dcCore::app()->ctx->activityreport_params = $params; ' . "\n" .
'App::frontend()->context()->activityreports = ' . ActivityReport::class . '::instance()->getLogs($params); unset($params); ' . "\n" . 'dcCore::app()->ctx->activityreports = ' . ActivityReport::class . '::instance()->getLogs($params); unset($params); ' . "\n" .
'while (App::frontend()->context()->activityreports->fetch()) : ?>' . $content . '<?php endwhile; ' . 'while (dcCore::app()->ctx->activityreports->fetch()) : ?>' . $content . '<?php endwhile; ' .
'App::frontend()->context()->pop("activityreports"); App::frontend()->context()->pop("activityreport_params"); ' . "\n" . 'dcCore::app()->ctx->pop("activityreports"); dcCore::app()->ctx->pop("activityreport_params"); ' . "\n" .
'?>'; '?>';
} }
@ -68,15 +74,15 @@ class FrontendTemplate
* *
* - any filters See self::getFilters() * - any filters See self::getFilters()
* *
* @param ArrayObject<string, mixed> $attr The attributes * @param ArrayObject $attr The attributes
* *
* @return string The code * @return string The code
*/ */
public static function activityReportFeedID(ArrayObject $attr): string public static function activityReportFeedID(ArrayObject $attr): string
{ {
return return
'urn:md5:<?php echo md5(App::frontend()->context()->activityreports->blog_id.' . 'urn:md5:<?php echo md5(dcCore::app()->ctx->activityreports->blog_id.' .
'App::frontend()->context()->activityreports->activity_id.App::frontend()->context()->activityreports->activity_dt); ' . 'dcCore::app()->ctx->activityreports->activity_id.dcCore::app()->ctx->activityreports->activity_dt); ' .
'?>'; '?>';
} }
@ -87,15 +93,15 @@ class FrontendTemplate
* *
* - any filters See self::getFilters() * - any filters See self::getFilters()
* *
* @param ArrayObject<string, mixed> $attr The attributes * @param ArrayObject $attr The attributes
* *
* @return string The code * @return string The code
*/ */
public static function activityReportTitle(ArrayObject $attr): string 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() * - any filters See self::getFilters()
* *
* @param ArrayObject<string, mixed> $attr The attributes * @param ArrayObject $attr The attributes
* *
* @return string The code * @return string The code
*/ */
public static function activityReportContent(ArrayObject $attr): string 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,9 +132,9 @@ class FrontendTemplate
* - rfc822 (1|0) Use Date::rfc822() * - rfc822 (1|0) Use Date::rfc822()
* - any filters See self::getFilters() * - any filters See self::getFilters()
* *
* @param ArrayObject<string, mixed> $attr The attributes * @param ArrayObject $attr The attributes
* *
* @return string The code * @return string The code
*/ */
public static function activityReportDate(ArrayObject $attr): string public static function activityReportDate(ArrayObject $attr): string
{ {
@ -140,16 +146,16 @@ class FrontendTemplate
$iso8601 = !empty($attr['iso8601']); $iso8601 = !empty($attr['iso8601']);
$rfc822 = !empty($attr['rfc822']); $rfc822 = !empty($attr['rfc822']);
$f = App::frontend()->template()->getFilters($attr); $f = dcCore::app()->tpl->getFilters($attr);
if ($rfc822) { 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) { } 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)) { } 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 <?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); declare(strict_types=1);
namespace Dotclear\Plugin\activityReport; namespace Dotclear\Plugin\activityReport;
use dcCore;
use Dotclear\Core\Process; use Dotclear\Core\Process;
use Dotclear\Plugin\Uninstaller\Uninstaller; use Dotclear\Plugin\Uninstaller\Uninstaller;
/** /**
* @brief activityReport uninstall class. * Uninstall process.
* @ingroup activityReport
* *
* @author Jean-Christian Denis (author) * Using plugin Uninstaller
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Uninstall extends Process class Uninstall extends Process
{ {
@ -23,7 +32,7 @@ class Uninstall extends Process
public static function process(): bool public static function process(): bool
{ {
if (!self::status()) { if (!self::status() || !dcCore::app()->plugins->moduleExists('Uninstaller')) {
return false; 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);
}
}