Compare commits

..

6 Commits
v3.3 ... master

29 changed files with 831 additions and 855 deletions

View File

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

View File

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

View File

@ -1,20 +0,0 @@
<?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.3</version> <version>3.4.1</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.3/plugin-activityReport.zip</file> <file>https://git.dotclear.watch/JcDenis/activityReport/releases/download/v3.4.1/plugin-activityReport.zip</file>
<da:dcmin>2.27</da:dcmin> <da:dcmin>2.28</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,23 +1,17 @@
<?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\App;
/** /**
* Action descriptor. * @brief activityReport action descriptor class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Action class Action
{ {
@ -39,7 +33,7 @@ class Action
) { ) {
// fake action has no behavior // fake action has no behavior
if (!is_null($function)) { if (!is_null($function)) {
dcCore::app()->addBehavior($behavior, $function); App::behavior()->addBehavior($behavior, $function);
} }
} }
} }

View File

@ -1,23 +1,12 @@
<?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 dcAuth; use Dotclear\App;
use dcBlog; use Dotclear\Core\Process;
use dcCore;
use Dotclear\Database\MetaRecord; use Dotclear\Database\MetaRecord;
use Dotclear\Database\Statement\{ use Dotclear\Database\Statement\{
DeleteStatement, DeleteStatement,
@ -34,34 +23,78 @@ 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;
/** /**
* Activity report main class. * @brief activityReport 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;
/** /**
@ -80,6 +113,23 @@ 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.
* *
@ -97,13 +147,13 @@ class ActivityReport
/** /**
* Get logs record. * Get logs record.
* *
* @param null|ArrayObject $params The query params * @param null|ArrayObject<string, mixed> $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 null|MetaRecord The logs record * @return 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();
@ -134,11 +184,11 @@ class ActivityReport
]); ]);
} }
$sql $sql
->from($sql->as(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME, 'E'), false, true) ->from($sql->as(App::con()->prefix() . My::ACTIVITY_TABLE_NAME, 'E'), false, true)
->join( ->join(
(new JoinStatement()) (new JoinStatement())
->left() ->left()
->from($sql->as(dcCore::app()->prefix . dcBlog::BLOG_TABLE_NAME, 'B')) ->from($sql->as(App::con()->prefix() . App::blog()::BLOG_TABLE_NAME, 'B'))
->on('E.blog_id = B.blog_id') ->on('E.blog_id = B.blog_id')
->statement() ->statement()
); );
@ -161,7 +211,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']) && is_null($params['blog_id'])) { if (isset($params['blog_id']) && $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'])) {
@ -169,7 +219,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((string) dcCore::app()->blog?->id)); $sql->and('E.blog_id = ' . $sql->quote(App::blog()->id()));
} }
if (isset($params['activity_status']) && is_numeric($params['activity_status'])) { if (isset($params['activity_status']) && is_numeric($params['activity_status'])) {
@ -201,8 +251,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 ($actions as $action) { foreach (array_keys($actions) as $action) {
$or[] = $sql->andGroup(['activity_group = ' . $sql->quote($group), 'activity_action = ' . $sql->quote($action)]); $or[] = $sql->andGroup(['activity_group = ' . $sql->quote((string) $group), 'activity_action = ' . $sql->quote((string) $action)]);
} }
} }
if (!empty($or)) { if (!empty($or)) {
@ -225,9 +275,10 @@ 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 $sql->select(); return is_null($rs) ? MetaRecord::newFromArray([]) : $rs;
} }
/** /**
@ -240,12 +291,12 @@ class ActivityReport
public function addLog(string $group, string $action, array $logs): void public function addLog(string $group, string $action, array $logs): void
{ {
try { try {
$cur = dcCore::app()->con->openCursor(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME); $cur = App::con()->openCursor(App::con()->prefix() . My::ACTIVITY_TABLE_NAME);
dcCore::app()->con->writeLock(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME); App::con()->writeLock(App::con()->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', (string) dcCore::app()->blog?->id); $cur->setField('blog_id', 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));
@ -253,13 +304,13 @@ class ActivityReport
$cur->setField('activity_status', self::STATUS_PENDING); $cur->setField('activity_status', self::STATUS_PENDING);
$cur->insert(); $cur->insert();
dcCore::app()->con->unlock(); App::con()->unlock();
# --BEHAVIOR-- coreAfterCategoryCreate -- ActivityReport, cursor # --BEHAVIOR-- coreAfterCategoryCreate -- ActivityReport, cursor
dcCore::app()->callBehavior('activityReportAfteAddLog', $this, $cur); App::behavior()->callBehavior('activityReportAfteAddLog', $this, $cur);
} catch (Exception $e) { } catch (Exception $e) {
dcCore::app()->con->unlock(); App::con()->unlock();
dcCore::app()->error->add($e->getMessage()); App::error()->add($e->getMessage());
} }
// Test if email report is needed // Test if email report is needed
@ -301,7 +352,7 @@ class ActivityReport
$from = time(); $from = time();
$to = 0; $to = 0;
$res = $blog_name = $blog_url = $group = ''; $res = $blog_name = $blog_url = $group = '';
$tz = dcCore::app()->blog?->settings->get('system')->get('blog_timezone'); $tz = 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');
@ -394,7 +445,7 @@ class ActivityReport
{ {
// Get blogs and logs count // Get blogs and logs count
$sql = new SelectStatement(); $sql = new SelectStatement();
$sql->from(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME) $sql->from(App::con()->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');
@ -410,16 +461,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(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME) $sql->from(App::con()->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 (dcCore::app()->con->changes()) { if (App::con()->changes()) {
try { try {
$cur = dcCore::app()->con->openCursor(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME); $cur = App::con()->openCursor(App::con()->prefix() . My::ACTIVITY_TABLE_NAME);
dcCore::app()->con->writeLock(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME); App::con()->writeLock(App::con()->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);
@ -431,10 +482,10 @@ class ActivityReport
$cur->setField('activity_status', self::STATUS_PENDING); $cur->setField('activity_status', self::STATUS_PENDING);
$cur->insert(); $cur->insert();
dcCore::app()->con->unlock(); App::con()->unlock();
} catch (Exception $e) { } catch (Exception $e) {
dcCore::app()->con->unlock(); App::con()->unlock();
dcCore::app()->error->add($e->getMessage()); App::error()->add($e->getMessage());
} }
} }
} }
@ -456,7 +507,7 @@ class ActivityReport
$sql->and('activity_status = ' . self::STATUS_REPORTED); $sql->and('activity_status = ' . self::STATUS_REPORTED);
} }
return $sql->from(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME) return $sql->from(App::con()->prefix() . My::ACTIVITY_TABLE_NAME)
->where('activity_type = ' . $sql->quote($this->type)) ->where('activity_type = ' . $sql->quote($this->type))
->delete(); ->delete();
} }
@ -470,10 +521,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(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME) $sql->from(App::con()->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((string) dcCore::app()->blog?->id)) ->where('blog_id = ' . $sql->quote(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)))
@ -488,7 +539,7 @@ class ActivityReport
public function getNextId(): int public function getNextId(): int
{ {
$sql = new SelectStatement(); $sql = new SelectStatement();
$sql->from(dcCore::app()->prefix . My::ACTIVITY_TABLE_NAME) $sql->from(App::con()->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;
@ -503,14 +554,14 @@ class ActivityReport
{ {
try { try {
# Cache writable ? # Cache writable ?
if (!is_writable(DC_TPL_CACHE)) { if (!is_writable(App::config()->cacheRoot())) {
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((string) dcCore::app()->blog?->id); $f_md5 = md5(App::blog()->id());
$file = sprintf( $file = sprintf(
'%s/%s/%s/%s/%s.txt', '%s/%s/%s/%s/%s.txt',
DC_TPL_CACHE, App::config()->cacheRoot(),
My::id(), My::id(),
substr($f_md5, 0, 2), substr($f_md5, 0, 2),
substr($f_md5, 2, 2), substr($f_md5, 2, 2),
@ -591,14 +642,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' => dcCore::app()->blog?->id, 'blog_id' => 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 (!is_null($logs) && !$logs->isEmpty()) { if (!$logs->isEmpty()) {
// Datas to readable text // Datas to readable text
$content = $this->parseLogs($logs); $content = $this->parseLogs($logs);
if (!empty($content)) { if (!empty($content)) {
@ -612,7 +663,7 @@ class ActivityReport
$this->updateStatus($lastreport, $now); $this->updateStatus($lastreport, $now);
$this->settings->set('lastreport', $now); $this->settings->set('lastreport', $now);
dcCore::app()->callBehavior('messageActivityReport', 'Activity report has been successfully send by mail.'); App::behavior()->callBehavior('messageActivityReport', 'Activity report has been successfully send by mail.');
} }
} }
@ -658,20 +709,20 @@ class ActivityReport
# Sending mails # Sending mails
try { try {
$subject = mb_encode_mimeheader( $subject = mb_encode_mimeheader(
sprintf(__('Blog "%s" activity report'), dcCore::app()->blog?->name), sprintf(__('Blog "%s" activity report'), App::blog()->name()),
'UTF-8', 'UTF-8',
'B' 'B'
); );
$headers = []; $headers = [];
$headers[] = 'From: ' . (defined('DC_ADMIN_MAILFROM') && str_contains(DC_ADMIN_MAILFROM, '@') ? DC_ADMIN_MAILFROM : 'dotclear@local'); $headers[] = 'From: ' . (defined('DC_ADMIN_MAILFROM') && str_contains(App::config()->adminMailFrom(), '@') ? App::config()->adminMailFrom() : '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(dcCore::app()->blog->id), 'UTF-8', 'B'); //$headers[] = 'X-Blog-Id: ' . mb_encode_mimeheader(App::blog()->id()), 'UTF-8', 'B');
//$headers[] = 'X-Blog-Name: ' . mb_encode_mimeheader(dcCore::app()->blog->name), 'UTF-8', 'B'); //$headers[] = 'X-Blog-Name: ' . mb_encode_mimeheader(App::blog()->name()), 'UTF-8', 'B');
//$headers[] = 'X-Blog-Url: ' . mb_encode_mimeheader(dcCore::app()->blog->url), 'UTF-8', 'B'); //$headers[] = 'X-Blog-Url: ' . mb_encode_mimeheader(App::>blog()->url()), 'UTF-8', 'B');
$done = true; $done = true;
foreach ($recipients as $email) { foreach ($recipients as $email) {
@ -693,9 +744,9 @@ class ActivityReport
*/ */
public function getUserCode(): string public function getUserCode(): string
{ {
$id = is_string(dcCore::app()->auth->userID()) ? dcCore::app()->auth->userID() : ''; $id = is_string(App::auth()->userID()) ? App::auth()->userID() : '';
$pw = is_string(dcCore::app()->auth->getInfo('user_pwd')) ? dcCore::app()->auth->getInfo('user_pwd') : ''; $pw = is_string(App::auth()->getInfo('user_pwd')) ? App::auth()->getInfo('user_pwd') : '';
$code = pack('a32', $id) . pack('H*', Crypt::hmac(DC_MASTER_KEY, $pw)); $code = pack('a32', $id) . pack('H*', Crypt::hmac(App::config()->masterKey(), $pw));
return bin2hex($code); return bin2hex($code);
} }
@ -721,7 +772,7 @@ class ActivityReport
$pwd = $pwd['hex']; $pwd = $pwd['hex'];
$sql = new SelectStatement(); $sql = new SelectStatement();
$sql->from(dcCore::app()->prefix . dcAuth::USER_TABLE_NAME) $sql->from(App::con()->prefix() . App::auth()::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));
@ -731,7 +782,7 @@ class ActivityReport
return false; return false;
} }
if (Crypt::hmac(DC_MASTER_KEY, $rs->f('user_pwd')) != $pwd) { if (Crypt::hmac(App::config()->masterKey(), $rs->f('user_pwd')) != $pwd) {
return false; return false;
} }

View File

@ -1,23 +1,13 @@
<?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 dcBlog; use Dotclear\App;
use dcCore; use Dotclear\Core\Process;
use dcUtils; use Dotclear\Interface\Core\BlogInterface;
use Dotclear\Database\{ use Dotclear\Database\{
Cursor, Cursor,
MetaRecord MetaRecord
@ -25,12 +15,27 @@ 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 ActivityBehaviors class ActivityReportAction extends Process
{ {
public static function register(): void public static function init(): bool
{ {
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'));
@ -45,7 +50,7 @@ class ActivityBehaviors
__('Special messages'), __('Special messages'),
__('%s'), __('%s'),
'messageActivityReport', 'messageActivityReport',
[self::class, 'messageActivityReport'] self::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
@ -56,7 +61,7 @@ class ActivityBehaviors
__('updating blog'), __('updating blog'),
__('Blog was updated by "%s"'), __('Blog was updated by "%s"'),
'adminAfterBlogUpdate', 'adminAfterBlogUpdate',
[self::class, 'blogUpdate'] self::blogUpdate(...)
)); ));
// from BEHAVIOR publicHeadContent in template // from BEHAVIOR publicHeadContent in template
@ -65,7 +70,7 @@ class ActivityBehaviors
__('404 error'), __('404 error'),
__('New 404 error page at "%s"'), __('New 404 error page at "%s"'),
'publicHeadContent', 'publicHeadContent',
[self::class, 'blogP404'] self::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)
@ -76,7 +81,7 @@ class ActivityBehaviors
__('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::class, 'postCreate'] self::postCreate(...)
)); ));
// Plugin contribute // Plugin contribute
@ -86,7 +91,7 @@ class ActivityBehaviors
__('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::class, 'postCreate'] self::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)
@ -96,7 +101,7 @@ class ActivityBehaviors
__('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::class, 'postUpdate'] self::postUpdate(...)
)); ));
// from BEHAVIOR adminBeforePostDelete in admin/post.php // from BEHAVIOR adminBeforePostDelete in admin/post.php
@ -105,7 +110,7 @@ class ActivityBehaviors
__('post deletion'), __('post deletion'),
__('Post called "%s" has been deleted by "%s"'), __('Post called "%s" has been deleted by "%s"'),
'adminBeforePostDelete', 'adminBeforePostDelete',
[self::class, 'postDelete'] self::postDelete(...)
)); ));
// Wrong attempt on passworded enrty // Wrong attempt on passworded enrty
@ -115,7 +120,7 @@ class ActivityBehaviors
__('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::class, 'postPasswordAttempt'] self::postPasswordAttempt(...)
)); ));
// from BEHAVIOR coreAfterCommentCreate in inc/core/class.dc.blog.php // from BEHAVIOR coreAfterCommentCreate in inc/core/class.dc.blog.php
@ -126,7 +131,7 @@ class ActivityBehaviors
__('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::class, 'commentCreate'] self::commentCreate(...)
)); ));
// from BEHAVIOR coreAfterCommentUpdate in inc/core/class.dc.blog.php // from BEHAVIOR coreAfterCommentUpdate in inc/core/class.dc.blog.php
@ -136,7 +141,7 @@ class ActivityBehaviors
__('updating comment'), __('updating comment'),
__('Comment has been updated by "%s" at %s'), __('Comment has been updated by "%s" at %s'),
'coreAfterCommentUpdate', 'coreAfterCommentUpdate',
[self::class, 'commentUpdate'] self::commentUpdate(...)
)); ));
// Missing coreBeforeCommentDelete in inc/core/class.dc.blog.php // Missing coreBeforeCommentDelete in inc/core/class.dc.blog.php
@ -149,7 +154,7 @@ class ActivityBehaviors
__('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::class, 'trackbackCreate'] self::trackbackCreate(...)
)); ));
// from BEHAVIOR adminAfterCategoryCreate in admin/category.php // from BEHAVIOR adminAfterCategoryCreate in admin/category.php
@ -158,7 +163,7 @@ class ActivityBehaviors
__('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::class, 'categoryCreate'] self::categoryCreate(...)
)); ));
// from BEHAVIOR adminAfterCategoryUpdate in admin/category.php // from BEHAVIOR adminAfterCategoryUpdate in admin/category.php
@ -167,7 +172,7 @@ class ActivityBehaviors
__('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::class, 'categoryUpdate'] self::categoryUpdate(...)
)); ));
// Missing adminBeforeCategoryDelete in admin/category.php // Missing adminBeforeCategoryDelete in admin/category.php
@ -178,7 +183,7 @@ class ActivityBehaviors
__('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::class, 'userCreate'] self::userCreate(...)
)); ));
// from BEHAVIOR adminAfterUserUpdated in admin/user.php // from BEHAVIOR adminAfterUserUpdated in admin/user.php
@ -187,7 +192,7 @@ class ActivityBehaviors
__('updating user'), __('updating user'),
__('User named "%s" has been updated by "%s"'), __('User named "%s" has been updated by "%s"'),
'adminAfterUserUpdate', 'adminAfterUserUpdate',
[self::class, 'userUpdate'] self::userUpdate(...)
)); ));
// from BEHAVIOR adminAfterUserProfileUpdate in admin/preferences.php // from BEHAVIOR adminAfterUserProfileUpdate in admin/preferences.php
@ -196,21 +201,21 @@ class ActivityBehaviors
__('updating user preference'), __('updating user preference'),
__('"%s" user preference has been updated'), __('"%s" user preference has been updated'),
'adminAfterUserProfileUpdate', 'adminAfterUserProfileUpdate',
[self::class, 'userPreference'] self::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::class, 'userPreference'] self::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::class, 'userOption'] self::userOption(...)
)); ));
// from BEHAVIOR adminBeforeUserDelete in admin/users.php // from BEHAVIOR adminBeforeUserDelete in admin/users.php
@ -219,7 +224,7 @@ class ActivityBehaviors
__('user deletion'), __('user deletion'),
__('User named "%s" has been deleted by "%"'), __('User named "%s" has been deleted by "%"'),
'adminBeforeUserDelete', 'adminBeforeUserDelete',
[self::class, 'userDelete'] self::userDelete(...)
)); ));
ActivityReport::instance()->groups ActivityReport::instance()->groups
@ -264,6 +269,8 @@ class ActivityBehaviors
'<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
@ -274,26 +281,26 @@ class ActivityBehaviors
public static function blogUpdate(Cursor $cur, string $blog_id): void public static function blogUpdate(Cursor $cur, string $blog_id): void
{ {
$logs = [self::str(dcCore::app()->auth->getInfo('user_cn'))]; $logs = [self::str(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 (dcCore::app()->url->type != '404') { if (App::url()->type != '404') {
return; return;
} }
$logs = [self::str(dcCore::app()->blog?->url) . $_SERVER['QUERY_STRING']]; $logs = [self::str(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 = dcCore::app()->blog?->getPostURL('', self::str($cur->getField('post_dt')), self::str($cur->getField('post_title')), $post_id); $post_url = 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(dcCore::app()->auth->getInfo('user_cn')), self::str(App::auth()->getInfo('user_cn')),
self::str(dcCore::app()->blog?->url) . dcCore::app()->url->getBase(self::str($cur->getField('post_type'))) . '/' . $post_url, self::str(App::blog()->url()) . App::url()->getBase(self::str($cur->getField('post_type'))) . '/' . $post_url,
]; ];
ActivityReport::instance()->addLog('post', 'create', $logs); ActivityReport::instance()->addLog('post', 'create', $logs);
} }
@ -301,28 +308,31 @@ class ActivityBehaviors
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 = dcCore::app()->blog?->getPostURL('', self::str($cur->getField('post_dt')), self::str($cur->getField('post_title')), $post_id); $post_url = 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(dcCore::app()->auth->getInfo('user_cn')), self::str(App::auth()->getInfo('user_cn')),
self::str(dcCore::app()->blog?->url) . dcCore::app()->url->getBase(self::str($cur->getField('post_type'))) . '/' . $post_url, self::str(App::blog()->url()) . 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 = dcCore::app()->blog?->getPosts(['post_id' => $post_id, 'limit' => 1]); $posts = App::blog()->getPosts(['post_id' => $post_id, 'limit' => 1]);
if (!$posts || $posts->isEmpty()) { if ($posts->isEmpty()) {
return; return;
} }
$logs = [ $logs = [
self::str($posts->f('post_title')), self::str($posts->f('post_title')),
self::str(dcCore::app()->auth->getInfo('user_cn')), self::str(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'])) {
@ -335,55 +345,55 @@ class ActivityBehaviors
ActivityReport::instance()->addLog('post', 'protection', $logs); ActivityReport::instance()->addLog('post', 'protection', $logs);
} }
public static function commentCreate(dcBlog $blog, Cursor $cur): void public static function commentCreate(BlogInterface $blog, Cursor $cur): void
{ {
if ($cur->getField('comment_trackback')) { if ($cur->getField('comment_trackback')) {
return; return;
} }
$posts = dcCore::app()->blog?->getPosts( $posts = 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 || $posts->isEmpty()) { if ($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(dcCore::app()->blog?->url) . dcCore::app()->url->getBase(self::str($posts->f('post_type'))) . self::str(App::blog()->url()) . 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(dcBlog $blog, Cursor $cur, MetaRecord $old): void public static function commentUpdate(BlogInterface $blog, Cursor $cur, MetaRecord $old): void
{ {
$posts = dcCore::app()->blog?->getPosts( $posts = App::blog()->getPosts(
['post_id' => $old->f('post_id'), 'limit' => 1] ['post_id' => $old->f('post_id'), 'limit' => 1]
); );
if (!$posts || $posts->isEmpty()) { if ($posts->isEmpty()) {
return; return;
} }
$logs = [ $logs = [
self::str(dcCore::app()->auth->getInfo('user_cn')), self::str(App::auth()->getInfo('user_cn')),
self::str($posts->f('post_title')), self::str($posts->f('post_title')),
self::str(dcCore::app()->blog?->url) . dcCore::app()->url->getBase(self::str($posts->f('post_type'))) . self::str(App::blog()->url()) . 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(dcBlog $blog, Cursor $cur): void public static function trackbackCreate(BlogInterface $blog, Cursor $cur): void
{ {
if (!$cur->getField('comment_trackback')) { if (!$cur->getField('comment_trackback')) {
return; return;
} }
$posts = dcCore::app()->blog?->getPosts( $posts = 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 || $posts->isEmpty()) { if ($posts->isEmpty()) {
return; return;
} }
@ -391,7 +401,7 @@ class ActivityBehaviors
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(dcCore::app()->blog?->url) . dcCore::app()->url->getBase(self::str($posts->f('post_type'))) . self::str(App::blog()->url()) . 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);
@ -401,8 +411,8 @@ class ActivityBehaviors
{ {
$logs = [ $logs = [
self::str($cur->getField('cat_title')), self::str($cur->getField('cat_title')),
self::str(dcCore::app()->auth->getInfo('user_cn')), self::str(App::auth()->getInfo('user_cn')),
self::str(dcCore::app()->blog?->url) . dcCore::app()->url->getBase('category') . '/' . self::str($cur->getField('cat_url')), self::str(App::blog()->url()) . App::url()->getBase('category') . '/' . self::str($cur->getField('cat_url')),
]; ];
ActivityReport::instance()->addLog('category', 'create', $logs); ActivityReport::instance()->addLog('category', 'create', $logs);
} }
@ -411,15 +421,15 @@ class ActivityBehaviors
{ {
$logs = [ $logs = [
self::str($cur->getField('cat_title')), self::str($cur->getField('cat_title')),
self::str(dcCore::app()->auth->getInfo('user_cn')), self::str(App::auth()->getInfo('user_cn')),
self::str(dcCore::app()->blog?->url) . dcCore::app()->url->getBase('category') . '/' . self::str($cur->getField('cat_url')), self::str(App::blog()->url()) . 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 = dcUtils::getUserCN( $user_cn = App::users()->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')),
@ -427,14 +437,14 @@ class ActivityBehaviors
); );
$logs = [ $logs = [
self::str($user_cn), self::str($user_cn),
self::str(dcCore::app()->auth->getInfo('user_cn')), self::str(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 = dcUtils::getUserCN( $user_cn = App::users()->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')),
@ -442,7 +452,7 @@ class ActivityBehaviors
); );
$logs = [ $logs = [
self::str($user_cn), self::str($user_cn),
self::str(dcCore::app()->auth->getInfo('user_cn')), self::str(App::auth()->getInfo('user_cn')),
]; ];
ActivityReport::instance()->addLog('user', 'update', $logs); ActivityReport::instance()->addLog('user', 'update', $logs);
} }
@ -454,11 +464,11 @@ class ActivityBehaviors
public static function userOption(string $user_id): void public static function userOption(string $user_id): void
{ {
$user = dcCore::app()->getUser($user_id); $user = App::users()->getUser($user_id);
if ($user->isEmpty()) { if ($user->isEmpty()) {
return; return;
} }
$user_cn = dcUtils::getUserCN( $user_cn = App::users()->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')),
@ -472,8 +482,8 @@ class ActivityBehaviors
public static function userDelete(string $user_id): void public static function userDelete(string $user_id): void
{ {
$users = dcCore::app()->getUser($user_id); $users = App::users()->getUser($user_id);
$user_cn = dcUtils::getUserCN( $user_cn = App::users()->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')),
@ -481,7 +491,7 @@ class ActivityBehaviors
); );
$logs = [ $logs = [
self::str($user_cn), self::str($user_cn),
self::str(dcCore::app()->auth->getInfo('user_cn')), self::str(App::auth()->getInfo('user_cn')),
]; ];
ActivityReport::instance()->addLog('user', 'delete', $logs); ActivityReport::instance()->addLog('user', 'delete', $logs);
} }

View File

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

View File

@ -1,35 +1,18 @@
<?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 Dotclear\App;
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
};
/** /**
* Backend process * @brief activityReport backend class.
* @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
{ {
@ -40,139 +23,22 @@ class Backend extends Process
public static function process(): bool public static function process(): bool
{ {
if (!self::status()) { if (self::status()) {
return false; // be sure to init report
} ActivityReport::init();
My::addBackendMenuItem(); My::addBackendMenuItem();
dcCore::app()->addBehaviors([ App::behavior()->addBehaviors([
// dashboard favorites icon 'adminDashboardFavoritesV2' => BackendBehaviors::adminDashboardFavoritesV2(...),
'adminDashboardFavoritesV2' => function (Favorites $favs): void { 'adminDashboardContentsV2' => BackendBehaviors::adminDashboardContentsV2(...),
$favs->register(My::id(), [ 'adminDashboardOptionsFormV2' => BackendBehaviors::adminDashboardOptionsFormV2(...),
'title' => My::name(), 'adminAfterDashboardOptionsUpdate' => BackendBehaviors::adminAfterDashboardOptionsUpdate(...),
'url' => My::manageUrl(), 'adminFiltersListsV2' => BackendBehaviors::adminFiltersListsV2(...),
'small-icon' => My::icons(), 'adminColumnsListsV2' => BackendBehaviors::adminColumnsListsV2(...),
'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([ return self::status();
'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

@ -0,0 +1,181 @@
<?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,21 +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;
/** /**
* Combo helper. * @brief activityReport combo class.
* @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,20 +1,10 @@
<?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\App;
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;
@ -32,7 +22,11 @@ use Dotclear\Helper\Html\Form\{
use Exception; use Exception;
/** /**
* Config process. * @brief activityReport config class.
* @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
{ {
@ -85,12 +79,12 @@ class Config extends Process
); );
} }
dcCore::app()->admin->url->redirect('admin.plugins', [ App::backend()->url()->redirect('admin.plugins', [
'module' => My::id(), 'module' => My::id(),
'conf' => 1, 'conf' => 1,
]); ]);
} catch (Exception $e) { } catch (Exception $e) {
dcCore::app()->error->add($e->getMessage()); App::error()->add($e->getMessage());
} }
return true; return true;
@ -103,19 +97,19 @@ class Config extends Process
} }
$s = ActivityReport::instance()->settings; $s = ActivityReport::instance()->settings;
$tz = is_string(dcCore::app()->auth->getInfo('user_tz')) ? dcCore::app()->auth->getInfo('user_tz') : 'UTC'; $tz = is_string(App::auth()->getInfo('user_tz')) ? 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(
dcCore::app()->blog?->settings->get('system')->get('date_format') . ', ' . dcCore::app()->blog?->settings->get('system')->get('time_format'), App::blog()->settings()->get('system')->get('date_format') . ', ' . App::blog()->settings()->get('system')->get('time_format'),
$s->lastreport, $s->lastreport,
$tz $tz
); );
$next_report = Date::str( $next_report = Date::str(
dcCore::app()->blog?->settings->get('system')->get('date_format') . ', ' . dcCore::app()->blog?->settings->get('system')->get('time_format'), App::blog()->settings()->get('system')->get('date_format') . ', ' . App::blog()->settings()->get('system')->get('time_format'),
$s->interval + $s->lastreport, $s->interval + $s->lastreport,
$tz $tz
); );
@ -137,13 +131,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)->maxlenght(255)->value(implode(';', $s->mailinglist)), (new Input('mailinglist'))->size(60)->maxlength(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)->maxlenght(255)->value($s->dateformat), (new Input('dateformat'))->size(60)->maxlength(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([
@ -166,11 +160,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="' .
dcCore::app()->blog?->url . dcCore::app()->url->getBase(My::id()) . '/rss2/' . ActivityReport::instance()->getUserCode() . '">' . App::blog()->url() . 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="' .
dcCore::app()->blog?->url . dcCore::app()->url->getBase(My::id()) . '/atom/' . ActivityReport::instance()->getUserCode() . '">' . App::blog()->url() . App::url()->getBase(My::id()) . '/atom/' . ActivityReport::instance()->getUserCode() . '">' .
__('Atom activities feed') . '</a></li>' __('Atom activities feed') . '</a></li>'
)), )),
]), ]),

View File

@ -1,21 +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;
/** /**
* Report format descriptor. * @brief activityReport format decriptor.
* @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,21 +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;
/** /**
* Email report formats stack. * @brief activityReport 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,24 +1,18 @@
<?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\App;
use Dotclear\Core\Process; use Dotclear\Core\Process;
/** /**
* Front end process. * @brief activityReport frontend class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Frontend extends Process class Frontend extends Process
{ {
@ -29,18 +23,19 @@ class Frontend extends Process
public static function process(): bool public static function process(): bool
{ {
if (!self::status()) { if (self::status()) {
return false; // be sure to init report
ActivityReport::init();
$tpl = App::frontend()->template();
$tpl->setPath($tpl->getPath(), implode(DIRECTORY_SEPARATOR, [My::path(), 'default-templates', 'tpl']));
$tpl->addBlock('activityReports', FrontendTemplate::activityReports(...));
$tpl->addValue('activityReportFeedID', FrontendTemplate::activityReportFeedID(...));
$tpl->addValue('activityReportTitle', FrontendTemplate::activityReportTitle(...));
$tpl->addValue('activityReportDate', FrontendTemplate::activityReportDate(...));
$tpl->addValue('activityReportContent', FrontendTemplate::activityReportContent(...));
} }
dcCore::app()->tpl->setPath(dcCore::app()->tpl->getPath(), implode(DIRECTORY_SEPARATOR, [My::path(), 'default-templates', 'tpl'])); return self::status();
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,26 +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 dcCore; use Dotclear\App;
use Dotclear\Database\MetaRecord; use Dotclear\Database\MetaRecord;
/** /**
* Template helper. * @brief activityReport frontend context class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Context class FrontendContext
{ {
/** /**
* Parse title. * Parse title.
@ -29,15 +23,14 @@ class Context
*/ */
public static function parseTitle(): string public static function parseTitle(): string
{ {
if (!dcCore::app()->ctx if (!App::frontend()->context()->exists('activityreports')
|| !dcCore::app()->ctx->exists('activityreports') || !(App::frontend()->context()->__get('activityreports') instanceof MetaRecord)
|| !(dcCore::app()->ctx->__get('activityreports') instanceof MetaRecord)
) { ) {
return ''; return '';
} }
$group = dcCore::app()->ctx->__get('activityreports')->f('activity_group'); $group = App::frontend()->context()->__get('activityreports')->f('activity_group');
$action = dcCore::app()->ctx->__get('activityreports')->f('activity_action'); $action = App::frontend()->context()->__get('activityreports')->f('activity_action');
if (!is_string($group) if (!is_string($group)
|| !is_string($action) || !is_string($action)
@ -56,16 +49,15 @@ class Context
*/ */
public static function parseContent(): string public static function parseContent(): string
{ {
if (!dcCore::app()->ctx if (!App::frontend()->context()->exists('activityreports')
|| !dcCore::app()->ctx->exists('activityreports') || !(App::frontend()->context()->__get('activityreports') instanceof MetaRecord)
|| !(dcCore::app()->ctx->__get('activityreports') instanceof MetaRecord)
) { ) {
return ''; return '';
} }
$group = dcCore::app()->ctx->__get('activityreports')->f('activity_group'); $group = App::frontend()->context()->__get('activityreports')->f('activity_group');
$action = dcCore::app()->ctx->__get('activityreports')->f('activity_action'); $action = App::frontend()->context()->__get('activityreports')->f('activity_action');
$logs = dcCore::app()->ctx->__get('activityreports')->f('activity_logs'); $logs = App::frontend()->context()->__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)
@ -76,9 +68,9 @@ class Context
return ''; return '';
} }
dcCore::app()->initWikiComment(); App::filter()->initWikiComment();
return dcCore::app()->wikiTransform(vsprintf( return App::filter()->wikiTransform(vsprintf(
__(ActivityReport::instance()->groups->get($group)->get($action)->message), __(ActivityReport::instance()->groups->get($group)->get($action)->message),
$logs $logs
)); ));

View File

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

View File

@ -0,0 +1,55 @@
<?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,21 +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;
/** /**
* Actions stack. * @brief activityReport actions stack class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Group class Group
{ {

View File

@ -1,25 +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;
/** /**
* Actions groups stack. * @brief activityReport actions groups stack class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
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 = [];
/** /**
@ -37,6 +35,8 @@ 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,20 +1,10 @@
<?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\App;
use Dotclear\Core\Process; use Dotclear\Core\Process;
use Dotclear\Database\Structure; use Dotclear\Database\Structure;
use Dotclear\Database\Statement\{ use Dotclear\Database\Statement\{
@ -24,7 +14,11 @@ use Dotclear\Database\Statement\{
use Exception; use Exception;
/** /**
* Install process. * @brief activityReport install class.
* @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
{ {
@ -42,7 +36,7 @@ class Install extends Process
try { try {
self::beforeGrowUp(); self::beforeGrowUp();
$s = new Structure(dcCore::app()->con, dcCore::app()->prefix); $s = new Structure(App::con(), App::con()->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() . "'")
@ -59,11 +53,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(dcCore::app()->con, dcCore::app()->prefix))->synchronize($s); (new Structure(App::con(), App::con()->prefix()))->synchronize($s);
return true; return true;
} catch (Exception $e) { } catch (Exception $e) {
dcCore::app()->error->add($e->getMessage()); App::error()->add($e->getMessage());
return false; return false;
} }
@ -75,17 +69,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(dcCore::app()->getVersion('activityReport')) if (is_string(App::version()->getVersion('activityReport'))
&& version_compare(dcCore::app()->getVersion('activityReport'), '3.0', '<') && version_compare(App::version()->getVersion('activityReport'), '3.0', '<')
) { ) {
$struct = new Structure(dcCore::app()->con, dcCore::app()->prefix); $struct = new Structure(App::con(), App::con()->prefix());
if ($struct->tableExists('activity')) { if ($struct->tableExists('activity')) {
(new TruncateStatement())->from(dcCore::app()->prefix . 'activity')->truncate(); (new TruncateStatement())->from(App::con()->prefix() . 'activity')->truncate();
} }
if ($struct->tableExists('activity_settings')) { if ($struct->tableExists('activity_settings')) {
(new TruncateStatement())->from(dcCore::app()->prefix . 'activity_settings')->truncate(); (new TruncateStatement())->from(App::con()->prefix() . 'activity_settings')->truncate();
(new DropStatement())->from(dcCore::app()->prefix . 'activity_settings')->drop(); (new DropStatement())->from(App::con()->prefix() . 'activity_settings')->drop();
} }
} }
} }

View File

@ -1,22 +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;
use ArrayObject; use ArrayObject;
use dcCore; use Dotclear\App;
use Dotclear\Core\Backend\Filter\Filters; use Dotclear\Core\Backend\Filter\{
Filters,
FiltersLibrary
};
use Dotclear\Core\Backend\{ use Dotclear\Core\Backend\{
Notices, Notices,
Page Page
@ -32,7 +25,11 @@ use Dotclear\Helper\Html\Form\{
use Exception; use Exception;
/** /**
* Manage process (admin logs list). * @brief activityReport manage class.
* @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
{ {
@ -53,7 +50,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) {
dcCore::app()->error->add($e->getMessage()); App::error()->add($e->getMessage());
} }
} }
@ -68,16 +65,15 @@ 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);
if (!is_null($logs) && !is_null($counter)) {
$list = new ManageList($logs, $counter->f(0)); $list = new ManageList($logs, $counter->f(0));
}
} catch (Exception $e) { } catch (Exception $e) {
dcCore::app()->error->add($e->getMessage()); App::error()->add($e->getMessage());
} }
Page::openModule( Page::openModule(
@ -87,7 +83,7 @@ class Manage extends Process
My::jsLoad('backend') . My::jsLoad('backend') .
# --BEHAVIOR-- activityReportListHeader -- # --BEHAVIOR-- activityReportListHeader --
dcCore::app()->callBehavior('activityReportListHeader') App::behavior()->callBehavior('activityReportListHeader')
); );
echo echo
@ -104,7 +100,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(dcCore::app()->admin->getPageURL())->fields([ (new Form('form-logs'))->method('post')->action(App::backend()->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,80 +1,107 @@
<?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 dcCore; use Dotclear\App;
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;
/** /**
* Logs admin list helper. * @brief activityReport manage logs list class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
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 || $this->rs->isEmpty()) { if ($this->rs->isEmpty()) {
if ($filter->show()) { echo (new Note())
echo '<p><strong>' . __('No log matches the filter') . '</strong></p>'; ->text($filter->show() ? __('No log matches the filter') : __('No log'))
} else { ->class('info')
echo '<p><strong>' . __('No log') . '</strong></p>'; ->render();
return;
} }
} else {
$page = $filter->value('page'); $page = is_numeric($filter->value('page')) ? (int) $filter->value('page') : 1;
$nbpp = $filter->value('nb'); $nbpp = is_numeric($filter->value('nb')) ? (int) $filter->value('nb') : 20;
$pager = new Pager(is_numeric($page) ? (int) $page : 1, (int) $this->rs_count, is_numeric($nbpp) ? (int) $nbpp : 20, 10); $count = (int) $this->rs_count;
$pager = new Pager($page, $count, $nbpp, 10);
$pager->var_page = 'page'; $pager->var_page = 'page';
$html_block = '<div class="table-outer"><table><caption>' . (
$filter->show() ?
sprintf(__('List of %s logs matching the filter.'), $this->rs_count) :
sprintf(__('List of %s logs.'), $this->rs_count)
) . '</caption>';
$cols = new ArrayObject([ $cols = new ArrayObject([
'activity_group' => '<th scope="col" class="nowrap">' . __('Group') . '</th>', 'activity_group' => (new Th())
'activity_action' => '<th scope="col" class="nowrap">' . __('Action') . '</th>', ->text(__('Group'))
'activity_logs' => '<th scope="col" class="nowrap">' . __('Message') . '</th>', ->scope('col'),
'activity_date' => '<th scope="col" class="nowrap">' . __('Date') . '</th>', 'activity_action' => (new Th())
'activity_status' => '<th scope="col" class="nowrap">' . __('Status') . '</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); $this->userColumns(My::id(), $cols);
$html_block .= '<tr>' . implode(iterator_to_array($cols)) . '</tr>%s</table>%s</div>'; $lines = [];
if ($enclose_block) {
$html_block = sprintf($enclose_block, $html_block);
}
$blocks = explode('%s', $html_block);
echo $pager->getLinks() . $blocks[0];
while ($this->rs->fetch()) { while ($this->rs->fetch()) {
echo $this->logsLine(); $lines[] = $this->line();
} }
echo $blocks[1] . $blocks[2] . $pager->getLinks(); 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 logsLine(): string private function line(): Tr
{ {
$row = new ActivityRow($this->rs); $row = new ActivityRow($this->rs);
@ -83,25 +110,34 @@ 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(
dcCore::app()->blog?->settings->get('system')->get('date_format') . ', ' . dcCore::app()->blog?->settings->get('system')->get('time_format'), App::blog()->settings()->get('system')->get('date_format') . ', ' . App::blog()->settings()->get('system')->get('time_format'),
(int) strtotime($row->dt), (int) strtotime($row->dt),
is_string(dcCore::app()->auth->getInfo('user_tz')) ? dcCore::app()->auth->getInfo('user_tz') : 'UTC' is_string(App::auth()->getInfo('user_tz')) ? 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' => '<td class="nowrap">' . __($group->title) . '</td>', 'activity_group' => (new Td())
'activity_action' => '<td class="nowrap">' . __($action->title) . '</td>', ->text(Html::escapeHTML(__($group->title)))
'activity_logs' => '<td class="maximal">' . $message . '</td>', ->class('nowrap'),
'activity_date' => '<td class="nowrap">' . $date . '</td>', 'activity_action' => (new Td())
'activity_status' => '<td class="nowrap">' . $status . '</td>', ->text(Html::escapeHTML(__($action->title)))
->class('nowrap'),
'activity_logs' => (new Td())
->text(Html::escapeHTML($message))
->class('maximal'),
'activity_date' => (new Td())
->text(Html::escapeHTML($date))
->class('nowrap'),
'activity_status' => (new Td())
->text(Html::escapeHTML($status))
->class('nowrap'),
]); ]);
$this->userColumns(My::id(), $cols); $this->userColumns(My::id(), $cols);
return return (new Tr('l' . $row->id))
'<tr class="line ' . $offline . '" id="l' . $row->id . '">' . ->class('line' . $offline)
implode(iterator_to_array($cols)) . ->items(iterator_to_array($cols));
'</tr>';
} }
} }

View File

@ -1,59 +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 dcCore;
use Dotclear\Module\MyPlugin; use Dotclear\Module\MyPlugin;
/** /**
* This module definitions. * @brief activityReport My helper.
* @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';
/** @var int Incremental version by breaking changes */ // Use default permissions
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,25 +1,18 @@
<?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\App;
use Dotclear\Core\Process; use Dotclear\Core\Process;
use Exception;
/** /**
* Prepend process. * @brief activityReport prepend class.
* @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
{ {
@ -34,30 +27,14 @@ class Prepend extends Process
return false; return false;
} }
if (defined('ACTIVITY_REPORT')) {
return true;
}
try {
// launch once activity report stuff
ActivityReport::instance();
// regirster activity feed URL // regirster activity feed URL
dcCore::app()->url->register( App::url()->register(
My::id(), My::id(),
'reports', 'reports',
'^reports/((atom|rss2)/(.+))$', '^reports/((atom|rss2)/(.+))$',
[UrlHandler::class, 'feed'] FrontendUrl::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,21 +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;
/** /**
* Module settings helper. * @brief activityReport settings helper class.
* @ingroup activityReport
*
* @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Settings class Settings
{ {

View File

@ -1,27 +1,18 @@
<?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;
/** /**
* Uninstall process. * @brief activityReport uninstall class.
* @ingroup activityReport
* *
* Using plugin Uninstaller * @author Jean-Christian Denis (author)
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class Uninstall extends Process class Uninstall extends Process
{ {
@ -32,7 +23,7 @@ class Uninstall extends Process
public static function process(): bool public static function process(): bool
{ {
if (!self::status() || !dcCore::app()->plugins->moduleExists('Uninstaller')) { if (!self::status()) {
return false; return false;
} }

View File

@ -1,64 +0,0 @@
<?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);
}
}