use namespace (and nearly full rewrite)

This commit is contained in:
Jean-Christian Denis 2023-04-20 14:22:43 +02:00
parent 81d9f1e944
commit 02edbcb877
Signed by: JcDenis
GPG key ID: 1B5B8C5B90B6C951
28 changed files with 2833 additions and 1683 deletions

View file

@ -17,6 +17,5 @@ if (!defined('DC_RC_PATH')) {
class initActivityReport class initActivityReport
{ {
public const ACTIVITY_TABLE_NAME = 'activity'; public const ACTIVITY_TABLE_NAME = 'activity';
public const SETTING_TABLE_NAME = 'activity_setting';
public const CACHE_DIR_NAME = 'activityreport'; public const CACHE_DIR_NAME = 'activityreport';
} }

BIN
icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 792 B

2
icon.svg Normal file
View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<svg fill="#B2B2B2" width="64px" height="64px" viewBox="0 0 24 24" id="frequency" data-name="Flat Color" xmlns="http://www.w3.org/2000/svg" class="icon flat-color"><path id="primary" d="M9,17a1,1,0,0,1-.89-.55L6.38,13H3a1,1,0,0,1,0-2H7a1,1,0,0,1,.89.55L9,13.76l3.11-6.21A1.05,1.05,0,0,1,13,7a1,1,0,0,1,.89.61l2.27,5.3,1-1.46A1,1,0,0,1,18,11h3a1,1,0,0,1,0,2H18.54l-1.71,2.55a1,1,0,0,1-.92.45,1,1,0,0,1-.83-.61l-2.15-5-3,6.07A1,1,0,0,1,9,17Z"></path></svg>

After

Width:  |  Height:  |  Size: 493 B

10
js/backend.js Normal file
View file

@ -0,0 +1,10 @@
/*global $, dotclear */
'use strict';
Object.assign(dotclear.msg, dotclear.getData('activityReport'));
$(() => {
$('#form-logs').on('submit', function () {
return window.confirm(dotclear.msg.confirm_delete);
});
});

View file

@ -1,91 +0,0 @@
/* -- BEGIN LICENSE BLOCK ----------------------------------
* This file is part of activityReport, a plugin for Dotclear 2.
*
* Copyright (c) 2009-2010 JC Denis and contributors
* jcdenis@gdwd.com
*
* Licensed under the GPL version 2.0 license.
* A copy of this license is available in LICENSE file or at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* -- END LICENSE BLOCK ------------------------------------*/
$(function(){
/* tools */
dotclear.jcTools = new jcToolsBox();
/* setting blog */
var bForm=$('#setting-blog-form');
if ($(bForm).attr('id')!=undefined){
dotclear.jcTools.formFieldsetToMenu(bForm);
$(bForm).submit(function(){dotclear.jcTools.waitMessage();return true;});
}
/* setting global */
var gForm=$('#setting-super-form');
if ($(gForm).attr('id')!=undefined){
dotclear.jcTools.formFieldsetToMenu(gForm);
$(gForm).submit(function(){dotclear.jcTools.waitMessage();return true;});
}
});
function jcToolsBox(){}
jcToolsBox.prototype={
text_wait:'Please wait',
section:'',
formFieldsetToMenu:function(form){
var This=this;
var section=$(form).children('fieldset[id='+This.section+']').attr('id');
var hidden_section=$(form).children('input[name=section]').attr('value');
var formMenu=$(form).children('p.formMenu').get(0);
if (formMenu==undefined) {
$(form).prepend($('<p></p>').addClass('formMenu'));
}
$(form).children('fieldset').each(function(){
var Fieldset=this;
$(Fieldset).hide();
var title=$(Fieldset).children('legend').text();
var menu=$('<a></a>').text(title).addClass('button').attr('tabindex','2').click(
function(){
var fieldset_visible=$(form).children('fieldset:visible');
if (fieldset_visible==undefined){
$(Fieldset).slideDown('slow');$(form).children('input[type=submit]').show();
}else{
$(fieldset_visible).fadeOut('fast',function(){$(Fieldset).fadeIn('fast');$(form).children('input[type=submit]').show();})
}
if (hidden_section==undefined){
$(form).children('input[name=section]').remove();
$(form).append($('<input type="hidden" />').attr('name','section').attr('value',$(Fieldset).attr('id')));
}
$('.message').fadeOut('slow',function(){$(this).slideUp('slow',function(){$(this).remove();})});
}
);
$(form).children('.formMenu').append(menu).append(' ');
});
if (section!=undefined){
$(form).children('fieldset[id='+section+']').show();
}else{
$(form).children('fieldset:first').show();
}
},
waitMessage:function(){
var This=this;
var content=$('div[id=content]');
if (content!=undefined){
$(content).hide();
}else{
$('input').hide();$('select').hide();
content=$('body');
}
var text=$('<p></p>').addClass('message').text(This.text_wait);
This.blinkItem(text);
var box=$('<div></div>').attr('style','margin: 60px auto auto;width:200px;').append(text);
$(content).before($(box));
},
blinkItem:function(item){
var This=this;
$(item).fadeOut('slow',function(){$(this).fadeIn('slow',function(){This.blinkItem(this);})});
}
}

108
locales/fr/main.lang.php Normal file
View file

@ -0,0 +1,108 @@
<?php
/**
* @package Dotclear
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
#
# DOT NOT MODIFY THIS FILE !
#
use Dotclear\Helper\L10n;
L10n::$locales['ActivityReport messages'] = 'Messages de l\'extension';
L10n::$locales['Actions on blog'] = 'Actions sur le blog';
L10n::$locales['Actions on posts'] = 'Actions sur les billets';
L10n::$locales['Actions on comments'] = 'Actions sur les commentaires';
L10n::$locales['Actions on categories'] = 'Actions sur les catégories';
L10n::$locales['Actions on users'] = 'Actions sur les utilisateurs';
L10n::$locales['Special messages'] = 'Messages spéciaux';
L10n::$locales['%s'] = '%s';
L10n::$locales['updating blog'] = 'Mise à jour du blog';
L10n::$locales['Blog was updated by "%s"'] = 'Blog mis à jour par "%s"';
L10n::$locales['404 error'] = 'Erreur 404';
L10n::$locales['New 404 error page at "%s"'] = 'Nouvelle erreur 404 à l\'adresse "%s"';
L10n::$locales['post creation'] = 'Création de billet';
L10n::$locales['A new post called "%s" was created by "%s" at %s'] = 'Un nouveau billet nommé "%s" a été créé par "%s" à l\'adresse "%s"';
L10n::$locales['updating post'] = 'Mise à jour de billet';
L10n::$locales['Post called "%s" has been updated by "%s" at %s'] = 'Le billet nommé "%s" a été mis à jour par "%s" à l\'adresse "%s"';
L10n::$locales['post deletion'] = 'Suppression de billet';
L10n::$locales['Post called "%s" has been deleted by "%s"'] = 'Le billet nommé "%s" a été supprimé par "%s"';
L10n::$locales['Post protection'] = 'Protection des billets';
L10n::$locales['An attempt failed on a passworded post with password "%s" at "%s"'] = 'Une tentative a échoué sur un billet protégé par mot de passe avec l\'essai "%s" à l\'adresse "%s"';
L10n::$locales['comment creation'] = 'Création de commentaire';
L10n::$locales['A new comment was created by "%s" on post "%s" at %s'] = 'Un nouveau commentaire a été créé par "%s" sur le billet "%s"';
L10n::$locales['updating comment'] = 'Mise à jour de commentaire';
L10n::$locales['Comment has been updated by "%s" at %s'] = 'Un commentaire a été mise à jour par "%s" à l\'adresse "%s"';
L10n::$locales['trackback creation'] = 'Création de trackback';
L10n::$locales['A new trackback to "%" at "%s" was created on post "%s" at %s'] = 'Un nouveau rétrolien vers "%s" a été créé sur le billet "%s" à l\'adresse "%s"';
L10n::$locales['category creation'] = 'Création de catégorie';
L10n::$locales['A new category called "%s" was created by "%s" at %s'] = 'Un nouvelle catégorie nommée "%s" a été créé par "%s" à l\'adresse "%s"';
L10n::$locales['updating category'] = 'Mise à jour de catégorie';
L10n::$locales['Category called "%s" has been updated by "%s" at %s'] = 'La catégorie nommée "%s" a été mise à jour par "%s" à l\'adresse "%s"';
L10n::$locales['user creation'] = 'Création d\'utilisateur';
L10n::$locales['A new user named "%s" was created by "%s"'] = 'Un nouvel utilisateur a été ajouté par "%s"';
L10n::$locales['updating user'] = 'Mise à jour d\'utilisateur';
L10n::$locales['User named "%s" has been updated by "%s"'] = 'L\'utilisateur "%s" a été mis à jour par "%s"';
L10n::$locales['user deletion'] = 'Suppression d\'utilisateur';
L10n::$locales['User named "%s" has been deleted by "%"'] = 'L\'utilisateur nommé "%s" a été supprimé par "%s"';
L10n::$locales['HTML'] = 'HTML';
L10n::$locales['Activity report'] = 'Rapport d\'activité';
L10n::$locales['An error occured when parsing report.'] = 'Une erreur est survenue lors de la compilation du rapport.';
L10n::$locales['You received a message from your blog\'s activity report module.'] = 'Vous recevez un message du module de rapport d\'activité de votre blog.';
L10n::$locales['Period from %s to %s'] = 'Période du %s au %s';
L10n::$locales['Activity report deletes some old logs.'] = 'L\'extension a automatiquement effacé des anciennes activités.';
L10n::$locales['Blog "%s" activity report'] = 'Rapport d\'activité du blog "%s"';
L10n::$locales['View all logs'] = 'Voir tous les logs';
L10n::$locales['Number of activities to show on dashboard:'] = 'Nombre d\'activités à afficher sur le tableau de bord :';
L10n::$locales['Do not show activity report'] = 'Ne pas afficher le rapport d\'activité';
L10n::$locales['Group'] = 'Groupe';
L10n::$locales['logs per page'] = 'enregistrements par page';
L10n::$locales['every hour'] = 'toutes les heures';
L10n::$locales['every 2 hours'] = 'toutes les 2 heures';
L10n::$locales['2 times by day'] = '2 fois par jour';
L10n::$locales['every day'] = 'tous les jours';
L10n::$locales['every 2 days'] = 'tous les 2 jours';
L10n::$locales['every week'] = 'toutes les semaines';
L10n::$locales['every 2 weeks'] = 'toutes les 2 semaines';
L10n::$locales['every 4 weeks'] = 'toutes les 4 semaines';
L10n::$locales['Report successfully sent.'] = 'Rapport envoyé avec succès.';
L10n::$locales['Logs successfully deleted.'] = 'Logs effacés avec succès.';
L10n::$locales['never'] = 'jamais';
L10n::$locales['on new activity'] = 'lors d\'une nouvelle activité';
L10n::$locales['This server has no mail function, activityReport does not send email report.'] = 'Ce server n\'a pas de fonction d\'envoie de mail, le rapport ne sera pas envoyer.';
L10n::$locales['Mail report'] = 'Rapport par email';
L10n::$locales['Send report:'] = 'Rapport envoyé :';
L10n::$locales['Recipients:'] = 'Destinataires :';
L10n::$locales['Separate multiple email addresses with a semicolon ";"'] = 'Séparer les adresses email par un point-virgule ";"';
L10n::$locales['Leave it empty to disable mail report.'] = 'Laisser vide pour désactiver le rapport par email.';
L10n::$locales['Use Dotclear date formaters. ex: %B %d at %H:%M'] = 'Utiliser le formatage des dates de Dotclear. ex: %d %B à %H:%M';
L10n::$locales['Report format:'] = 'Format du rapport :';
L10n::$locales['Last report by email:'] = 'Dernier rapport par email :';
L10n::$locales['Next report by email:'] = 'Prochain rapport par email :';
L10n::$locales['Feeds'] = 'Flux';
L10n::$locales['Enable activity feed'] = 'Activer le flux d\'activités';
L10n::$locales['RSS feed'] = 'Flux RSS';
L10n::$locales['Rss2 activities feed'] = 'Flux RSS2 d\'activités';
L10n::$locales['Atom feed'] = 'Flux Atom';
L10n::$locales['Atom activities feed'] = 'Flux Atom d\'activités';
L10n::$locales['Activities'] = 'Activités';
L10n::$locales['Select actions by activity type to add to report'] = 'Sélectionner les actions par type d\'activité à ajouter au rapport';
L10n::$locales['Automatic cleaning of old logs:'] = 'Nettoyage automatique des anciennes activités:';
L10n::$locales['Send report now'] = 'Envoyer un rapport maintenant';
L10n::$locales['Delete all logs now'] = 'Effacer tous les logs maintenant';
L10n::$locales['Plain text'] = 'Texte brut';
L10n::$locales['Logs successfully deleted'] = 'Enregistrements effacés avec succès';
L10n::$locales['Are you sure you want to delete logs?'] = 'Êtes-vous sure de vouloir effacer les enregistrements ?';
L10n::$locales['Delete all aticivity logs'] = 'Effacer tous les enregistrements d\'activités';
L10n::$locales['Delete all allready reported logs'] = 'Effacer les enregistrements d\'activités déjà envoyé par email';
L10n::$locales['No log matches the filter'] = 'Aucun enregistrement ne correspond au filtre';
L10n::$locales['No log'] = 'Pas d\'enregistrement';
L10n::$locales['List of %s logs matching the filter.'] = 'Liste des %s logs correspondants au filtre.';
L10n::$locales['List of %s logs.'] = 'Listes des %s enregistrements';
L10n::$locales['Message'] = 'Message';
L10n::$locales['undefined'] = 'inconnu';
L10n::$locales['reported'] = 'envoyé';
L10n::$locales['Activity log'] = 'Journal d\'activité';
L10n::$locales['Log and receive your blog activity by email, feed, or on dashboard'] = 'Enregistrer et recevoir l\'activité de votre blog par mail, flux ou sur le tableau de bord';

View file

@ -1,270 +1,398 @@
# Language: Français
# Module: activityReport - 3.0
# Date: 2023-04-20 12:20:14
# Author: , contact@jcdenis.fr
# Translated with translater 2023.03.19
msgid "" msgid ""
msgstr "" msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: activityReport 2.0\n" "Project-Id-Version: activityReport 3.0\n"
"POT-Creation-Date: \n" "POT-Creation-Date: \n"
"PO-Revision-Date: 2022-11-27T21:29:52+00:00\n" "PO-Revision-Date: 2023-04-20T12:20:14+00:00\n"
"Last-Translator: Jean-Crhistian Denis\n" "Last-Translator: Jean-Christian Denis\n"
"Language-Team: \n" "Language-Team: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
msgid "Activity report" #: src/ActivityBehaviors.php:32
msgstr "Rapport d'activité"
msgid "View all logs"
msgstr "Voir tous les logs"
msgid "Number of activities to show on dashboard:"
msgstr "Nombre d'activités à afficher sur le tableau de bord :"
msgid "Do not show activity report"
msgstr "Ne pas afficher le rapport d'activité"
msgid "every hour"
msgstr "toutes les heures"
msgid "every 2 hours"
msgstr "toutes les 2 heures"
msgid "2 times by day"
msgstr "2 fois par jour"
msgid "every day"
msgstr "tous les jours"
msgid "every 2 days"
msgstr "tous les 2 jours"
msgid "every week"
msgstr "toutes les semaines"
msgid "every 2 weeks"
msgstr "toutes les 2 semaines"
msgid "every 4 weeks"
msgstr "toutes les 4 semaines"
msgid "Plain text"
msgstr "Texte brut"
msgid "HTML"
msgstr "HTML"
msgid "Report successfully sent."
msgstr "Rapport envoyé avec succès."
msgid "Logs successfully deleted."
msgstr "Logs effacés avec succès."
msgid "never"
msgstr "jamais"
msgid "on new activity"
msgstr "lors d'une nouvelle activité"
msgid "Configure activity report for %s"
msgstr "Configurer le rapport d'activité pour %s"
msgid "current blog"
msgstr "le blog courant"
msgid "all blogs"
msgstr "tous les blogs"
msgid "This server has no mail function, activityReport does not send email report."
msgstr "Ce server n'a pas de fonction d'envoie de mail, le rapport ne sera pas envoyer."
msgid "Enable super administrator report"
msgstr "Autoriser le rapport de super administrateur"
msgid "Enable report on this blog"
msgstr "Autoriser le rapport sur ce blog"
msgid "Automatic cleaning of old logs:"
msgstr "Nettoyage automatique des anciennes activités:"
msgid "Use Dotclear date formaters. ex: %B %d at %H:%M"
msgstr "Utiliser le formatage des dates de Dotclear. ex: %d %B à %H:%M"
msgid "RSS feed"
msgstr "Flux RSS"
msgid "Rss2 feed for activity on this blog"
msgstr "Flux Rss2 pour l'activité de ce blog"
msgid "Atom feed"
msgstr "Flux Atom"
msgid "Atom feed for activity on this blog"
msgstr "Flux Atom pour l'activité de ce blog"
msgid "Send report:"
msgstr "Rapport envoyé :"
msgid "Recipients:"
msgstr "Destinataires :"
msgid "Separate multiple email addresses with a semicolon \";\""
msgstr "Séparer les adresses email par un point-virgule \";\""
msgid "Report format:"
msgstr "Format du rapport :"
msgid "Last report by email:"
msgstr "Dernier rapport par email :"
msgid "Next report by email:"
msgstr "Prochain rapport par email :"
msgid "Select blogs to add to report"
msgstr "Sélectionner les blogs à ajouter au rapport"
msgid "Report"
msgstr "Rapport"
msgid "Select actions by activity type to add to report"
msgstr "Sélectionner les actions par type d'activité à ajouter au rapport"
msgid "Send report now"
msgstr "Envoyer un rapport maintenant"
msgid "Delete all logs now"
msgstr "Effacer tous les logs maintenant"
msgid "ActivityReport messages" msgid "ActivityReport messages"
msgstr "Messages de l'extension" msgstr "Messages de l'extension"
msgid "Special messages" #: src/ActivityBehaviors.php:33
msgstr "Messages spéciaux"
msgid "%s"
msgstr "%s"
msgid "Actions on blog" msgid "Actions on blog"
msgstr "Actions sur le blog" msgstr "Actions sur le blog"
msgid "updating blog" #: src/ActivityBehaviors.php:34
msgstr "Mise à jour du blog"
msgid "Blog was updated by \"%s\""
msgstr "Blog mis à jour par \"%s\""
msgid "404 error"
msgstr "Erreur 404"
msgid "New 404 error page at \"%s\""
msgstr "Nouvelle erreur 404 à l'adresse \"%s\""
msgid "Actions on posts" msgid "Actions on posts"
msgstr "Actions sur les billets" msgstr "Actions sur les billets"
msgid "post creation" #: src/ActivityBehaviors.php:35
msgstr "Création de billet"
msgid "A new post called \"%s\" was created by \"%s\" at %s"
msgstr "Un nouveau billet nommé \"%s\" a été créé par \"%s\" à l'adresse \"%s\""
msgid "updating post"
msgstr "Mise à jour de billet"
msgid "Post called \"%s\" has been updated by \"%s\" at %s"
msgstr "Le billet nommé \"%s\" a été mis à jour par \"%s\" à l'adresse \"%s\""
msgid "post deletion"
msgstr "Suppression de billet"
msgid "Post called \"%s\" has been deleted by \"%s\""
msgstr "Le billet nommé \"%s\" a été supprimé par \"%s\""
msgid "Post protection"
msgstr "Protection des billets"
msgid "An attempt failed on a passworded post with password \"%s\" at \"%s\""
msgstr "Une tentative a échoué sur un billet protégé par mot de passe avec l'essai \"%s\" à l'adresse \"%s\""
msgid "Actions on comments" msgid "Actions on comments"
msgstr "Actions sur les commentaires" msgstr "Actions sur les commentaires"
msgid "comment creation" #: src/ActivityBehaviors.php:36
msgstr "Création de commentaire"
msgid "A new comment was created by \"%s\" on post \"%s\" at %s"
msgstr "Un nouveau commentaire a été créé par \"%s\" sur le billet \"%s\""
msgid "updating comment"
msgstr "Mise à jour de commentaire"
msgid "Comment has been updated by \"%s\" at %s"
msgstr "Un commentaire a été mise à jour par \"%s\" à l'adresse \"%s\""
msgid "trackback creation"
msgstr "Création de trackback"
msgid "A new trackback to \"%\" at \"%s\" was created on post \"%s\" at %s"
msgstr "Un nouveau rétrolien vers \"%s\" a été créé sur le billet \"%s\" à l'adresse \"%s\""
msgid "Actions on categories" msgid "Actions on categories"
msgstr "Actions sur les catégories" msgstr "Actions sur les catégories"
msgid "category creation" #: src/ActivityBehaviors.php:37
msgstr "Création de catégorie"
msgid "A new category called \"%s\" was created by \"%s\" at %s"
msgstr "Un nouvelle catégorie nommée \"%s\" a été créé par \"%s\" à l'adresse \"%s\""
msgid "updating category"
msgstr "Mise à jour de catégorie"
msgid "Category called \"%s\" has been updated by \"%s\" at %s"
msgstr "La catégorie nommée \"%s\" a été mise à jour par \"%s\" à l'adresse \"%s\""
msgid "Actions on users" msgid "Actions on users"
msgstr "Actions sur les utilisateurs" msgstr "Actions sur les utilisateurs"
#: src/ActivityBehaviors.php:43
msgid "Special messages"
msgstr "Messages spéciaux"
#: src/ActivityBehaviors.php:44
msgid "%s"
msgstr "%s"
#: src/ActivityBehaviors.php:54
msgid "updating blog"
msgstr "Mise à jour du blog"
#: src/ActivityBehaviors.php:55
msgid "Blog was updated by \"%s\""
msgstr "Blog mis à jour par \"%s\""
#: src/ActivityBehaviors.php:63
msgid "404 error"
msgstr "Erreur 404"
#: src/ActivityBehaviors.php:64
msgid "New 404 error page at \"%s\""
msgstr "Nouvelle erreur 404 à l'adresse \"%s\""
#: src/ActivityBehaviors.php:74
msgid "post creation"
msgstr "Création de billet"
#: src/ActivityBehaviors.php:75
msgid "A new post called \"%s\" was created by \"%s\" at %s"
msgstr "Un nouveau billet nommé \"%s\" a été créé par \"%s\" à l'adresse \"%s\""
#: src/ActivityBehaviors.php:94
msgid "updating post"
msgstr "Mise à jour de billet"
#: src/ActivityBehaviors.php:95
msgid "Post called \"%s\" has been updated by \"%s\" at %s"
msgstr "Le billet nommé \"%s\" a été mis à jour par \"%s\" à l'adresse \"%s\""
#: src/ActivityBehaviors.php:103
msgid "post deletion"
msgstr "Suppression de billet"
#: src/ActivityBehaviors.php:104
msgid "Post called \"%s\" has been deleted by \"%s\""
msgstr "Le billet nommé \"%s\" a été supprimé par \"%s\""
#: src/ActivityBehaviors.php:113
msgid "Post protection"
msgstr "Protection des billets"
#: src/ActivityBehaviors.php:114
msgid "An attempt failed on a passworded post with password \"%s\" at \"%s\""
msgstr "Une tentative a échoué sur un billet protégé par mot de passe avec l'essai \"%s\" à l'adresse \"%s\""
#: src/ActivityBehaviors.php:124
msgid "comment creation"
msgstr "Création de commentaire"
#: src/ActivityBehaviors.php:125
msgid "A new comment was created by \"%s\" on post \"%s\" at %s"
msgstr "Un nouveau commentaire a été créé par \"%s\" sur le billet \"%s\""
#: src/ActivityBehaviors.php:134
msgid "updating comment"
msgstr "Mise à jour de commentaire"
#: src/ActivityBehaviors.php:135
msgid "Comment has been updated by \"%s\" at %s"
msgstr "Un commentaire a été mise à jour par \"%s\" à l'adresse \"%s\""
#: src/ActivityBehaviors.php:147
msgid "trackback creation"
msgstr "Création de trackback"
#: src/ActivityBehaviors.php:148
msgid "A new trackback to \"%\" at \"%s\" was created on post \"%s\" at %s"
msgstr "Un nouveau rétrolien vers \"%s\" a été créé sur le billet \"%s\" à l'adresse \"%s\""
#: src/ActivityBehaviors.php:156
msgid "category creation"
msgstr "Création de catégorie"
#: src/ActivityBehaviors.php:157
msgid "A new category called \"%s\" was created by \"%s\" at %s"
msgstr "Un nouvelle catégorie nommée \"%s\" a été créé par \"%s\" à l'adresse \"%s\""
#: src/ActivityBehaviors.php:165
msgid "updating category"
msgstr "Mise à jour de catégorie"
#: src/ActivityBehaviors.php:166
msgid "Category called \"%s\" has been updated by \"%s\" at %s"
msgstr "La catégorie nommée \"%s\" a été mise à jour par \"%s\" à l'adresse \"%s\""
#: src/ActivityBehaviors.php:176
msgid "user creation" msgid "user creation"
msgstr "Création d'utilisateur" msgstr "Création d'utilisateur"
#: src/ActivityBehaviors.php:177
msgid "A new user named \"%s\" was created by \"%s\"" msgid "A new user named \"%s\" was created by \"%s\""
msgstr "Un nouvel utilisateur a été ajouté par \"%s\"" msgstr "Un nouvel utilisateur a été ajouté par \"%s\""
#: src/ActivityBehaviors.php:185
msgid "updating user" msgid "updating user"
msgstr "Mise à jour d'utilisateur" msgstr "Mise à jour d'utilisateur"
#: src/ActivityBehaviors.php:186
msgid "User named \"%s\" has been updated by \"%s\"" msgid "User named \"%s\" has been updated by \"%s\""
msgstr "L'utilisateur \"%s\" a été mis à jour par \"%s\"" msgstr "L'utilisateur \"%s\" a été mis à jour par \"%s\""
#: src/ActivityBehaviors.php:217
msgid "user deletion" msgid "user deletion"
msgstr "Suppression d'utilisateur" msgstr "Suppression d'utilisateur"
#: src/ActivityBehaviors.php:218
msgid "User named \"%s\" has been deleted by \"%\"" msgid "User named \"%s\" has been deleted by \"%\""
msgstr "L'utilisateur nommé \"%s\" a été supprimé par \"%s\"" msgstr "L'utilisateur nommé \"%s\" a été supprimé par \"%s\""
#: src/ActivityBehaviors.php:235
msgid "HTML"
msgstr "HTML"
#: src/ActivityBehaviors.php:248
#: src/ActivityReport.php:341
msgid "Activity report"
msgstr "Rapport d'activité"
#: src/ActivityReport.php:331
msgid "An error occured when parsing report." msgid "An error occured when parsing report."
msgstr "Une erreur est survenue lors de la compilation du rapport." msgstr "Une erreur est survenue lors de la compilation du rapport."
#: src/ActivityReport.php:347
msgid "You received a message from your blog's activity report module." msgid "You received a message from your blog's activity report module."
msgstr "Vous recevez un message du module de rapport d'activité de votre blog." msgstr "Vous recevez un message du module de rapport d'activité de votre blog."
#: src/ActivityReport.php:356
msgid "Period from %s to %s" msgid "Period from %s to %s"
msgstr "Période du %s au %s" msgstr "Période du %s au %s"
#: src/ActivityReport.php:404
msgid "Activity report deletes some old logs." msgid "Activity report deletes some old logs."
msgstr "L'extension a automatiquement effacé des anciennes activités." msgstr "L'extension a automatiquement effacé des anciennes activités."
msgid "Blog activity report" #: src/ActivityReport.php:661
msgstr "Rapport d'activité du blog" msgid "Blog \"%s\" activity report"
msgstr "Rapport d'activité du blog \"%s\""
msgid "Logs" #: src/Backend.php:126
msgstr "Enregistrements" msgid "View all logs"
msgstr "Voir tous les logs"
#: src/Backend.php:142
msgid "Number of activities to show on dashboard:"
msgstr "Nombre d'activités à afficher sur le tableau de bord :"
#: src/Backend.php:144
msgid "Do not show activity report"
msgstr "Ne pas afficher le rapport d'activité"
#: src/Backend.php:170
#: src/ManageList.php:48
msgid "Group"
msgstr "Groupe"
#: src/Backend.php:177
msgid "logs per page"
msgstr "enregistrements par page"
#: src/Combo.php:45
msgid "every hour"
msgstr "toutes les heures"
#: src/Combo.php:46
msgid "every 2 hours"
msgstr "toutes les 2 heures"
#: src/Combo.php:47
msgid "2 times by day"
msgstr "2 fois par jour"
#: src/Combo.php:48
msgid "every day"
msgstr "tous les jours"
#: src/Combo.php:49
msgid "every 2 days"
msgstr "tous les 2 jours"
#: src/Combo.php:50
msgid "every week"
msgstr "toutes les semaines"
#: src/Combo.php:68
msgid "every 2 weeks"
msgstr "toutes les 2 semaines"
#: src/Combo.php:69
msgid "every 4 weeks"
msgstr "toutes les 4 semaines"
#: src/Config.php:85
msgid "Report successfully sent."
msgstr "Rapport envoyé avec succès."
#: src/Config.php:92
msgid "Logs successfully deleted."
msgstr "Logs effacés avec succès."
#: src/Config.php:116
msgid "never"
msgstr "jamais"
#: src/Config.php:117
msgid "on new activity"
msgstr "lors d'une nouvelle activité"
#: src/Config.php:133
msgid "This server has no mail function, activityReport does not send email report."
msgstr "Ce server n'a pas de fonction d'envoie de mail, le rapport ne sera pas envoyer."
#: src/Config.php:140
msgid "Mail report"
msgstr "Rapport par email"
#: src/Config.php:142
msgid "Send report:"
msgstr "Rapport envoyé :"
#: src/Config.php:146
msgid "Recipients:"
msgstr "Destinataires :"
#: src/Config.php:149
msgid "Separate multiple email addresses with a semicolon \";\""
msgstr "Séparer les adresses email par un point-virgule \";\""
#: src/Config.php:150
msgid "Leave it empty to disable mail report."
msgstr "Laisser vide pour désactiver le rapport par email."
#: src/Config.php:155
msgid "Use Dotclear date formaters. ex: %B %d at %H:%M"
msgstr "Utiliser le formatage des dates de Dotclear. ex: %d %B à %H:%M"
#: src/Config.php:157
msgid "Report format:"
msgstr "Format du rapport :"
#: src/Config.php:162
msgid "Last report by email:"
msgstr "Dernier rapport par email :"
#: src/Config.php:163
msgid "Next report by email:"
msgstr "Prochain rapport par email :"
#: src/Config.php:167
msgid "Feeds"
msgstr "Flux"
#: src/Config.php:170
msgid "Enable activity feed"
msgstr "Activer le flux d'activités"
#: src/Config.php:174
msgid "RSS feed"
msgstr "Flux RSS"
#: src/Config.php:177
msgid "Rss2 activities feed"
msgstr "Flux RSS2 d'activités"
#: src/Config.php:178
msgid "Atom feed"
msgstr "Flux Atom"
#: src/Config.php:181
msgid "Atom activities feed"
msgstr "Flux Atom d'activités"
#: src/Config.php:188
msgid "Activities"
msgstr "Activités"
#: src/Config.php:189
msgid "Select actions by activity type to add to report"
msgstr "Sélectionner les actions par type d'activité à ajouter au rapport"
#: src/Config.php:214
msgid "Automatic cleaning of old logs:"
msgstr "Nettoyage automatique des anciennes activités:"
#: src/Config.php:219
msgid "Send report now"
msgstr "Envoyer un rapport maintenant"
#: src/Config.php:223
msgid "Delete all logs now"
msgstr "Effacer tous les logs maintenant"
#: src/Format.php:68
msgid "Plain text"
msgstr "Texte brut"
#: src/Manage.php:58
msgid "Logs successfully deleted"
msgstr "Enregistrements effacés avec succès"
#: src/Manage.php:91
msgid "Are you sure you want to delete logs?"
msgstr "Êtes-vous sure de vouloir effacer les enregistrements ?"
#: src/Manage.php:114
msgid "Delete all aticivity logs"
msgstr "Effacer tous les enregistrements d'activités"
#: src/Manage.php:115
msgid "Delete all allready reported logs"
msgstr "Effacer les enregistrements d'activités déjà envoyé par email"
#: src/ManageList.php:33
msgid "No log matches the filter"
msgstr "Aucun enregistrement ne correspond au filtre"
#: src/ManageList.php:35
msgid "No log" msgid "No log"
msgstr "Pas d'enregistrement" msgstr "Pas d'enregistrement"
#: src/ManageList.php:43
msgid "List of %s logs matching the filter."
msgstr "Liste des %s logs correspondants au filtre."
#: src/ManageList.php:44
msgid "List of %s logs."
msgstr "Listes des %s enregistrements"
#: src/ManageList.php:50
msgid "Message" msgid "Message"
msgstr "Message" msgstr "Message"
msgid "Activity report module" #: src/ManageList.php:79
msgstr "Module de rapport d'activité" msgid "undefined"
msgstr "inconnu"
#: src/ManageList.php:85
msgid "reported"
msgstr "envoyé"
msgid "Activity log"
msgstr "Journal d'activité"
msgid "Log and receive your blog activity by email, feed, or on dashboard"
msgstr "Enregistrer et recevoir l'activité de votre blog par mail, flux ou sur le tableau de bord"

45
src/Action.php Normal file
View file

@ -0,0 +1,45 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use dcCore;
/**
* Action descriptor.
*/
class Action
{
/**
* Constructor sets action description.
*
* @param string $id The action ID
* @param string $title The action title
* @param string $message The action message
* @param string $behavior The behavior name
* @param null|callable $function The callback function
*/
public function __construct(
public readonly string $id,
public readonly string $title,
public readonly string $message,
string $behavior,
?callable $function
) {
// fake action has no behavior
if (null !== $function) {
dcCore::app()->addBehavior($behavior, $function);
}
}
}

View file

@ -10,393 +10,470 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_RC_PATH')) { declare(strict_types=1);
return null;
}
if (!defined('ACTIVITY_REPORT_V2')) { namespace Dotclear\Plugin\activityReport;
return null;
}
class activityReportBehaviors use ArrayObject;
use cursor;
use dcBlog;
use dcCore;
use dcRecord;
use dcUtils;
use Dotclear\Helper\Network\Http;
/**
* Register default activities and export mail formats.
*/
class ActivityBehaviors
{ {
public static function registerBehaviors() public static function register(): void
{ {
$my = new Group(My::id(), __('ActivityReport messages'));
$blog = new Group('blog', __('Actions on blog'));
$post = new Group('post', __('Actions on posts'));
$comment = new Group('comment', __('Actions on comments'));
$category = new Group('category', __('Actions on categories'));
$user = new Group('user', __('Actions on users'));
// ActivityReport plugin // ActivityReport plugin
dcCore::app()->activityReport->addGroup(basename(dirname(__DIR__)), __('ActivityReport messages'));
dcCore::app()->activityReport->addAction( $my->add(new Action(
basename(dirname(__DIR__)),
'message', 'message',
__('Special messages'), __('Special messages'),
__('%s'), __('%s'),
'messageActivityReport', 'messageActivityReport',
['activityReportBehaviors', 'messageActivityReport'] [self::class, 'messageActivityReport']
); ));
// Blog
dcCore::app()->activityReport->addGroup('blog', __('Actions on blog'));
// Not use as it is global : BEHAVIOR adminAfterBlogCreate in admin/blog.php // Not use as it is global : BEHAVIOR adminAfterBlogCreate in admin/blog.php
// from BEHAVIOR adminAfterBlogUpdate in admin/blog_pref.php // from BEHAVIOR adminAfterBlogUpdate in admin/blog_pref.php
dcCore::app()->activityReport->addAction( $blog->add(new Action(
'blog',
'update', 'update',
__('updating blog'), __('updating blog'),
__('Blog was updated by "%s"'), __('Blog was updated by "%s"'),
'adminAfterBlogUpdate', 'adminAfterBlogUpdate',
['activityReportBehaviors', 'blogUpdate'] [self::class, 'blogUpdate']
); ));
// from BEHAVIOR publicHeadContent in template // from BEHAVIOR publicHeadContent in template
dcCore::app()->activityReport->addAction( $blog->add(new Action(
'blog',
'p404', 'p404',
__('404 error'), __('404 error'),
__('New 404 error page at "%s"'), __('New 404 error page at "%s"'),
'publicHeadContent', 'publicHeadContent',
['activityReportBehaviors', 'blogP404'] [self::class, 'blogP404']
); ));
// Post
dcCore::app()->activityReport->addGroup('post', __('Actions on posts'));
// 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)
// duplicate adminAfterPostCreate in admin/post.php // duplicate adminAfterPostCreate in admin/post.php
// duplicate adminAfterPostCreate in admin/services.php // duplicate adminAfterPostCreate in admin/services.php
dcCore::app()->activityReport->addAction( $post->add(new Action(
'post',
'create', 'create',
__('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',
['activityReportBehaviors', 'postCreate'] [self::class, 'postCreate']
); ));
// Plugin contribute // Plugin contribute
// from BEHAVIOR publicAfterPostCreate in plugins/contribute/_public.php // from BEHAVIOR publicAfterPostCreate in plugins/contribute/_public.php
dcCore::app()->activityReport->addAction( $post->add(new Action(
'post',
'create', 'create',
__('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',
['activityReportBehaviors', 'postCreate'] [self::class, 'postCreate']
); ));
// from BEHAVIOR coreAfterPostUpdate in inc/core/class.dc.blog.php (DC2.2) // from BEHAVIOR coreAfterPostUpdate in inc/core/class.dc.blog.php (DC2.2)
// duplicate adminAfterPostUpdate in admin/post.php // duplicate adminAfterPostUpdate in admin/post.php
dcCore::app()->activityReport->addAction( $post->add(new Action(
'post',
'update', 'update',
__('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',
['activityReportBehaviors', 'postUpdate'] [self::class, 'postUpdate']
); ));
// from BEHAVIOR adminBeforePostDelete in admin/post.php // from BEHAVIOR adminBeforePostDelete in admin/post.php
dcCore::app()->activityReport->addAction( $post->add(new Action(
'post',
'delete', 'delete',
__('post deletion'), __('post deletion'),
__('Post called "%s" has been deleted by "%s"'), __('Post called "%s" has been deleted by "%s"'),
'adminBeforePostDelete', 'adminBeforePostDelete',
['activityReportBehaviors', 'postDelete'] [self::class, 'postDelete']
); ));
// Wrong attempt on passworded enrty // Wrong attempt on passworded enrty
// from BEHAVIOR urlHandlerServeDocument in inc/public/lib.urlhandlers.php // from BEHAVIOR urlHandlerServeDocument in inc/public/lib.urlhandlers.php
dcCore::app()->activityReport->addAction( $post->add(new Action(
'post',
'protection', 'protection',
__('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',
['activityReportBehaviors', 'postPasswordAttempt'] [self::class, 'postPasswordAttempt']
); ));
// Comment
dcCore::app()->activityReport->addGroup('comment', __('Actions on comments'));
// from BEHAVIOR coreAfterCommentCreate in inc/core/class.dc.blog.php // from BEHAVIOR coreAfterCommentCreate in inc/core/class.dc.blog.php
// duplicate adminAfterCommentCreate in admin/comment.php // duplicate adminAfterCommentCreate in admin/comment.php
// duplicate publicAfterCommentCreate in inc/public/lib.urlhandlers.php // duplicate publicAfterCommentCreate in inc/public/lib.urlhandlers.php
dcCore::app()->activityReport->addAction( $comment->add(new Action(
'comment',
'create', 'create',
__('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',
['activityReportBehaviors', 'commentCreate'] [self::class, 'commentCreate']
); ));
// from BEHAVIOR coreAfterCommentUpdate in inc/core/class.dc.blog.php // from BEHAVIOR coreAfterCommentUpdate in inc/core/class.dc.blog.php
// duplicate adminAfterCommentUpdate in admin/comment.php // duplicate adminAfterCommentUpdate in admin/comment.php
dcCore::app()->activityReport->addAction( $comment->add(new Action(
'comment',
'update', 'update',
__('updating comment'), __('updating comment'),
__('Comment has been updated by "%s" at %s'), __('Comment has been updated by "%s" at %s'),
'coreAfterCommentUpdate', 'coreAfterCommentUpdate',
['activityReportBehaviors', 'commentUpdate'] [self::class, 'commentUpdate']
); ));
// Missing coreBeforeCommentDelete in inc/core/class.dc.blog.php // Missing coreBeforeCommentDelete in inc/core/class.dc.blog.php
// Missing adminBeforeCommentDelete in admin/comment.php // Missing adminBeforeCommentDelete in admin/comment.php
// from BEHAVIOR coreAfterCommentCreate in inc/core/class.dc.blog.php // from BEHAVIOR coreAfterCommentCreate in inc/core/class.dc.blog.php
// duplicate publicAfterTrackbackCreate in inc/core/class.dc.trackback.php // duplicate publicAfterTrackbackCreate in inc/core/class.dc.trackback.php
dcCore::app()->activityReport->addAction( $comment->add(new Action(
'comment',
'trackback', 'trackback',
__('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',
['activityReportBehaviors', 'trackbackCreate'] [self::class, 'trackbackCreate']
); ));
// Category
dcCore::app()->activityReport->addGroup('category', __('Actions on categories'));
// from BEHAVIOR adminAfterCategoryCreate in admin/category.php // from BEHAVIOR adminAfterCategoryCreate in admin/category.php
dcCore::app()->activityReport->addAction( $category->add(new Action(
'category',
'create', 'create',
__('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',
['activityReportBehaviors', 'categoryCreate'] [self::class, 'categoryCreate']
); ));
// from BEHAVIOR adminAfterCategoryUpdate in admin/category.php // from BEHAVIOR adminAfterCategoryUpdate in admin/category.php
dcCore::app()->activityReport->addAction( $category->add(new Action(
'category',
'update', 'update',
__('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',
['activityReportBehaviors', 'categoryUpdate'] [self::class, 'categoryUpdate']
); ));
// Missing adminBeforeCategoryDelete in admin/category.php // Missing adminBeforeCategoryDelete in admin/category.php
// User
dcCore::app()->activityReport->addGroup('user', __('Actions on users'));
// from BEHAVIOR adminAfterUserCreate in admin/user.php // from BEHAVIOR adminAfterUserCreate in admin/user.php
dcCore::app()->activityReport->addAction( $user->add(new Action(
'user',
'create', 'create',
__('user creation'), __('user creation'),
__('A new user named "%s" was created by "%s"'), __('A new user named "%s" was created by "%s"'),
'adminAfterUserCreate', 'adminAfterUserCreate',
['activityReportBehaviors', 'userCreate'] [self::class, 'userCreate']
); ));
// from BEHAVIOR adminAfterUserUpdated in admin/user.php // from BEHAVIOR adminAfterUserUpdated in admin/user.php
dcCore::app()->activityReport->addAction( $user->add(new Action(
'user',
'update', 'update',
__('updating user'), __('updating user'),
__('User named "%s" has been updated by "%s"'), __('User named "%s" has been updated by "%s"'),
'adminAfterUserUpdate', 'adminAfterUserUpdate',
['activityReportBehaviors', 'userUpdate'] [self::class, 'userUpdate']
); ));
// from BEHAVIOR adminAfterUserProfileUpdate in admin/preferences.php
$user->add(new Action(
'preference',
__('updating user preference'),
__('User named "%s" preference has been updated by "%s"'),
'adminAfterUserProfileUpdate',
[self::class, 'userPreference']
));
$user->add(new Action(
'preference',
__('updating user preference'),
__('User named "%s" preference has been updated by "%s"'),
'adminAfterUserOptionsUpdate',
[self::class, 'userPreference']
));
$user->add(new Action(
'preference',
__('updating user preference'),
__('User named "%s" preference has been updated by "%s"'),
'adminAfterDashboardOptionsUpdate',
[self::class, 'userPreference']
));
// from BEHAVIOR adminBeforeUserDelete in admin/users.php // from BEHAVIOR adminBeforeUserDelete in admin/users.php
dcCore::app()->activityReport->addAction( $user->add(new Action(
'user',
'delete', 'delete',
__('user deletion'), __('user deletion'),
__('User named "%s" has been deleted by "%"'), __('User named "%s" has been deleted by "%"'),
'adminBeforeUserDelete', 'adminBeforeUserDelete',
['activityReportBehaviors', 'userDelete'] [self::class, 'userDelete']
); ));
ActivityReport::instance()->groups
->add($my)
->add($blog)
->add($post)
->add($comment)
->add($category)
->add($user);
// Add default email report formats
ActivityReport::instance()->formats
->add(new Format('plain', [])) // plain text format is build with default values
->add(new Format('html', [
'name' => __('HTML'),
'blog_title' => '<h2><a href="%URL%">%TEXT%</a></h2>',
'group_title' => '<h3>%TEXT%</h3>',
'group_open' => '<ul>',
'group_close' => '</ul>',
'action' => '<li><em>%TIME%</em><br />%TEXT%</li>',
'error' => '<p>%TEXT%</p>',
'period_title' => '<h1>%TEXT%</h1>',
'period_open' => '<ul>',
'period_close' => '</ul>',
'info' => '<li>%TEXT%</li>',
'page' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n" .
'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">' . "\n" .
'<head><title>' . __('Activity report') . '</title>' .
'<style type="text/css">' .
' body { color: #303030; background: #FCFCFC; font-size: 0.7em;font-family: Georgia, Tahoma, Arial, Helvetica, sans-serif; }' .
' a { color: #303030; text-decoration: none; }' .
' h1 { text-align: center; font-size: 2em; }' .
' h2 { color: #303030; text-align:center; }' .
' h3 { color: #7F3F3F; }' .
' li em { color: #303030; }' .
' div.info { color: #3F497F; background-color: #F8F8EB; border: 1px solid #888888; margin: 4px; padding: 4px; }' .
' div.content { color: #3F7F47; background-color: #F8F8EB; border: 1px solid #888888; margin: 4px; padding: 4px; }' .
' div.foot { text-align:center; font-size: 0.9em; }' .
'</style>' .
'</head><body>' .
'<div class="info">%PERIOD%</div><div class="content">%TEXT%</div>' .
'<div class="foot"><p>Powered by <a href="https://github.com/JcDenis/activityReport">activityReport</a></p></div>' .
'</body></html>',
]));
} }
public static function messageActivityReport($message) public static function messageActivityReport(string $message): void
{ {
$logs = [$message]; $logs = [$message];
dcCore::app()->activityReport->addLog(basename(dirname(__DIR__)), 'message', $logs); ActivityReport::instance()->addLog(My::id(), 'message', $logs);
} }
public static function blogUpdate($cur, $blog_id) public static function blogUpdate(cursor $cur, string $blog_id): void
{ {
$logs = [dcCore::app()->auth->getInfo('user_cn')]; $logs = [(string) dcCore::app()->auth?->getInfo('user_cn')];
dcCore::app()->activityReport->addLog('blog', 'update', $logs); ActivityReport::instance()->addLog('blog', 'update', $logs);
} }
public static function blogP404() public static function blogP404(): void
{ {
if (dcCore::app()->url->type != '404') { if (dcCore::app()->url->type != '404') {
return null; return;
} }
$logs = [dcCore::app()->blog->url . $_SERVER['QUERY_STRING']]; $logs = [(string) dcCore::app()->blog?->url . $_SERVER['QUERY_STRING']];
dcCore::app()->activityReport->addLog('blog', 'p404', $logs); ActivityReport::instance()->addLog('blog', 'p404', $logs);
} }
public static function postCreate($cur, $post_id) public static function postCreate(cursor $cur, int $post_id): void
{ {
$type = $cur->post_type ? $cur->post_type : 'post'; $type = $cur->getField('post_type') ?? 'post';
$post_url = dcCore::app()->blog->getPostURL('', $cur->post_dt, $cur->post_title, $post_id); $post_url = dcCore::app()->blog?->getPostURL('', $cur->getField('post_dt'), $cur->getField('post_title'), $post_id);
$logs = [ $logs = [
$cur->post_title, (string) $cur->getField('post_title'),
dcCore::app()->auth->getInfo('user_cn'), (string) dcCore::app()->auth?->getInfo('user_cn'),
dcCore::app()->blog->url . dcCore::app()->url->getBase($type) . '/' . $post_url, (string) dcCore::app()->blog?->url . dcCore::app()->url->getBase($type) . '/' . $post_url,
]; ];
dcCore::app()->activityReport->addLog('post', 'create', $logs); ActivityReport::instance()->addLog('post', 'create', $logs);
} }
public static function postUpdate($cur, $post_id) public static function postUpdate(cursor $cur, int $post_id): void
{ {
$type = $cur->post_type ? $cur->post_type : 'post'; $type = $cur->getField('post_type') ?? 'post';
$post_url = dcCore::app()->blog->getPostURL('', $cur->post_dt, $cur->post_title, $post_id); $post_url = dcCore::app()->blog?->getPostURL('', $cur->getField('post_dt'), $cur->getField('post_title'), $post_id);
$logs = [ $logs = [
$cur->post_title, (string) $cur->getField('post_title'),
dcCore::app()->auth->getInfo('user_cn'), (string) dcCore::app()->auth?->getInfo('user_cn'),
dcCore::app()->blog->url . dcCore::app()->url->getBase($type) . '/' . $post_url, (string) dcCore::app()->blog?->url . dcCore::app()->url->getBase($type) . '/' . $post_url,
]; ];
dcCore::app()->activityReport->addLog('post', 'update', $logs); ActivityReport::instance()->addLog('post', 'update', $logs);
} }
public static function postDelete($post_id) public static function postDelete(int $post_id): void
{ {
$posts = dcCore::app()->blog->getPosts(['post_id' => $post_id, 'limit' => 1]); $posts = dcCore::app()->blog?->getPosts(['post_id' => $post_id, 'limit' => 1]);
if (!$posts || $posts->isEmpty()) {
return;
}
$logs = [ $logs = [
$posts->post_title, (string) $posts->f('post_title'),
dcCore::app()->auth->getInfo('user_cn'), (string) dcCore::app()->auth?->getInfo('user_cn'),
]; ];
dcCore::app()->activityReport->addLog('post', 'delete', $logs); ActivityReport::instance()->addLog('post', 'delete', $logs);
} }
public static function postPasswordAttempt($result) 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'])) {
return null; return;
} }
$logs = [ $logs = [
$_POST['password'], $_POST['password'],
http::getSelfURI(), Http::getSelfURI(),
]; ];
dcCore::app()->activityReport->addLog('post', 'protection', $logs); ActivityReport::instance()->addLog('post', 'protection', $logs);
} }
public static function commentCreate($blog, $cur) public static function commentCreate(dcBlog $blog, cursor $cur): void
{ {
if ($cur->comment_trackback) { if ($cur->getField('comment_trackback')) {
return null; return;
} }
$posts = dcCore::app()->blog->getPosts(['post_id' => $cur->post_id, 'limit' => 1]); $posts = dcCore::app()->blog?->getPosts(
$logs = [ ['post_id' => $cur->getField('post_id'), 'limit' => 1, 'post_type' => '']
$cur->comment_author,
$posts->post_title,
dcCore::app()->blog->url . dcCore::app()->url->getBase($posts->post_type) .
'/' . $posts->post_url . '#c' . $cur->comment_id,
];
dcCore::app()->activityReport->addLog('comment', 'create', $logs);
}
public static function commentUpdate($blog, $cur, $old)
{
$posts = dcCore::app()->blog->getPosts(['post_id' => $old->post_id, 'limit' => 1]);
$logs = [
dcCore::app()->auth->getInfo('user_cn'),
$posts->post_title,
dcCore::app()->blog->url . dcCore::app()->url->getBase($posts->post_type) .
'/' . $posts->post_url . '#c' . $old->comment_id,
];
dcCore::app()->activityReport->addLog('comment', 'update', $logs);
}
public static function trackbackCreate($cur, $comment_id)
{
// From blog args are $blog, $cur #thks to bruno
$c = $cur instanceof dcBlog ? $comment_id : $cur;
if (!$c->comment_trackback || !$c->comment_site) {
return null;
}
$posts = dcCore::app()->blog->getPosts(
['post_id' => $c->post_id, 'no_content' => true, 'limit' => 1]
); );
if ($posts->isEmpty()) { if (!$posts || $posts->isEmpty()) {
return null; return;
}
$logs = [
$c->comment_author,
$c->comment_site,
$posts->post_title,
dcCore::app()->blog->url . dcCore::app()->url->getBase($posts->post_type) .
'/' . $posts->post_url,
];
dcCore::app()->activityReport->addLog('comment', 'trackback', $logs);
} }
public static function categoryCreate($cur, $cat_id) $logs = [
(string) $cur->getField('comment_author'),
(string) $posts->f('post_title'),
(string) dcCore::app()->blog?->url . dcCore::app()->url->getBase((string) $posts->f('post_type')) .
'/' . $posts->f('post_url') . '#c' . $cur->getField('comment_id'),
];
ActivityReport::instance()->addLog('comment', 'create', $logs);
}
public static function commentUpdate(dcBlog $blog, cursor $cur, dcRecord $old): void
{
$posts = dcCore::app()->blog?->getPosts(
['post_id' => $old->f('post_id'), 'limit' => 1]
);
if (!$posts || $posts->isEmpty()) {
return;
}
$logs = [
(string) dcCore::app()->auth?->getInfo('user_cn'),
(string) $posts->f('post_title'),
(string) dcCore::app()->blog?->url . dcCore::app()->url->getBase((string) $posts->f('post_type')) .
'/' . $posts->f('post_url') . '#c' . $old->f('comment_id'),
];
ActivityReport::instance()->addLog('comment', 'update', $logs);
}
public static function trackbackCreate(dcBlog $blog, cursor $cur): void
{
if (!$cur->getField('comment_trackback')) {
return;
}
$posts = dcCore::app()->blog?->getPosts(
['post_id' => $cur->getField('post_id'), 'no_content' => true, 'limit' => 1]
);
if (!$posts || $posts->isEmpty()) {
return;
}
$logs = [
(string) $cur->getField('comment_author'),
(string) $cur->getField('comment_site'),
(string) $posts->f('post_title'),
(string) dcCore::app()->blog?->url . dcCore::app()->url->getBase($posts->f('post_type')) .
'/' . $posts->f('post_url'),
];
ActivityReport::instance()->addLog('comment', 'trackback', $logs);
}
public static function categoryCreate(cursor $cur, int $cat_id): void
{ {
$logs = [ $logs = [
$cur->cat_title, (string) $cur->getField('cat_title'),
dcCore::app()->auth->getInfo('user_cn'), (string) dcCore::app()->auth?->getInfo('user_cn'),
dcCore::app()->blog->url . dcCore::app()->url->getBase('category') . '/' . $cur->cat_url, (string) dcCore::app()->blog?->url . dcCore::app()->url->getBase('category') . '/' . $cur->getField('cat_url'),
]; ];
dcCore::app()->activityReport->addLog('category', 'create', $logs); ActivityReport::instance()->addLog('category', 'create', $logs);
} }
public static function categoryUpdate($cur, $cat_id) public static function categoryUpdate(cursor $cur, int $cat_id): void
{ {
$logs = [ $logs = [
$cur->cat_title, (string) $cur->getField('cat_title'),
dcCore::app()->auth->getInfo('user_cn'), (string) dcCore::app()->auth?->getInfo('user_cn'),
dcCore::app()->blog->url . dcCore::app()->url->getBase('category') . '/' . $cur->cat_url, (string) dcCore::app()->blog?->url . dcCore::app()->url->getBase('category') . '/' . $cur->getField('cat_url'),
]; ];
dcCore::app()->activityReport->addLog('category', 'update', $logs); ActivityReport::instance()->addLog('category', 'update', $logs);
} }
public static function userCreate($cur, $user_id) public static function userCreate(cursor $cur, string $user_id): void
{ {
$user_cn = dcUtils::getUserCN( $user_cn = dcUtils::getUserCN(
$cur->user_id, $cur->getField('user_id'),
$cur->user_name, $cur->getField('user_name'),
$cur->user_firstname, $cur->getField('user_firstname'),
$cur->user_displayname $cur->getField('user_displayname')
); );
$logs = [ $logs = [
$user_cn, (string) $user_cn,
dcCore::app()->auth->getInfo('user_cn'), (string) dcCore::app()->auth?->getInfo('user_cn'),
]; ];
dcCore::app()->activityReport->addLog('user', 'create', $logs); ActivityReport::instance()->addLog('user', 'create', $logs);
} }
public static function usertUpdate($cur, $user_id) public static function userUpdate(cursor $cur, string $user_id): void
{ {
$user_cn = dcUtils::getUserCN( $user_cn = dcUtils::getUserCN(
$cur->user_id, $cur->getField('user_id'),
$cur->user_name, $cur->getField('user_name'),
$cur->user_firstname, $cur->getField('user_firstname'),
$cur->user_displayname $cur->getField('user_displayname')
); );
$logs = [ $logs = [
$user_cn, (string) $user_cn,
dcCore::app()->auth->getInfo('user_cn'), (string) dcCore::app()->auth?->getInfo('user_cn'),
]; ];
dcCore::app()->activityReport->addLog('user', 'update', $logs); ActivityReport::instance()->addLog('user', 'update', $logs);
} }
public static function userDelete($user_id) public static function userPreference(cursor $cur, string $user_id): void
{
$user_cn = dcUtils::getUserCN(
$cur->getField('user_id'),
$cur->getField('user_name'),
$cur->getField('user_firstname'),
$cur->getField('user_displayname')
);
$logs = [
(string) $user_cn,
(string) dcCore::app()->auth?->getInfo('user_cn'),
];
ActivityReport::instance()->addLog('user', 'preference', $logs);
}
public static function userDelete(string $user_id): void
{ {
$users = dcCore::app()->getUser($user_id); $users = dcCore::app()->getUser($user_id);
$user_cn = dcUtils::getUserCN( $user_cn = dcUtils::getUserCN(
$users->user_id, $users->f('user_id'),
$users->user_name, $users->f('user_name'),
$users->user_firstname, $users->f('user_firstname'),
$users->user_displayname $users->f('user_displayname')
); );
$logs = [ $logs = [
$user_cn, (string) $user_cn,
dcCore::app()->auth->getInfo('user_cn'), (string) dcCore::app()->auth?->getInfo('user_cn'),
]; ];
dcCore::app()->activityReport->addLog('user', 'delete', $logs); ActivityReport::instance()->addLog('user', 'delete', $logs);
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -10,125 +10,137 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_CONTEXT_ADMIN')) { declare(strict_types=1);
return null;
namespace Dotclear\Plugin\activityReport;
use ArrayObject;
use dcAdmin;
use dcAuth;
use dcCore;
use dcFavorites;
use dcNsProcess;
use dcPage;
use Dotclear\Helper\Date;
use Dotclear\Helper\Html\Form\{
Div,
Label,
Para,
Select,
Text
};
/**
* Backend process
*/
class Backend extends dcNsProcess
{
public static function init(): bool
{
static::$init = defined('DC_CONTEXT_ADMIN')
&& defined('ACTIVITY_REPORT')
&& My::phpCompliant()
&& My::isInstalled();
return static::$init;
} }
if (!defined('ACTIVITY_REPORT_V2')) {
return null; public static function process(): bool
{
if (!static::$init) {
return false;
} }
dcCore::app()->menu[dcAdmin::MENU_PLUGINS]->addItem( dcCore::app()->menu[dcAdmin::MENU_PLUGINS]->addItem(
__('Activity report'), My::name(),
dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__)), dcCore::app()->adminurl?->get('admin.plugin.' . My::id()),
dcPage::getPF(basename(__DIR__) . '/icon.png'), dcPage::getPF(My::id() . '/icon.svg'),
preg_match( preg_match(
'/' . preg_quote(dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__))) . '(&.*)?$/', '/' . preg_quote((string) dcCore::app()->adminurl?->get('admin.plugin.' . My::id())) . '(&.*)?$/',
$_SERVER['REQUEST_URI'] $_SERVER['REQUEST_URI']
), ),
dcCore::app()->auth->check(dcCore::app()->auth->makePermissions([ dcCore::app()->auth?->check(dcCore::app()->auth->makePermissions([
dcAuth::PERMISSION_ADMIN, dcAuth::PERMISSION_ADMIN,
]), dcCore::app()->blog->id) ]), dcCore::app()->blog?->id)
); );
if (dcCore::app()->activityReport->getSetting('active')) { dcCore::app()->addBehaviors([
dcCore::app()->addBehavior('adminDashboardContentsV2', ['activityReportAdmin', 'adminDashboardContentsV2']); // dashboard favorites icon
dcCore::app()->addBehavior('adminDashboardOptionsFormV2', ['activityReportAdmin', 'adminDashboardOptionsFormV2']); 'adminDashboardFavoritesV2' => function (dcFavorites $favs): void {
dcCore::app()->addBehavior('adminAfterDashboardOptionsUpdate', ['activityReportAdmin', 'adminAfterDashboardOptionsUpdate']); $favs->register(My::id(), [
} 'title' => My::name(),
'url' => dcCore::app()->adminurl?->get('admin.plugin.' . My::id()),
class activityReportAdmin 'small-icon' => dcPage::getPF(My::id() . '/icon.svg'),
{ 'large-icon' => dcPage::getPF(My::id() . '/icon.svg'),
public static function adminDashboardContentsV2($items) 'permissions' => dcCore::app()->auth?->makePermissions([
{ dcAuth::PERMISSION_ADMIN,
dcCore::app()->auth->user_prefs->addWorkspace(basename(__DIR__)); ]),
$limit = abs((int) dcCore::app()->auth->user_prefs->__get(basename(__DIR__))->dashboard_item); ]);
},
// dashboard content display
'adminDashboardContentsV2' => function (ArrayObject $items): void {
$limit = abs((int) dcCore::app()->auth?->user_prefs?->get(My::id())->get('dashboard_item'));
if (!$limit) { if (!$limit) {
return null; return ;
} }
$p = [
'limit' => $limit,
'order' => 'activity_dt DESC',
'sql' => dcCore::app()->activityReport->requests2params(dcCore::app()->activityReport->getSetting('requests')),
];
$lines = [];
$rs = dcCore::app()->activityReport->getLogs($p);
if ($rs->isEmpty()) {
return null;
}
$groups = dcCore::app()->activityReport->getGroups();
while ($rs->fetch()) {
$group = $rs->activity_group;
if (!isset($groups[$group])) { $params = new ArrayObject([
'limit' => $limit,
'requests' => true,
]);
$rs = ActivityReport::instance()->getLogs($params);
if (!$rs || $rs->isEmpty()) {
return;
}
$lines = [];
$groups = ActivityReport::instance()->groups;
while ($rs->fetch()) {
if (!$groups->has($rs->f('activity_group'))) {
continue; continue;
} }
$lines[] = '<dt title="' . __($groups[$group]['title']) . '">' . $group = $groups->get($rs->f('activity_group'));
'<strong>' . __($groups[$group]['actions'][$rs->activity_action]['title']) . '</strong>' . $lines[] = '<dt title="' . __($group->title) . '">' .
'<br />' . dt::str( '<strong>' . __($group->get($rs->f('activity_action'))->title) . '</strong>' .
dcCore::app()->blog->settings->system->date_format . ', ' . dcCore::app()->blog->settings->system->time_format, '<br />' . Date::str(
strtotime($rs->activity_dt), dcCore::app()->blog?->settings->get('system')->get('date_format') . ', ' . dcCore::app()->blog?->settings->get('system')->get('time_format'),
dcCore::app()->auth->getInfo('user_tz') (int) strtotime($rs->f('activity_dt')),
dcCore::app()->auth?->getInfo('user_tz')
) . '<dt>' . ) . '<dt>' .
'<dd><p>' . '<dd><p>' .
'<em>' . vsprintf( '<em>' . vsprintf(
__($groups[$group]['actions'][$rs->activity_action]['msg']), __($group->get($rs->f('activity_action'))->message),
dcCore::app()->activityReport->decode($rs->activity_logs) json_decode($rs->f('activity_logs'), true)
) . '</em></p></dd>'; ) . '</em></p></dd>';
} }
if (empty($lines)) { if (empty($lines)) {
return null; return ;
} }
$items[] = new ArrayObject([ $items[] = new ArrayObject([
'<div id="activity-report-logs" class="box medium">' . '<div id="activity-report-logs" class="box medium">' .
'<h3>' . __('Activity report') . '</h3>' . '<h3>' . My::name() . '</h3>' .
'<dl id="reports">' . implode('', $lines) . '</dl>' . '<dl id="reports">' . implode('', $lines) . '</dl>' .
'<p class="modules"><a class="module-details" href="' . '<p class="modules"><a class="module-details" href="' .
dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__)) . '">' . dcCore::app()->adminurl?->get('admin.plugin.' . My::id()) . '">' .
__('View all logs') . '</a> - <a class="module-config" href="' . __('View all logs') . '</a> - <a class="module-config" href="' .
dcCore::app()->adminurl->get('admin.plugins', [ dcCore::app()->adminurl?->get('admin.plugins', [
'module' => basename(__DIR__), 'module' => My::id(),
'conf' => 1, 'conf' => 1,
'redir' => dcCore::app()->adminurl->get('admin.home') . '#activity-report-logs', 'redir' => dcCore::app()->adminurl->get('admin.home'),
]) . '">' . ]) . '">' .
__('Configure plugin') . '</a></p>' . __('Configure plugin') . '</a></p>' .
'</div>', '</div>',
]); ]);
} },
// dashboard content user preference form
public static function adminDashboardOptionsFormV2() 'adminDashboardOptionsFormV2' => function (): void {
{
dcCore::app()->auth->user_prefs->addWorkspace(basename(__DIR__));
echo echo
'<div class="fieldset">' . (new Div())->class('fieldset')->items([
'<h4>' . __('Activity report') . '</h4>' . (new Text('h4', My::name())),
'<p><label for="activityReport_dashboard_item">' . (new Para())->items([
__('Number of activities to show on dashboard:') . '</label>' . (new Label(__('Number of activities to show on dashboard:'), Label::OUTSIDE_LABEL_BEFORE))->for(My::id() . '_dashboard_item'),
form::combo( (new Select(My::id() . '_dashboard_item'))->default((string) dcCore::app()->auth?->user_prefs?->get(My::id())->get('dashboard_item'))->items([
'activityReport_dashboard_item',
self::comboList(),
self::comboList(dcCore::app()->auth->user_prefs->__get(basename(__DIR__))->dashboard_item)
) . '</p>' .
'</div>';
}
public static function adminAfterDashboardOptionsUpdate($user_id = null)
{
if (is_null($user_id)) {
return;
}
dcCore::app()->auth->user_prefs->addWorkspace(basename(__DIR__));
dcCore::app()->auth->user_prefs->__get(basename(__DIR__))->put(
'dashboard_item',
self::comboList(@$_POST['activityReport_dashboard_item']),
'integer'
);
}
private static function comboList($q = true)
{
$l = [
__('Do not show activity report') => 0, __('Do not show activity report') => 0,
5 => 5, 5 => 5,
10 => 10, 10 => 10,
@ -136,14 +148,49 @@ class activityReportAdmin
20 => 20, 20 => 20,
50 => 50, 50 => 50,
100 => 100, 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],
]; ];
if (true === $q) { },
return $l; // list columns user preference
} 'adminColumnsListsV2' => function (ArrayObject $cols): void {
if (!$q) { $cols[My::id()] = [
$q = -1; My::name(),
} [
'activity_group' => [true, __('Group')],
'activity_action' => [true, __('Action')],
'activity_dt' => [true, __('Date')],
'activity_status' => [false, __('Status')],
],
];
},
]);
return in_array($q, $l) ? $l[$q] : 0; return true;
} }
} }

72
src/Combo.php Normal file
View file

@ -0,0 +1,72 @@
<?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;
/**
* Combo helper.
*/
class Combo
{
/**
* Mail content formats.
*
* @return array The formats combo
*/
public static function mailformat(): array
{
$combo = [];
foreach (ActivityReport::instance()->formats->dump() as $format) {
$combo[$format->name] = $format->id;
}
return $combo;
}
/**
* Get report intervals.
*
* @return array The intervals combo
*/
public static function interval(): array
{
return [
__('every hour') => 3600,
__('every 2 hours') => 7200,
__('2 times by day') => 43200,
__('every day') => 86400,
__('every 2 days') => 172800,
__('every week') => 604800,
];
}
/**
* Get obsolete period.
*
* @return array The obsolete period combo
*/
public static function obselete(): array
{
return [
__('every hour') => 3600,
__('every 2 hours') => 7200,
__('2 times by day') => 43200,
__('every day') => 86400,
__('every 2 days') => 172800,
__('every week') => 604800,
__('every 2 weeks') => 1209600,
__('every 4 weeks') => 2419200,
];
}
}

View file

@ -10,232 +10,218 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_CONTEXT_MODULE')) { declare(strict_types=1);
return null;
namespace Dotclear\Plugin\activityReport;
use dcAuth;
use dcCore;
use dcPage;
use dcNsProcess;
use Dotclear\Helper\Date;
use Dotclear\Helper\Html\Form\{
Checkbox,
Div,
Hidden,
Input,
Label,
Note,
Para,
Select,
Text
};
use Exception;
/**
* Config process.
*/
class Config extends dcNsProcess
{
public static function init(): bool
{
static::$init == defined('DC_CONTEXT_ADMIN')
&& defined('ACTIVITY_REPORT')
&& My::phpCompliant()
&& dcCore::app()->auth?->check(dcCore::app()->auth->makePermissions([
dcAuth::PERMISSION_ADMIN,
]), dcCore::app()->blog?->id);
return static::$init;
} }
$super = dcCore::app()->auth->isSuperAdmin() && !empty($_REQUEST['super']); public static function process(): bool
$redir = empty($_REQUEST['redir']) ? dcCore::app()->admin->list->getURL() . '#plugins' : $_REQUEST['redir']; {
if (!static::$init) {
if ($super) { return false;
dcCore::app()->activityReport->setGlobal();
} }
$combo_interval = [ if (empty($_POST['save'])) {
__('every hour') => 3600, return true;
__('every 2 hours') => 7200, }
__('2 times by day') => 43200,
__('every day') => 86400,
__('every 2 days') => 172800,
__('every week') => 604800,
];
$combo_obselete = [
__('every hour') => 3600,
__('every 2 hours') => 7200,
__('2 times by day') => 43200,
__('every day') => 86400,
__('every 2 days') => 172800,
__('every week') => 604800,
__('every 2 weeks') => 1209600,
__('every 4 weeks') => 2419200,
];
$combo_format = [
__('Plain text') => 'plain',
__('HTML') => 'html',
];
if (!empty($_POST['save'])) {
try { try {
dcCore::app()->activityReport->setSetting('active', !empty($_POST['active'])); $s = ActivityReport::instance()->settings;
if (in_array($_POST['interval'], $combo_interval)) {
dcCore::app()->activityReport->setSetting('interval', (int) $_POST['interval']); $s->set('feed_active', !empty($_POST['feed_active']));
if (in_array($_POST['interval'], Combo::interval())) {
$s->set('interval', (int) $_POST['interval']);
} }
if (in_array($_POST['obsolete'], $combo_obselete)) { if (in_array($_POST['obsolete'], Combo::obselete())) {
dcCore::app()->activityReport->setSetting('obsolete', (int) $_POST['obsolete']); $s->set('obsolete', (int) $_POST['obsolete']);
} }
dcCore::app()->activityReport->setSetting('mailinglist', explode(';', $_POST['mailinglist'])); $s->set('mailinglist', explode(';', $_POST['mailinglist']));
dcCore::app()->activityReport->setSetting('mailformat', isset($_POST['mailformat']) && $_POST['mailformat'] == 'html' ? 'html' : 'plain'); $s->set('mailformat', isset($_POST['mailformat']) && $_POST['mailformat'] == 'html' ? 'html' : 'plain');
dcCore::app()->activityReport->setSetting('dateformat', html::escapeHTML($_POST['dateformat'])); $s->set('dateformat', $_POST['dateformat']);
dcCore::app()->activityReport->setSetting('requests', $_POST['requests'] ?? []); $s->set('requests', $_POST['requests'] ?? []);
dcCore::app()->activityReport->setSetting('blogs', $_POST['blogs'] ?? []);
dcPage::addSuccessNotice(
__('Configuration successfully updated.')
);
if (!empty($_POST['send_report_now'])) { if (!empty($_POST['send_report_now'])) {
dcCore::app()->activityReport->needReport(true); ActivityReport::instance()->needReport(true);
dcAdminNotices::addSuccessNotice( dcPage::addSuccessNotice(
__('Report successfully sent.') __('Report successfully sent.')
); );
} }
if (!empty($_POST['delete_report_now'])) { if (!empty($_POST['delete_report_now'])) {
dcCore::app()->activityReport->deleteLogs(); ActivityReport::instance()->deleteLogs();
dcAdminNotices::addSuccessNotice( dcPage::addSuccessNotice(
__('Logs successfully deleted.') __('Logs successfully deleted.')
); );
} }
dcAdminNotices::addSuccessNotice( dcCore::app()->adminurl?->redirect('admin.plugins', [
__('Configuration successfully updated.') 'module' => My::id(),
); 'conf' => 1,
dcCore::app()->adminurl->redirect('admin.plugins', ['module' => basename(__DIR__), 'conf' => 1, 'super' => $super]); ]);
} catch (Exception $e) { } catch (Exception $e) {
dcCore::app()->error->add($e->getMessage()); dcCore::app()->error->add($e->getMessage());
} }
return true;
} }
$last_report_ts = dcCore::app()->activityReport->getSetting('lastreport'); public static function render(): void
if (!$last_report_ts) { {
if (!static::$init) {
return;
}
$s = ActivityReport::instance()->settings;
if (!$s->lastreport) {
$last_report = __('never'); $last_report = __('never');
$next_report = __('on new activity'); $next_report = __('on new activity');
} else { } else {
$last_report = dt::str( $last_report = Date::str(
dcCore::app()->blog->settings->system->date_format . ', ' . dcCore::app()->blog->settings->system->time_format, dcCore::app()->blog?->settings->get('system')->get('date_format') . ', ' . dcCore::app()->blog?->settings->get('system')->get('time_format'),
$last_report_ts, $s->lastreport,
dcCore::app()->auth->getInfo('user_tz') dcCore::app()->auth?->getInfo('user_tz')
); );
$next_report = dt::str( $next_report = Date::str(
dcCore::app()->blog->settings->system->date_format . ', ' . dcCore::app()->blog->settings->system->time_format, dcCore::app()->blog?->settings->get('system')->get('date_format') . ', ' . dcCore::app()->blog?->settings->get('system')->get('time_format'),
(int) dcCore::app()->activityReport->getSetting('interval') + $last_report_ts, $s->interval + $s->lastreport,
dcCore::app()->auth->getInfo('user_tz') dcCore::app()->auth?->getInfo('user_tz')
); );
} }
$emails = implode(';', dcCore::app()->activityReport->getSetting('mailinglist'));
echo '<p class="warn">' . ($super ? __('All blogs') : __('Current blog')) . '</p>'; if (!ActivityReport::hasMailer()) {
if (dcCore::app()->auth->isSuperAdmin()) {
echo sprintf(
'<p class="modules right"><a class="module-config" href="%s">%s</a><br class="clear"/></p>',
dcCore::app()->adminurl->get('admin.plugins', ['module' => basename(__DIR__), 'conf' => 1, 'super' => !$super]),
sprintf(__('Configure activity report for %s'), $super ? __('current blog') : __('all blogs'))
);
}
if (!activityReport::hasMailer()) {
echo '<p class="message">' . echo '<p class="message">' .
__('This server has no mail function, activityReport does not send email report.') . __('This server has no mail function, activityReport does not send email report.') .
'</p>'; '</p>';
} }
echo '
<div class="fieldset two-cols" id="settings"><h4>' . __('Settings') . '</h4>
<div class="col">
<p><label class="classic" for="active">' .
form::checkbox('active', '1', dcCore::app()->activityReport->getSetting('active')) . ' ' .
(
$super ?
__('Enable super administrator report') :
__('Enable report on this blog')
) . '</label></p>
<p><label for="obselete">' . __('Automatic cleaning of old logs:') . '</label>' .
form::combo('obsolete', $combo_obselete, dcCore::app()->activityReport->getSetting('obsolete')) . '</p>
<p><label for="dateformat">' . __('Date format:') . '<br />' .
form::field('dateformat', 60, 255, dcCore::app()->activityReport->getSetting('dateformat')) . '</label></p>
<p class="form-note">' . __('Use Dotclear date formaters. ex: %B %d at %H:%M') . '</p>' .
form::hidden(['super'], $super);
if (!$super) {
echo echo
'<p><img alt="' . __('RSS feed') . '" src="' . dcPage::getPF(basename(__DIR__) . '/inc/img/feed.png') . '" />' . (new Div())->class('two-boxes')->separator('')->items([
(new Div())->class('fieldset box odd')->items([
(new Text('h4', __('Mail report'))),
(new Para())->items([
(new Label(__('Send report:'), Label::OUTSIDE_LABEL_BEFORE))->for('interval'),
(new Select('interval'))->default((string) $s->interval)->items(Combo::interval()),
]),
(new Para())->items([
(new Label(__('Recipients:'), Label::OUTSIDE_LABEL_BEFORE))->for('mailinglist'),
(new Input('mailinglist'))->size(60)->maxlenght(255)->value(implode(';', $s->mailinglist)),
]),
(new Note())->class('form-note')->text(__('Separate multiple email addresses with a semicolon ";"')),
(new Note())->class('form-note')->text(__('Leave it empty to disable mail report.')),
(new Para())->items([
(new Label(__('Date format:'), Label::OUTSIDE_LABEL_BEFORE))->for('dateformat'),
(new Input('dateformat'))->size(60)->maxlenght(255)->value($s->dateformat),
]),
(new Note())->class('form-note')->text(__('Use Dotclear date formaters. ex: %B %d at %H:%M')),
(new Para())->items([
(new Label(__('Report format:'), Label::OUTSIDE_LABEL_BEFORE))->for('mailformat'),
(new Select('mailformat'))->default($s->mailformat)->items(Combo::mailformat()),
]),
(new Text(
'ul',
'<li>' . __('Last report by email:') . ' ' . $last_report . '</li>' .
'<li>' . __('Next report by email:') . ' ' . $next_report . '</li>'
)),
]),
(new Div())->class('fieldset box even')->items([
(new Text('h4', __('Feeds'))),
(new Para())->items([
(new Checkbox('feed_active', $s->feed_active))->value(1),
(new Label(__('Enable activity feed'), Label::OUTSIDE_LABEL_AFTER))->for('feed_active')->class('classic'),
]),
(new Text(
'ul',
'<li><img alt="' . __('RSS feed') . '" src="' . dcPage::getPF(My::id() . '/img/feed.png') . '" /> ' .
'<a title="' . __('RSS feed') . '" href="' . '<a title="' . __('RSS feed') . '" href="' .
dcCore::app()->blog->url . dcCore::app()->url->getBase(basename(__DIR__)) . '/rss2/' . dcCore::app()->activityReport->getUserCode() . '">' . dcCore::app()->blog?->url . dcCore::app()->url->getBase(My::id()) . '/rss2/' . ActivityReport::instance()->getUserCode() . '">' .
__('Rss2 feed for activity on this blog') . '</a><br />' . __('Rss2 activities feed') . '</a></li>' .
'<img alt="' . __('Atom feed') . '" src="' . dcPage::getPF(basename(__DIR__) . '/inc/img/feed.png') . '" />' . '<li><img alt="' . __('Atom feed') . '" src="' . dcPage::getPF(My::id() . '/img/feed.png') . '" /> ' .
'<a title="' . __('Atom feed') . '" href="' . '<a title="' . __('Atom feed') . '" href="' .
dcCore::app()->blog->url . dcCore::app()->url->getBase(basename(__DIR__)) . '/atom/' . dcCore::app()->activityReport->getUserCode() . '">' . dcCore::app()->blog?->url . dcCore::app()->url->getBase(My::id()) . '/atom/' . ActivityReport::instance()->getUserCode() . '">' .
__('Atom feed for activity on this blog') . '</a></p>'; __('Atom activities feed') . '</a></li>'
} )),
echo ' ]),
</div><div class="col"> ])->render();
<p><label for="interval">' . __('Send report:') . '</label>' .
form::combo('interval', $combo_interval, dcCore::app()->activityReport->getSetting('interval')) . '</p>
<p><label for="mailinglist">' . __('Recipients:') . '<br />' .
form::field('mailinglist', 60, 255, $emails) . '</label></p>
<p class="form-note">' . __('Separate multiple email addresses with a semicolon ";"') . '</p>
<p><label for="mailformat">' . __('Report format:') . '</label>' .
form::combo('mailformat', $combo_format, dcCore::app()->activityReport->getSetting('mailformat')) . '</p>
<ul>
<li>' . __('Last report by email:') . ' ' . $last_report . '</li>
<li>' . __('Next report by email:') . ' ' . $next_report . '</li>
</ul>
</div><br class="clear"/>
</div><br class="clear"/>';
if ($super) {
echo '
<div class="fieldset one-box" id="setting_blog"><h4>' . __('Blogs') . '</h4>
<p>' . __('Select blogs to add to report') . '</p>';
$i = $j = 0;
$selected_blogs = dcCore::app()->activityReport->getSetting('blogs');
$blogs = dcCore::app()->getBlogs();
$num_blogs = $blogs->count();
while ($blogs->fetch()) {
$blog_id = dcCore::app()->con->escape($blogs->blog_id);
echo '
<div class="fieldset box">
<p><label class="classic" for="blogs_' . $i . '_">' .
form::checkbox(
['blogs[' . $i . ']', 'blogs_' . $i . '_'],
$blog_id,
in_array($blog_id, $selected_blogs)
) . ' ' .
$blogs->blog_name . ' (' . $blog_id . ')</label></p>
</div>';
$i++;
}
echo '</div>';
} else {
echo form::hidden('blogs[0]', dcCore::app()->blog->id);
}
echo '
<div class="fieldset one-box" id="setting_report"><h4>' . __('Report') . '</h4>
<p>' . __('Select actions by activity type to add to report') . '</p>';
$groups = dcCore::app()->activityReport->getGroups();
$blog_request = dcCore::app()->activityReport->getSetting('requests');
$i = 0; $i = 0;
foreach ($groups as $group_id => $group) { $g = [
echo '<div class="fieldset box"><h5>' . __($group['title']) . '</h5>'; (new Text('h4', __('Activities'))),
(new Text('p', __('Select actions by activity type to add to report'))),
foreach ($group['actions'] as $action_id => $action) { ];
echo ' foreach (ActivityReport::instance()->groups->dump() as $group_id => $group) {
<p><label class="classic" for="requests_' . $group_id . '_' . $action_id . '_">' . $a = [];
form::checkbox( $a[] = (new Text('h5', __($group->title)));
foreach ($group->dump() as $action_id => $action) {
$a[] = (new Para())->items([
(new Checkbox(
['requests[' . $group_id . '][' . $action_id . ']', 'requests_' . $group_id . '_' . $action_id . '_'], ['requests[' . $group_id . '][' . $action_id . ']', 'requests_' . $group_id . '_' . $action_id . '_'],
1, isset($s->requests[$group_id][$action_id])
isset($blog_request[$group_id][$action_id]) ))->value(1),
) . ' ' . __($action['title']) . '</label></p>'; (new Label(__($action->title), Label::OUTSIDE_LABEL_AFTER))->for(
'requests_' . $group_id . '_' . $action_id . '_'
)->class('classic'),
]);
} }
echo '</div>'; $g[] = (new Div())->class('fieldset box')->items($a);
} }
echo '</div>'; echo
(new Div('setting_report'))->class('fieldset one-box')->items($g)->render();
if (1) { echo
echo ' (new Div('settings'))->class('fieldset')->items([
<div class="fieldset" id="settings"><h4>' . __('Special') . '</h4> (new Text('h4', __('Maintenance'))),
(new Para())->items([
<p><label class="classic" for="send_report_now">' . (new Label(__('Automatic cleaning of old logs:'), Label::OUTSIDE_LABEL_BEFORE))->for('obselete'),
form::checkbox('send_report_now', '1', false) . ' ' . (new Select('obselete'))->default((string) $s->obsolete)->items(Combo::obselete()),
__('Send report now') . '</label></p> ]),
(new Para())->items([
<p><label class="classic" for="delete_report_now">' . (new Checkbox('send_report_now'))->value(1),
form::checkbox('delete_report_now', '1', false) . ' ' . (new Label(__('Send report now'), Label::OUTSIDE_LABEL_AFTER))->for('send_report_now')->class('classic'),
__('Delete all logs now') . '</label></p> ]),
(new Para())->items([
</div>'; (new Checkbox('delete_report_now'))->value(1),
(new Label(__('Delete all logs now'), Label::OUTSIDE_LABEL_AFTER))->for('delete_report_now')->class('classic'),
]),
])->render();
}
} }
dcCore::app()->activityReport->unsetGlobal();

71
src/Context.php Normal file
View file

@ -0,0 +1,71 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use dcCore;
/**
* Template helper.
*/
class Context
{
/**
* Parse title.
*
* @return string The parsed title
*/
public static function parseTitle(): string
{
if (!dcCore::app()->ctx || !dcCore::app()->ctx->exists('activityreports')) {
return '';
}
$group = dcCore::app()->ctx->__get('activityreports')->activity_group;
$action = dcCore::app()->ctx->__get('activityreports')->activity_action;
if (!ActivityReport::instance()->groups->get($group)->has($action)) {
return '';
}
return __(ActivityReport::instance()->groups->get($group)->get($action)->title);
}
/**
* Parse content.
*
* @return string The parsed content
*/
public static function parseContent(): string
{
if (!dcCore::app()->ctx || !dcCore::app()->ctx->exists('activityreports')) {
return '';
}
$group = dcCore::app()->ctx->__get('activityreports')->activity_group;
$action = dcCore::app()->ctx->__get('activityreports')->activity_action;
$logs = json_decode((string) dcCore::app()->ctx->__get('activityreports')->activity_logs, true);
if (!is_array($logs) || !ActivityReport::instance()->groups->get($group)->has($action)) {
return '';
}
dcCore::app()->initWikiComment();
return dcCore::app()->wikiTransform(vsprintf(
__(ActivityReport::instance()->groups->get($group)->get($action)->message),
$logs
));
}
}

81
src/Format.php Normal file
View file

@ -0,0 +1,81 @@
<?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;
/**
* Report format descriptor.
*/
class Format
{
/** @var string $name The name format */
public readonly string $name;
/** @var string $blog_title The blog title format */
public readonly string $blog_title;
/** @var string $group_title The group title format */
public readonly string $group_title;
/** @var string $group_open The group opening format */
public readonly string $group_open;
/** @var string $group_close The group closing format */
public readonly string $group_close;
/** @var string $action The action format */
public readonly string $action;
/** @var string $error The error format */
public readonly string $error;
/** @var string $period_title The period title format */
public readonly string $period_title;
/** @var string $period_open The period opening format */
public readonly string $period_open;
/** @var string $period_close The period closing format */
public readonly string $period_close;
/** @var string $info The info format */
public readonly string $info;
/** @var string $page The page format */
public readonly string $page;
/**
* Constructor sets format id.
*
* @param string $id The format ID
* @param array $format The format values
*/
public function __construct(
public readonly string $id,
array $format
) {
$this->name = $format['name'] ?? __('Plain text');
$this->blog_title = $format['blog_title'] ?? "\n--- %TEXT% ---\n";
$this->group_title = $format['group_title'] ?? "\n-- %TEXT% --\n\n";
$this->group_open = $format['group_open'] ?? '';
$this->group_close = $format['group_close'] ?? '';
$this->action = $format['action'] ?? "- %TIME% : %TEXT%\n";
$this->error = $format['error'] ?? '%TEXT%';
$this->period_title = $format['period_title'] ?? "%TEXT%\n";
$this->period_open = $format['period_open'] ?? '';
$this->period_close = $format['period_close'] ?? '';
$this->info = $format['info'] ?? "%TEXT%\n";
$this->page = $format['page'] ?? "%PERIOD%\n-----------------------------------------------------------\n%TEXT%";
}
}

View file

@ -10,52 +10,63 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
$format = [ declare(strict_types=1);
'html' => [
'blog_title' => '<h2><a href="%URL%">%TEXT%</a></h2>', namespace Dotclear\Plugin\activityReport;
'blog_open' => '',
'blog_close' => '', /**
'group_title' => '<h3>%TEXT%</h3>', * Email report formats stack.
'group_open' => '<ul>', */
'group_close' => '</ul>', class Formats
'action' => '<li><em>%TIME%</em><br />%TEXT%</li>', {
'error' => '<p>%TEXT%</p>', /** @var array<string, Format> $stack The formats stack */
'period_title' => '<h1>%TEXT%</h1>', private array $stack = [];
'period_open' => '<ul>',
'period_close' => '</ul>', /**
'info' => '<li>%TEXT%</li>', * Chek if a format exists.
'page' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n" . *
'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">' . "\n" . * @param string $id The format ID
'<head><title>' . __('Activity report') . '</title>' . *
'<style type="text/css">' . * @return bool True if it exists
' body { color: #303030; background: #FCFCFC; font-size: 0.7em;font-family: Georgia, Tahoma, Arial, Helvetica, sans-serif; }' . */
' a { color: #303030; text-decoration: none; }' . public function has(string $id): bool
' h1 { text-align: center; font-size: 2em; }' . {
' h2 { color: #303030; text-align:center; }' . return isset($this->stack[$id]);
' h3 { color: #7F3F3F; }' . }
' li em { color: #303030; }' .
' div.info { color: #3F497F; background-color: #F8F8EB; border: 1px solid #888888; margin: 4px; padding: 4px; }' . /**
' div.content { color: #3F7F47; background-color: #F8F8EB; border: 1px solid #888888; margin: 4px; padding: 4px; }' . * Add a format.
' div.foot { text-align:center; font-size: 0.9em; }' . *
'</style>' . * @param Format $format The format object
'</head><body>' . *
'<div class="info">%PERIOD%</div><div class="content">%TEXT%</div>' . * @return Formats The formats instance
'<div class="foot"><p>Powered by <a href="https://github.com/JcDenis/activityReport">activityReport</a></p></div>' . */
'</body></html>', public function add(Format $format): Formats
], {
'plain' => [ $this->stack[$format->id] = $format;
'blog_title' => "\n--- %TEXT% ---\n",
'blog_open' => '', return $this;
'blog_close' => '', }
'group_title' => "\n-- %TEXT% --\n\n",
'group_open' => '', /**
'group_close' => '', * Get a format.
'action' => "- %TIME% : %TEXT%\n", *
'error' => '%TEXT%', * @param string $id The format ID
'period_title' => "%TEXT%\n", *
'period_open' => '', * @return Format The format descriptor
'period_close' => '', */
'info' => "%TEXT%\n", public function get(string $id): Format
'page' => "%PERIOD%\n-----------------------------------------------------------\n%TEXT%", {
], return $this->stack[$id] ?? new Format('plain', []);
]; }
/**
* Get all formats.
*
* @return array<string, Format> The formats stack
*/
public function dump(): array
{
return $this->stack;
}
}

View file

@ -10,165 +10,41 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_RC_PATH')) { declare(strict_types=1);
return null;
}
if (!defined('ACTIVITY_REPORT_V2')) {
return null;
}
dcCore::app()->tpl->setPath(dcCore::app()->tpl->getPath(), __DIR__ . '/default-templates/tpl'); namespace Dotclear\Plugin\activityReport;
dcCore::app()->tpl->addBlock('activityReports', ['activityReportPublicTpl','activityReports']);
dcCore::app()->tpl->addValue('activityReportFeedID', ['activityReportPublicTpl','activityReportFeedID']);
dcCore::app()->tpl->addValue('activityReportTitle', ['activityReportPublicTpl','activityReportTitle']);
dcCore::app()->tpl->addValue('activityReportDate', ['activityReportPublicTpl','activityReportDate']);
dcCore::app()->tpl->addValue('activityReportContent', ['activityReportPublicTpl','activityReportContent']);
class activityReportPublicUrl extends dcUrlHandlers use dcCore;
use dcNsProcess;
/**
* Front end process.
*/
class Frontend extends dcNsProcess
{ {
public static function feed(?string $args): void public static function init(): bool
{ {
if (!preg_match('/^(atom|rss2)\/(.+)$/', $args, $m)) { static::$init = defined('ACTIVITY_REPORT')
self::p404(); && My::phpCompliant()
&& My::isInstalled();
return; return static::$init;
}
if (!defined('ACTIVITY_REPORT_V2')) {
self::p404();
return;
}
if (!dcCore::app()->activityReport->getSetting('active')) {
self::p404();
return;
}
$mime = $m[1] == 'atom' ? 'application/atom+xml' : 'application/xml';
if (false === dcCore::app()->activityReport->checkUserCode($m[2])) {
self::p404();
return;
} }
dcCore::app()->ctx->nb_entry_per_page = (int) dcCore::app()->blog->settings->system->nb_post_per_feed; public static function process(): bool
dcCore::app()->ctx->short_feed_items = (int) dcCore::app()->blog->settings->system->short_feed_items;
header('X-Robots-Tag: ' . context::robotsPolicy(dcCore::app()->blog->settings->system->robots_policy, ''));
self::serveDocument('activityreport-' . $m[1] . '.xml', $mime);
}
}
class activityReportPublicTpl
{ {
public static function activityReports($attr, $content) if (!static::$init) {
{ return false;
$lastn = 0;
if (isset($attr['lastn'])) {
$lastn = abs((int) $attr['lastn']) + 0;
} }
$p = '$_page_number = dcCore::app()->public->getPageNumber(); if ($_page_number < 1) { $_page_number = 1; }' . "\n\$params = array();\n"; dcCore::app()->tpl->setPath(dcCore::app()->tpl->getPath(), implode(DIRECTORY_SEPARATOR, [My::root(), 'default-templates', 'tpl']));
if ($lastn > 0) { dcCore::app()->tpl->addBlock('activityReports', [Template::class, 'activityReports']);
$p .= "\$params['limit'] = " . $lastn . ";\n"; dcCore::app()->tpl->addValue('activityReportFeedID', [Template::class, 'activityReportFeedID']);
} else { dcCore::app()->tpl->addValue('activityReportTitle', [Template::class, 'activityReportTitle']);
$p .= "\$params['limit'] = dcCore::app()->ctx->nb_entry_per_page;\n"; dcCore::app()->tpl->addValue('activityReportDate', [Template::class, 'activityReportDate']);
} dcCore::app()->tpl->addValue('activityReportContent', [Template::class, 'activityReportContent']);
if (!isset($attr['ignore_pagination']) || $attr['ignore_pagination'] == '0') { return true;
$p .= "\$params['limit'] = array(((\$_page_number-1)*\$params['limit']),\$params['limit']);\n";
} else {
$p .= "\$params['limit'] = array(0, \$params['limit']);\n";
}
return
"<?php \n" .
$p .
'dcCore::app()->ctx->activityreport_params = $params; ' . "\n" .
'dcCore::app()->ctx->activityreports = dcCore::app()->activityReport->getLogs($params); unset($params); ' . "\n" .
'while (dcCore::app()->ctx->activityreports->fetch()) : ?>' . $content . '<?php endwhile; ' .
'dcCore::app()->ctx->pop("activityreports"); dcCore::app()->ctx->pop("activityreport_params"); ' . "\n" .
'?>';
}
public static function activityReportFeedID($attr)
{
return
'urn:md5:<?php echo md5(dcCore::app()->ctx->activityreports->blog_id.' .
'dcCore::app()->ctx->activityreports->activity_id.dcCore::app()->ctx->activityreports->activity_dt); ' .
'?>';
}
public static function activityReportTitle($attr)
{
$f = dcCore::app()->tpl->getFilters($attr);
return '<?php echo ' . sprintf($f, 'activityReportContext::parseTitle()') . '; ?>';
}
public static function activityReportContent($attr)
{
$f = dcCore::app()->tpl->getFilters($attr);
return '<?php echo ' . sprintf($f, 'activityReportContext::parseContent()') . '; ?>';
}
public static function activityReportDate($attr)
{
$format = '';
if (!empty($attr['format'])) {
$format = addslashes($attr['format']);
}
$iso8601 = !empty($attr['iso8601']);
$rfc822 = !empty($attr['rfc822']);
$f = dcCore::app()->tpl->getFilters($attr);
if ($rfc822) {
return '<?php echo ' . sprintf($f, 'dt::rfc822(strtotime(dcCore::app()->ctx->activityreports->activity_dt),dcCore::app()->blog->settings->system->blog_timezone)') . '; ?>';
} elseif ($iso8601) {
return '<?php echo ' . sprintf($f, 'dt::iso8601(strtotime(dcCore::app()->ctx->activityreports->activity_dt),dcCore::app()->blog->settings->system->blog_timezone)') . '; ?>';
} elseif (!empty($format)) {
return '<?php echo ' . sprintf($f, "dt::dt2str('" . $format . "',dcCore::app()->ctx->activityreports->activity_dt)") . '; ?>';
}
return '<?php echo ' . sprintf($f, 'dt::dt2str(dcCore::app()->blog->settings->system->date_format,dcCore::app()->ctx->activityreports->activity_dt)') . '; ?>';
}
}
class activityReportContext
{
public static function parseTitle()
{
$groups = dcCore::app()->activityReport->getGroups();
$group = dcCore::app()->ctx->activityreports->activity_group;
$action = dcCore::app()->ctx->activityreports->activity_action;
if (!empty($groups[$group]['actions'][$action]['title'])) {
return __($groups[$group]['actions'][$action]['title']);
}
return '';
}
public static function parseContent()
{
$groups = dcCore::app()->activityReport->getGroups();
$group = dcCore::app()->ctx->activityreports->activity_group;
$action = dcCore::app()->ctx->activityreports->activity_action;
$logs = dcCore::app()->ctx->activityreports->activity_logs;
$logs = dcCore::app()->activityReport->decode($logs);
if (!empty($groups[$group]['actions'][$action]['msg'])) {
dcCore::app()->initWikiComment();
return dcCore::app()->wikiTransform(vsprintf(__($groups[$group]['actions'][$action]['msg']), $logs));
}
return '';
} }
} }

82
src/Group.php Normal file
View file

@ -0,0 +1,82 @@
<?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;
/**
* Actions stack.
*/
class Group
{
/** @var array<string, Action> $stack The actions stack */
private array $stack = [];
/**
* Constructor sets group description.
*
* @param string $id The group ID
* @param string $title The group title
*/
public function __construct(public readonly string $id, public readonly string $title)
{
}
/**
* Chek if a action exists.
*
* @param string $id The action ID
*
* @return bool True if it exists
*/
public function has(string $id): bool
{
return isset($this->stack[$id]);
}
/**
* Add an action.
*
* @param Action $action The action object
*
* @return Group The group instance
*/
public function add(Action $action): Group
{
$this->stack[$action->id] = $action;
return $this;
}
/**
* Get an action.
*
* @param string $id The action ID
*
* @return Action The action descriptor
*/
public function get(string $id): Action
{
return $this->stack[$id] ?? new Action($id, 'undefined', 'undefined', 'undefined', null);
}
/**
* Get all actions.
*
* @return array<string, Action> The actions stack
*/
public function dump(): array
{
return $this->stack;
}
}

72
src/Groups.php Normal file
View file

@ -0,0 +1,72 @@
<?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;
/**
* Actions groups stack.
*/
class Groups
{
/** @var array<string, Group> $stack The actions groups stack */
private array $stack = [];
/**
* Chek if a group exists.
*
* @param string $id The group ID
*
* @return bool True if it exists
*/
public function has(string $id): bool
{
return isset($this->stack[$id]);
}
/**
* Add a group.
*
* @param Group $group The group object
*
* @return Groups The groups instance
*/
public function add(Group $group): Groups
{
$this->stack[$group->id] = $group;
return $this;
}
/**
* Get a group.
*
* @param string $id The group ID
*
* @return Group The group descriptor
*/
public function get(string $id): Group
{
return $this->stack[$id] ?? new Group($id, 'undefined');
}
/**
* Get all groups.
*
* @return array<string, Group> The groups stack
*/
public function dump(): array
{
return $this->stack;
}
}

View file

@ -10,53 +10,87 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_CONTEXT_ADMIN')) { declare(strict_types=1);
return null;
namespace Dotclear\Plugin\activityReport;
use dbStruct;
use dcCore;
use dcNsProcess;
use Dotclear\Database\Statement\{
DropStatement,
TruncateStatement
};
use Exception;
/**
* Install process.
*/
class Install extends dcNsProcess
{
public static function init(): bool
{
static::$init = defined('DC_CONTEXT_ADMIN')
&& My::phpCompliant()
&& dcCore::app()->newVersion(My::id(), dcCore::app()->plugins->moduleInfo(My::id(), 'version'));
return static::$init;
} }
if (!dcCore::app()->newVersion( public static function process(): bool
basename(__DIR__), {
dcCore::app()->plugins->moduleInfo(basename(__DIR__), 'version') if (!static::$init) {
)) { return false;
return null;
} }
try { try {
self::beforeGrowUp();
$s = new dbStruct(dcCore::app()->con, dcCore::app()->prefix); $s = new dbStruct(dcCore::app()->con, dcCore::app()->prefix);
$s->{initActivityReport::ACTIVITY_TABLE_NAME} $s->{My::ACTIVITY_TABLE_NAME}
->activity_id('bigint', 0, false) ->activity_id('bigint', 0, false)
->activity_type('varchar', 32, false, "'" . basename(__DIR__) . "'") ->activity_type('varchar', 32, false, "'" . My::id() . "'")
->blog_id('varchar', 32, true) ->blog_id('varchar', 32, true)
->activity_group('varchar', 32, false) ->activity_group('varchar', 32, false)
->activity_action('varchar', 32, false) ->activity_action('varchar', 32, false)
->activity_logs('text', 0, false) ->activity_logs('text', 0, false)
->activity_dt('timestamp', 0, false, 'now()') ->activity_dt('timestamp', 0, false, 'now()')
->activity_blog_status('smallint', 0, false, 0) ->activity_status('smallint', 0, false, 0)
->activity_super_status('smallint', 0, false, 0)
->primary('pk_activity', 'activity_id') ->primary('pk_activity', 'activity_id')
->index('idx_activity_type', 'btree', 'activity_type') ->index('idx_activity_type', 'btree', 'activity_type')
->index('idx_activity_blog_id', 'btree', 'blog_id') ->index('idx_activity_blog_id', 'btree', 'blog_id')
->index('idx_activity_action', 'btree', 'activity_group', 'activity_action') ->index('idx_activity_action', 'btree', 'activity_group', 'activity_action')
->index('idx_activity_blog_status', 'btree', 'activity_blog_status') ->index('idx_activity_status', 'btree', 'activity_status');
->index('idx_activity_super_status', 'btree', 'activity_super_status');
$s->{initActivityReport::SETTING_TABLE_NAME} (new dbStruct(dcCore::app()->con, dcCore::app()->prefix))->synchronize($s);
->setting_id('varchar', 64, false)
->blog_id('varchar', 32, true)
->setting_type('varchar', 32, false)
->setting_value('text', 0, false)
->unique('uk_activity_setting', 'setting_id', 'blog_id', 'setting_type')
->index('idx_activity_setting_blog_id', 'btree', 'blog_id')
->index('idx_activity_setting_type', 'btree', 'setting_type');
$si = new dbStruct(dcCore::app()->con, dcCore::app()->prefix);
$changes = $si->synchronize($s);
return true; return true;
} catch (Exception $e) { } catch (Exception $e) {
dcCore::app()->error->add($e->getMessage()); dcCore::app()->error->add($e->getMessage());
}
return false; return false;
}
}
/**
* Do some action on previous version before install.
*/
private static function beforeGrowUp(): void
{
$current = dcCore::app()->getVersion('activityReport');
// sorry not sorry we restart from scratch
if ($current && version_compare($current, '3.0', '<')) {
$struct = new dbStruct(dcCore::app()->con, dcCore::app()->prefix);
if ($struct->tableExists('activity')) {
(new TruncateStatement())->from(dcCore::app()->prefix . 'activity')->truncate();
}
if ($struct->tableExists('activity_settings')) {
(new TruncateStatement())->from(dcCore::app()->prefix . 'activity_settings')->truncate();
(new DropStatement())->from(dcCore::app()->prefix . 'activity_settings')->drop();
}
}
}
}

View file

@ -10,83 +10,114 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_CONTEXT_ADMIN')) { declare(strict_types=1);
return null;
}
if (!defined('ACTIVITY_REPORT_V2')) { namespace Dotclear\Plugin\activityReport;
return null;
}
dcPage::check(dcCore::app()->auth->makePermissions([ use ArrayObject;
use adminGenericFilter;
use dcAuth;
use dcCore;
use dcNsProcess;
use dcPage;
use Dotclear\Helper\Html\Form\{
Form,
Hidden,
Para,
Submit,
Text
};
use Exception;
/**
* Manage process (admin logs list).
*/
class Manage extends dcNsProcess
{
public static function init(): bool
{
static::$init = defined('DC_CONTEXT_ADMIN')
&& defined('ACTIVITY_REPORT')
&& My::phpCompliant()
&& dcCore::app()->auth?->check(dcCore::app()->auth->makePermissions([
dcAuth::PERMISSION_ADMIN, dcAuth::PERMISSION_ADMIN,
])); ]), dcCore::app()->blog?->id);
$super = dcCore::app()->auth->isSuperAdmin() && !empty($_REQUEST['super']); return static::$init;
if ($super) {
dcCore::app()->activityReport->setGlobal();
} }
$logs = dcCore::app()->activityReport->getLogs([]); public static function process(): bool
{
if (!static::$init) {
return false;
}
if ($super) { if (!empty($_POST['delete_all_logs']) || !empty($_POST['delete_reported_logs'])) {
$breadcrumb = [ try {
__('Current blog') => dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__), ['super' => 0]), ActivityReport::instance()->deleteLogs(!empty($_POST['delete_reported_logs']));
'<span class="page-title">' . __('All blogs') . '</span>' => '', dcPage::addSuccessNotice(__('Logs successfully deleted'));
]; dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id());
} else { } catch (Exception $e) {
$breadcrumb = ['<span class="page-title">' . __('Current blog') . '</span>' => '']; dcCore::app()->error->add($e->getMessage());
if (dcCore::app()->auth->isSuperAdmin()) {
$breadcrumb[__('All blogs')] = dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__), ['super' => 1]);
} }
} }
echo '<html><head><title>' . __('Activity report') . '</title></head><body>' . return true;
dcPage::breadcrumb(array_merge([__('Activity report') => '', __('Logs') => ''], $breadcrumb), ['hl' => false]) . }
public static function render(): void
{
if (!static::$init) {
return;
}
$logs = $counter = $list = null;
$filter = new adminGenericFilter(dcCore::app(), My::id());
$params = new ArrayObject($filter->params());
try {
$logs = ActivityReport::instance()->getLogs($params);
$counter = ActivityReport::instance()->getLogs($params, true);
if (!is_null($logs) && !is_null($counter)) {
$list = new ManageList(dcCore::app(), $logs, $counter->f(0));
}
} catch (Exception $e) {
dcCore::app()->error->add($e->getMessage());
}
dcPage::openModule(
My::name(),
$filter->js((string) dcCore::app()->adminurl?->get('admin.plugin.' . My::id())) .
dcPage::jsJson(My::id(), ['confirm_delete' => __('Are you sure you want to delete logs?')]) .
dcPage::jsModuleLoad(My::id() . '/js/backend.js') .
# --BEHAVIOR-- activityReportListHeader --
dcCore::app()->callBehavior('activityReportListHeader')
);
echo
dcPage::breadcrumb([
__('Plugins') => '',
My::name() => '',
]) .
dcPage::notices(); dcPage::notices();
if ($logs->isEmpty()) { if (!is_null($list)) {
echo '<p>' . __('No log') . '</p>'; $filter->display('admin.plugin.' . My::id(), (new Hidden('p', My::id()))->render());
} else { $list->logsDisplay($filter, '%s');
echo '
<div class="table-outer"><table><thead>
<tr>
<th>' . __('Action') . '</th>
<th>' . __('Message') . '</th>
<th>' . __('Date') . '</th>';
if ($super) {
echo '<th>' . __('Blog') . '</th>';
}
echo '</tr></thead><tbody>';
while ($logs->fetch()) {
$action = dcCore::app()->activityReport->getGroups($logs->activity_group, $logs->activity_action);
if (empty($action)) {
continue;
} }
$off = $super && $logs->activity_blog_status == 1 ? ' offline' : ''; if (!is_null($logs) && !$logs->isEmpty()) {
$date = dt::str( echo
dcCore::app()->blog->settings->system->date_format . ', ' . dcCore::app()->blog->settings->system->time_format, (new Form('form-logs'))->method('post')->action(dcCore::app()->admin->getPageURL())->fields([
strtotime($logs->activity_dt), (new Para())->class('right')->separator(' ')->items([
dcCore::app()->auth->getInfo('user_tz') (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')),
$msg = vsprintf(__($action['msg']), dcCore::app()->activityReport->decode($logs->activity_logs)); dcCore::app()->formNonce(false),
]),
])->render();
}
echo ' dcPage::closeModule();
<tr class="line' . $off . '">
<td class="nowrap">' . __($action['title']) . '</td>
<td class="maximal">' . $msg . '</td>
<td class="nowrap">' . $date . '</td>';
if ($super) {
echo '<td class="nowrap">' . $logs->blog_id . '</td>';
} }
echo '</tr>';
} }
echo '</tbody></table></div>';
}
dcCore::app()->activityReport->unsetGlobal();
echo '</body></html>';

102
src/ManageList.php Normal file
View file

@ -0,0 +1,102 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use ArrayObject;
use adminGenericFilter;
use adminGenericList;
use dcCore;
use dcPager;
use Dotclear\Helper\Date;
/**
* Logs admin list helper.
*/
class ManageList extends adminGenericList
{
public function logsDisplay(adminGenericFilter $filter, string $enclose_block = ''): void
{
if (!$this->rs || $this->rs->isEmpty()) {
if ($filter->show()) {
echo '<p><strong>' . __('No log matches the filter') . '</strong></p>';
} else {
echo '<p><strong>' . __('No log') . '</strong></p>';
}
} else {
$pager = new dcPager((int) $filter->value('page'), (int) $this->rs_count, (int) $filter->value('nb'), 10);
$pager->var_page = 'page';
$html_block = '<div class="table-outer"><table><caption>' . (
$filter->show() ?
sprintf(__('List of %s logs matching the filter.'), $this->rs_count) :
sprintf(__('List of %s logs.'), $this->rs_count)
) . '</caption>';
$cols = new ArrayObject([
'activity_group' => '<th scope="col" class="nowrap">' . __('Group') . '</th>',
'activity_action' => '<th scope="col" class="nowrap">' . __('Action') . '</th>',
'activity_logs' => '<th scope="col" class="nowrap">' . __('Message') . '</th>',
'activity_date' => '<th scope="col" class="nowrap">' . __('Date') . '</th>',
'activity_status' => '<th scope="col" class="nowrap">' . __('Status') . '</th>',
]);
$this->userColumns(My::id(), $cols);
$html_block .= '<tr>' . implode(iterator_to_array($cols)) . '</tr>%s</table>%s</div>';
if ($enclose_block) {
$html_block = sprintf($enclose_block, $html_block);
}
$blocks = explode('%s', $html_block);
echo $pager->getLinks() . $blocks[0];
while ($this->rs->fetch()) {
echo $this->logsLine();
}
echo $blocks[1] . $blocks[2] . $pager->getLinks();
}
}
private function logsLine(): string
{
$offline = (int) $this->rs->f('activity_status') == ActivityReport::STATUS_REPORTED ? ' offline' : '';
$group = ActivityReport::instance()->groups->get($this->rs->f('activity_group'));
$action = $group->get($this->rs->f('activity_action'));
$message = json_decode((string) $this->rs->f('activity_logs'), true);
$message = $message[0] == 'undefined' ? __('undefined') : vsprintf(__($action->message), $message);
$date = Date::str(
dcCore::app()->blog?->settings->get('system')->get('date_format') . ', ' . dcCore::app()->blog?->settings->get('system')->get('time_format'),
(int) strtotime((string) $this->rs->f('activity_dt')),
dcCore::app()->auth?->getInfo('user_tz')
);
$status = (int) $this->rs->f('activity_status') == ActivityReport::STATUS_PENDING ? __('pending') : __('reported');
$cols = new ArrayObject([
'activity_group' => '<td class="nowrap">' . __($group->title) . '</td>',
'activity_action' => '<td class="nowrap">' . __($action->title) . '</td>',
'activity_logs' => '<td class="maximal">' . $message . '</td>',
'activity_date' => '<td class="nowrap">' . $date . '</td>',
'activity_status' => '<td class="nowrap">' . $status . '</td>',
]);
$this->userColumns(My::id(), $cols);
return
'<tr class="line ' . $offline . '" id="l' . $this->rs->f('activity_id') . '">' .
implode(iterator_to_array($cols)) .
'</tr>';
}
}

77
src/My.php Normal file
View file

@ -0,0 +1,77 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use dcCore;
/**
* Module definitions
*/
class My
{
/** @var string Required php version */
public const PHP_MIN = '8.1';
/** @var string Activity database table name */
public const ACTIVITY_TABLE_NAME = 'activity';
/** @var string Cache sub directory name */
public const CACHE_DIR_NAME = 'activityreport';
/** @var int Incremental version by breaking changes */
public const COMPATIBILITY_VERSION = 3;
/**
* This module id.
*/
public static function id(): string
{
return basename(dirname(__DIR__));
}
/**
* This module name.
*/
public static function name(): string
{
return __((string) dcCore::app()->plugins->moduleInfo(self::id(), 'name'));
}
/**
* This module root.
*/
public static function root(): string
{
return dirname(__DIR__);
}
/**
* Check is module is trully installed.
*
* Required as table structrue has changed
*/
public static function isInstalled(): bool
{
return dcCore::app()->getVersion(self::id()) == dcCore::app()->plugins->moduleInfo(self::id(), 'version');
}
/**
* Check php version.
*/
public static function phpCompliant(): bool
{
return version_compare(phpversion(), self::PHP_MIN, '>=');
}
}

View file

@ -10,30 +10,58 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_RC_PATH')) { declare(strict_types=1);
return null;
namespace Dotclear\Plugin\activityReport;
use dcCore;
use dcNsProcess;
use Exception;
/**
* Prepend process.
*/
class Prepend extends dcNsProcess
{
public static function init(): bool
{
static::$init = defined('DC_RC_PATH')
&& My::phpCompliant()
&& My::isInstalled();
return static::$init;
} }
Clearbricks::lib()->autoload([ public static function process(): bool
'activityReport' => __DIR__ . '/inc/class.activity.report.php', {
'activityReportBehaviors' => __DIR__ . '/inc/class.activity.report.behaviors.php', if (!static::$init) {
]); return false;
}
if (defined('ACTIVITY_REPORT')) {
return true;
}
try { try {
if (!defined('ACTIVITY_REPORT_V2')) { // launch once activity report stuff
dcCore::app()->__set('activityReport', new activityReport()); ActivityReport::instance();
// regirster activity feed URL
dcCore::app()->url->register( dcCore::app()->url->register(
basename(__DIR__), My::id(),
'reports', 'reports',
'^reports/((atom|rss2)/(.+))$', '^reports/((atom|rss2)/(.+))$',
['activityReportPublicUrl', 'feed'] [UrlHandler::class, 'feed']
); );
define('ACTIVITY_REPORT_V2', true); // declare report open
define('ACTIVITY_REPORT', My::COMPATIBILITY_VERSION);
activityReportBehaviors::registerBehaviors(); // register predefined activities scan
} ActivityBehaviors::register();
} catch (Exception $e) { } catch (Exception $e) {
//throw new Exception('Failed to launch activityReport'); }
return true;
}
} }

113
src/Settings.php Normal file
View file

@ -0,0 +1,113 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use dcCore;
use Exception;
/**
* Module settings helper.
*/
class Settings
{
/** @var bool $feed_active The activity feed activation */
public readonly bool $feed_active;
/** @var int $obsolete The logs obsolete time */
public readonly int $obsolete;
/** @var int $interval The report interval time */
public readonly int $interval;
/** @var int $lastreport The last report time */
public readonly int $lastreport;
/** @var array $mailinglist The mailing list */
public readonly array $mailinglist;
/** @var string $mailformat The mail content format */
public readonly string $mailformat;
/** @var string $date format The date format */
public readonly string $dateformat;
/** @var array $requests The selected actions list to report */
public readonly array $requests;
/**
* Constructor sets settings properties.
*/
public function __construct()
{
if (dcCore::app()->blog === null) {
throw new Exception('Blog is not set');
}
$this->feed_active = (bool) ($this->get('feed_active') ?? false);
$this->obsolete = (int) ($this->get('obsolete') ?? 2419200);
$this->interval = (int) ($this->get('interval') ?? 86400);
$this->lastreport = (int) ($this->get('lastreport') ?? 0);
$this->mailinglist = (array) ($this->get('mailinglist') ?? []);
$this->mailformat = (string) ($this->get('mailformat') ?? 'plain');
$this->dateformat = (string) ($this->get('dateformat') ?? '%Y-%m-%d %H:%M:%S');
$this->requests = (array) ($this->get('requests') ?? []);
}
/**
* Dump properties.
*
* @return array<string, mixed> The settings properties
*/
public function dump(): array
{
$vars = get_class_vars(__CLASS__);
return $vars ? $vars : [];
}
/**
* Set a setting in database.
*
* Setting is modified in database but not in script !
*
* @param string $key The setting key
* @param mixed $value The setting value
*/
public function set(string $key, mixed $value): void
{
if (property_exists($this, $key) && gettype($value) == gettype($this->{$key})) {
dcCore::app()->blog?->settings->get(My::id())->put(
$key,
$value,
gettype($value),
null,
true,
false
);
}
}
/**
* Get a setting from database.
*
* @param string $key The setting key
*
* @return mixed The setting value
*/
private function get(string $key): mixed
{
return dcCore::app()->blog?->settings->get(My::id())->get($key);
}
}

161
src/Template.php Normal file
View file

@ -0,0 +1,161 @@
<?php
/**
* @brief activityReport, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\activityReport;
use ArrayObject;
use dcCore;
use Dotclear\Helper\Date;
/**
* Template blocs and values.
*/
class Template
{
/**
* tpl:activityReports [attributes] : Activity report logs (tpl block)
*
* attributes:
*
* - lastn integer Limit to last n logs
* - ingnore_pagination 1|0 Ignore pagination paramaters
*
* @param ArrayObject $attr The attributes
* @param string $content The content
*
* @return string The code
*/
public static function activityReports(ArrayObject $attr, string $content): string
{
$lastn = 0;
if (isset($attr['lastn'])) {
$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";
if ($lastn > 0) {
$p .= "\$params['limit'] = " . $lastn . ";\n";
} else {
$p .= "\$params['limit'] = dcCore::app()->ctx->nb_entry_per_page;\n";
}
if (!isset($attr['ignore_pagination']) || $attr['ignore_pagination'] == '0') {
$p .= "\$params['limit'] = array(((\$_page_number-1)*\$params['limit']),\$params['limit']);\n";
} else {
$p .= "\$params['limit'] = array(0, \$params['limit']);\n";
}
return
"<?php \n" .
$p .
'dcCore::app()->ctx->activityreport_params = $params; ' . "\n" .
'dcCore::app()->ctx->activityreports = ' . ActivityReport::class . '::instance()->getLogs($params); unset($params); ' . "\n" .
'while (dcCore::app()->ctx->activityreports->fetch()) : ?>' . $content . '<?php endwhile; ' .
'dcCore::app()->ctx->pop("activityreports"); dcCore::app()->ctx->pop("activityreport_params"); ' . "\n" .
'?>';
}
/**
* tpl:activityReportFeedID [attributes] : Activity report feed ID (tpl value)
*
* attributes:
*
* - any filters See self::getFilters()
*
* @param ArrayObject $attr The attributes
*
* @return string The code
*/
public static function activityReportFeedID(ArrayObject $attr): string
{
return
'urn:md5:<?php echo md5(dcCore::app()->ctx->activityreports->blog_id.' .
'dcCore::app()->ctx->activityreports->activity_id.dcCore::app()->ctx->activityreports->activity_dt); ' .
'?>';
}
/**
* tpl:activityReportTitle [attributes] : Activity report log title (tpl value)
*
* attributes:
*
* - any filters See self::getFilters()
*
* @param ArrayObject $attr The attributes
*
* @return string The code
*/
public static function activityReportTitle(ArrayObject $attr): string
{
$f = dcCore::app()->tpl->getFilters($attr);
return '<?php echo ' . sprintf($f, Context::class . '::parseTitle()') . '; ?>';
}
/**
* tpl:activityReportContent [attributes] : Activity report log message (tpl value)
*
* attributes:
*
* - any filters See self::getFilters()
*
* @param ArrayObject $attr The attributes
*
* @return string The code
*/
public static function activityReportContent(ArrayObject $attr): string
{
$f = dcCore::app()->tpl->getFilters($attr);
return '<?php echo ' . sprintf($f, Context::class . '::parseContent()') . '; ?>';
}
/**
* tpl:activityReportDate [attributes] : Activity report log date (tpl value)
*
* attributes:
*
* - format Use Date::str() (if iso8601 nor rfc822 were specified default to %Y-%m-%d %H:%M:%S)
* - iso8601 (1|0) Use Date::iso8601()
* - rfc822 (1|0) Use Date::rfc822()
* - any filters See self::getFilters()
*
* @param ArrayObject $attr The attributes
*
* @return string The code
*/
public static function activityReportDate(ArrayObject $attr): string
{
$format = '';
if (!empty($attr['format'])) {
$format = addslashes($attr['format']);
}
$iso8601 = !empty($attr['iso8601']);
$rfc822 = !empty($attr['rfc822']);
$f = dcCore::app()->tpl->getFilters($attr);
if ($rfc822) {
return '<?php echo ' . sprintf($f, Date::class . '::rfc822(strtotime(dcCore::app()->ctx->activityreports->activity_dt),dcCore::app()->blog->settings->system->blog_timezone)') . '; ?>';
} elseif ($iso8601) {
return '<?php echo ' . sprintf($f, Date::class . '::iso8601(strtotime(dcCore::app()->ctx->activityreports->activity_dt),dcCore::app()->blog->settings->system->blog_timezone)') . '; ?>';
} elseif (!empty($format)) {
return '<?php echo ' . sprintf($f, Date::class . "::dt2str('" . $format . "',dcCore::app()->ctx->activityreports->activity_dt)") . '; ?>';
}
return '<?php echo ' . sprintf($f, Date::class . '::dt2str(dcCore::app()->blog->settings->system->date_format,dcCore::app()->ctx->activityreports->activity_dt)') . '; ?>';
}
}

View file

@ -10,72 +10,78 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_CONTEXT_ADMIN')) { declare(strict_types=1);
return null;
namespace Dotclear\Plugin\activityReport;
use dcCore;
use dcNsProcess;
use Dotclear\Plugin\Uninstaller\Uninstaller;
/**
* Uninstall process.
*
* Using plugin Uninstaller
*/
class Uninstall extends dcNsProcess
{
public static function init(): bool
{
static::$init = defined('DC_CONTEXT_ADMIN');
return static::$init;
} }
$this->addUserAction( public static function process(): bool
/* type */ {
if (!static::$init || !dcCore::app()->plugins->moduleExists('Uninstaller')) {
return false;
}
Uninstaller::instance()
->addUserAction(
'tables', 'tables',
/* action */
'delete', 'delete',
/* ns */ My::ACTIVITY_TABLE_NAME
initActivityReport::ACTIVITY_TABLE_NAME, )
/* description */ ->addUserAction(
sprintf(__('delete %s table'), '"activity"') 'settings',
); 'delete_all',
My::id()
$this->addUserAction( )
/* type */ ->addUserAction(
'versions',
'delete',
My::id()
)
->addUserAction(
'plugins',
'delete',
My::id()
)
->addDirectAction(
'tables', 'tables',
/* action */
'delete', 'delete',
/* ns */ My::ACTIVITY_TABLE_NAME
initActivityReport::SETTING_TABLE_NAME, )
/* description */ ->addDirectAction(
sprintf(__('delete %s table'), '"activity_setting"') 'settings',
); 'delete_all',
My::id()
$this->addUserAction( )
/* type */ ->addDirectAction(
'plugins',
/* action */
'delete',
/* ns */
basename(__DIR__),
/* description */
__('delete plugin files')
);
$this->addUserAction(
/* type */
'versions', 'versions',
/* action */
'delete', 'delete',
/* ns */ My::id()
basename(__DIR__), )
/* description */ ->addDirectAction(
__('delete the version number')
);
$this->addDirectAction(
/* type */
'versions',
/* action */
'delete',
/* ns */
basename(__DIR__),
/* description */
sprintf(__('delete %s version number'), basename(__DIR__))
);
$this->addDirectAction(
/* type */
'plugins', 'plugins',
/* action */
'delete', 'delete',
/* ns */ My::id()
basename(__DIR__), )
/* description */ ;
sprintf(__('delete %s plugin files'), basename(__DIR__))
); // no custom action
return false;
}
}

56
src/UrlHandler.php Normal file
View file

@ -0,0 +1,56 @@
<?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();
}
// get type of feed
$mime = $m[1] == 'atom' ? 'application/atom+xml' : 'application/xml';
if (false === ActivityReport::instance()->checkUserCode($m[2])) {
self::p404();
}
// feed limits
dcCore::app()->ctx->__set('nb_entry_per_page', (int) dcCore::app()->blog?->settings->get('system')->get('nb_post_per_feed'));
dcCore::app()->ctx->__set('short_feed_items', (int) dcCore::app()->blog?->settings->get('system')->get('short_feed_items'));
// serve activity feed template
header('X-Robots-Tag: ' . context::robotsPolicy(dcCore::app()->blog?->settings->get('system')->get('robots_policy'), ''));
self::serveDocument('activityreport-' . $m[1] . '.xml', $mime);
}
}