diff --git a/ChangeLog.md b/ChangeLog.md index 80f2897..cbc6132 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,33 @@ # Changelog NetDiag +## 1.1.3 — 2026-08-16 + +### Internet-Kette im PDF + ein Zeichenfehler, der jedes Protokoll betraf + +- **`netdiagPdfKette()`** für das neue App-Werkzeug „Internet-Kette": Fehlerglied + als eigene hervorgehobene Zeile, darunter die vier geprüften Stufen einzeln, + dann Systemurteil und Hinweis. Ohne eigenen Zweig stünden die Stufen als + `|`-Kette in einer Zelle und das Fehlerglied — die eigentliche Aussage — + mittendrin. +- **`netdiagPdfText()`: Pfeile wurden als „?" gedruckt.** `pdf_getPDFFont()` + liefert hier Helvetica, einen Core-Font mit WinAnsi-Kodierung; alles außerhalb + dieses Zeichenvorrats setzt TCPDF wortlos als „?" (bekannt aus KB #1025). + Aufgefallen an „www.google.de → 142.250.185.67", das im Kundendokument als + „www.google.de ? 142.250.185.67" ankam — der Pfeil ist dort kein Schmuck, + er trennt Frage und Antwort. Die Ersetzung greift jetzt in **allen** + PDF-Zweigen, also auch für Altdaten und künftige Werkzeuge. + Bewusst keine Unicode-Schrift: `dejavusans` würde Schriftbild und Dateigröße + aller Protokolle ändern. Gedankenstrich, Mittelpunkt, Auslassungspunkte und + Anführungszeichen sind in WinAnsi enthalten und bleiben unangetastet + (im erzeugten Dokument nachgeprüft). +- Feld-Whitelist um `fehlerglied`, `stufen`, `systemUrteil`, `validated` und + `captivePortal` erweitert, Werkzeugname `internetkette` → „Internet-Kette". + `validated`/`captivePortal` kommen aus dem LinkInfo-Objekt und erscheinen + damit auch beim IP-Test an der Dose. + +Geprüft an vier Fällen gegen die lokale Testinstanz, zwei davon sind die echten +Emulator-Messungen dieser Nacht (Gutfall und Flugmodus). + ## 1.1.2 — 2026-08-16 ### Geräte-Monitor bekommt einen eigenen PDF-Zweig diff --git a/core/modules/modNetDiag.class.php b/core/modules/modNetDiag.class.php index 82bfdb5..8504442 100644 --- a/core/modules/modNetDiag.class.php +++ b/core/modules/modNetDiag.class.php @@ -67,7 +67,7 @@ class modNetDiag extends DolibarrModules $this->editor_url = ''; $this->editor_squarred_logo = ''; - $this->version = '1.1.2'; + $this->version = '1.1.3'; // Konstanten-Name in llx_const für Modul-Status $this->const_name = 'MAIN_MODULE_'.strtoupper($this->name); diff --git a/lib/netdiag.lib.php b/lib/netdiag.lib.php index afdf782..45b5dde 100644 --- a/lib/netdiag.lib.php +++ b/lib/netdiag.lib.php @@ -345,6 +345,15 @@ function netdiagKundenfelder() 'aussetzer' => array('Aussetzer', ''), 'geraete' => array('Je Gerät', ''), 'ausfaelle' => array('Einzelne Ausfälle', ''), + // Internet-Kette (Anschluss → Gateway → Namensauflösung → HTTPS). + // 'fehlerglied' ist die eigentliche Aussage des Werkzeugs — ohne diesen + // Eintrag stünde im Kundendokument nur die Ampel. + 'fehlerglied' => array('Fehlerglied', ''), + 'stufen' => array('Geprüfte Stufen', ''), + 'systemUrteil' => array('Urteil des Betriebssystems', ''), + // Kommen aus dem LinkInfo-Objekt und damit auch im IP-Test (Dose) vor + 'validated' => array('Internetverbindung bestätigt', ''), + 'captivePortal' => array('Anmeldeseite erkannt', ''), // Netz/DHCP 'server' => array('DHCP-Server', ''), 'lease' => array('Lease-Dauer', 's'), @@ -560,6 +569,7 @@ function netdiagToolName($tool) 'traceroute' => 'Traceroute', 'stresstest' => 'Dauer-/Stresstest', 'iperf' => 'Durchsatz-Test', + 'internetkette' => 'Internet-Kette', 'wifikanal' => 'WLAN-Kanalanalyse', 'monitor' => 'Geräte-Monitor', 'iptest' => 'IP-Test (Dose)', diff --git a/lib/netdiag_pdf.lib.php b/lib/netdiag_pdf.lib.php index 3de9165..6b904be 100644 --- a/lib/netdiag_pdf.lib.php +++ b/lib/netdiag_pdf.lib.php @@ -191,7 +191,7 @@ function netdiagGeneratePdf($db, $protocol, $outputlangs) $params = netdiagFormatParams($m->params); if ($params !== '') { $pdf->SetFont('', 'I', 7); - $pdf->MultiCell(180, 4, $params, 1, 'L'); + $pdf->MultiCell(180, 4, netdiagPdfText($params), 1, 'L'); $pdf->SetFont('', '', 8); } if ($m->tool === 'stresstest') { @@ -200,6 +200,8 @@ function netdiagGeneratePdf($db, $protocol, $outputlangs) netdiagPdfWifiKanal($pdf, $m->result); } elseif ($m->tool === 'monitor') { netdiagPdfMonitor($pdf, $m->result); + } elseif ($m->tool === 'internetkette') { + netdiagPdfKette($pdf, $m->result); } else { $pdf->MultiCell(180, 5, netdiagPdfFlattenResult($m->result), 1, 'L'); } @@ -222,6 +224,48 @@ function netdiagGeneratePdf($db, $protocol, $outputlangs) return 1; } +/** + * Zeichen ersetzen, die der PDF-Standardzeichensatz nicht darstellen kann. + * + * `pdf_getPDFFont()` liefert in dieser Dolibarr-Installation Helvetica — einen + * Core-Font mit WinAnsi-Kodierung, kein Unicode. Alles außerhalb dieses Vorrats + * druckt TCPDF als „?", ohne zu warnen (bekannt aus KB #1025, dort mit dem + * Ankreuzkästchen ☐). + * + * Aufgefallen am Pfeil in der Internet-Kette: aus + * „Namensauflösung: www.google.de → 142.250.185.67" wurde im Kundendokument + * „www.google.de ? 142.250.185.67" — der Pfeil ist dort kein Schmuck, er + * trennt Frage und Antwort. + * + * Bewusst eine Ersetzung statt einer Unicode-Schrift: `dejavusans` würde jedes + * Zeichen können, aber Schriftbild und Dateigröße aller Protokolle ändern. + * Gedankenstrich, Mittelpunkt, Auslassungspunkte und Anführungszeichen sind in + * WinAnsi enthalten und bleiben unangetastet (im Dokument nachgeprüft). + * + * @param string $s Text aus der App oder aus Altdaten + * @return string druckbarer Text + */ +function netdiagPdfText($s) +{ + return strtr((string) $s, array( + '→' => '->', + '←' => '<-', + '↔' => '<->', + '⇒' => '=>', + '✓' => 'ja', + '✔' => 'ja', + '✗' => 'nein', + '✘' => 'nein', + '≤' => '<=', + '≥' => '>=', + '≠' => '!=', + '☐' => '[ ]', + '☑' => '[x]', + '⚠' => '!', + '≈' => 'ca. ', + )); +} + /** * Mess-Ergebnis (JSON) als einzeiligen Text für PDF-Ausgabe. * @@ -235,7 +279,7 @@ function netdiagPdfFlattenResult($json) } $data = json_decode($json, true); if (!is_array($data)) { - return netdiagKundentext((string) $json); + return netdiagPdfText(netdiagKundentext((string) $json)); } $parts = array(); foreach ($data as $key => $val) { @@ -244,7 +288,7 @@ function netdiagPdfFlattenResult($json) $parts[] = $feld; } } - return empty($parts) ? '-' : implode(' | ', $parts); + return empty($parts) ? '-' : netdiagPdfText(implode(' | ', $parts)); } /** @@ -369,7 +413,69 @@ function netdiagPdfStressTest($pdf, $json) $pdf->Cell(180, 5, 'Ausfälle ('.count($ausfaelle).')', 1, 1, 'L'); $pdf->SetFont('', '', 8); foreach ($ausfaelle as $line) { - $pdf->Cell(180, 5, dol_trunc((string) $line, 120), 1, 1, 'L'); + $pdf->Cell(180, 5, dol_trunc(netdiagPdfText($line), 120), 1, 1, 'L'); + } +} + +/** + * Internet-Kette ins PDF schreiben: Fehlerglied zuerst, dann die Stufen. + * + * Ohne eigenen Zweig stünden die vier Stufen als `|`-Kette in einer Zelle und + * das Fehlerglied — die eigentliche Aussage — mittendrin. Beim Kunden wird + * genau diese eine Zeile gelesen: „woran liegt es". + * + * @param TCPDF $pdf PDF-Objekt (Cursor steht direkt hinter der Titelzeile) + * @param string $json result-JSON der Messung + * @return void + */ +function netdiagPdfKette($pdf, $json) +{ + $data = json_decode((string) $json, true); + if (!is_array($data)) { + $pdf->MultiCell(180, 5, netdiagPdfFlattenResult($json), 1, 'L'); + return; + } + + // Fehlerglied als eigene, hervorgehobene Zeile + $glied = isset($data['fehlerglied']) ? trim((string) $data['fehlerglied']) : ''; + if ($glied !== '') { + $pdf->SetFont('', 'B', 8); + $pdf->Cell(180, 5, $glied === 'keines' + ? 'Kein Glied der Kette hat versagt.' + : 'Fehlerglied: '.$glied, 1, 1, 'L'); + $pdf->SetFont('', '', 8); + } + + $stufen = (isset($data['stufen']) && is_array($data['stufen'])) ? $data['stufen'] : array(); + if (!empty($stufen)) { + netdiagPdfEnsureSpace($pdf, 10); + $pdf->SetFont('', 'B', 8); + $pdf->Cell(180, 5, 'Geprüfte Stufen', 1, 1, 'L'); + $pdf->SetFont('', '', 8); + foreach ($stufen as $line) { + netdiagPdfEnsureSpace($pdf, 5, function ($p) { + $p->SetFont('', 'B', 8); + $p->Cell(180, 5, 'Geprüfte Stufen — Fortsetzung', 1, 1, 'L'); + $p->SetFont('', '', 8); + }); + // MultiCell statt Cell: die Stufenzeilen enthalten Fehlertexte des + // Systems („Unable to resolve host …"), die auch mal lang werden. + // Abschneiden würde ausgerechnet die Begründung kappen. + $pdf->MultiCell(180, 5, netdiagPdfText(netdiagKundentext((string) $line)), 1, 'L'); + } + } + + if (!empty($data['systemUrteil'])) { + netdiagPdfEnsureSpace($pdf, 8); + $pdf->SetFont('', 'I', 7); + $pdf->MultiCell(180, 4, netdiagPdfText(netdiagKundentext((string) $data['systemUrteil'])), 1, 'L'); + $pdf->SetFont('', '', 8); + } + if (!empty($data['hinweis'])) { + netdiagPdfEnsureSpace($pdf, 8); + $pdf->SetFont('', 'I', 7); + $pdf->MultiCell(180, 4, netdiagPdfText(netdiagKundentext((string) $data['hinweis'])), 1, 'L'); + $pdf->SetFont('', '', 8); } } @@ -401,7 +507,7 @@ function netdiagPdfMonitor($pdf, $json) return; } if (!empty($data['fehler']) || !empty($data['error'])) { - $pdf->MultiCell(180, 5, netdiagKundentext((string) ($data['fehler'] ?? $data['error'])), 1, 'L'); + $pdf->MultiCell(180, 5, netdiagPdfText(netdiagKundentext((string) ($data['fehler'] ?? $data['error']))), 1, 'L'); return; } @@ -448,7 +554,7 @@ function netdiagPdfMonitor($pdf, $json) $p->Cell(180, 5, 'Überwachte Geräte ('.count($geraete).') — Fortsetzung', 1, 1, 'L'); $p->SetFont('', '', 8); }); - $pdf->Cell(180, 5, dol_trunc((string) $line, 120), 1, 1, 'L'); + $pdf->Cell(180, 5, dol_trunc(netdiagPdfText($line), 120), 1, 1, 'L'); } } @@ -483,7 +589,7 @@ function netdiagPdfMonitor($pdf, $json) $p->Cell(180, 5, 'Einzelne Ausfälle ('.count($ausfaelle).') — Fortsetzung', 1, 1, 'L'); $p->SetFont('', '', 8); }); - $pdf->Cell(180, 5, dol_trunc((string) $line, 120), 1, 1, 'L'); + $pdf->Cell(180, 5, dol_trunc(netdiagPdfText($line), 120), 1, 1, 'L'); $i++; } $rest = count($ausfaelle) - $max; @@ -499,7 +605,7 @@ function netdiagPdfMonitor($pdf, $json) if (!empty($data['hinweis'])) { netdiagPdfEnsureSpace($pdf, 10); $pdf->SetFont('', 'I', 7); - $pdf->MultiCell(180, 4, netdiagKundentext((string) $data['hinweis']), 1, 'L'); + $pdf->MultiCell(180, 4, netdiagPdfText(netdiagKundentext((string) $data['hinweis'])), 1, 'L'); $pdf->SetFont('', '', 8); } } @@ -558,7 +664,7 @@ function netdiagPdfWifiKanal($pdf, $json) if (!empty($data['warnungen']) && is_array($data['warnungen'])) { foreach ($data['warnungen'] as $w) { - $pdf->MultiCell(180, 5, '! '.dol_trunc((string) $w, 160), 1, 'L'); + $pdf->MultiCell(180, 5, '! '.dol_trunc(netdiagPdfText($w), 160), 1, 'L'); } }