- 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.
199 lines
7.7 KiB
PHP
199 lines
7.7 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/netdiagindex.php
|
|
* \ingroup netdiag
|
|
* \brief Liste aller Netzwerk-Diagnose-Protokolle (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.'/core/class/html.formcompany.class.php';
|
|
require_once __DIR__.'/class/netdiagprotocol.class.php';
|
|
|
|
/**
|
|
* @var DoliDB $db
|
|
* @var Translate $langs
|
|
* @var User $user
|
|
*/
|
|
|
|
$langs->loadLangs(array("netdiag@netdiag", "companies", "orders"));
|
|
|
|
if (!$user->hasRight('netdiag', 'protocol', 'read')) {
|
|
accessforbidden();
|
|
}
|
|
|
|
$search_ref = GETPOST('search_ref', 'alpha');
|
|
$search_soc = GETPOST('search_soc', 'alpha');
|
|
// „nur mit Befund": zeigt Protokolle, in denen mindestens eine Messung
|
|
// Warnung oder Fehler ist — beim Durchsehen vieler Protokolle die eigentliche
|
|
// Frage („wo muss ich nochmal ran?")
|
|
$search_problem = GETPOST('search_problem', 'int');
|
|
$limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
|
|
$page = GETPOSTINT('page');
|
|
if ($page < 0) {
|
|
$page = 0;
|
|
}
|
|
$offset = $limit * $page;
|
|
|
|
/*
|
|
* Ansicht
|
|
*/
|
|
|
|
$form = new Form($db);
|
|
$title = $langs->trans("NetDiagProtocols");
|
|
llxHeader('', $title, '', '', 0, 0, '', '', '', 'mod-netdiag page-list');
|
|
|
|
// Datensätze laden
|
|
$sql = "SELECT p.rowid, p.ref, p.label, p.date_diag, p.standort, p.subnet, p.status,";
|
|
$sql .= " s.rowid as socid, s.nom as socname,";
|
|
$sql .= " (SELECT COUNT(*) FROM ".$db->prefix()."netdiag_device d WHERE d.fk_protocol = p.rowid) as devcount,";
|
|
// Schlechteste Einzelmessung als Protokoll-Ampel. Status 3 („nicht messbar")
|
|
// darf dabei NICHT als schlechtester Wert gelten — er ist keine Aussage über
|
|
// das Kundennetz; deshalb wird er separat gezählt statt in MAX() einzugehen.
|
|
$sql .= " (SELECT MAX(m.measure_status) FROM ".$db->prefix()."netdiag_measurement m
|
|
WHERE m.fk_protocol = p.rowid AND m.measure_status IN (0,1,2)) as worststatus,";
|
|
$sql .= " (SELECT COUNT(*) FROM ".$db->prefix()."netdiag_measurement m
|
|
WHERE m.fk_protocol = p.rowid) as meascount,";
|
|
$sql .= " (SELECT COUNT(*) FROM ".$db->prefix()."netdiag_measurement m
|
|
WHERE m.fk_protocol = p.rowid AND m.measure_status = 3) as unmesscount";
|
|
$sql .= " FROM ".$db->prefix()."netdiag_protocol as p";
|
|
$sql .= " LEFT JOIN ".$db->prefix()."societe as s ON s.rowid = p.fk_soc";
|
|
$sql .= " WHERE p.entity IN (".getEntity('netdiagprotocol').")";
|
|
if ($search_ref) {
|
|
$sql .= natural_search('p.ref', $search_ref);
|
|
}
|
|
if ($search_soc) {
|
|
$sql .= natural_search('s.nom', $search_soc);
|
|
}
|
|
if ($search_problem) {
|
|
$sql .= " AND EXISTS (SELECT 1 FROM ".$db->prefix()."netdiag_measurement m2
|
|
WHERE m2.fk_protocol = p.rowid AND m2.measure_status IN (1,2))";
|
|
}
|
|
$sql .= " ORDER BY p.date_diag DESC, p.rowid DESC";
|
|
|
|
$sql .= $db->plimit($limit, $offset);
|
|
|
|
$resql = $db->query($sql);
|
|
$rows = array();
|
|
if ($resql) {
|
|
while ($obj = $db->fetch_object($resql)) {
|
|
$rows[] = $obj;
|
|
}
|
|
}
|
|
|
|
print load_fiche_titre($title, '', 'fa-network-wired');
|
|
|
|
print '<form method="GET" action="'.$_SERVER["PHP_SELF"].'">';
|
|
print '<div class="div-table-responsive">';
|
|
print '<table class="tagtable liste">';
|
|
|
|
print '<tr class="liste_titre">';
|
|
print '<td><input type="text" name="search_ref" class="maxwidth100" value="'.dol_escape_htmltag($search_ref).'" placeholder="'.$langs->trans("Ref").'"></td>';
|
|
print '<td><input type="text" name="search_soc" class="maxwidth150" value="'.dol_escape_htmltag($search_soc).'" placeholder="'.$langs->trans("ThirdParty").'"></td>';
|
|
print '<td colspan="3" class="right"><label class="opacitymedium small"><input type="checkbox" name="search_problem" value="1"'.($search_problem ? ' checked' : '').'> '.$langs->trans("NetDiagOnlyWithFindings").'</label></td>';
|
|
print '<td class="right"><input type="submit" class="button small" value="'.$langs->trans("Search").'"></td>';
|
|
print '</tr>';
|
|
|
|
print '<tr class="liste_titre">';
|
|
print '<th>'.$langs->trans("Ref").'</th>';
|
|
print '<th>'.$langs->trans("ThirdParty").'</th>';
|
|
print '<th>'.$langs->trans("DateDiag").'</th>';
|
|
print '<th>'.$langs->trans("Location").'</th>';
|
|
print '<th class="right">'.$langs->trans("NetDiagDevices").'</th>';
|
|
print '<th class="center">'.$langs->trans("NetDiagMeasurements").'</th>';
|
|
print '<th class="center">'.$langs->trans("Status").'</th>';
|
|
print '</tr>';
|
|
|
|
$proto = new NetDiagProtocol($db);
|
|
foreach ($rows as $obj) {
|
|
$proto->id = $obj->rowid;
|
|
$proto->status = $obj->status;
|
|
$cardurl = dol_buildpath('/netdiag/netdiagprotocol_card.php', 1).'?id='.$obj->rowid;
|
|
print '<tr class="oddeven">';
|
|
print '<td><a href="'.$cardurl.'">'.img_picto('', 'fa-network-wired').' '.dol_escape_htmltag($obj->ref).'</a></td>';
|
|
if ($obj->socid) {
|
|
print '<td><a href="'.DOL_URL_ROOT.'/societe/card.php?socid='.$obj->socid.'">'.dol_escape_htmltag($obj->socname).'</a></td>';
|
|
} else {
|
|
print '<td></td>';
|
|
}
|
|
print '<td>'.dol_print_date($db->jdate($obj->date_diag), 'dayhour').'</td>';
|
|
print '<td>'.dol_escape_htmltag($obj->standort).'</td>';
|
|
print '<td class="right">'.((int) $obj->devcount).'</td>';
|
|
// Messungs-Ampel: schlechteste Einzelmessung, plus Hinweis auf nicht
|
|
// messbare Punkte. So sieht man in der Liste, wo noch etwas zu tun ist,
|
|
// ohne jedes Protokoll einzeln zu öffnen.
|
|
print '<td class="center">';
|
|
$meascount = (int) $obj->meascount;
|
|
if ($meascount === 0) {
|
|
print '<span class="opacitymedium">-</span>';
|
|
} else {
|
|
$worst = ($obj->worststatus === null) ? 3 : (int) $obj->worststatus;
|
|
if ($worst < 0 || $worst > 3) {
|
|
$worst = 1;
|
|
}
|
|
$ampellabels = array(0 => 'NetDiagMeasureOk', 1 => 'NetDiagMeasureWarn', 2 => 'NetDiagMeasureFail', 3 => 'NetDiagMeasureUnmeasurable');
|
|
$ampelcss = array(0 => 'badge-status4', 1 => 'badge-status1', 2 => 'badge-status8', 3 => 'badge-status0');
|
|
print '<span class="badge '.$ampelcss[$worst].'">'.$langs->trans($ampellabels[$worst]).'</span>';
|
|
print ' <span class="opacitymedium small">'.$meascount;
|
|
if ((int) $obj->unmesscount > 0) {
|
|
print ', '.((int) $obj->unmesscount).' n.m.';
|
|
}
|
|
print '</span>';
|
|
}
|
|
print '</td>';
|
|
print '<td class="center">'.$proto->getLibStatut(3).'</td>';
|
|
print '</tr>';
|
|
}
|
|
if (empty($rows)) {
|
|
print '<tr><td colspan="7" class="opacitymedium center">'.$langs->trans("NoRecordFound").'</td></tr>';
|
|
}
|
|
|
|
print '</table>';
|
|
print '</div>';
|
|
print '</form>';
|
|
|
|
llxFooter();
|
|
$db->close();
|