Compare commits

...

10 Commits

6 changed files with 132 additions and 71 deletions

View File

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

View File

@ -2,10 +2,10 @@
<modules xmlns:da="http://dotaddict.org/da/">
<module id="DotclearWatch">
<name>Dotclear Watch</name>
<version>0.3</version>
<version>0.5</version>
<author>Jean-Christian Denis and contributors</author>
<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:details>http://plugins.dotaddict.org/dc2/details/DotclearWatch</da:details>
<da:support>https://github.com/JcDenis/DotclearWatch/issues</da:support>

View File

@ -18,7 +18,7 @@ L10n::$locales['Settings are globals. Reports are by blog.']
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['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['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';

View File

@ -32,7 +32,7 @@ msgid "Distant API URL:"
msgstr "URL de l'API distante :"
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"
msgstr "Nettoyer le répertoire de cache des rapports."

View File

@ -78,8 +78,13 @@ class Config extends Process
if (!empty($_POST['send_report'])) {
Utils::sendReport(true);
$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']);

View File

@ -15,11 +15,11 @@ declare(strict_types=1);
namespace Dotclear\Plugin\DotclearWatch;
use dcCore;
use dcLog;
use dcModuleDefine;
use dcThemes;
use Dotclear\Helper\Crypt;
use Dotclear\Helper\File\Files;
use Dotclear\Helper\File\Path;
use Dotclear\Helper\Date;
use Dotclear\Helper\Network\HttpClient;
use Exception;
@ -32,7 +32,7 @@ class Utils
public const DISTANT_API_URL = 'https://dotclear.watch/api';
/** @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 */
private static array $hiddens = [];
@ -45,8 +45,14 @@ class Utils
*/
public static function addMark(): void
{
if (My::settings()->get('distant_api_url')) {
echo '<p>' . __('/!\ Tracked by dotclear.watch') . '</p>';
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://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() : '';
}
/**
* 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.
*/
@ -162,31 +180,46 @@ class Utils
return;
}
$file = self::file();
if (!$force && !self::expired($file)) {
if (!$force && !self::expired()) {
return;
}
$contents = self::contents();
self::write($file, $contents);
self::write($contents);
$status = 500;
$response = '';
$url = sprintf(self::url(), 'report');
$path = '';
if ($client = HttpClient::initClient($url, $path)) {
try {
$rsp = HttpClient::quickPost(sprintf(self::url(), 'report'), ['key' => self::key(), 'report' => $contents]);
if ($rsp !== 'ok') {
throw new Exception('bad API response');
$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 = $client->getStatus();
$response = $client->getContent();
unset($client);
if ($status != 202) {
self::error((string) '(' . $status . ') ' . $response);
}
return;
} catch (Exception $e) {
if ($force) {
dcCore::app()->error->add(__('Dotclear.watch report failed'));
unset($client);
}
}
if ($force) {
self::error('Dotclear.watch report failed');
}
}
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
@ -212,61 +245,71 @@ class Utils
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');
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
{
$path = (string) Path::real(DC_TPL_CACHE) . DIRECTORY_SEPARATOR . My::id();
if (is_dir($path)) {
Files::delTree($path);
}
$rs = dcCore::app()->log->getLogs([
'log_table' => [
My::id() . '_report',
My::id() . '_error',
],
]);
if ($rs->isEmpty()) {
return;
}
private static function write(string $file, string $contents): void
$logs = [];
while ($rs->fetch()) {
$logs[] = (int) $rs->f('log_id');
}
dcCore::app()->log->delLogs($logs);
}
private static function error(string $message): void
{
$dir = dirname($file);
if (!is_dir($dir)) {
Files::makeDir($dir, true);
}
file_put_contents($file, $contents);
self::clear();
$cur = dcCore::app()->con->openCursor(dcCore::app()->prefix . dcLog::LOG_TABLE_NAME);
$cur->setField('log_table', My::id() . '_error');
$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) {
return true;
$rs = dcCore::app()->log->getLogs([
'log_table' => My::id() . '_report',
]);
return $rs->isEmpty() || !is_string($rs->f('log_msg')) ? '' : $rs->f('log_msg');
}
$time = date('U', $time);
if (!is_numeric($time) || (int) $time + self::EXPIRED_DELAY < time()) {
return true;
}
private static function expired(): bool
{
$rs = dcCore::app()->log->getLogs([
'log_table' => My::id() . '_report',
]);
return false;
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
@ -275,16 +318,29 @@ class Utils
return (string) json_encode([
'uid' => self::uid(),
'buid' => self::buid(),
'plugin' => self::getPlugins(), // enabled plugins
'theme' => self::getThemes(), // enabled themes
'server' => [
'blogs_count' => (int) dcCore::app()->getBlogs([], true)->f(0),
'core' => DC_VERSION,
'php' => phpversion(),
'thm' => (string) dcCore::app()->blog->settings->get('system')->get('theme'), // selected theme
'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'),
],
'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' => [
dcCore::app()->con->driver() => dcCore::app()->con->version(),
'driver' => dcCore::app()->con->driver(),
'version' => dcCore::app()->con->version(),
],
], JSON_PRETTY_PRINT);
}