- Werkzeug-Klarnamen statt interner IDs: "IP-Scanner — 46 Geräte im Netz …" statt "[netzwerk] ipscan — …". Bewusst eine kurze Zuordnung in netdiagToolName() statt eines zweiten Satzes Sprachschlüssel — die kanonischen Namen stehen in der App, doppelte Pflege wäre eine Fehlerquelle. Enthält auch dhcpcheck/wifiscan: in der App gibt es sie nicht mehr, in der PRODUKTIONSDATENBANK stehen dazu aber noch Messungen (4 bzw. 2). Die Gegenprüfung hielt den Punkt für gegenstandslos, hatte dabei aber nur die Testdatenbank angesehen. - Messparameter anzeigen (Prod-Messung #126): unter jeder Messung steht jetzt "Ziel: 192.168.1.1 · Dauer (s): 300" in Karte und PDF. Vorher stand das Ergebnis ohne Bezugspunkt da — man sah nicht, wogegen gemessen wurde. - Listenseite: Ampel je Protokoll (schlechteste Einzelmessung) plus Anzahl, dazu ein Filter "nur mit Befund". Status 3 "nicht messbar" geht bewusst NICHT ins Maximum ein — er ist keine Aussage über das Kundennetz — sondern wird separat als "n.m." ausgewiesen. Im Browser geprüft: der Filter liefert ausschließlich Protokolle mit Warnung oder Fehler. - N+1-Queries behoben: fetchAllByProtocol() las nur die rowids und setzte je Zeile ein eigenes fetch() ab. Jetzt eine Abfrage mit setVarsFromFetchObj(), zusätzlich mit Entity-Filter (fehlte bisher ganz). Nachgemessen über SHOW SESSION STATUS: 46 Geräte + 7 Messungen brauchen statt 55 Abfragen noch eine. - Standort aus der Kundenadresse vorbelegen, wenn der Techniker nichts eingetragen hat; eine vorhandene Angabe wird nie überschrieben. Über die echte API geprüft. Offen bleibt aus Phase 5 nur der Vergleich zweier Protokolle (Geräte-Diff nach MAC) — eigenes Feature mit eigener Ansicht.
281 lines
11 KiB
PHP
281 lines
11 KiB
PHP
<?php
|
|
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* \file netdiag/netdiagprotocol_card.php
|
|
* \ingroup netdiag
|
|
* \brief Detailansicht eines Diagnose-Protokolls (Backend)
|
|
*/
|
|
|
|
// Dolibarr-Umgebung laden
|
|
$res = 0;
|
|
if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) {
|
|
$res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php";
|
|
}
|
|
$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME'];
|
|
$tmp2 = realpath(__FILE__);
|
|
$i = strlen($tmp) - 1;
|
|
$j = strlen($tmp2) - 1;
|
|
while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) {
|
|
$i--;
|
|
$j--;
|
|
}
|
|
if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) {
|
|
$res = @include substr($tmp, 0, ($i + 1))."/main.inc.php";
|
|
}
|
|
if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) {
|
|
$res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php";
|
|
}
|
|
if (!$res && file_exists("../main.inc.php")) {
|
|
$res = @include "../main.inc.php";
|
|
}
|
|
if (!$res && file_exists("../../main.inc.php")) {
|
|
$res = @include "../../main.inc.php";
|
|
}
|
|
if (!$res) {
|
|
die("Include of main fails");
|
|
}
|
|
|
|
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
|
|
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
|
|
require_once __DIR__.'/class/netdiagprotocol.class.php';
|
|
require_once __DIR__.'/class/netdiagdevice.class.php';
|
|
require_once __DIR__.'/class/netdiagmeasurement.class.php';
|
|
require_once __DIR__.'/lib/netdiag.lib.php';
|
|
require_once __DIR__.'/lib/netdiag_pdf.lib.php';
|
|
|
|
/**
|
|
* @var Conf $conf
|
|
* @var DoliDB $db
|
|
* @var Translate $langs
|
|
* @var User $user
|
|
*/
|
|
|
|
$langs->loadLangs(array("netdiag@netdiag", "companies", "orders", "other"));
|
|
|
|
if (!$user->hasRight('netdiag', 'protocol', 'read')) {
|
|
accessforbidden();
|
|
}
|
|
|
|
$id = GETPOSTINT('id');
|
|
$action = GETPOST('action', 'aZ09');
|
|
|
|
$object = new NetDiagProtocol($db);
|
|
if ($id <= 0 || $object->fetch($id) <= 0) {
|
|
accessforbidden('Protocol not found');
|
|
}
|
|
|
|
// Aktion: PDF generieren
|
|
if ($action == 'builddoc' && $user->hasRight('netdiag', 'protocol', 'read')) {
|
|
$result = netdiagGeneratePdf($db, $object, $langs);
|
|
if ($result > 0) {
|
|
setEventMessages($langs->trans("FileGenerated"), null, 'mesgs');
|
|
} else {
|
|
setEventMessages($object->error, $object->errors, 'errors');
|
|
}
|
|
header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id);
|
|
exit;
|
|
}
|
|
|
|
// Aktion: löschen
|
|
if ($action == 'confirm_delete' && GETPOST('confirm') == 'yes' && $user->hasRight('netdiag', 'protocol', 'delete')) {
|
|
if ($object->delete($user) > 0) {
|
|
setEventMessages($langs->trans("RecordDeleted"), null, 'mesgs');
|
|
header("Location: ".dol_buildpath('/netdiag/netdiagindex.php', 1));
|
|
exit;
|
|
}
|
|
setEventMessages($object->error, $object->errors, 'errors');
|
|
}
|
|
|
|
/*
|
|
* Ansicht
|
|
*/
|
|
|
|
$form = new Form($db);
|
|
llxHeader('', $object->ref, '', '', 0, 0, '', '', '', 'mod-netdiag page-card');
|
|
|
|
$head = netdiagProtocolPrepareHead($object);
|
|
print dol_get_fiche_head($head, 'card', $langs->trans("NetDiagProtocol"), -1, 'fa-network-wired');
|
|
|
|
// Lösch-Bestätigung
|
|
if ($action == 'delete') {
|
|
print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id, $langs->trans("Delete"), $langs->trans("ConfirmDeleteObject"), "confirm_delete", '', '', 1);
|
|
}
|
|
|
|
$linkback = '<a href="'.dol_buildpath('/netdiag/netdiagindex.php', 1).'">'.$langs->trans("BackToList").'</a>';
|
|
dol_banner_tab($object, 'id', $linkback, 1, 'rowid', 'ref');
|
|
|
|
print '<div class="fichecenter">';
|
|
print '<div class="underbanner clearboth"></div>';
|
|
print '<table class="border centpercent tableforfield">';
|
|
|
|
print '<tr><td class="titlefield">'.$langs->trans("Label").'</td><td>'.dol_escape_htmltag($object->label).'</td></tr>';
|
|
|
|
$soc = new Societe($db);
|
|
print '<tr><td>'.$langs->trans("ThirdParty").'</td><td>';
|
|
if ($object->fk_soc > 0 && $soc->fetch($object->fk_soc) > 0) {
|
|
print $soc->getNomUrl(1);
|
|
}
|
|
print '</td></tr>';
|
|
|
|
print '<tr><td>'.$langs->trans("Order").'</td><td>';
|
|
if ($object->fk_commande > 0) {
|
|
$cmd = new Commande($db);
|
|
if ($cmd->fetch($object->fk_commande) > 0) {
|
|
print $cmd->getNomUrl(1);
|
|
}
|
|
}
|
|
print '</td></tr>';
|
|
|
|
print '<tr><td>'.$langs->trans("DateDiag").'</td><td>'.dol_print_date($object->date_diag, 'dayhour').'</td></tr>';
|
|
print '<tr><td>'.$langs->trans("Location").'</td><td>'.dol_escape_htmltag($object->standort).'</td></tr>';
|
|
print '<tr><td>'.$langs->trans("Subnet").'</td><td>'.dol_escape_htmltag($object->subnet).'</td></tr>';
|
|
print '<tr><td>'.$langs->trans("Status").'</td><td>'.$object->getLibStatut(4).'</td></tr>';
|
|
print '<tr><td>'.$langs->trans("Note").'</td><td>'.dol_nl2br(dol_escape_htmltag($object->note)).'</td></tr>';
|
|
|
|
print '</table>';
|
|
print '</div>';
|
|
|
|
print dol_get_fiche_end();
|
|
|
|
// Aktionsknöpfe
|
|
print '<div class="tabsAction">';
|
|
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=builddoc&token='.newToken().'">'.$langs->trans("BuildDoc").'</a>';
|
|
if ($user->hasRight('netdiag', 'protocol', 'delete')) {
|
|
print '<a class="butActionDelete" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=delete&token='.newToken().'">'.$langs->trans("Delete").'</a>';
|
|
}
|
|
print '</div>';
|
|
|
|
// Vorhandene PDF-Dokumente
|
|
$upload_dir = netdiagGetOutputDir().'/'.dol_sanitizeFileName($object->ref);
|
|
if (is_dir($upload_dir)) {
|
|
$files = dol_dir_list($upload_dir, 'files', 0, '\.pdf$');
|
|
if (!empty($files)) {
|
|
print '<br><table class="noborder centpercent"><tr class="liste_titre"><th>'.$langs->trans("Documents").'</th></tr>';
|
|
foreach ($files as $f) {
|
|
$durl = DOL_URL_ROOT.'/document.php?modulepart=netdiag&file='.urlencode(dol_sanitizeFileName($object->ref).'/'.$f['name']);
|
|
print '<tr class="oddeven"><td>'.img_mime($f['name']).' <a href="'.$durl.'">'.dol_escape_htmltag($f['name']).'</a></td></tr>';
|
|
}
|
|
print '</table>';
|
|
}
|
|
}
|
|
|
|
// Geräteliste
|
|
$devObj = new NetDiagDevice($db);
|
|
$devices = $devObj->fetchAllByProtocol($object->id);
|
|
print '<br>';
|
|
print load_fiche_titre($langs->trans("NetDiagDevices").' ('.count($devices).')', '', 'fa-desktop');
|
|
print '<div class="div-table-responsive-no-min"><table class="noborder centpercent">';
|
|
print '<tr class="liste_titre">';
|
|
print '<th>'.$langs->trans("IpAddress").'</th><th>'.$langs->trans("MacAddress").'</th>';
|
|
print '<th>'.$langs->trans("Hostname").'</th><th>'.$langs->trans("NetDiagVendor").'</th><th>'.$langs->trans("DeviceType").'</th>';
|
|
print '<th>'.$langs->trans("NetDiagOpenPorts").'</th><th>'.$langs->trans("NetDiagFoundVia").'</th>';
|
|
print '</tr>';
|
|
// Fundweg als Klartext — "ping"/"port"/"arp"/"mdns" sagt sonst nur dem Entwickler etwas
|
|
$foundvialabels = array(
|
|
'ping' => $langs->trans("NetDiagFoundViaPing"),
|
|
'port' => $langs->trans("NetDiagFoundViaPort"),
|
|
'arp' => $langs->trans("NetDiagFoundViaArp"),
|
|
'mdns' => $langs->trans("NetDiagFoundViaMdns"),
|
|
);
|
|
foreach ($devices as $dev) {
|
|
// Anzeigename: eigener Name vor mDNS-/Host-/NetBIOS-Name (wie in der App)
|
|
$anzeige = '';
|
|
foreach (array($dev->custom_name, $dev->mdns_name, $dev->hostname, $dev->netbios_name) as $kandidat) {
|
|
if (!empty($kandidat)) {
|
|
$anzeige = $kandidat;
|
|
break;
|
|
}
|
|
}
|
|
print '<tr class="oddeven">';
|
|
print '<td>'.dol_escape_htmltag($dev->ip).'</td>';
|
|
print '<td>'.dol_escape_htmltag($dev->mac).'</td>';
|
|
print '<td>'.dol_escape_htmltag($anzeige);
|
|
// Weitere Bezeichner nur zeigen, wenn sie vom Anzeigenamen abweichen
|
|
$weitere = array();
|
|
if (!empty($dev->netbios_name) && $dev->netbios_name !== $anzeige) {
|
|
$weitere[] = 'NB: '.$dev->netbios_name;
|
|
}
|
|
if (!empty($dev->mdns_name) && $dev->mdns_name !== $anzeige) {
|
|
$weitere[] = 'mDNS: '.$dev->mdns_name;
|
|
}
|
|
if (!empty($weitere)) {
|
|
print '<br><span class="opacitymedium small">'.dol_escape_htmltag(implode(' · ', $weitere)).'</span>';
|
|
}
|
|
print '</td>';
|
|
print '<td>'.dol_escape_htmltag($dev->vendor).'</td>';
|
|
print '<td>'.dol_escape_htmltag($dev->devicetype).'</td>';
|
|
print '<td>'.dol_escape_htmltag($dev->open_ports).'</td>';
|
|
print '<td>'.dol_escape_htmltag($foundvialabels[$dev->found_via] ?? $dev->found_via).'</td>';
|
|
print '</tr>';
|
|
}
|
|
if (empty($devices)) {
|
|
print '<tr><td colspan="7" class="opacitymedium center">-</td></tr>';
|
|
}
|
|
print '</table></div>';
|
|
|
|
// Messungen
|
|
$measObj = new NetDiagMeasurement($db);
|
|
$measurements = $measObj->fetchAllByProtocol($object->id);
|
|
$statuslabels = array(
|
|
0 => 'NetDiagMeasureOk',
|
|
1 => 'NetDiagMeasureWarn',
|
|
2 => 'NetDiagMeasureFail',
|
|
3 => 'NetDiagMeasureUnmeasurable',
|
|
);
|
|
$statuscss = array(
|
|
0 => 'badge-status4',
|
|
1 => 'badge-status1',
|
|
2 => 'badge-status8',
|
|
3 => 'badge-status0', // grau: Test war nicht durchfuehrbar
|
|
);
|
|
print '<br>';
|
|
print load_fiche_titre($langs->trans("NetDiagMeasurements").' ('.count($measurements).')', '', 'fa-wave-square');
|
|
print '<div class="div-table-responsive-no-min"><table class="noborder centpercent">';
|
|
print '<tr class="liste_titre">';
|
|
print '<th>'.$langs->trans("DateMeasure").'</th><th>'.$langs->trans("ToolCategory").'</th>';
|
|
print '<th>'.$langs->trans("Tool").'</th><th>'.$langs->trans("Label").'</th>';
|
|
print '<th>'.$langs->trans("Result").'</th><th class="center">'.$langs->trans("MeasureStatus").'</th>';
|
|
print '</tr>';
|
|
foreach ($measurements as $m) {
|
|
$st = (int) $m->measure_status;
|
|
// Unbekannte Werte nie als OK durchgehen lassen (Array-Zugriff waere sonst leer)
|
|
if ($st < 0 || $st > 3) {
|
|
$st = 1;
|
|
}
|
|
print '<tr class="oddeven">';
|
|
print '<td class="nowraponall">'.dol_print_date($m->date_measure, 'dayhour').'</td>';
|
|
print '<td>'.dol_escape_htmltag($m->category).'</td>';
|
|
print '<td>'.dol_escape_htmltag(netdiagToolName($m->tool)).'</td>';
|
|
print '<td>'.dol_escape_htmltag($m->label);
|
|
// Messparameter darunter: sonst ist nicht erkennbar, wogegen gemessen wurde
|
|
$params = netdiagFormatParams($m->params);
|
|
if ($params !== '') {
|
|
print '<br><span class="opacitymedium small">'.dol_escape_htmltag($params).'</span>';
|
|
}
|
|
print '</td>';
|
|
print '<td>'.netdiagFormatResult($m->result, $m->tool).'</td>';
|
|
print '<td class="center"><span class="badge '.$statuscss[$st].'">'.$langs->trans($statuslabels[$st]).'</span></td>';
|
|
print '</tr>';
|
|
}
|
|
if (empty($measurements)) {
|
|
print '<tr><td colspan="6" class="opacitymedium center">-</td></tr>';
|
|
}
|
|
print '</table></div>';
|
|
|
|
llxFooter();
|
|
$db->close();
|