dolibarr.netdiag/class/netdiagdevice.class.php
Eduard Wisch 0b3db47ec1 Phase 5: Kundendokument lesbar, Gerätemerkmale kommen endlich an
Vorab: drei Punkte der Roadmap-Liste waren Fehlannahmen. Eine Analyse mit
anschließender Gegenprüfung (jeder Befund musste einen Widerlegungsversuch
überstehen) hat sie ausgeräumt, bevor Code geändert wurde:
 - "ab Seite 2 alles nach rechts verschoben" existiert nicht. Nachgemessen am
   Prod-PDF ND2026-0015 mit pdftotext -bbox: Seite 1 und Seite 2 beginnen beide
   bei 16,0 mm. Die echten Umbruchfehler waren andere.
 - measure_status validieren war seit Phase 1 erledigt.
 - Werkzeug-IDs / Teilnetz-Gruppierung / TCPDF-Fußzeile: verworfen, die
   vorgeschlagenen Änderungen hätten das PDF verschlechtert.

PDF (alle Punkte am mehrseitigen Dokument nachgeprüft):
- Tabellenkopf der Geräteliste wird auf Folgeseiten wiederholt. Vorher standen
  ab Seite 2 unbeschriftete Spalten — bei leeren MAC/Hostname-Feldern vier
  namenlose Spalten.
- Messungs-Titelzeile und Ergebnis werden zusammengehalten. Vorher blieb die
  Überschrift samt Ampel am Seitenende allein zurück, darunter ein leerer,
  unten offener Rahmen; in einem Testlauf über 61 Umbruchlagen 5-mal (~8 %).
- Spalte "Gerätetyp" hatte 15 mm, ließ aber 10 Zeichen zu — "Chromecast/TV"
  lief bis 199,0 mm bei 195 mm Tabellenkante über den Rahmen in den Druckrand.
- Deutsche Bezeichnungen mit Einheiten statt roher JSON-Schlüssel: aus
  "VerlustProzent: 0 | MinMs: 4.4 | UptimeSek: 8123456" wird "Paketverlust: 0 %
  | Kürzeste Antwortzeit: 4.4 ms | Betriebszeit: 94 Tage 1 Std". Als Whitelist
  (netdiagKundenfelder(), gemeinsam für Karte und PDF) — interne Felder wie
  arpAvailable, mdnsOk, probed, answered fallen damit automatisch heraus.
- "ARP-Tabelle nicht lesbar (/proc/net/arp) — braucht Root" wird beim Drucken
  zu einem kundentauglichen Satz. Altdaten stehen so in der DB, deshalb
  Ersetzung beim Drucken statt nur in der App.

Gerätemerkmale (der eigentliche Roadmap-Punkt): Der Techniker sah in der App
"Drucker HP, Port 9100", im Kundenprotokoll stand nur die IP. Die Felder
fehlten dabei nicht in der Übertragung, sondern durchgängig — ein Fix allein
in der API wäre folgenlos geblieben, weil Dolibarrs setSaveQuery() nur
deklarierte $fields schreibt. Ergänzt über die ganze Kette:
  sql/llx_netdiag_device.sql + neue Migration llx_netdiag_device_v2.sql
  (ADD COLUMN IF NOT EXISTS, wiederholbar, läuft bei jedem Modul-Update),
  NetDiagDevice::$fields + Properties, api/protocols.php POST und GET,
  Kartenansicht und PDF.
Neu: netbios_name, mdns_name, mdns_services, custom_name, open_ports,
found_via, last_seen. Im PDF steht jetzt statt "192.168.178.20" die Zeile
"Brother HL-L2350DW · Brother · Drucker · 80,443,9100" — der Name kommt aus
mDNS, obwohl der Hostname leer ist.

Sprachschlüssel Vendor -> NetDiagVendor: Die Gegenprüfung hielt den Punkt für
falsch (Translate::load() ist first-wins, im CLI-Test kam "Hersteller"), im
Browser stand in der Kartenansicht aber "Lieferant" — im HTTP-Kontext lädt
Dolibarr vorher andere Sprachdateien als im CLI. Statt der Ursache nachzugehen
jetzt ein eigener, kollisionsfreier Schlüssel; im Browser gegengeprüft.

Nebenbei: doppeltes "OK OK" beim Status 0 im PDF.

Gegen das Test-Dolibarr geprüft: Sync über die echte API (Login, POST, GET),
Felder in der DB kontrolliert, Kartenansicht im Browser, mehrseitiges PDF
gerendert und angesehen, Migration zweimal ausgeführt (idempotent).
2026-08-15 18:54:01 +02:00

177 lines
6.5 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 htdocs/custom/netdiag/class/netdiagdevice.class.php
* \ingroup netdiag
* \brief Klasse für ein im Diagnose-Protokoll gefundenes Gerät
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
/**
* Klasse NetDiagDevice — ein im Netzwerk gefundenes Gerät.
*/
class NetDiagDevice extends CommonObject
{
/** @var string Modul-Name */
public $module = 'netdiag';
/** @var string Element-Typ */
public $element = 'netdiagdevice';
/** @var string Datenbanktabelle (ohne Präfix) */
public $table_element = 'netdiag_device';
/** @var string Icon */
public $picto = 'fa-desktop';
/** @var int 1 = Tabelle hat ein 'entity'-Feld */
public $ismultientitymanaged = 1;
/**
* @var array<string,array<string,mixed>> Felddefinitionen
*/
public $fields = array(
'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'index' => 1, 'position' => 1),
'entity' => array('type' => 'integer', 'label' => 'Entity', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'default' => 1, 'index' => 1, 'position' => 5),
'fk_protocol' => array('type' => 'integer', 'label' => 'Protocol', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'index' => 1, 'position' => 10),
'ip' => array('type' => 'varchar(45)', 'label' => 'IpAddress', 'enabled' => 1, 'visible' => 1, 'position' => 20),
'mac' => array('type' => 'varchar(17)', 'label' => 'MacAddress', 'enabled' => 1, 'visible' => 1, 'position' => 25),
'hostname' => array('type' => 'varchar(255)', 'label' => 'Hostname', 'enabled' => 1, 'visible' => 1, 'position' => 30),
'netbios_name' => array('type' => 'varchar(64)', 'label' => 'NetDiagNetbiosName', 'enabled' => 1, 'visible' => 1, 'position' => 31),
'mdns_name' => array('type' => 'varchar(255)', 'label' => 'NetDiagMdnsName', 'enabled' => 1, 'visible' => 1, 'position' => 32),
'mdns_services' => array('type' => 'varchar(512)', 'label' => 'NetDiagMdnsServices', 'enabled' => 1, 'visible' => 3, 'position' => 33),
'custom_name' => array('type' => 'varchar(255)', 'label' => 'NetDiagCustomName', 'enabled' => 1, 'visible' => 1, 'position' => 34),
'vendor' => array('type' => 'varchar(128)', 'label' => 'Vendor', 'enabled' => 1, 'visible' => 1, 'position' => 35),
'devicetype' => array('type' => 'varchar(64)', 'label' => 'DeviceType', 'enabled' => 1, 'visible' => 1, 'position' => 40),
'open_ports' => array('type' => 'varchar(255)', 'label' => 'NetDiagOpenPorts', 'enabled' => 1, 'visible' => 1, 'position' => 41),
'found_via' => array('type' => 'varchar(16)', 'label' => 'NetDiagFoundVia', 'enabled' => 1, 'visible' => 1, 'position' => 42),
'last_seen' => array('type' => 'datetime', 'label' => 'NetDiagLastSeen', 'enabled' => 1, 'visible' => 1, 'position' => 43),
'note' => array('type' => 'text', 'label' => 'Note', 'enabled' => 1, 'visible' => 3, 'position' => 50),
'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 500),
'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => 0, 'notnull' => 0, 'position' => 501),
);
public $rowid;
public $entity;
public $fk_protocol;
public $ip;
public $mac;
public $hostname;
/** @var string NetBIOS-Name (UDP-137-Abfrage) */
public $netbios_name;
/** @var string mDNS-/Bonjour-Anzeigename */
public $mdns_name;
/** @var string angebotene mDNS-Dienste, kommagetrennt */
public $mdns_services;
/** @var string vom Techniker vergebener Name */
public $custom_name;
public $vendor;
public $devicetype;
/** @var string offene Ports, kommagetrennt (z.B. "80,443,9100") */
public $open_ports;
/** @var string Fundweg: ping | port | arp | mdns */
public $found_via;
/** @var int|string Unix-Zeit/Datum der letzten Sichtung */
public $last_seen;
public $note;
public $date_creation;
public $tms;
/**
* Konstruktor
*
* @param DoliDB $db Datenbank-Handler
*/
public function __construct(DoliDB $db)
{
$this->db = $db;
}
/**
* Datensatz anlegen
*
* @param User $user Benutzer
* @param int $notrigger 1 = Trigger unterdrücken
* @return int >0 wenn OK, <=0 bei Fehler
*/
public function create(User $user, $notrigger = 0)
{
return $this->createCommon($user, $notrigger);
}
/**
* Datensatz laden
*
* @param int $id Rowid
* @param string $ref Referenz
* @return int >0 wenn OK, 0 wenn nicht gefunden, <0 bei Fehler
*/
public function fetch($id, $ref = null)
{
return $this->fetchCommon($id, $ref);
}
/**
* Alle Geräte eines Protokolls laden
*
* @param int $fk_protocol Protokoll-Rowid
* @return NetDiagDevice[] Liste der Geräte
*/
public function fetchAllByProtocol($fk_protocol)
{
$result = array();
$sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
$sql .= " WHERE fk_protocol = ".((int) $fk_protocol);
$sql .= " ORDER BY INET_ATON(ip), rowid";
$resql = $this->db->query($sql);
if ($resql) {
while ($obj = $this->db->fetch_object($resql)) {
$dev = new self($this->db);
if ($dev->fetch((int) $obj->rowid) > 0) {
$result[] = $dev;
}
}
}
return $result;
}
/**
* Datensatz aktualisieren
*
* @param User $user Benutzer
* @param int $notrigger 1 = Trigger unterdrücken
* @return int >0 wenn OK, <=0 bei Fehler
*/
public function update(User $user, $notrigger = 0)
{
return $this->updateCommon($user, $notrigger);
}
/**
* Datensatz löschen
*
* @param User $user Benutzer
* @param int $notrigger 1 = Trigger unterdrücken
* @return int >0 wenn OK, <=0 bei Fehler
*/
public function delete(User $user, $notrigger = 0)
{
return $this->deleteCommon($user, $notrigger);
}
}