Compare commits

..

10 Commits

6 changed files with 132 additions and 71 deletions

View File

@ -14,7 +14,7 @@ $this->registerModule(
'Dotclear Watch', 'Dotclear Watch',
'Send report about your Dotclear', 'Send report about your Dotclear',
'Jean-Christian Denis and contributors', 'Jean-Christian Denis and contributors',
'0.3', '0.5',
[ [
'requires' => [ 'requires' => [
['php', '7.4'], ['php', '7.4'],

View File

@ -2,10 +2,10 @@
<modules xmlns:da="http://dotaddict.org/da/"> <modules xmlns:da="http://dotaddict.org/da/">
<module id="DotclearWatch"> <module id="DotclearWatch">
<name>Dotclear Watch</name> <name>Dotclear Watch</name>
<version>0.3</version> <version>0.5</version>
<author>Jean-Christian Denis and contributors</author> <author>Jean-Christian Denis and contributors</author>
<desc>Send report about your Dotclear</desc> <desc>Send report about your Dotclear</desc>
<file>https://github.com/JcDenis/DotclearWatch/releases/download/v0.3/plugin-DotclearWatch.zip</file> <file>https://github.com/JcDenis/DotclearWatch/releases/download/v0.5/plugin-DotclearWatch.zip</file>
<da:dcmin>2.27</da:dcmin> <da:dcmin>2.27</da:dcmin>
<da:details>http://plugins.dotaddict.org/dc2/details/DotclearWatch</da:details> <da:details>http://plugins.dotaddict.org/dc2/details/DotclearWatch</da:details>
<da:support>https://github.com/JcDenis/DotclearWatch/issues</da:support> <da:support>https://github.com/JcDenis/DotclearWatch/issues</da:support>

View File

@ -18,9 +18,9 @@ L10n::$locales['Settings are globals. Reports are by blog.']
L10n::$locales['Hidden modules:'] = 'Modules cachés :'; L10n::$locales['Hidden modules:'] = 'Modules cachés :';
L10n::$locales['This is the comma separated list of plugins IDs and themes IDs to ignore in report.'] = 'C\'est la liste des modules cachés séparés par une virgule.'; L10n::$locales['This is the comma separated list of plugins IDs and themes IDs to ignore in report.'] = 'C\'est la liste des modules cachés séparés par une virgule.';
L10n::$locales['Distant API URL:'] = 'URL de l\'API distante :'; L10n::$locales['Distant API URL:'] = 'URL de l\'API distante :';
L10n::$locales['This is the URL of the API to send report. Leave empty to reset value.'] = 'C\'est L\'URL de l\'API où sera envoyer le rapport. Laisser vide pour remettre par défaut.'; L10n::$locales['This is the URL of the API to send report. Leave empty to reset value.'] = 'C\'est L\'URL de l\'API où sera envoyé le rapport. Laisser vide pour remettre par défaut.';
L10n::$locales['Clear reports cache directory'] = 'Nettoyer le répertoire de cache des rapports.'; L10n::$locales['Clear reports cache directory'] = 'Nettoyer le répertoire de cache des rapports.';
L10n::$locales['This deletes all blogs reports in cache.'] = 'Ceci efface tous les rapports des blogs en cache.'; L10n::$locales['This deletes all blogs reports in cache.'] = 'Ceci efface tous les rapports des blogs en cache.';
L10n::$locales['Send report now'] = 'Envoyer le rapport maintenant'; L10n::$locales['Send report now'] = 'Envoyer le rapport maintenant';
L10n::$locales['This sent report for current blog even if report exists in cache.'] = 'Ceci envoie le rapport pou r le blog courant même si un rapport existe en cache.'; L10n::$locales['This sent report for current blog even if report exists in cache.'] = 'Ceci envoie le rapport pour le blog courant même si un rapport existe en cache.';
L10n::$locales['Report that will be sent for this blog:'] = 'Rapport qui sera envoyé pour ce blog :'; L10n::$locales['Report that will be sent for this blog:'] = 'Rapport qui sera envoyé pour ce blog :';

View File

@ -32,7 +32,7 @@ msgid "Distant API URL:"
msgstr "URL de l'API distante :" msgstr "URL de l'API distante :"
msgid "This is the URL of the API to send report. Leave empty to reset value." msgid "This is the URL of the API to send report. Leave empty to reset value."
msgstr "C'est L'URL de l'API où sera envoyer le rapport. Laisser vide pour remettre par défaut." msgstr "C'est L'URL de l'API où sera envoyé le rapport. Laisser vide pour remettre par défaut."
msgid "Clear reports cache directory" msgid "Clear reports cache directory"
msgstr "Nettoyer le répertoire de cache des rapports." msgstr "Nettoyer le répertoire de cache des rapports."
@ -44,7 +44,7 @@ msgid "Send report now"
msgstr "Envoyer le rapport maintenant" msgstr "Envoyer le rapport maintenant"
msgid "This sent report for current blog even if report exists in cache." msgid "This sent report for current blog even if report exists in cache."
msgstr "Ceci envoie le rapport pou r le blog courant même si un rapport existe en cache." msgstr "Ceci envoie le rapport pour le blog courant même si un rapport existe en cache."
msgid "Report that will be sent for this blog:" msgid "Report that will be sent for this blog:"
msgstr "Rapport qui sera envoyé pour ce blog :" msgstr "Rapport qui sera envoyé pour ce blog :"

View File

@ -78,7 +78,12 @@ class Config extends Process
if (!empty($_POST['send_report'])) { if (!empty($_POST['send_report'])) {
Utils::sendReport(true); Utils::sendReport(true);
Notices::AddSuccessNotice(__('Report sent.')); $error = Utils::getError();
if (!empty($error)) {
Notices::AddWarningNotice($error);
} else {
Notices::AddSuccessNotice(__('Report sent.'));
}
} }
dcCore::app()->admin->url->redirect('admin.plugins', ['module' => My::id(), 'conf' => '1']); dcCore::app()->admin->url->redirect('admin.plugins', ['module' => My::id(), 'conf' => '1']);

View File

@ -15,11 +15,11 @@ declare(strict_types=1);
namespace Dotclear\Plugin\DotclearWatch; namespace Dotclear\Plugin\DotclearWatch;
use dcCore; use dcCore;
use dcLog;
use dcModuleDefine; use dcModuleDefine;
use dcThemes; use dcThemes;
use Dotclear\Helper\Crypt; use Dotclear\Helper\Crypt;
use Dotclear\Helper\File\Files; use Dotclear\Helper\Date;
use Dotclear\Helper\File\Path;
use Dotclear\Helper\Network\HttpClient; use Dotclear\Helper\Network\HttpClient;
use Exception; use Exception;
@ -32,7 +32,7 @@ class Utils
public const DISTANT_API_URL = 'https://dotclear.watch/api'; public const DISTANT_API_URL = 'https://dotclear.watch/api';
/** @var string The distant API version */ /** @var string The distant API version */
public const DISTANT_API_VERSION = '1.0'; public const DISTANT_API_VERSION = '1.1';
/** @var array<int,string> The hiddens modules IDs */ /** @var array<int,string> The hiddens modules IDs */
private static array $hiddens = []; private static array $hiddens = [];
@ -45,8 +45,14 @@ class Utils
*/ */
public static function addMark(): void public static function addMark(): void
{ {
if (My::settings()->get('distant_api_url')) { if (My::settings()->getGlobal('distant_api_url')) {
echo '<p>' . __('/!\ Tracked by dotclear.watch') . '</p>'; echo sprintf(
'<ul><li><a href="%s" title="%s" class="outgoing">%s<img src="%s" /></a></ul></li>',
'https://dotclear.watch/statistics',
__('DotclearWatch plugin statistics'),
__('Tracked by dotclear.watch'),
My::fileURL('icon.svg')
);
} }
} }
@ -138,6 +144,18 @@ class Utils
return self::check() ? self::uid() : ''; return self::check() ? self::uid() : '';
} }
/**
* Get request error.
*/
public static function getError(): string
{
$rs = dcCore::app()->log->getLogs([
'log_table' => My::id() . '_error',
]);
return $rs->isEmpty() || !is_string($rs->f('log_msg')) ? '' : $rs->f('log_msg');
}
/** /**
* Clear cache directory. * Clear cache directory.
*/ */
@ -162,31 +180,46 @@ class Utils
return; return;
} }
$file = self::file(); if (!$force && !self::expired()) {
if (!$force && !self::expired($file)) {
return; return;
} }
$contents = self::contents(); $contents = self::contents();
self::write($file, $contents); self::write($contents);
try { $status = 500;
$rsp = HttpClient::quickPost(sprintf(self::url(), 'report'), ['key' => self::key(), 'report' => $contents]); $response = '';
if ($rsp !== 'ok') { $url = sprintf(self::url(), 'report');
throw new Exception('bad API response'); $path = '';
} if ($client = HttpClient::initClient($url, $path)) {
} catch (Exception $e) { try {
if ($force) { $client->setUserAgent('Dotclear.watch ' . My::id() . '/' . self::DISTANT_API_VERSION);
dcCore::app()->error->add(__('Dotclear.watch report failed')); $client->useGzip(false);
$client->setPersistReferers(false);
$client->post($path, ['key' => self::key(), 'report' => $contents]);
$status = $client->getStatus();
$response = $client->getContent();
unset($client);
if ($status != 202) {
self::error((string) '(' . $status . ') ' . $response);
}
return;
} catch (Exception $e) {
unset($client);
} }
} }
if ($force) {
self::error('Dotclear.watch report failed');
}
} }
private static function check(): bool private static function check(): bool
{ {
return defined('DC_CRYPT_ALGO') && defined('DC_TPL_CACHE') && is_dir(DC_TPL_CACHE) && is_writable(DC_TPL_CACHE); return defined('DC_CRYPT_ALGO');
} }
private static function key(): string private static function key(): string
@ -212,79 +245,102 @@ class Utils
return md5(self::uid() . dcCore::app()->blog->uid); return md5(self::uid() . dcCore::app()->blog->uid);
} }
private static function url() private static function url(): string
{ {
$api_url = My::settings()->getGlobal('distant_api_url'); $api_url = My::settings()->getGlobal('distant_api_url');
return (is_string($api_url) ? $api_url : self::DISTANT_API_URL) . '/' . self::DISTANT_API_VERSION . '/%s/' . self::uid(); return (is_string($api_url) ? $api_url : self::DISTANT_API_URL) . '/' . self::DISTANT_API_VERSION . '/%s/' . self::uid();
} }
private static function file(): string
{
$file = self::buid();
return sprintf(
'%s/%s/%s/%s/%s.json',
(string) Path::real(DC_TPL_CACHE),
My::id(),
substr($file, 0, 2),
substr($file, 2, 2),
$file
);
}
private static function clear(): void private static function clear(): void
{ {
$path = (string) Path::real(DC_TPL_CACHE) . DIRECTORY_SEPARATOR . My::id(); $rs = dcCore::app()->log->getLogs([
if (is_dir($path)) { 'log_table' => [
Files::delTree($path); My::id() . '_report',
My::id() . '_error',
],
]);
if ($rs->isEmpty()) {
return;
} }
$logs = [];
while ($rs->fetch()) {
$logs[] = (int) $rs->f('log_id');
}
dcCore::app()->log->delLogs($logs);
} }
private static function write(string $file, string $contents): void private static function error(string $message): void
{ {
$dir = dirname($file); self::clear();
if (!is_dir($dir)) {
Files::makeDir($dir, true); $cur = dcCore::app()->con->openCursor(dcCore::app()->prefix . dcLog::LOG_TABLE_NAME);
} $cur->setField('log_table', My::id() . '_error');
file_put_contents($file, $contents); $cur->setField('log_msg', $message);
dcCore::app()->log->addLog($cur);
} }
private static function read(string $file): string private static function write(string $contents): void
{ {
return is_file($file) && is_readable($file) ? (string) file_get_contents($file) : ''; self::clear();
$cur = dcCore::app()->con->openCursor(dcCore::app()->prefix . dcLog::LOG_TABLE_NAME);
$cur->setField('log_table', My::id() . '_report');
$cur->setField('log_msg', $contents);
dcCore::app()->log->addLog($cur);
} }
private static function expired(string $file): bool private static function read(): string
{ {
if (!is_file($file) || !is_readable($file) || ($time = filemtime($file)) === false) { $rs = dcCore::app()->log->getLogs([
return true; 'log_table' => My::id() . '_report',
} ]);
$time = date('U', $time); return $rs->isEmpty() || !is_string($rs->f('log_msg')) ? '' : $rs->f('log_msg');
if (!is_numeric($time) || (int) $time + self::EXPIRED_DELAY < time()) { }
return true;
}
return false; private static function expired(): bool
{
$rs = dcCore::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();
} }
private static function contents(): string private static function contents(): string
{ {
// Build json response // Build json response
return (string) json_encode([ return (string) json_encode([
'uid' => self::uid(), 'uid' => self::uid(),
'buid' => self::buid(), 'buid' => self::buid(),
'plugin' => self::getPlugins(), // enabled plugins 'plugins' => self::getPlugins(), // enabled plugins
'theme' => self::getThemes(), // enabled themes 'themes' => self::getThemes(), // enabled themes
'server' => [ 'blog' => [
'blogs_count' => (int) dcCore::app()->getBlogs([], true)->f(0), 'lang' => (string) dcCore::app()->blog->settings->get('system')->get('lang'),
'core' => DC_VERSION, 'theme' => (string) dcCore::app()->blog->settings->get('system')->get('theme'),
'php' => phpversion(), ],
'thm' => (string) dcCore::app()->blog->settings->get('system')->get('theme'), // selected theme 'blogs' => [
'count' => (int) dcCore::app()->getBlogs([], true)->f(0),
],
'core' => [
'version' => DC_VERSION,
],
'php' => [
'sapi' => php_sapi_name() ?: 'php',
'version' => phpversion(),
],
'system' => [
'name' => php_uname('s'),
'version' => php_uname('r'),
], ],
'database' => [ 'database' => [
dcCore::app()->con->driver() => dcCore::app()->con->version(), 'driver' => dcCore::app()->con->driver(),
'version' => dcCore::app()->con->version(),
], ],
], JSON_PRETTY_PRINT); ], JSON_PRETTY_PRINT);
} }