Skip to content

Commit 4670d24

Browse files
Merge pull request #386 from JLG-WOCFR-DEV/codex/implement-common-helper-for-scan-history
Log scan resets and expose reset hooks
2 parents cb433c5 + 5ffd31e commit 4670d24

File tree

4 files changed

+215
-4
lines changed

4 files changed

+215
-4
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,24 @@ add_action('blc_link_mass_update_performed', function (array $context) {
162162
});
163163
```
164164

165+
### `blc_link_scan_status_reset` et `blc_image_scan_status_reset`
166+
Ces actions sont déclenchées lorsqu’un administrateur remet à zéro l’état du scanner (liens ou images). Une entrée est automatiquement ajoutée dans l’historique persistant (`blc_link_scan_history` / `blc_image_scan_history`) avec le motif `reset`, l’horodatage et l’utilisateur à l’origine de l’action. Les tableaux de bord peuvent ainsi suivre les remises à zéro manuelles, corréler l’évènement avec une reprise de scan et notifier les équipes de support.
167+
168+
```php
169+
add_action('blc_link_scan_status_reset', function (array $historyEntry, array $context) {
170+
error_log(sprintf(
171+
'[BLC] Réinitialisation du scan liens (%s) par %s',
172+
gmdate('c', $historyEntry['timestamp']),
173+
$historyEntry['user']['display_name'] ?: $historyEntry['user']['login']
174+
));
175+
});
176+
177+
add_action('blc_image_scan_status_reset', function (array $historyEntry) {
178+
// Exemple : pousser l’évènement dans un tableau de bord interne.
179+
my_dashboard()->pushEvent('blc_image_reset', $historyEntry);
180+
});
181+
```
182+
165183
## Pistes d'amélioration
166184

167185
- **Intégration de rapports automatisés** : proposer un export planifié (PDF/CSV ou envoi vers Google Sheets) afin de partager les résultats des scans avec des équipes éditoriales sans accès à l’administration.

docs/roadmap-ameliorations.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ Cette feuille de route décline les pistes d'amélioration listées dans le `REA
5252
1. Créer un composant `DashboardSummary` réutilisable avec données issues de `blc_get_link_scan_status_payload()`.
5353
2. Ajouter des tests d'accessibilité automatisés (axe-core) et corriger les problèmes détectés.
5454
3. Documenter une charte UX (taille des cibles tactiles, contrastes, animations).
55+
4. Aligner la timeline sur les événements `blc_link_scan_status_reset` / `blc_image_scan_status_reset` afin d'afficher les remises à zéro manuelles directement dans les dashboards.
5556

5657
> ✅ Le composant `DashboardSummary` affiche désormais l’état du scan, la progression et le rythme d’analyse à partir de `blc_get_link_scan_status_payload()` et se met à jour en temps réel, avec un habillage visuel dédié. 【F:liens-morts-detector-jlg/includes/blc-admin-pages.php†L688-L780】【F:liens-morts-detector-jlg/assets/js/blc-admin-scripts.js†L2554-L3726】【F:liens-morts-detector-jlg/assets/css/blc-admin-styles.css†L120-L236】
5758

liens-morts-detector-jlg/includes/blc-scanner.php

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,11 +1119,24 @@ function blc_update_link_scan_status(array $changes) {
11191119
/**
11201120
* Clear the stored link scan status.
11211121
*
1122+
* @param array<string, mixed>|mixed $context Optional additional context forwarded to observers.
1123+
*
11221124
* @return void
11231125
*/
1124-
function blc_reset_link_scan_status() {
1126+
function blc_reset_link_scan_status($context = []) {
1127+
$context = is_array($context) ? $context : [];
1128+
1129+
$history_entry = null;
1130+
if (function_exists('blc_record_scan_reset')) {
1131+
$history_entry = blc_record_scan_reset('link', $context);
1132+
}
1133+
11251134
delete_option('blc_link_scan_status');
11261135
blc_link_scan_status_cache(null, true);
1136+
1137+
if (function_exists('do_action')) {
1138+
do_action('blc_link_scan_status_reset', $history_entry, $context);
1139+
}
11271140
}
11281141
}
11291142

@@ -1315,6 +1328,82 @@ function blc_update_image_scan_history_entry($job_id, array $changes) {
13151328
}
13161329
}
13171330

1331+
if (!function_exists('blc_record_scan_reset')) {
1332+
/**
1333+
* Record a scan reset event in the persisted history.
1334+
*
1335+
* @param string $dataset_type Either "link" or "image".
1336+
* @param array<string, mixed> $context Additional metadata about the reset trigger.
1337+
*
1338+
* @return array<string, mixed>|null The recorded history entry when successful, null otherwise.
1339+
*/
1340+
function blc_record_scan_reset($dataset_type, array $context = []) {
1341+
$dataset_type = is_string($dataset_type) ? strtolower($dataset_type) : '';
1342+
1343+
if ($dataset_type !== '' && function_exists('sanitize_key')) {
1344+
$dataset_type = sanitize_key($dataset_type);
1345+
}
1346+
1347+
$appenders = [
1348+
'link' => 'blc_append_link_scan_history_entry',
1349+
'image' => 'blc_append_image_scan_history_entry',
1350+
];
1351+
1352+
if ($dataset_type === '' || !isset($appenders[$dataset_type])) {
1353+
return null;
1354+
}
1355+
1356+
$user = [
1357+
'id' => 0,
1358+
'login' => '',
1359+
'display_name' => '',
1360+
];
1361+
1362+
if (function_exists('wp_get_current_user')) {
1363+
$current_user = wp_get_current_user();
1364+
1365+
if (is_object($current_user)) {
1366+
if (isset($current_user->ID)) {
1367+
$user['id'] = (int) $current_user->ID;
1368+
} elseif (function_exists('get_current_user_id')) {
1369+
$user['id'] = (int) get_current_user_id();
1370+
}
1371+
1372+
if (isset($current_user->user_login)) {
1373+
$user['login'] = (string) $current_user->user_login;
1374+
}
1375+
1376+
if (isset($current_user->display_name)) {
1377+
$user['display_name'] = (string) $current_user->display_name;
1378+
} elseif (isset($current_user->user_nicename)) {
1379+
$user['display_name'] = (string) $current_user->user_nicename;
1380+
}
1381+
}
1382+
} elseif (function_exists('get_current_user_id')) {
1383+
$user['id'] = (int) get_current_user_id();
1384+
}
1385+
1386+
$history_entry = [
1387+
'event' => 'reset',
1388+
'reason' => 'reset',
1389+
'dataset_type' => $dataset_type,
1390+
'timestamp' => time(),
1391+
'user' => $user,
1392+
'context' => $context,
1393+
];
1394+
1395+
$appender = $appenders[$dataset_type];
1396+
1397+
if (is_callable($appender)) {
1398+
call_user_func($appender, $history_entry);
1399+
1400+
return $history_entry;
1401+
}
1402+
1403+
return null;
1404+
}
1405+
}
1406+
13181407
if (!function_exists('blc_get_image_scan_transition_rules')) {
13191408
/**
13201409
* Describe the allowed state transitions for image scans.
@@ -1618,11 +1707,24 @@ function blc_update_image_scan_status(array $changes) {
16181707
/**
16191708
* Clear the stored image scan status.
16201709
*
1710+
* @param array<string, mixed>|mixed $context Optional additional context forwarded to observers.
1711+
*
16211712
* @return void
16221713
*/
1623-
function blc_reset_image_scan_status() {
1714+
function blc_reset_image_scan_status($context = []) {
1715+
$context = is_array($context) ? $context : [];
1716+
1717+
$history_entry = null;
1718+
if (function_exists('blc_record_scan_reset')) {
1719+
$history_entry = blc_record_scan_reset('image', $context);
1720+
}
1721+
16241722
delete_option('blc_image_scan_status');
16251723
blc_image_scan_status_cache(null, true);
1724+
1725+
if (function_exists('do_action')) {
1726+
do_action('blc_image_scan_status_reset', $history_entry, $context);
1727+
}
16261728
}
16271729
}
16281730

tests/LinkScanStatusTest.php

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ protected function setUp(): void
4141
Functions\when('apply_filters')->alias(static fn($hook, $value) => $value);
4242
Functions\when('do_action')->justReturn(null);
4343
Functions\when('rest_ensure_response')->alias(static fn($value) => $value);
44+
Functions\when('wp_get_current_user')->alias(static function () {
45+
return (object) [
46+
'ID' => 0,
47+
'user_login' => '',
48+
'display_name' => '',
49+
];
50+
});
4451

4552
$testCase = $this;
4653
Functions\when('get_option')->alias(function ($name, $default = false) use ($testCase) {
@@ -67,6 +74,8 @@ protected function setUp(): void
6774
if (function_exists('\\blc_reset_image_scan_status')) {
6875
\blc_reset_image_scan_status();
6976
}
77+
78+
$this->getOptionCalls = 0;
7079
}
7180

7281
protected function tearDown(): void
@@ -449,13 +458,94 @@ public function test_get_link_scan_status_payload_includes_metrics(): void
449458
$this->assertArrayHasKey('lock_active', $payload);
450459
}
451460

452-
public function test_reset_link_scan_status_deletes_option(): void
461+
public function test_reset_link_scan_status_records_history_and_fires_hook(): void
453462
{
454463
$this->options['blc_link_scan_status'] = ['state' => 'completed'];
455464

456-
\blc_reset_link_scan_status();
465+
Functions\when('time')->justReturn(1_700_000_000);
466+
Functions\when('wp_get_current_user')->alias(static function () {
467+
return (object) [
468+
'ID' => 7,
469+
'user_login' => 'auditor',
470+
'display_name' => 'Audit Bot',
471+
];
472+
});
473+
474+
$fired_actions = [];
475+
Functions\when('do_action')->alias(static function ($hook, ...$arguments) use (&$fired_actions) {
476+
$fired_actions[] = [$hook, $arguments];
477+
});
478+
479+
$context = ['triggered_by' => 'unit-test'];
480+
481+
\blc_reset_link_scan_status($context);
457482

458483
$this->assertArrayNotHasKey('blc_link_scan_status', $this->options);
484+
485+
$history = $this->options['blc_link_scan_history'] ?? [];
486+
$this->assertNotEmpty($history);
487+
$entry = $history[0];
488+
489+
$this->assertSame('reset', $entry['event']);
490+
$this->assertSame('reset', $entry['reason']);
491+
$this->assertSame('link', $entry['dataset_type']);
492+
$this->assertSame(1_700_000_000, $entry['timestamp']);
493+
$this->assertSame($context, $entry['context']);
494+
$this->assertSame([
495+
'id' => 7,
496+
'login' => 'auditor',
497+
'display_name' => 'Audit Bot',
498+
], $entry['user']);
499+
500+
$this->assertNotEmpty($fired_actions);
501+
$this->assertSame('blc_link_scan_status_reset', $fired_actions[0][0]);
502+
$this->assertSame($entry, $fired_actions[0][1][0]);
503+
$this->assertSame($context, $fired_actions[0][1][1]);
504+
}
505+
506+
public function test_reset_image_scan_status_records_history_and_fires_hook(): void
507+
{
508+
$this->options['blc_image_scan_status'] = ['state' => 'running'];
509+
510+
Functions\when('time')->justReturn(1_700_000_500);
511+
Functions\when('wp_get_current_user')->alias(static function () {
512+
return (object) [
513+
'ID' => 42,
514+
'user_login' => 'media-admin',
515+
'display_name' => 'Media Admin',
516+
];
517+
});
518+
519+
$fired_actions = [];
520+
Functions\when('do_action')->alias(static function ($hook, ...$arguments) use (&$fired_actions) {
521+
$fired_actions[] = [$hook, $arguments];
522+
});
523+
524+
$context = ['triggered_by' => 'dashboard'];
525+
526+
\blc_reset_image_scan_status($context);
527+
528+
$this->assertArrayNotHasKey('blc_image_scan_status', $this->options);
529+
530+
$history = $this->options['blc_image_scan_history'] ?? [];
531+
$this->assertNotEmpty($history);
532+
$entry = $history[0];
533+
534+
$this->assertSame('reset', $entry['event']);
535+
$this->assertSame('reset', $entry['reason']);
536+
$this->assertSame('image', $entry['dataset_type']);
537+
$this->assertSame(1_700_000_500, $entry['timestamp']);
538+
$this->assertSame($context, $entry['context']);
539+
$this->assertSame([
540+
'id' => 42,
541+
'login' => 'media-admin',
542+
'display_name' => 'Media Admin',
543+
], $entry['user']);
544+
545+
$this->assertNotEmpty($fired_actions);
546+
$this->assertSame('blc_image_scan_status_reset', $fired_actions[0][0]);
547+
$this->assertSame($entry, $fired_actions[0][1][0]);
548+
$this->assertSame($context, $fired_actions[0][1][1]);
459549
}
460550
}
461551

0 commit comments

Comments
 (0)