Compare commits

..

19 Commits
v0.6 ... master

Author SHA1 Message Date
Jean-Christian Paul Denis 8b352b5a81
code review 2023-10-22 19:04:35 +02:00
Jean-Christian Paul Denis 3f32615115
fix last minute update from dc2.28 2023-10-14 10:33:38 +02:00
Jean-Christian Paul Denis 118bd06ead
release 0.9.1 2023-10-09 22:38:35 +02:00
Jean-Christian Paul Denis 81bd9889d0
ouppppps 2023-10-07 15:49:35 +02:00
Jean-Christian Paul Denis dac7de41d6
oups 2023-10-07 15:47:08 +02:00
Jean-Christian Paul Denis 332930aab9
update to Dotclear 2.28-dev 2023-10-07 15:33:13 +02:00
Jean-Christian Paul Denis 1a13ca2405
release 0.8 2023-08-27 18:20:01 +02:00
Jean-Christian Paul Denis 4d5066d118
add web server stat 2023-08-27 18:19:40 +02:00
Jean-Christian Paul Denis 80b0032e2b
release 0.7.3 2023-08-26 17:56:23 +02:00
Jean-Christian Paul Denis 51c636f018
fix type 2023-08-26 17:53:05 +02:00
Jean-Christian Paul Denis 5fa9b0bdea
release 0.7.2 2023-08-16 16:24:24 +02:00
Jean-Christian Paul Denis 2672486b69
cosmetic 2023-08-16 16:21:05 +02:00
Jean-Christian Paul Denis a656621b62
change page footer 2023-08-16 16:18:24 +02:00
Jean-Christian Paul Denis 5f6c8e80b9
fix unset user prefs 2023-08-16 16:16:51 +02:00
Jean-Christian Paul Denis f406831b7f
release 0.7.1 2023-08-14 17:46:51 +02:00
Jean-Christian Paul Denis 68e87ed8e2
fix server and help URLs, fix #2 2023-08-14 17:46:23 +02:00
Jean-Christian Paul Denis c3adc7123b
release 0.7 2023-08-12 11:51:49 +02:00
Jean-Christian Paul Denis 182aa2d4c9
use asynchronous report sending, fix #1 2023-08-12 11:51:29 +02:00
Jean-Christian Paul Denis 2441981717
use HttpClient first 2023-08-12 11:48:55 +02:00
13 changed files with 441 additions and 193 deletions

View File

@ -1,3 +1,65 @@
DotclearWatch 0.9.3 - 2023.10.22
===========================================================
* Require dotclear 2.28
* Require php 8.1
* Add PHP minor version (using PHP constants)
* Add uninstall feature (plugin Uninstaller)
* Fix colored synthax
* Use Helper Form everywhere
DotclearWatch 0.9.2 - 2023.10.14
===========================================================
* Require dotclear 2.28
* Require php 8.1
* Fix last minute update from Dtoclear 2.28
DotclearWatch 0.9.1 - 2023.10.09
===========================================================
* Require dotclear 2.28
* Require php 8.1
* Fix permissions
* Code review
DotclearWatch 0.9 - 2023.10.07
===========================================================
* Require dotclear 2.28
* Require php 8.1
* Update to Dotclear 2.28-dev
DotclearWatch 0.8 - 2023.08.27
===========================================================
* Require dotclear 2.27
* Require php 7.4
* Add web server name and version to stats
DotclearWatch 0.7.2 - 2023.08.26
===========================================================
* Require dotclear 2.27
* Require php 7.4
* fix type hint
* fix README
DotclearWatch 0.7.2 - 2023.08.16
===========================================================
* Require dotclear 2.27
* Require php 7.4
* Fix unset user prefs
* Change page footer
DotclearWatch 0.7.1 - 2023.08.14
===========================================================
* Require dotclear 2.27
* Require php 7.4
* Fix help URLs, fix #2
* Fix server public URL
DotclearWatch 0.7 - 2023.08.12
===========================================================
* require dotclear 2.27
* require php 7.4
* Use asynchronous report sending
* Use HttpClient first to send report
DotclearWatch 0.6 - 2023.08.09
===========================================================
* require dotclear 2.27

View File

@ -1,18 +1,19 @@
# README
[![Release](https://img.shields.io/badge/release-0.6-a2cbe9.svg)](https://git.dotclear.watch/dw/DotclearWatch/releases)
[![Date](https://img.shields.io/badge/date-2023.08.09-c44d58.svg)](https://git.dotclear.watch/dw/DotclearWatch/releases)
[![Dotclear](https://img.shields.io/badge/dotclear-v2.27-137bbb.svg)](https://fr.dotclear.org/download)
[![Release](https://img.shields.io/badge/release-0.9.3-a2cbe9.svg)](https://git.dotclear.watch/dw/DotclearWatch/releases)
![Date](https://img.shields.io/badge/date-2023.10.22-c44d58.svg)
[![Dotclear](https://img.shields.io/badge/dotclear-v2.28-137bbb.svg)](https://fr.dotclear.org/download)
[![Dotaddict](https://img.shields.io/badge/dotaddict-official-9ac123.svg)](https://plugins.dotaddict.org/dc2/details/DotclearWatch)
[![License](https://img.shields.io/github/license/JcDenis/DotclearWatch)](https://git.dotclear.watch/dw/DotclearWatch/blob/master/LICENSE)
[![License](https://img.shields.io/badge/license-GPL--2.0-ececec.svg)](https://git.dotclear.watch/dw/DotclearWatch/src/branch/master/LICENSE)
## WHAT IS DOTCLEARWATCH ?
## ABOUT
"dotclear watch" is a plugin for the open-source
web publishing software called Dotclear.
_dotclear watch_ is a plugin for the open-source web publishing software called [Dotclear](https://www.dotclear.org).
> Send report about your Dotclear
It tracks Dotclear's installation to get stats about it.
Default statistics server is located at https://dotclear.watch/statistics
Default statistics server is located at https://stat.dotclear.watch
What's being track :
* Dotclear version,
@ -28,11 +29,9 @@ in aboutConfig global parameters called DotclearWatch->hidden_modules
## REQUIREMENTS
DotclearWatch requires:
* super admin permission to intall it
* Dotclear 2.27
* PHP 7.4+
* Dotclear 2.28
* PHP 8.1+
* Dotclear super admin permission to intall it
## USAGE
@ -43,10 +42,10 @@ To disable sending stats, just deactivate or uninstall this plugin.
## LINKS
* License : [GNU GPL v2](https://www.gnu.org/licenses/old-licenses/lgpl-2.0.html)
* Source & contributions : [Gitea Page](https://git.dotclear.watch/dw/DotclearWatch) or [GitHub Page](https://github.com/JcDenis/DotclearWatch)
* Packages & details : [Gitea Page](https://git.dotclear.watch/dw/DotclearWatch/releases) or[Dotaddict Page](https://plugins.dotaddict.org/dc2/details/DotclearWatch)
* Discussion & Help : [Gitea Page](https://github.com/dw/DotclearWatch/issues)
* [License](https://git.dotclear.watch/dw/DotclearWatch/src/branch/master/LICENSE)
* [Packages & details](https://git.dotclear.watch/dw/DotclearWatch/releases) (or on [Dotaddict](https://plugins.dotaddict.org/dc2/details/DotclearWatch))
* [Sources & contributions](https://git.dotclear.watch/dw/DotclearWatch) (or on [GitHub](https://github.com/JcDenis/DotclearWatch))
* [Issues & security](https://git.dotclear.watch/dw/DotclearWatch/issues) (or on [GitHub](https://github.com/JcDenis/DotclearWatch/issues))
## CONTRIBUTORS

View File

@ -1,28 +1,30 @@
<?php
/**
* @brief DotclearWatch, a plugin for Dotclear 2
* @file
* @brief The plugin DotclearWatch definition
* @ingroup DotclearWatch
*
* @package Dotclear
* @subpackage Plugin
* @defgroup DotclearWatch Plugin DotclearWatch.
*
* @author Jean-Christian Denis and contributors
* QSend report about your Dotclear.
*
* @copyright Jean-Christain Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* @author Jean-Christian Denis
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
$this->registerModule(
'Dotclear Watch',
'Send report about your Dotclear',
'Jean-Christian Denis and contributors',
'0.6',
'0.9.3',
[
'requires' => [
['php', '7.4'],
['core', '2.27'],
],
'type' => 'plugin',
'support' => 'https://git.dotclear.watch/JcDenis/' . basename(__DIR__) . '/issues',
'details' => 'https://git.dotclear.watch/JcDenis/' . basename(__DIR__) . '/src/branch/master/README.md',
'repository' => 'https://git.dotclear.watch/JcDenis/' . basename(__DIR__) . '/raw/branch/master/dcstore.xml',
'requires' => [['core', '2.28']],
'permissions' => 'My',
'type' => 'plugin',
'support' => 'https://git.dotclear.watch/dw/' . basename(__DIR__) . '/issues',
'details' => 'https://git.dotclear.watch/dw/' . basename(__DIR__) . '/src/branch/master/README.md',
'repository' => 'https://git.dotclear.watch/dw/' . basename(__DIR__) . '/raw/branch/master/dcstore.xml',
]
);

View File

@ -2,12 +2,12 @@
<modules xmlns:da="http://dotaddict.org/da/">
<module id="DotclearWatch">
<name>Dotclear Watch</name>
<version>0.6</version>
<version>0.9.3</version>
<author>Jean-Christian Denis and contributors</author>
<desc>Send report about your Dotclear</desc>
<file>https://git.dotclear.watch/dw/DotclearWatch/releases/download/v0.6/plugin-DotclearWatch.zip</file>
<da:dcmin>2.27</da:dcmin>
<da:details>http://plugins.dotaddict.org/dc2/details/DotclearWatch</da:details>
<file>https://git.dotclear.watch/dw/DotclearWatch/releases/download/v0.9.3/plugin-DotclearWatch.zip</file>
<da:dcmin>2.28</da:dcmin>
<da:details>https://git.dotclear.watch/dw/DotclearWatch/src/branch/master/README.md</da:details>
<da:support>https://git.dotclear.watch/dw/DotclearWatch/issues</da:support>
</module>
</modules>

29
js/service.js 100644
View File

@ -0,0 +1,29 @@
/*global $, dotclear */
'use strict';
dotclear.adminDotclearWatchSendReport = () => {
dotclear.services(
'adminDotclearWatchSendReport',
(data) => {
try {
const response = JSON.parse(data);
if (response?.success) {
} else {
console.log(dotclear.debug && response?.message ? response.message : 'Dotclear REST server error');
return;
}
} catch (e) {
console.log(e);
}
},
(error) => {
console.log(error);
},
true, // Use GET method
{ json: 1 },
);
};
$(() => {
dotclear.adminDotclearWatchSendReport();
});

View File

@ -11,6 +11,8 @@
use Dotclear\Helper\L10n;
L10n::$locales['Uses DotclearWatch plugin statistics'] = 'Utilise le plugin de statistiques DotclearWatch';
L10n::$locales['Shared statistics'] = 'Statistiques partagées';
L10n::$locales['Cache directory sucessfully cleared.'] = 'Répertoire de cache des rapports nettoyé.';
L10n::$locales['Settings successfully updated.'] = 'Paramètres mis à jour.';
L10n::$locales['Report sent.'] = 'Rapport envoyé.';

View File

@ -1,15 +1,21 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: DotclearWatch 0.1\n"
"Project-Id-Version: DotclearWatch 0.7.1\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: 2023-07-23T09:13:29+00:00\n"
"PO-Revision-Date: 2023-08-16T14:14:45+00:00\n"
"Last-Translator: Jean-Christain Denis\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
msgid "Uses DotclearWatch plugin statistics"
msgstr "Utilise le plugin de statistiques DotclearWatch"
msgid "Shared statistics"
msgstr "Statistiques partagées"
msgid "Cache directory sucessfully cleared."
msgstr "Répertoire de cache des rapports nettoyé."

View File

@ -1,22 +1,25 @@
<?php
/**
* @brief DotclearWatch, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christain Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\DotclearWatch;
use dcCore;
use Dotclear\App;
use Dotclear\Core\Process;
use Dotclear\Helper\Html\Form\{
Img,
Li,
Link,
Ul
};
/**
* @brief DotclearWatch backend class.
* @ingroup DotclearWatch
*
* @author Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
class Backend extends Process
{
public static function init(): bool
@ -30,13 +33,43 @@ class Backend extends Process
return false;
}
//My::addBackendMenuItem();
dcCore::app()->addBehaviors([
'adminDashboardHeaders' => [Utils::class, 'sendReport'],
'adminPageFooterV2' => [Utils::class, 'addMark'],
App::behavior()->addBehaviors([
// Add JS for asynchronous report sending
'adminDashboardHeaders' => function (): string {
return My::jsLoad('service', App::version()->getVersion(My::id()));
},
// Add icon on bottom of dashboard sidebar menu
'adminPageFooterV2' => function (): void {
if (My::settings()->getGlobal('distant_api_url')) {
echo (new Ul())->items([
(new li())->items([
(new Link())
->class('outgoing')
->href('https://stat.dotclear.watch')
->title(__('Uses DotclearWatch plugin statistics'))
->text(__('Shared statistics'))
->items([
(new Img(My::fileURL('icon.svg'))),
]),
]),
])->render();
}
},
]);
App::rest()->addFunction(
// Add REST service for asynchronous report sending
'adminDotclearWatchSendReport',
function (): array {
Utils::sendReport();
return [
'ret' => true,
'msg' => 'report sent',
];
},
);
return true;
}
}

View File

@ -1,18 +1,10 @@
<?php
/**
* @brief DotclearWatch, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugins
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
declare(strict_types=1);
namespace Dotclear\Plugin\DotclearWatch;
use dcCore;
use Dotclear\App;
use Dotclear\Core\Backend\Notices;
use Dotclear\Core\Backend\Page;
use Dotclear\Core\Process;
@ -30,14 +22,32 @@ use Dotclear\Helper\Html\Form\{
};
use Dotclear\Helper\Html\Html;
/**
* @brief DotclearWatch configuration class.
* @ingroup DotclearWatch
*
* @author Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
class Config extends Process
{
private static string $hidden_modules = '';
/**
* List of hidden modules.
*
* @var string $hidden_modules
*/
private static string $hidden_modules = '';
/**
* Distant API URL.
*
* @var string $distant_api_url
*/
private static string $distant_api_url = '';
public static function init(): bool
{
return self::status(My::checkContext(My::MANAGE));
return self::status(My::checkContext(My::CONFIG));
}
public static function process(): bool
@ -46,8 +56,8 @@ class Config extends Process
return false;
}
if (dcCore::app()->auth->user_prefs->get('interface')->get('colorsyntax')) {
dcCore::app()->addBehavior('pluginsToolsHeadersV2', fn (bool $plugin): string => Page::jsLoadCodeMirror(dcCore::app()->auth->user_prefs->get('interface')->get('colorsyntax_theme')));
if (App::auth()->prefs()->get('interface')->get('colorsyntax')) {
App::behavior()->addBehavior('pluginsToolsHeadersV2', fn (bool $plugin): string => Page::jsLoadCodeMirror(App::auth()->prefs()->get('interface')->get('colorsyntax_theme')));
}
self::$hidden_modules = (string) My::settings()->getGlobal('hidden_modules');
@ -62,7 +72,8 @@ class Config extends Process
Notices::AddSuccessNotice(__('Cache directory sucessfully cleared.'));
}
self::$hidden_modules = '';
self::$distant_api_url = !empty($_POST['distant_api_url']) && is_string($_POST['distant_api_url']) ? $_POST['distant_api_url'] : Utils::DISTANT_API_URL;
self::$hidden_modules = '';
foreach (explode(',', $_POST['hidden_modules']) as $hidden) {
$hidden = trim($hidden);
if (!empty($hidden)) {
@ -70,8 +81,6 @@ class Config extends Process
}
}
self::$distant_api_url = !empty($_POST['distant_api_url']) && is_string($_POST['distant_api_url']) ? $_POST['distant_api_url'] : Utils::DISTANT_API_URL;
My::settings()->put('hidden_modules', self::$hidden_modules, 'string', 'Hidden modules from report', true, true);
My::settings()->put('distant_api_url', self::$distant_api_url, 'string', 'Distant API report URL', true, true);
Notices::AddSuccessNotice(__('Settings successfully updated.'));
@ -86,7 +95,7 @@ class Config extends Process
}
}
dcCore::app()->admin->url->redirect('admin.plugins', ['module' => My::id(), 'conf' => '1']);
App::backend()->url()->redirect('admin.plugins', ['module' => My::id(), 'conf' => '1']);
return true;
}
@ -106,12 +115,12 @@ class Config extends Process
]),
(new Para())->items([
(new Label(__('Hidden modules:')))->for('hidden_modules'),
(new Input('hidden_modules'))->class('maximal')->size(65)->maxlenght(255)->value(self::$hidden_modules),
(new Input('hidden_modules'))->class('maximal')->size(65)->maxlength(255)->value(self::$hidden_modules),
]),
(new Note())->class('form-note')->text(__('This is the comma separated list of plugins IDs and themes IDs to ignore in report.')),
(new Para())->items([
(new Label(__('Distant API URL:')))->for('distant_api_url'),
(new Input('distant_api_url'))->class('maximal')->size(65)->maxlenght(255)->value(self::$distant_api_url),
(new Input('distant_api_url'))->class('maximal')->size(65)->maxlength(255)->value(self::$distant_api_url),
]),
(new Note())->class('form-note')->text(__('This is the URL of the API to send report. Leave empty to reset value.')),
(new Para())->items([
@ -138,8 +147,8 @@ class Config extends Process
->class('maximal'),
])->render() .
(
!dcCore::app()->auth->user_prefs->get('interface')->get('colorsyntax') ? '' :
Page::jsRunCodeMirror(My::id() . 'editor', 'report_contents', 'json', dcCore::app()->auth->user_prefs->get('interface')->get('colorsyntax_theme'))
App::auth()->prefs()->get('interface')->get('colorsyntax') ?
Page::jsRunCodeMirror(My::id() . 'editor', 'report_contents', 'json', App::auth()->prefs()->get('interface')->get('colorsyntax_theme')) : ''
);
}
}

View File

@ -1,21 +1,18 @@
<?php
/**
* @brief DotclearWatch, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christain Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\DotclearWatch;
use Dotclear\Core\Process;
/**
* @brief DotclearWatch install class.
* @ingroup DotclearWatch
*
* @author Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
class Install extends Process
{
public static function init(): bool
@ -25,9 +22,8 @@ class Install extends Process
public static function process(): bool
{
$s = My::settings();
if (self::status() && $s !== null) {
$s->put(
if (self::status()) {
My::settings()->put(
'hidden_modules',
'DotclearWatch',
'string',
@ -35,7 +31,7 @@ class Install extends Process
false,
true
);
$s->put(
My::settings()->put(
'distant_api_url',
'https://dotclear.watch/api',
'string',

View File

@ -1,27 +1,19 @@
<?php
/**
* @brief DotclearWatch, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christain Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\DotclearWatch;
use dcCore;
use Dotclear\Module\MyPlugin;
/**
* @brief DotclearWatch My helper.
* @ingroup DotclearWatch
*
* @author Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
class My extends MyPlugin
{
protected static function checkCustomContext(int $context): ?bool
{
return $context === My::INSTALL ? null :
defined('DC_CONTEXT_ADMIN') && dcCore::app()->auth->isSuperAdmin();
}
// Use default permissions
}

66
src/Uninstall.php 100644
View File

@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace Dotclear\Plugin\DotclearWatch;
use Dotclear\Core\Process;
use Dotclear\Plugin\Uninstaller\Uninstaller;
/**
* @brief DotclearWatch uninstall class.
* @ingroup DotclearWatch
*
* @author Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
class Uninstall extends Process
{
public static function init(): bool
{
return self::status(My::checkContext(My::UNINSTALL));
}
public static function process(): bool
{
if (!self::status()) {
return false;
}
Uninstaller::instance()
->addUserAction(
'settings',
'delete_all',
My::id()
)
->addUserAction(
'plugins',
'delete',
My::id()
)
->addUserAction(
'versions',
'delete',
My::id()
)
->addDirectAction(
'settings',
'delete_all',
My::id()
)
->addDirectAction(
'plugins',
'delete',
My::id()
)
->addDirectAction(
'versions',
'delete',
My::id()
)
;
// no custom action
return false;
}
}

View File

@ -1,60 +1,58 @@
<?php
/**
* @brief DotclearWatch, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christain Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\DotclearWatch;
use dcCore;
use dcLog;
use dcModuleDefine;
use dcThemes;
use Dotclear\App;
use Dotclear\Helper\Crypt;
use Dotclear\Helper\Date;
use Dotclear\Helper\Network\HttpClient;
use Dotclear\Module\ModuleDefine;
use Exception;
/**
* @brief DotclearWatch utils class.
* @ingroup DotclearWatch
*
* @author Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
class Utils
{
/** @var int The expiration delay before resend report (one week) */
/**
* The expiration delay before resend report (one week).
*
* @var int EXPIRED_DELAY
*/
public const EXPIRED_DELAY = 604800;
/** @var string The default distant API URL */
/**
* The default distant API URL.
*
* @var string DISTANT_API_URL
*/
public const DISTANT_API_URL = 'https://dotclear.watch/api';
/** @var string The distant API version */
/**
* The distant API version.
*
* @var string DISTANT_API_VERSION
*/
public const DISTANT_API_VERSION = '1.1';
/** @var array<int,string> The hiddens modules IDs */
/**
* The hiddens modules IDs.
*
* @var array<int,string> $hiddens
*/
private static array $hiddens = [];
/** @var string Multiblog unique identifiant */
private static string $uid = '';
/**
* Add mark to backend menu footer.
* Multiblog unique identifiant.
*
* @var string $uid
*/
public static function addMark(): void
{
if (My::settings()->getGlobal('distant_api_url')) {
echo sprintf(
'<ul><li><a href="%s" title="%s" class="outgoing">%s<img src="%s" /></a></ul></li>',
'https://statistics.dotclear.watch',
__('DotclearWatch plugin statistics'),
__('Tracked by dotclear.watch'),
My::fileURL('icon.svg')
);
}
}
private static string $uid = '';
/**
* Get hidden modules.
@ -88,12 +86,12 @@ class Utils
{
$modules = [];
$hiddens = self::getHiddens();
$defines = dcCore::app()->plugins->getDefines($strict ? ['state' => dcModuleDefine::STATE_ENABLED] : []);
$defines = App::plugins()->getDefines($strict ? ['state' => ModuleDefine::STATE_ENABLED] : []);
foreach ($defines as $define) {
if ($strict && in_array($define->getId(), $hiddens)) {
continue;
}
$modules[$define->getId()] = $define->get('version');
$modules[(string) $define->getId()] = (string) $define->get('version');
}
return $modules;
@ -108,24 +106,48 @@ class Utils
*/
public static function getThemes(bool $strict = true): array
{
if (!(dcCore::app()->themes instanceof dcThemes)) {
dcCore::app()->themes = new dcThemes();
dcCore::app()->themes->loadModules(dcCore::app()->blog->themes_path);
if (App::themes()->isEmpty()) {
App::themes()->loadModules(App::blog()->themesPath());
}
$modules = [];
$hiddens = self::getHiddens();
$defines = dcCore::app()->themes->getDefines($strict ? ['state' => dcModuleDefine::STATE_ENABLED] : []);
$defines = App::themes()->getDefines($strict ? ['state' => ModuleDefine::STATE_ENABLED] : []);
foreach ($defines as $define) {
if ($strict && in_array($define->getId(), $hiddens)) {
continue;
}
$modules[$define->getId()] = $define->get('version');
$modules[(string) $define->getId()] = (string) $define->get('version');
}
return $modules;
}
/**
* Get server software and version.
*
* @return array<string,string> The server info
*/
public static function getServer(): array
{
$res = [
'name' => 'undefined',
'version' => 'undefined',
];
if (!empty($_SERVER['SERVER_SOFTWARE'])) {
$exp = explode('/', $_SERVER['SERVER_SOFTWARE']);
if (count($exp) == 2) {
$res = [
'name' => $exp[0],
'version' => $exp[1],
];
}
}
return $res;
}
/**
* Get report contents.
*
@ -149,7 +171,7 @@ class Utils
*/
public static function getError(): string
{
$rs = dcCore::app()->log->getLogs([
$rs = App::log()->getLogs([
'log_table' => My::id() . '_error',
]);
@ -192,9 +214,18 @@ class Utils
$status = 500;
$response = '';
$url = sprintf(self::url(), 'report');
$path = '';
try {
if (function_exists('curl_init')) {
if (false !== ($client = HttpClient::initClient($url, $path))) {
$client->setUserAgent('Dotclear.watch ' . My::id() . '/' . self::DISTANT_API_VERSION);
$client->useGzip(false);
$client->setPersistReferers(false);
$client->post($path, ['key' => self::key(), 'report' => $contents]);
$status = (int) $client->getStatus();
$response = $client->getContent();
} elseif (function_exists('curl_init')) {
if (false !== ($client = curl_init($url))) {
curl_setopt($client, CURLOPT_RETURNTRANSFER, true);
curl_setopt($client, CURLOPT_POST, true);
@ -204,17 +235,6 @@ class Utils
$status = (int) curl_getinfo($client, CURLINFO_HTTP_CODE);
}
}
} else {
$path = '';
if (false !== ($client = HttpClient::initClient($url, $path))) {
$client->setUserAgent('Dotclear.watch ' . My::id() . '/' . self::DISTANT_API_VERSION);
$client->useGzip(false);
$client->setPersistReferers(false);
$client->post($path, ['key' => self::key(), 'report' => $contents]);
$status = (int) $client->getStatus();
$response = $client->getContent();
}
}
unset($client);
@ -235,14 +255,24 @@ class Utils
}
}
/**
* Check if report can be done.
*
* @return bool True if it can
*/
private static function check(): bool
{
return defined('DC_CRYPT_ALGO');
return true; // not yet
}
/**
* Get report key.
*
* @return string The report key
*/
private static function key(): string
{
return Crypt::hmac(self::uid() . My::id(), DC_CRYPT_ALGO);
return Crypt::hmac(self::uid() . My::id(), App::config()->cryptAlgo());
}
private static function uid(): string
@ -258,11 +288,21 @@ class Utils
return self::$uid;
}
/**
* Get blog report uid.
*
* @return string The blog report uid
*/
private static function buid(): string
{
return md5(self::uid() . dcCore::app()->blog->uid);
return md5(self::uid() . App::blog()->uid());
}
/**
* Get query URL.
*
* @return string The URL
*/
private static function url(): string
{
$api_url = My::settings()->getGlobal('distant_api_url');
@ -270,9 +310,12 @@ class Utils
return (is_string($api_url) ? $api_url : self::DISTANT_API_URL) . '/' . self::DISTANT_API_VERSION . '/%s/' . self::uid();
}
/**
* Clear report logs.
*/
private static function clear(): void
{
$rs = dcCore::app()->log->getLogs([
$rs = App::log()->getLogs([
'log_table' => [
My::id() . '_report',
My::id() . '_error',
@ -287,49 +330,56 @@ class Utils
while ($rs->fetch()) {
$logs[] = (int) $rs->f('log_id');
}
dcCore::app()->log->delLogs($logs);
App::log()->delLogs($logs);
}
/**
* Log error.
*/
private static function error(string $message): void
{
self::clear();
$cur = dcCore::app()->con->openCursor(dcCore::app()->prefix . dcLog::LOG_TABLE_NAME);
$cur = App::log()->openLogCursor();
$cur->setField('log_table', My::id() . '_error');
$cur->setField('log_msg', $message);
dcCore::app()->log->addLog($cur);
App::log()->addLog($cur);
}
/**
* Write report.
*/
private static function write(string $contents): void
{
self::clear();
$cur = dcCore::app()->con->openCursor(dcCore::app()->prefix . dcLog::LOG_TABLE_NAME);
$cur = App::log()->openLogCursor();
$cur->setField('log_table', My::id() . '_report');
$cur->setField('log_msg', $contents);
dcCore::app()->log->addLog($cur);
}
private static function read(): string
{
$rs = dcCore::app()->log->getLogs([
'log_table' => My::id() . '_report',
]);
return $rs->isEmpty() || !is_string($rs->f('log_msg')) ? '' : $rs->f('log_msg');
App::log()->addLog($cur);
}
/**
* Check if report is expired.
*
* @return bool True if expired
*/
private static function expired(): bool
{
$rs = dcCore::app()->log->getLogs([
$rs = App::log()->getLogs([
'log_table' => My::id() . '_report',
]);
return $rs->isEmpty() || !is_string($rs->f('log_dt')) || (int) Date::str('%s', $rs->f('log_dt')) + self::EXPIRED_DELAY < time();
return $rs->isEmpty() || !is_string($rs->f('log_dt')) || (int) strtotime($rs->f('log_dt')) + self::EXPIRED_DELAY < time();
}
/**
* Get report content.
*
* @return string Teh report content
*/
private static function contents(): string
{
// Build json response
@ -339,26 +389,28 @@ class Utils
'plugins' => self::getPlugins(), // enabled plugins
'themes' => self::getThemes(), // enabled themes
'blog' => [
'lang' => (string) dcCore::app()->blog->settings->get('system')->get('lang'),
'theme' => (string) dcCore::app()->blog->settings->get('system')->get('theme'),
'lang' => (string) App::blog()->settings()->get('system')->get('lang'),
'theme' => (string) App::blog()->settings()->get('system')->get('theme'),
],
'blogs' => [
'count' => (int) dcCore::app()->getBlogs([], true)->f(0),
'count' => (int) App::blogs()->getBlogs([], true)->f(0),
],
'core' => [
'version' => DC_VERSION,
'version' => App::config()->dotclearVersion(),
],
'php' => [
'server' => self::getServer(),
'php' => [
'sapi' => php_sapi_name() ?: 'php',
'version' => phpversion(),
'version' => PHP_VERSION,
'minor' => PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION,
],
'system' => [
'name' => php_uname('s'),
'version' => php_uname('r'),
],
'database' => [
'driver' => dcCore::app()->con->driver(),
'version' => dcCore::app()->con->version(),
'driver' => App::con()->driver(),
'version' => App::con()->version(),
],
], JSON_PRETTY_PRINT);
}