Skip to content

Commit bbd2552

Browse files
Merge pull request #378 from JLG-WOCFR-DEV/codex/continue-developpement-des-priorites-et-analyse
Enrichir la file d'actions prioritaires et recenser les manques
2 parents e30ee96 + 6b938fa commit bbd2552

File tree

3 files changed

+417
-14
lines changed

3 files changed

+417
-14
lines changed

docs/priorites-manquantes.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# État des priorités restantes
2+
3+
## Manques face à la feuille de route interne
4+
- **Notifications multicanales** : le moteur mutualisé et les connecteurs Slack/Teams/Mattermost décrits dans la roadmap n’ont pas encore été implémentés.
5+
- **Optimisations d’interface** : les tests d’accessibilité automatisés (axe-core) et la documentation de la charte UX restent à livrer malgré la création du composant `DashboardSummary`.
6+
- **Renforcement de la qualité logicielle** : les scénarios Playwright et la chaîne CI complète (lint, packaging) restent planifiés mais absents du code.
7+
- **Surveillance proactive** : la gestion de seuils configurables, l’escalade multicanale et les visualisations sparkline doivent encore être conçues.
8+
9+
## Écarts persistants avec les suites professionnelles
10+
- **Supervision temps réel** : l’interface n’expose toujours pas l’historique et les métriques pourtant stockés côté base, contrairement aux consoles pro.
11+
- **Scalabilité horizontale** : aucune file distribuée (Redis/SQS) ni worker externe ne complète encore WP-Cron pour les catalogues volumineux.
12+
- **Résilience réseau avancée** : la rotation de proxys/IP et les stratégies multi-sorties restent à ajouter pour égaler les solutions premium.
13+
- **Workflows collaboratifs** : l’assignation, la journalisation fine des corrections et le partage de vues enregistrées ne sont pas encore disponibles.
14+
- **Connecteurs prêts à l’emploi** : l’extension ne propose pas d’intégrations Slack/Jira/ServiceNow ni de documentation REST industrialisée comme les concurrents.

liens-morts-detector-jlg/includes/blc-admin-pages.php

Lines changed: 254 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,75 @@ function blc_get_summary_placeholder() {
10381038
}
10391039
}
10401040

1041+
/**
1042+
* Build a dashboard URL with the provided query arguments, handling fallbacks when WordPress helpers are unavailable.
1043+
*
1044+
* @param string $base_url Base dashboard URL.
1045+
* @param array<string,mixed> $args Query arguments.
1046+
*
1047+
* @return string
1048+
*/
1049+
function blc_build_dashboard_filtered_url($base_url, array $args) {
1050+
$base_url = (string) $base_url;
1051+
1052+
if ($args === array()) {
1053+
return $base_url;
1054+
}
1055+
1056+
if (function_exists('add_query_arg')) {
1057+
return add_query_arg($args, $base_url);
1058+
}
1059+
1060+
$separator = (false === strpos($base_url, '?')) ? '?' : '&';
1061+
$query = http_build_query($args, '', '&', PHP_QUERY_RFC3986);
1062+
1063+
return $base_url . $separator . $query;
1064+
}
1065+
1066+
/**
1067+
* Identify the most impacted domain for each severity class to guide priority recommendations.
1068+
*
1069+
* @param array<int,array<string,int|string>> $domains Domain aggregates.
1070+
*
1071+
* @return array{
1072+
* server:array{host:string,count:int}|null,
1073+
* client:array{host:string,count:int}|null,
1074+
* redirect:array{host:string,count:int}|null
1075+
* }
1076+
*/
1077+
function blc_identify_priority_focus_domains(array $domains) {
1078+
$focus = array(
1079+
'server' => null,
1080+
'client' => null,
1081+
'redirect' => null,
1082+
);
1083+
1084+
foreach ($domains as $domain) {
1085+
if (!is_array($domain) || empty($domain['host'])) {
1086+
continue;
1087+
}
1088+
1089+
$host = (string) $domain['host'];
1090+
1091+
$server_errors = isset($domain['server_errors']) ? max(0, (int) $domain['server_errors']) : 0;
1092+
if ($server_errors > 0 && ($focus['server'] === null || $server_errors > $focus['server']['count'])) {
1093+
$focus['server'] = array('host' => $host, 'count' => $server_errors);
1094+
}
1095+
1096+
$client_errors = isset($domain['client_errors']) ? max(0, (int) $domain['client_errors']) : 0;
1097+
if ($client_errors > 0 && ($focus['client'] === null || $client_errors > $focus['client']['count'])) {
1098+
$focus['client'] = array('host' => $host, 'count' => $client_errors);
1099+
}
1100+
1101+
$redirects = isset($domain['redirects']) ? max(0, (int) $domain['redirects']) : 0;
1102+
if ($redirects > 0 && ($focus['redirect'] === null || $redirects > $focus['redirect']['count'])) {
1103+
$focus['redirect'] = array('host' => $host, 'count' => $redirects);
1104+
}
1105+
}
1106+
1107+
return $focus;
1108+
}
1109+
10411110
if (!function_exists('blc_format_summary_interval_label')) {
10421111
/**
10431112
* Format a time interval into a human readable unit (seconds, minutes, hours, days, weeks).
@@ -1428,17 +1497,193 @@ function blc_build_dashboard_summary_items(array $scan_status, $scan_details_lin
14281497
* }>
14291498
*/
14301499
function blc_build_priority_action_queue(array $top_domains, $dashboard_base_url, array $count_keys) {
1431-
if ($top_domains === array()) {
1432-
return array();
1433-
}
1434-
14351500
$actions = array();
14361501
$total_active = isset($count_keys['active_count']) ? max(0, (int) $count_keys['active_count']) : 0;
14371502
$total_active = max(1, $total_active);
14381503

1504+
$server_error_total = isset($count_keys['server_error_count']) ? max(0, (int) $count_keys['server_error_count']) : 0;
1505+
$client_error_total = isset($count_keys['not_found_count']) ? max(0, (int) $count_keys['not_found_count']) : 0;
1506+
$redirect_total = isset($count_keys['redirect_count']) ? max(0, (int) $count_keys['redirect_count']) : 0;
1507+
$needs_recheck_total = isset($count_keys['needs_recheck_count']) ? max(0, (int) $count_keys['needs_recheck_count']) : 0;
1508+
1509+
$max_actions = 5;
1510+
if (function_exists('apply_filters')) {
1511+
$max_actions = (int) apply_filters('blc_priority_actions_max_count', $max_actions);
1512+
}
1513+
if ($max_actions <= 0) {
1514+
$max_actions = 5;
1515+
}
1516+
1517+
$focus_domains = blc_identify_priority_focus_domains($top_domains);
1518+
1519+
$global_specs = array(
1520+
'server' => array(
1521+
'total' => $server_error_total,
1522+
'min_ratio' => 0,
1523+
'min_count' => 1,
1524+
'link_type' => 'status_5xx',
1525+
'severity' => array('class' => 'is-critical', 'label' => __('Critique', 'liens-morts-detector-jlg')),
1526+
'title' => __('Stabiliser les erreurs serveur', 'liens-morts-detector-jlg'),
1527+
'description' => static function($total, $focus = null) {
1528+
if ($focus !== null) {
1529+
return sprintf(
1530+
/* translators: 1: number of server errors, 2: number for the main domain, 3: domain name. */
1531+
__('Résolvez %1$s erreur(s) 5xx restantes — dont %2$s sur %3$s — pour rétablir les pages critiques.', 'liens-morts-detector-jlg'),
1532+
number_format_i18n($total),
1533+
number_format_i18n($focus['count']),
1534+
$focus['host']
1535+
);
1536+
}
1537+
1538+
return sprintf(
1539+
/* translators: %s: number of server errors. */
1540+
__('Résolvez %s erreur(s) 5xx restantes pour rétablir les pages critiques.', 'liens-morts-detector-jlg'),
1541+
number_format_i18n($total)
1542+
);
1543+
},
1544+
),
1545+
'client' => array(
1546+
'total' => $client_error_total,
1547+
'min_ratio' => 10,
1548+
'min_count' => 5,
1549+
'link_type' => 'status_404_410',
1550+
'severity' => array('class' => 'is-high', 'label' => __('Prioritaire', 'liens-morts-detector-jlg')),
1551+
'title' => __('Résorber les erreurs 4xx récurrentes', 'liens-morts-detector-jlg'),
1552+
'description' => static function($total, $focus = null) {
1553+
if ($focus !== null) {
1554+
return sprintf(
1555+
/* translators: 1: number of client errors, 2: number for the main domain, 3: domain name. */
1556+
__('Réparez %1$s lien(s) en erreur 4xx — dont %2$s sur %3$s — pour restaurer les contenus attendus ou configurer une redirection.', 'liens-morts-detector-jlg'),
1557+
number_format_i18n($total),
1558+
number_format_i18n($focus['count']),
1559+
$focus['host']
1560+
);
1561+
}
1562+
1563+
return sprintf(
1564+
/* translators: %s: number of client errors. */
1565+
__('Réparez %s lien(s) en erreur 4xx pour restaurer les contenus attendus ou configurer une redirection.', 'liens-morts-detector-jlg'),
1566+
number_format_i18n($total)
1567+
);
1568+
},
1569+
),
1570+
'redirect' => array(
1571+
'total' => $redirect_total,
1572+
'min_ratio' => 15,
1573+
'min_count' => 5,
1574+
'link_type' => 'status_redirects',
1575+
'severity' => array('class' => 'is-medium', 'label' => __('À optimiser', 'liens-morts-detector-jlg')),
1576+
'title' => __('Optimiser les redirections détectées', 'liens-morts-detector-jlg'),
1577+
'description' => static function($total, $focus = null) {
1578+
if ($focus !== null) {
1579+
return sprintf(
1580+
/* translators: 1: number of redirects, 2: number for the main domain, 3: domain name. */
1581+
__('Passez en revue %1$s redirection(s) — dont %2$s sur %3$s — afin de réduire les détours et préserver le référencement.', 'liens-morts-detector-jlg'),
1582+
number_format_i18n($total),
1583+
number_format_i18n($focus['count']),
1584+
$focus['host']
1585+
);
1586+
}
1587+
1588+
return sprintf(
1589+
/* translators: %s: number of redirects. */
1590+
__('Passez en revue %s redirection(s) pour réduire les détours et préserver le référencement.', 'liens-morts-detector-jlg'),
1591+
number_format_i18n($total)
1592+
);
1593+
},
1594+
),
1595+
'recheck' => array(
1596+
'total' => $needs_recheck_total,
1597+
'min_ratio' => 20,
1598+
'min_count' => 5,
1599+
'link_type' => 'needs_recheck',
1600+
'severity' => array('class' => 'is-low', 'label' => __('À surveiller', 'liens-morts-detector-jlg')),
1601+
'title' => __('Relancer les liens à re-tester', 'liens-morts-detector-jlg'),
1602+
'description' => static function($total, $focus = null) {
1603+
return sprintf(
1604+
/* translators: %s: number of links to recheck. */
1605+
__('Planifiez une nouvelle vérification pour %s lien(s) en attente afin de confirmer la résolution ou de détecter les rechutes.', 'liens-morts-detector-jlg'),
1606+
number_format_i18n($total)
1607+
);
1608+
},
1609+
),
1610+
);
1611+
1612+
if (function_exists('apply_filters')) {
1613+
$global_specs = apply_filters('blc_priority_actions_global_specs', $global_specs, $count_keys, $top_domains);
1614+
}
1615+
1616+
foreach ($global_specs as $key => $spec) {
1617+
$total = isset($spec['total']) ? (int) $spec['total'] : 0;
1618+
if ($total <= 0) {
1619+
continue;
1620+
}
1621+
1622+
$percentage = ($total / $total_active) * 100;
1623+
$min_ratio = isset($spec['min_ratio']) ? (float) $spec['min_ratio'] : 0.0;
1624+
$min_count = isset($spec['min_count']) ? (int) $spec['min_count'] : 0;
1625+
1626+
if ($percentage < $min_ratio && $total < $min_count) {
1627+
continue;
1628+
}
1629+
1630+
$focus_key = ($key === 'client') ? 'client' : $key;
1631+
$focus = isset($focus_domains[$focus_key]) ? $focus_domains[$focus_key] : null;
1632+
1633+
$description_callback = isset($spec['description']) && is_callable($spec['description'])
1634+
? $spec['description']
1635+
: static function() {
1636+
return '';
1637+
};
1638+
1639+
if ($focus !== null) {
1640+
$description = $description_callback($total, $focus);
1641+
} else {
1642+
$description = $description_callback($total);
1643+
}
1644+
1645+
$meta_parts = array(
1646+
sprintf(
1647+
/* translators: %s: percentage of active broken links. */
1648+
__('Impact : %s %% des liens cassés actifs.', 'liens-morts-detector-jlg'),
1649+
number_format_i18n(min(100, max(0, $percentage)), 1)
1650+
),
1651+
);
1652+
1653+
if ($focus !== null) {
1654+
$meta_parts[] = sprintf(
1655+
/* translators: %s: domain name. */
1656+
__('Focus : %s', 'liens-morts-detector-jlg'),
1657+
$focus['host']
1658+
);
1659+
}
1660+
1661+
$cta_args = array();
1662+
if (!empty($spec['link_type']) && 'all' !== $spec['link_type']) {
1663+
$cta_args['link_type'] = (string) $spec['link_type'];
1664+
}
1665+
1666+
$actions[] = array(
1667+
'title' => isset($spec['title']) ? (string) $spec['title'] : '',
1668+
'description' => $description,
1669+
'severity_label' => isset($spec['severity']['label']) ? (string) $spec['severity']['label'] : '',
1670+
'severity_class' => isset($spec['severity']['class']) ? (string) $spec['severity']['class'] : 'is-low',
1671+
'cta_url' => blc_build_dashboard_filtered_url($dashboard_base_url, $cta_args),
1672+
'cta_label' => __('Ouvrir la liste filtrée', 'liens-morts-detector-jlg'),
1673+
'meta' => implode(' · ', array_filter($meta_parts)),
1674+
);
1675+
1676+
if (count($actions) >= $max_actions) {
1677+
return array_slice($actions, 0, $max_actions);
1678+
}
1679+
}
1680+
14391681
$domain_slice = array_slice($top_domains, 0, 3);
14401682

14411683
foreach ($domain_slice as $domain) {
1684+
if (count($actions) >= $max_actions) {
1685+
break;
1686+
}
14421687
if (!is_array($domain) || empty($domain['host'])) {
14431688
continue;
14441689
}
@@ -1526,17 +1771,12 @@ function blc_build_priority_action_queue(array $top_domains, $dashboard_base_url
15261771
number_format_i18n($percentage, 1)
15271772
);
15281773

1529-
$cta_url = $dashboard_base_url;
1530-
if ($target_link_type !== 'all' && function_exists('add_query_arg')) {
1531-
$cta_url = add_query_arg('link_type', $target_link_type, $cta_url);
1774+
$cta_args = array('s' => $host);
1775+
if ($target_link_type !== 'all') {
1776+
$cta_args['link_type'] = $target_link_type;
15321777
}
15331778

1534-
if (function_exists('add_query_arg')) {
1535-
$cta_url = add_query_arg('s', $host, $cta_url);
1536-
} else {
1537-
$separator = (false === strpos($cta_url, '?')) ? '?' : '&';
1538-
$cta_url .= $separator . 's=' . rawurlencode($host);
1539-
}
1779+
$cta_url = blc_build_dashboard_filtered_url($dashboard_base_url, $cta_args);
15401780

15411781
$actions[] = array(
15421782
'title' => sprintf(
@@ -1553,7 +1793,7 @@ function blc_build_priority_action_queue(array $top_domains, $dashboard_base_url
15531793
);
15541794
}
15551795

1556-
return $actions;
1796+
return array_slice($actions, 0, $max_actions);
15571797
}
15581798

15591799
/**

0 commit comments

Comments
 (0)