Initiales Commit — Dolibarr-Modul NetDiag [deploy]
Some checks are pending
Deploy netdiag / deploy (push) Waiting to run
Some checks are pending
Deploy netdiag / deploy (push) Waiting to run
Netzwerk-Diagnose-Modul mit JSON-API für die NetDiag-App: - 3 Tabellen (protocol/device/measurement), generisches JSON-result - JSON-API: auth, customers, orders, protocols (idempotenter Sync), pdf - JWT-Auth (HS256), CORS für die Capacitor-App - Tabs an Thirdparty + Auftrag, Protokoll-Card, PDF-Generator - QR-Code zum App-Download in der Modul-Konfiguration - de_DE + en_US, Rechtesystem netdiag->protocol read/write/delete Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
c576726a26
28 changed files with 3213 additions and 0 deletions
70
.forgejo/workflows/deploy.yml
Normal file
70
.forgejo/workflows/deploy.yml
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
name: Deploy netdiag
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: docker
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v') || contains(github.event.head_commit.message, '[deploy]')
|
||||||
|
steps:
|
||||||
|
- name: Notify Start
|
||||||
|
uses: https://git.data-it-solution.de/data/ntfy-action@main
|
||||||
|
with:
|
||||||
|
status: start
|
||||||
|
project: NetDiag
|
||||||
|
ntfy_auth: ${{ secrets.NTFY_AUTH }}
|
||||||
|
run_number: ${{ github.run_number }}
|
||||||
|
message: ${{ github.event.head_commit.message }}
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
run: |
|
||||||
|
git clone --depth 1 --branch "${GITHUB_REF_NAME}" \
|
||||||
|
"https://token:${{ secrets.GIT_TOKEN }}@git.data-it-solution.de/${GITHUB_REPOSITORY}.git" .
|
||||||
|
|
||||||
|
- name: Deploy nach Dolibarr
|
||||||
|
run: |
|
||||||
|
DEPLOY_PATH="/mnt/appdata/firma/dolibarr-202509/modules/netdiag"
|
||||||
|
REF="${GITHUB_REF#refs/*/}"
|
||||||
|
|
||||||
|
echo "Deploye ${REF} nach ${DEPLOY_PATH} ..."
|
||||||
|
|
||||||
|
if [ -d "$DEPLOY_PATH" ]; then
|
||||||
|
find "$DEPLOY_PATH" -mindepth 1 -not -path '*/.git/*' -not -name '.git' -delete 2>/dev/null || true
|
||||||
|
else
|
||||||
|
mkdir -p "$DEPLOY_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rsync -a \
|
||||||
|
--exclude='.git' \
|
||||||
|
--exclude='.forgejo' \
|
||||||
|
--exclude='.gitignore' \
|
||||||
|
--exclude='CLAUDE.md' \
|
||||||
|
--exclude='test/' \
|
||||||
|
./ "$DEPLOY_PATH/"
|
||||||
|
|
||||||
|
echo "Deployment erfolgreich: ${REF} -> ${DEPLOY_PATH}"
|
||||||
|
ls -la "$DEPLOY_PATH/core/modules/"
|
||||||
|
|
||||||
|
- name: Notify Success
|
||||||
|
if: success()
|
||||||
|
uses: https://git.data-it-solution.de/data/ntfy-action@main
|
||||||
|
with:
|
||||||
|
status: success
|
||||||
|
project: NetDiag
|
||||||
|
ntfy_auth: ${{ secrets.NTFY_AUTH }}
|
||||||
|
run_number: ${{ github.run_number }}
|
||||||
|
|
||||||
|
- name: Notify Failure
|
||||||
|
if: failure()
|
||||||
|
uses: https://git.data-it-solution.de/data/ntfy-action@main
|
||||||
|
with:
|
||||||
|
status: failure
|
||||||
|
project: NetDiag
|
||||||
|
ntfy_auth: ${{ secrets.NTFY_AUTH }}
|
||||||
|
run_number: ${{ github.run_number }}
|
||||||
|
click_url: https://git.data-it-solution.de/${{ github.repository }}/actions
|
||||||
11
ChangeLog.md
Normal file
11
ChangeLog.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Changelog NetDiag
|
||||||
|
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
|
- Erstversion
|
||||||
|
- Datenmodell: Protokoll, Gerät, Messung
|
||||||
|
- JSON-API: auth, customers, orders, protocols (Sync), pdf
|
||||||
|
- Tab "Netzwerk-Diagnose" an Kunde und Auftrag
|
||||||
|
- PDF-Protokoll-Generator + ECM-Ablage
|
||||||
|
- Backend: Protokoll-Liste und Detailansicht
|
||||||
|
- Admin: Token-Einstellung, QR-Code zum App-Download
|
||||||
49
README.md
Normal file
49
README.md
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
# NetDiag — Netzwerk-Diagnose für Dolibarr
|
||||||
|
|
||||||
|
Dolibarr-Modul für die Ablage von Netzwerk-Diagnose-Protokollen. Erfasst per
|
||||||
|
mobiler App (siehe Projekt `NetzwerkDiagnose/app`) gefundene Geräte, Ports und
|
||||||
|
Messungen und hängt die Protokolle dauerhaft an **Kunde** und **Auftrag**.
|
||||||
|
|
||||||
|
## Funktionen
|
||||||
|
|
||||||
|
- Datenmodell: Protokoll → Geräte → Messungen (`llx_netdiag_*`)
|
||||||
|
- Tab **Netzwerk-Diagnose** an Kunde (thirdparty) und Auftrag (commande)
|
||||||
|
- JSON-API unter `/custom/netdiag/api/` für die mobile App (JWT-Auth)
|
||||||
|
- PDF-Protokoll, wird im Dokumentenarchiv (ECM) abgelegt
|
||||||
|
- Rechtesystem: `netdiag → protocol → read/write/delete`
|
||||||
|
- Mehrsprachig (de_DE, en_US)
|
||||||
|
- QR-Code zum App-Download in der Modul-Einrichtung
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Verzeichnis `netdiag/` nach `htdocs/custom/` auf den Dolibarr-Server kopieren.
|
||||||
|
2. In Dolibarr: **Einrichtung → Module → NetDiag** aktivieren.
|
||||||
|
3. Beim Aktivieren werden die Tabellen `llx_netdiag_protocol`,
|
||||||
|
`llx_netdiag_device`, `llx_netdiag_measurement` angelegt und ein
|
||||||
|
JWT-Schlüssel erzeugt.
|
||||||
|
4. Benutzern das Recht **NetDiag → Protokolle lesen/schreiben** geben.
|
||||||
|
|
||||||
|
## API-Endpunkte
|
||||||
|
|
||||||
|
Alle unter `https://<dolibarr>/custom/netdiag/api/`:
|
||||||
|
|
||||||
|
| Endpunkt | Methode | Zweck |
|
||||||
|
|----------|---------|-------|
|
||||||
|
| `auth.php` | POST `{login,password}` | Anmeldung → `{token,expiresIn,user}` |
|
||||||
|
| `customers.php` | GET `?q=` / `?id=` | Kundensuche / Kundendetail |
|
||||||
|
| `orders.php` | GET `?open=1&q=` / `?id=` | Auftragsliste / Auftragsdetail |
|
||||||
|
| `protocols.php` | GET `?id=` | Protokoll mit Geräten + Messungen |
|
||||||
|
| `protocols.php` | POST `{action:"sync",protocol:{…}}` | Protokoll anlegen/aktualisieren (idempotent über `clientUuid`) |
|
||||||
|
| `pdf.php` | GET `?id=&jwt=` | Protokoll-PDF streamen |
|
||||||
|
|
||||||
|
Authentifizierung per `Authorization: Bearer <token>` oder `?jwt=<token>`.
|
||||||
|
|
||||||
|
## Einrichtung
|
||||||
|
|
||||||
|
**Einrichtung → Module → NetDiag → Einstellungen:**
|
||||||
|
- Token-Gültigkeit (Sekunden)
|
||||||
|
- App-Download-URL (APK) — wird als QR-Code angezeigt
|
||||||
|
|
||||||
|
## Lizenz
|
||||||
|
|
||||||
|
GPLv3
|
||||||
87
admin/about.php
Normal file
87
admin/about.php
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?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/admin/about.php
|
||||||
|
* \ingroup netdiag
|
||||||
|
* \brief Info-Seite des Moduls NetDiag
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 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/lib/admin.lib.php";
|
||||||
|
require_once '../lib/netdiag.lib.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DoliDB $db
|
||||||
|
* @var Translate $langs
|
||||||
|
* @var User $user
|
||||||
|
*/
|
||||||
|
|
||||||
|
$langs->loadLangs(array("admin", "netdiag@netdiag"));
|
||||||
|
|
||||||
|
if (!$user->admin) {
|
||||||
|
accessforbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = $langs->trans("NetDiagAbout");
|
||||||
|
llxHeader('', $title, '', '', 0, 0, '', '', '', 'mod-netdiag page-admin');
|
||||||
|
|
||||||
|
$linkback = '<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
|
||||||
|
print load_fiche_titre($title, $linkback, 'title_setup');
|
||||||
|
|
||||||
|
$head = netdiagAdminPrepareHead();
|
||||||
|
print dol_get_fiche_head($head, 'about', $langs->trans("ModuleNetDiagName"), -1, 'fa-network-wired');
|
||||||
|
|
||||||
|
print '<div class="paddingtop paddingbottom">';
|
||||||
|
print '<strong>'.$langs->trans("ModuleNetDiagName").'</strong> — '.$langs->trans("ModuleNetDiagDesc").'<br><br>';
|
||||||
|
print 'Version: 1.0.0<br>';
|
||||||
|
print 'Autor: Alles Watt läuft (Eduard Wisch)<br>';
|
||||||
|
print 'Lizenz: GPLv3<br>';
|
||||||
|
print '</div>';
|
||||||
|
|
||||||
|
print dol_get_fiche_end();
|
||||||
|
|
||||||
|
llxFooter();
|
||||||
|
$db->close();
|
||||||
147
admin/setup.php
Normal file
147
admin/setup.php
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
<?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/admin/setup.php
|
||||||
|
* \ingroup netdiag
|
||||||
|
* \brief Einrichtungsseite des Moduls NetDiag
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 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/lib/admin.lib.php";
|
||||||
|
require_once '../lib/netdiag.lib.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Conf $conf
|
||||||
|
* @var DoliDB $db
|
||||||
|
* @var Translate $langs
|
||||||
|
* @var User $user
|
||||||
|
*/
|
||||||
|
|
||||||
|
$langs->loadLangs(array("admin", "netdiag@netdiag"));
|
||||||
|
|
||||||
|
if (!$user->admin) {
|
||||||
|
accessforbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
$action = GETPOST('action', 'aZ09');
|
||||||
|
|
||||||
|
// Konstanten speichern
|
||||||
|
if ($action == 'updateconst') {
|
||||||
|
$ttl = GETPOSTINT('NETDIAG_API_TOKEN_TTL');
|
||||||
|
if ($ttl < 60) {
|
||||||
|
$ttl = 604800;
|
||||||
|
}
|
||||||
|
dolibarr_set_const($db, 'NETDIAG_API_TOKEN_TTL', $ttl, 'chaine', 0, '', $conf->entity);
|
||||||
|
dolibarr_set_const($db, 'NETDIAG_APK_URL', GETPOST('NETDIAG_APK_URL', 'alpha'), 'chaine', 0, '', $conf->entity);
|
||||||
|
setEventMessages($langs->trans("SetupSaved"), null, 'mesgs');
|
||||||
|
header("Location: ".$_SERVER["PHP_SELF"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ansicht
|
||||||
|
*/
|
||||||
|
|
||||||
|
$form = new Form($db);
|
||||||
|
$help_url = '';
|
||||||
|
$title = $langs->trans("NetDiagSetup");
|
||||||
|
|
||||||
|
llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-netdiag page-admin');
|
||||||
|
|
||||||
|
$linkback = '<a href="'.($backtopage ? $backtopage : DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1').'">'.$langs->trans("BackToModuleList").'</a>';
|
||||||
|
print load_fiche_titre($title, $linkback, 'title_setup');
|
||||||
|
|
||||||
|
$head = netdiagAdminPrepareHead();
|
||||||
|
print dol_get_fiche_head($head, 'settings', $langs->trans("ModuleNetDiagName"), -1, 'fa-network-wired');
|
||||||
|
|
||||||
|
print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
|
||||||
|
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||||
|
print '<input type="hidden" name="action" value="updateconst">';
|
||||||
|
|
||||||
|
print '<table class="noborder centpercent">';
|
||||||
|
print '<tr class="liste_titre"><td>'.$langs->trans("Parameter").'</td><td>'.$langs->trans("Value").'</td></tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven"><td>'.$langs->trans("NETDIAG_API_TOKEN_TTL");
|
||||||
|
print ' '.$form->textwithpicto('', $langs->trans("NETDIAG_API_TOKEN_TTLTooltip")).'</td>';
|
||||||
|
print '<td><input type="number" name="NETDIAG_API_TOKEN_TTL" value="'.dol_escape_htmltag(getDolGlobalString('NETDIAG_API_TOKEN_TTL', '604800')).'" min="60" class="width150"></td></tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven"><td>'.$langs->trans("NETDIAG_APK_URL");
|
||||||
|
print ' '.$form->textwithpicto('', $langs->trans("NETDIAG_APK_URLTooltip")).'</td>';
|
||||||
|
print '<td><input type="text" name="NETDIAG_APK_URL" value="'.dol_escape_htmltag(getDolGlobalString('NETDIAG_APK_URL')).'" class="minwidth400 quatrevingtpercent"></td></tr>';
|
||||||
|
|
||||||
|
print '</table>';
|
||||||
|
|
||||||
|
print '<div class="center"><input type="submit" class="button" value="'.$langs->trans("Save").'"></div>';
|
||||||
|
print '</form>';
|
||||||
|
|
||||||
|
print '<br><div class="opacitymedium">'.$langs->trans("NetDiagApiSecretInfo").'</div>';
|
||||||
|
|
||||||
|
// QR-Code zum App-Download
|
||||||
|
$apkurl = getDolGlobalString('NETDIAG_APK_URL');
|
||||||
|
if (!empty($apkurl)) {
|
||||||
|
print '<div class="center" style="margin-top:25px;padding:20px;border:1px solid #ddd;border-radius:8px;display:inline-block;">';
|
||||||
|
print '<strong>'.$langs->trans("NetDiagAppDownload").'</strong><br>';
|
||||||
|
print '<div class="opacitymedium" style="margin:6px 0 12px 0;">'.$langs->trans("NetDiagAppDownloadHint").'</div>';
|
||||||
|
|
||||||
|
$qrfile = DOL_DOCUMENT_ROOT.'/includes/tecnickcom/tcpdf/tcpdf_barcodes_2d.php';
|
||||||
|
if (file_exists($qrfile)) {
|
||||||
|
require_once $qrfile;
|
||||||
|
$barcode = new TCPDF2DBarcode($apkurl, 'QRCODE,M');
|
||||||
|
// SVG-Ausgabe -> komplett lokal, kein externer Dienst
|
||||||
|
print '<div style="width:220px;height:220px;margin:0 auto;">';
|
||||||
|
print $barcode->getBarcodeSVGcode(5, 5, 'black');
|
||||||
|
print '</div>';
|
||||||
|
} else {
|
||||||
|
print '<div class="warning">QR-Bibliothek (TCPDF) nicht gefunden.</div>';
|
||||||
|
}
|
||||||
|
print '<div style="margin-top:10px;"><a href="'.dol_escape_htmltag($apkurl).'" target="_blank">'.dol_escape_htmltag($apkurl).'</a></div>';
|
||||||
|
print '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
print dol_get_fiche_end();
|
||||||
|
|
||||||
|
llxFooter();
|
||||||
|
$db->close();
|
||||||
91
api/auth.php
Normal file
91
api/auth.php
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
<?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/api/auth.php
|
||||||
|
* \ingroup netdiag
|
||||||
|
* \brief API-Endpunkt: Anmeldung der mobilen App, liefert JWT.
|
||||||
|
*
|
||||||
|
* POST {login, password} -> {token, expiresIn, user}
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once __DIR__.'/netdiag_api.lib.php';
|
||||||
|
|
||||||
|
netdiag_api_bootstrap();
|
||||||
|
|
||||||
|
/** @var DoliDB $db */
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
netdiag_api_error('Nur POST erlaubt', 405);
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = netdiag_api_read_body();
|
||||||
|
$login = isset($body['login']) ? trim((string) $body['login']) : '';
|
||||||
|
$password = isset($body['password']) ? (string) $body['password'] : '';
|
||||||
|
|
||||||
|
if ($login === '' || $password === '') {
|
||||||
|
netdiag_api_error('Login und Passwort erforderlich', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
|
||||||
|
|
||||||
|
// Zugangsdaten gegen Dolibarr prüfen (Standard-Login-Backend)
|
||||||
|
$entitytocheck = (int) $conf->entity;
|
||||||
|
$authmode = (getDolGlobalString('MAIN_AUTHENTICATION_MODE') ? getDolGlobalString('MAIN_AUTHENTICATION_MODE') : 'dolibarr');
|
||||||
|
$resultlogin = checkLoginPassEntity($login, $password, $entitytocheck, explode(',', $authmode));
|
||||||
|
|
||||||
|
if (empty($resultlogin)) {
|
||||||
|
// Kurze Verzögerung gegen Brute-Force
|
||||||
|
sleep(1);
|
||||||
|
netdiag_api_error('Login fehlgeschlagen', 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = new User($db);
|
||||||
|
if ($user->fetch('', $resultlogin, '', 0, $entitytocheck) <= 0 || empty($user->id)) {
|
||||||
|
netdiag_api_error('Benutzer nicht gefunden', 401);
|
||||||
|
}
|
||||||
|
if (!empty($user->statut) && $user->statut == 0) {
|
||||||
|
netdiag_api_error('Benutzer deaktiviert', 403);
|
||||||
|
}
|
||||||
|
$user->loadRights();
|
||||||
|
|
||||||
|
if (!$user->hasRight('netdiag', 'protocol', 'read')) {
|
||||||
|
netdiag_api_error('Keine Berechtigung für NetDiag', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ttl = (int) getDolGlobalString('NETDIAG_API_TOKEN_TTL', '604800');
|
||||||
|
if ($ttl < 60) {
|
||||||
|
$ttl = 604800;
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = netdiag_jwt_encode(array(
|
||||||
|
'sub' => (int) $user->id,
|
||||||
|
'name' => $user->getFullName($langs),
|
||||||
|
), $ttl);
|
||||||
|
|
||||||
|
netdiag_api_respond(array(
|
||||||
|
'token' => $token,
|
||||||
|
'expiresIn' => $ttl,
|
||||||
|
'user' => array(
|
||||||
|
'id' => (int) $user->id,
|
||||||
|
'login' => $user->login,
|
||||||
|
'name' => $user->getFullName($langs),
|
||||||
|
'email' => $user->email,
|
||||||
|
'canWrite' => (bool) $user->hasRight('netdiag', 'protocol', 'write'),
|
||||||
|
),
|
||||||
|
));
|
||||||
119
api/customers.php
Normal file
119
api/customers.php
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
<?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/api/customers.php
|
||||||
|
* \ingroup netdiag
|
||||||
|
* \brief API-Endpunkt: Kunden suchen (?q=) oder Kundendetail (?id=).
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once __DIR__.'/netdiag_api.lib.php';
|
||||||
|
|
||||||
|
netdiag_api_bootstrap();
|
||||||
|
|
||||||
|
/** @var DoliDB $db */
|
||||||
|
|
||||||
|
$user = netdiag_api_authenticate($db);
|
||||||
|
|
||||||
|
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
||||||
|
$q = isset($_GET['q']) ? trim((string) $_GET['q']) : '';
|
||||||
|
$limit = isset($_GET['limit']) ? min(200, max(1, (int) $_GET['limit'])) : 50;
|
||||||
|
|
||||||
|
$prefix = $db->prefix();
|
||||||
|
|
||||||
|
// ---- Kundendetail ----
|
||||||
|
if ($id > 0) {
|
||||||
|
$sql = "SELECT s.rowid, s.nom, s.code_client, s.address, s.zip, s.town, s.phone, s.email, s.tva_intra";
|
||||||
|
$sql .= " FROM ".$prefix."societe as s";
|
||||||
|
$sql .= " WHERE s.rowid = ".((int) $id);
|
||||||
|
$sql .= " AND s.entity IN (".getEntity('societe').")";
|
||||||
|
$resql = $db->query($sql);
|
||||||
|
if (!$resql || !($obj = $db->fetch_object($resql))) {
|
||||||
|
netdiag_api_error('Kunde nicht gefunden', 404);
|
||||||
|
}
|
||||||
|
$customer = array(
|
||||||
|
'id' => (int) $obj->rowid,
|
||||||
|
'name' => $obj->nom,
|
||||||
|
'code' => $obj->code_client,
|
||||||
|
'address' => $obj->address,
|
||||||
|
'zip' => $obj->zip,
|
||||||
|
'town' => $obj->town,
|
||||||
|
'phone' => $obj->phone,
|
||||||
|
'email' => $obj->email,
|
||||||
|
'vat' => $obj->tva_intra,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Aufträge des Kunden
|
||||||
|
$orders = array();
|
||||||
|
$sqlo = "SELECT rowid, ref, ref_client, date_commande, fk_statut FROM ".$prefix."commande";
|
||||||
|
$sqlo .= " WHERE fk_soc = ".((int) $id)." AND entity IN (".getEntity('commande').")";
|
||||||
|
$sqlo .= " ORDER BY date_commande DESC, rowid DESC";
|
||||||
|
$reso = $db->query($sqlo);
|
||||||
|
if ($reso) {
|
||||||
|
while ($o = $db->fetch_object($reso)) {
|
||||||
|
$orders[] = array(
|
||||||
|
'id' => (int) $o->rowid,
|
||||||
|
'ref' => $o->ref,
|
||||||
|
'refClient' => $o->ref_client,
|
||||||
|
'date' => $db->jdate($o->date_commande),
|
||||||
|
'status' => (int) $o->fk_statut,
|
||||||
|
'open' => ((int) $o->fk_statut >= 0 && (int) $o->fk_statut < 3),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diagnose-Protokolle des Kunden
|
||||||
|
$protocols = netdiag_api_protocol_list($db, ' AND p.fk_soc = '.((int) $id));
|
||||||
|
|
||||||
|
netdiag_api_respond(array(
|
||||||
|
'customer' => $customer,
|
||||||
|
'orders' => $orders,
|
||||||
|
'protocols' => $protocols,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Kundensuche ----
|
||||||
|
$sql = "SELECT s.rowid, s.nom, s.code_client, s.zip, s.town, s.phone,";
|
||||||
|
$sql .= " (SELECT COUNT(*) FROM ".$prefix."netdiag_protocol p WHERE p.fk_soc = s.rowid) as protocolcount";
|
||||||
|
$sql .= " FROM ".$prefix."societe as s";
|
||||||
|
$sql .= " WHERE s.entity IN (".getEntity('societe').")";
|
||||||
|
$sql .= " AND s.client IN (1, 2, 3)";
|
||||||
|
$sql .= " AND s.status = 1";
|
||||||
|
if ($q !== '') {
|
||||||
|
$sql .= " AND (".natural_search(array('s.nom', 's.name_alias', 's.code_client', 's.town', 's.zip'), $q, 0, 1).")";
|
||||||
|
}
|
||||||
|
$sql .= " ORDER BY s.nom ASC";
|
||||||
|
$sql .= $db->plimit($limit, 0);
|
||||||
|
|
||||||
|
$resql = $db->query($sql);
|
||||||
|
if (!$resql) {
|
||||||
|
netdiag_api_error('Datenbankfehler: '.$db->lasterror(), 500);
|
||||||
|
}
|
||||||
|
$customers = array();
|
||||||
|
while ($obj = $db->fetch_object($resql)) {
|
||||||
|
$customers[] = array(
|
||||||
|
'id' => (int) $obj->rowid,
|
||||||
|
'name' => $obj->nom,
|
||||||
|
'code' => $obj->code_client,
|
||||||
|
'zip' => $obj->zip,
|
||||||
|
'town' => $obj->town,
|
||||||
|
'phone' => $obj->phone,
|
||||||
|
'protocolCount' => (int) $obj->protocolcount,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
netdiag_api_respond(array('customers' => $customers));
|
||||||
330
api/netdiag_api.lib.php
Normal file
330
api/netdiag_api.lib.php
Normal file
|
|
@ -0,0 +1,330 @@
|
||||||
|
<?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/api/netdiag_api.lib.php
|
||||||
|
* \ingroup netdiag
|
||||||
|
* \brief Gemeinsame Funktionen der JSON-API: Bootstrap, JWT, Antworten.
|
||||||
|
*
|
||||||
|
* Wird von jedem API-Endpunkt eingebunden. Lädt die Dolibarr-Umgebung
|
||||||
|
* ohne Web-Session und authentifiziert die mobile App per JWT.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Konstanten setzen BEVOR Dolibarr geladen wird (kein Menü, kein HTML, kein Login)
|
||||||
|
if (!defined('NOLOGIN')) {
|
||||||
|
define('NOLOGIN', '1');
|
||||||
|
}
|
||||||
|
if (!defined('NOCSRFCHECK')) {
|
||||||
|
define('NOCSRFCHECK', '1');
|
||||||
|
}
|
||||||
|
if (!defined('NOTOKENRENEWAL')) {
|
||||||
|
define('NOTOKENRENEWAL', '1');
|
||||||
|
}
|
||||||
|
if (!defined('NOREQUIREMENU')) {
|
||||||
|
define('NOREQUIREMENU', '1');
|
||||||
|
}
|
||||||
|
if (!defined('NOREQUIREHTML')) {
|
||||||
|
define('NOREQUIREHTML', '1');
|
||||||
|
}
|
||||||
|
if (!defined('NOREQUIREAJAX')) {
|
||||||
|
define('NOREQUIREAJAX', '1');
|
||||||
|
}
|
||||||
|
if (!defined('NOREQUIRESOC')) {
|
||||||
|
define('NOREQUIRESOC', '1');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dolibarr-Umgebung laden (master.inc.php) und CORS-Header setzen.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function netdiag_api_bootstrap()
|
||||||
|
{
|
||||||
|
// master.inc.php aus dem Webroot finden
|
||||||
|
$res = 0;
|
||||||
|
$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))."/master.inc.php")) {
|
||||||
|
$res = @include substr($tmp, 0, ($i + 1))."/master.inc.php";
|
||||||
|
}
|
||||||
|
if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/master.inc.php")) {
|
||||||
|
$res = @include dirname(substr($tmp, 0, ($i + 1)))."/master.inc.php";
|
||||||
|
}
|
||||||
|
if (!$res && file_exists("../../../master.inc.php")) {
|
||||||
|
$res = @include "../../../master.inc.php";
|
||||||
|
}
|
||||||
|
if (!$res && file_exists("../../../../master.inc.php")) {
|
||||||
|
$res = @include "../../../../master.inc.php";
|
||||||
|
}
|
||||||
|
if (!$res) {
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(array('error' => 'Dolibarr environment not found'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CORS: Bearer-Token-Auth, daher Wildcard-Origin erlaubt (keine Cookies)
|
||||||
|
header('Access-Control-Allow-Origin: *');
|
||||||
|
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
||||||
|
header('Access-Control-Allow-Headers: Content-Type, Authorization');
|
||||||
|
header('Access-Control-Max-Age: 86400');
|
||||||
|
|
||||||
|
// Preflight sofort beantworten
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||||
|
http_response_code(204);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON-Antwort senden und Skript beenden.
|
||||||
|
*
|
||||||
|
* @param mixed $data Antwortdaten
|
||||||
|
* @param int $httpstatus HTTP-Statuscode
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function netdiag_api_respond($data, $httpstatus = 200)
|
||||||
|
{
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
http_response_code($httpstatus);
|
||||||
|
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fehler-Antwort senden und Skript beenden.
|
||||||
|
*
|
||||||
|
* @param string $message Fehlermeldung
|
||||||
|
* @param int $httpstatus HTTP-Statuscode
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function netdiag_api_error($message, $httpstatus = 400)
|
||||||
|
{
|
||||||
|
netdiag_api_respond(array('error' => $message), $httpstatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64-URL-kodieren (JWT-konform, ohne Padding).
|
||||||
|
*
|
||||||
|
* @param string $data Rohdaten
|
||||||
|
* @return string Kodierter String
|
||||||
|
*/
|
||||||
|
function netdiag_base64url_encode($data)
|
||||||
|
{
|
||||||
|
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64-URL-dekodieren.
|
||||||
|
*
|
||||||
|
* @param string $data Kodierter String
|
||||||
|
* @return string Rohdaten
|
||||||
|
*/
|
||||||
|
function netdiag_base64url_decode($data)
|
||||||
|
{
|
||||||
|
return base64_decode(strtr($data, '-_', '+/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Geheimen JWT-Schlüssel des Moduls holen.
|
||||||
|
*
|
||||||
|
* @return string Schlüssel
|
||||||
|
*/
|
||||||
|
function netdiag_jwt_secret()
|
||||||
|
{
|
||||||
|
$secret = getDolGlobalString('NETDIAG_API_JWT_SECRET');
|
||||||
|
if (empty($secret)) {
|
||||||
|
// Fallback: Instanz-eindeutiger Wert (sollte nach Modulaktivierung nicht eintreten)
|
||||||
|
$secret = md5(DOL_DOCUMENT_ROOT.getDolGlobalString('MAIN_INFO_SOCIETE_NOM'));
|
||||||
|
}
|
||||||
|
return $secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT (HS256) erzeugen.
|
||||||
|
*
|
||||||
|
* @param array<string,mixed> $payload Nutzdaten (sub, name, exp werden ergänzt)
|
||||||
|
* @param int $ttl Gültigkeit in Sekunden
|
||||||
|
* @return string Signiertes Token
|
||||||
|
*/
|
||||||
|
function netdiag_jwt_encode($payload, $ttl)
|
||||||
|
{
|
||||||
|
$header = array('alg' => 'HS256', 'typ' => 'JWT');
|
||||||
|
$now = dol_now();
|
||||||
|
$payload['iat'] = $now;
|
||||||
|
$payload['exp'] = $now + $ttl;
|
||||||
|
|
||||||
|
$seg = array();
|
||||||
|
$seg[] = netdiag_base64url_encode(json_encode($header));
|
||||||
|
$seg[] = netdiag_base64url_encode(json_encode($payload));
|
||||||
|
$signinginput = implode('.', $seg);
|
||||||
|
$signature = hash_hmac('sha256', $signinginput, netdiag_jwt_secret(), true);
|
||||||
|
$seg[] = netdiag_base64url_encode($signature);
|
||||||
|
|
||||||
|
return implode('.', $seg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT prüfen und Nutzdaten zurückgeben.
|
||||||
|
*
|
||||||
|
* @param string $token JWT
|
||||||
|
* @return array<string,mixed>|null Nutzdaten oder null bei ungültig/abgelaufen
|
||||||
|
*/
|
||||||
|
function netdiag_jwt_decode($token)
|
||||||
|
{
|
||||||
|
$parts = explode('.', (string) $token);
|
||||||
|
if (count($parts) !== 3) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
list($h, $p, $s) = $parts;
|
||||||
|
$expected = hash_hmac('sha256', $h.'.'.$p, netdiag_jwt_secret(), true);
|
||||||
|
$given = netdiag_base64url_decode($s);
|
||||||
|
if (!hash_equals($expected, $given)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$payload = json_decode(netdiag_base64url_decode($p), true);
|
||||||
|
if (!is_array($payload)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (empty($payload['exp']) || $payload['exp'] < dol_now()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return $payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token aus Request lesen (Authorization-Header oder ?jwt=).
|
||||||
|
*
|
||||||
|
* @return string Token oder leerer String
|
||||||
|
*/
|
||||||
|
function netdiag_api_read_token()
|
||||||
|
{
|
||||||
|
$auth = '';
|
||||||
|
if (!empty($_SERVER['HTTP_AUTHORIZATION'])) {
|
||||||
|
$auth = $_SERVER['HTTP_AUTHORIZATION'];
|
||||||
|
} elseif (!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
|
||||||
|
$auth = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
|
||||||
|
} elseif (function_exists('apache_request_headers')) {
|
||||||
|
$headers = apache_request_headers();
|
||||||
|
if (!empty($headers['Authorization'])) {
|
||||||
|
$auth = $headers['Authorization'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stripos($auth, 'Bearer ') === 0) {
|
||||||
|
return trim(substr($auth, 7));
|
||||||
|
}
|
||||||
|
if (!empty($_GET['jwt'])) {
|
||||||
|
return (string) $_GET['jwt'];
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aktuellen Request authentifizieren. Bricht mit 401 ab, wenn ungültig.
|
||||||
|
*
|
||||||
|
* @param DoliDB $db Datenbank-Handler
|
||||||
|
* @return User Geladenes Benutzer-Objekt
|
||||||
|
*/
|
||||||
|
function netdiag_api_authenticate($db)
|
||||||
|
{
|
||||||
|
$token = netdiag_api_read_token();
|
||||||
|
if (empty($token)) {
|
||||||
|
netdiag_api_error('Kein Token übermittelt', 401);
|
||||||
|
}
|
||||||
|
$payload = netdiag_jwt_decode($token);
|
||||||
|
if ($payload === null || empty($payload['sub'])) {
|
||||||
|
netdiag_api_error('Token ungültig oder abgelaufen', 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
|
||||||
|
$user = new User($db);
|
||||||
|
if ($user->fetch((int) $payload['sub']) <= 0 || empty($user->id)) {
|
||||||
|
netdiag_api_error('Benutzer nicht gefunden', 401);
|
||||||
|
}
|
||||||
|
if (!empty($user->statut) && $user->statut == 0) {
|
||||||
|
netdiag_api_error('Benutzer deaktiviert', 403);
|
||||||
|
}
|
||||||
|
$user->loadRights();
|
||||||
|
|
||||||
|
if (!$user->hasRight('netdiag', 'protocol', 'read')) {
|
||||||
|
netdiag_api_error('Keine Berechtigung für NetDiag', 403);
|
||||||
|
}
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON-Body eines POST-Requests einlesen.
|
||||||
|
*
|
||||||
|
* @return array<string,mixed> Dekodierte Daten (leer bei Fehler)
|
||||||
|
*/
|
||||||
|
function netdiag_api_read_body()
|
||||||
|
{
|
||||||
|
$raw = file_get_contents('php://input');
|
||||||
|
if (empty($raw)) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
$data = json_decode($raw, true);
|
||||||
|
return is_array($data) ? $data : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liste von Diagnose-Protokollen als Array zurückgeben (für API-Antworten).
|
||||||
|
*
|
||||||
|
* @param DoliDB $db Datenbank-Handler
|
||||||
|
* @param string $filtersql Zusätzlicher SQL-Filter, beginnend mit ' AND ...'
|
||||||
|
* @return array<int,array<string,mixed>> Liste der Protokolle
|
||||||
|
*/
|
||||||
|
function netdiag_api_protocol_list($db, $filtersql = '')
|
||||||
|
{
|
||||||
|
$prefix = $db->prefix();
|
||||||
|
$sql = "SELECT p.rowid, p.ref, p.label, p.client_uuid, p.fk_soc, p.fk_commande,";
|
||||||
|
$sql .= " p.date_diag, p.standort, p.subnet, p.status,";
|
||||||
|
$sql .= " (SELECT COUNT(*) FROM ".$prefix."netdiag_device d WHERE d.fk_protocol = p.rowid) as devcount,";
|
||||||
|
$sql .= " (SELECT COUNT(*) FROM ".$prefix."netdiag_measurement m WHERE m.fk_protocol = p.rowid) as meascount";
|
||||||
|
$sql .= " FROM ".$prefix."netdiag_protocol as p";
|
||||||
|
$sql .= " WHERE p.entity IN (".getEntity('netdiagprotocol').")";
|
||||||
|
$sql .= $filtersql;
|
||||||
|
$sql .= " ORDER BY p.date_diag DESC, p.rowid DESC";
|
||||||
|
|
||||||
|
$list = array();
|
||||||
|
$resql = $db->query($sql);
|
||||||
|
if ($resql) {
|
||||||
|
while ($obj = $db->fetch_object($resql)) {
|
||||||
|
$list[] = array(
|
||||||
|
'id' => (int) $obj->rowid,
|
||||||
|
'ref' => $obj->ref,
|
||||||
|
'label' => $obj->label,
|
||||||
|
'clientUuid' => $obj->client_uuid,
|
||||||
|
'socId' => $obj->fk_soc ? (int) $obj->fk_soc : null,
|
||||||
|
'orderId' => $obj->fk_commande ? (int) $obj->fk_commande : null,
|
||||||
|
'dateDiag' => $db->jdate($obj->date_diag),
|
||||||
|
'location' => $obj->standort,
|
||||||
|
'subnet' => $obj->subnet,
|
||||||
|
'status' => (int) $obj->status,
|
||||||
|
'deviceCount' => (int) $obj->devcount,
|
||||||
|
'measureCount' => (int) $obj->meascount,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
117
api/orders.php
Normal file
117
api/orders.php
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
<?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/api/orders.php
|
||||||
|
* \ingroup netdiag
|
||||||
|
* \brief API-Endpunkt: Aufträge listen (?open=1&q=) oder Auftragsdetail (?id=).
|
||||||
|
*
|
||||||
|
* Auftragsstatus (Dolibarr commande.fk_statut):
|
||||||
|
* -1 = storniert, 0 = Entwurf, 1 = validiert, 2 = in Bearbeitung,
|
||||||
|
* 3 = abgeschlossen. "Aktiv" = Status 0/1/2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once __DIR__.'/netdiag_api.lib.php';
|
||||||
|
|
||||||
|
netdiag_api_bootstrap();
|
||||||
|
|
||||||
|
/** @var DoliDB $db */
|
||||||
|
|
||||||
|
$user = netdiag_api_authenticate($db);
|
||||||
|
|
||||||
|
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
||||||
|
$q = isset($_GET['q']) ? trim((string) $_GET['q']) : '';
|
||||||
|
$onlyopen = !empty($_GET['open']);
|
||||||
|
$limit = isset($_GET['limit']) ? min(200, max(1, (int) $_GET['limit'])) : 80;
|
||||||
|
|
||||||
|
$prefix = $db->prefix();
|
||||||
|
|
||||||
|
// ---- Auftragsdetail ----
|
||||||
|
if ($id > 0) {
|
||||||
|
$sql = "SELECT c.rowid, c.ref, c.ref_client, c.date_commande, c.fk_statut, c.note_public,";
|
||||||
|
$sql .= " s.rowid as socid, s.nom as socname, s.address, s.zip, s.town, s.phone, s.email";
|
||||||
|
$sql .= " FROM ".$prefix."commande as c";
|
||||||
|
$sql .= " LEFT JOIN ".$prefix."societe as s ON s.rowid = c.fk_soc";
|
||||||
|
$sql .= " WHERE c.rowid = ".((int) $id)." AND c.entity IN (".getEntity('commande').")";
|
||||||
|
$resql = $db->query($sql);
|
||||||
|
if (!$resql || !($obj = $db->fetch_object($resql))) {
|
||||||
|
netdiag_api_error('Auftrag nicht gefunden', 404);
|
||||||
|
}
|
||||||
|
$order = array(
|
||||||
|
'id' => (int) $obj->rowid,
|
||||||
|
'ref' => $obj->ref,
|
||||||
|
'refClient' => $obj->ref_client,
|
||||||
|
'date' => $db->jdate($obj->date_commande),
|
||||||
|
'status' => (int) $obj->fk_statut,
|
||||||
|
'open' => ((int) $obj->fk_statut >= 0 && (int) $obj->fk_statut < 3),
|
||||||
|
'note' => $obj->note_public,
|
||||||
|
'customer' => array(
|
||||||
|
'id' => (int) $obj->socid,
|
||||||
|
'name' => $obj->socname,
|
||||||
|
'address' => $obj->address,
|
||||||
|
'zip' => $obj->zip,
|
||||||
|
'town' => $obj->town,
|
||||||
|
'phone' => $obj->phone,
|
||||||
|
'email' => $obj->email,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$protocols = netdiag_api_protocol_list($db, ' AND p.fk_commande = '.((int) $id));
|
||||||
|
|
||||||
|
netdiag_api_respond(array('order' => $order, 'protocols' => $protocols));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Auftragsliste ----
|
||||||
|
$sql = "SELECT c.rowid, c.ref, c.ref_client, c.date_commande, c.fk_statut,";
|
||||||
|
$sql .= " s.rowid as socid, s.nom as socname, s.zip, s.town,";
|
||||||
|
$sql .= " (SELECT COUNT(*) FROM ".$prefix."netdiag_protocol p WHERE p.fk_commande = c.rowid) as protocolcount";
|
||||||
|
$sql .= " FROM ".$prefix."commande as c";
|
||||||
|
$sql .= " LEFT JOIN ".$prefix."societe as s ON s.rowid = c.fk_soc";
|
||||||
|
$sql .= " WHERE c.entity IN (".getEntity('commande').")";
|
||||||
|
if ($onlyopen) {
|
||||||
|
// Aktive Aufträge: Entwurf / validiert / in Bearbeitung
|
||||||
|
$sql .= " AND c.fk_statut IN (0, 1, 2)";
|
||||||
|
}
|
||||||
|
if ($q !== '') {
|
||||||
|
$sql .= " AND (".natural_search(array('c.ref', 'c.ref_client', 's.nom', 's.town'), $q, 0, 1).")";
|
||||||
|
}
|
||||||
|
$sql .= " ORDER BY c.date_commande DESC, c.rowid DESC";
|
||||||
|
$sql .= $db->plimit($limit, 0);
|
||||||
|
|
||||||
|
$resql = $db->query($sql);
|
||||||
|
if (!$resql) {
|
||||||
|
netdiag_api_error('Datenbankfehler: '.$db->lasterror(), 500);
|
||||||
|
}
|
||||||
|
$orders = array();
|
||||||
|
while ($obj = $db->fetch_object($resql)) {
|
||||||
|
$orders[] = array(
|
||||||
|
'id' => (int) $obj->rowid,
|
||||||
|
'ref' => $obj->ref,
|
||||||
|
'refClient' => $obj->ref_client,
|
||||||
|
'date' => $db->jdate($obj->date_commande),
|
||||||
|
'status' => (int) $obj->fk_statut,
|
||||||
|
'open' => ((int) $obj->fk_statut >= 0 && (int) $obj->fk_statut < 3),
|
||||||
|
'protocolCount' => (int) $obj->protocolcount,
|
||||||
|
'customer' => array(
|
||||||
|
'id' => $obj->socid ? (int) $obj->socid : null,
|
||||||
|
'name' => $obj->socname,
|
||||||
|
'zip' => $obj->zip,
|
||||||
|
'town' => $obj->town,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
netdiag_api_respond(array('orders' => $orders));
|
||||||
67
api/pdf.php
Normal file
67
api/pdf.php
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?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/api/pdf.php
|
||||||
|
* \ingroup netdiag
|
||||||
|
* \brief API-Endpunkt: Protokoll-PDF streamen (GET ?id=&jwt=).
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once __DIR__.'/netdiag_api.lib.php';
|
||||||
|
|
||||||
|
netdiag_api_bootstrap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DoliDB $db
|
||||||
|
* @var Translate $langs
|
||||||
|
*/
|
||||||
|
|
||||||
|
$user = netdiag_api_authenticate($db);
|
||||||
|
|
||||||
|
require_once __DIR__.'/../class/netdiagprotocol.class.php';
|
||||||
|
require_once __DIR__.'/../lib/netdiag.lib.php';
|
||||||
|
require_once __DIR__.'/../lib/netdiag_pdf.lib.php';
|
||||||
|
|
||||||
|
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
||||||
|
if ($id <= 0) {
|
||||||
|
netdiag_api_error('Parameter id fehlt', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$protocol = new NetDiagProtocol($db);
|
||||||
|
if ($protocol->fetch($id) <= 0) {
|
||||||
|
netdiag_api_error('Protokoll nicht gefunden', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = netdiagGetOutputDir().'/'.dol_sanitizeFileName($protocol->ref).'/'.dol_sanitizeFileName($protocol->ref).'.pdf';
|
||||||
|
|
||||||
|
// PDF erzeugen, wenn noch nicht vorhanden oder Neuerzeugung verlangt
|
||||||
|
if (!dol_is_file($file) || !empty($_GET['regenerate'])) {
|
||||||
|
if (netdiagGeneratePdf($db, $protocol, $langs) <= 0) {
|
||||||
|
netdiag_api_error('PDF-Erzeugung fehlgeschlagen', 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dol_is_file($file)) {
|
||||||
|
netdiag_api_error('PDF nicht gefunden', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PDF ausliefern
|
||||||
|
header('Content-Type: application/pdf');
|
||||||
|
header('Content-Disposition: inline; filename="'.dol_sanitizeFileName($protocol->ref).'.pdf"');
|
||||||
|
header('Content-Length: '.dol_filesize($file));
|
||||||
|
header('Cache-Control: private, max-age=0');
|
||||||
|
readfile($file);
|
||||||
|
exit;
|
||||||
215
api/protocols.php
Normal file
215
api/protocols.php
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
<?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/api/protocols.php
|
||||||
|
* \ingroup netdiag
|
||||||
|
* \brief API-Endpunkt: Protokolle lesen (GET ?id=) und synchronisieren
|
||||||
|
* (POST action=sync). Der Sync ist idempotent über client_uuid.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once __DIR__.'/netdiag_api.lib.php';
|
||||||
|
|
||||||
|
netdiag_api_bootstrap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Conf $conf
|
||||||
|
* @var DoliDB $db
|
||||||
|
* @var Translate $langs
|
||||||
|
*/
|
||||||
|
|
||||||
|
$user = netdiag_api_authenticate($db);
|
||||||
|
|
||||||
|
require_once __DIR__.'/../class/netdiagprotocol.class.php';
|
||||||
|
require_once __DIR__.'/../class/netdiagdevice.class.php';
|
||||||
|
require_once __DIR__.'/../class/netdiagmeasurement.class.php';
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// GET: einzelnes Protokoll mit Geräten und Messungen
|
||||||
|
// =========================================================================
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
||||||
|
if ($id <= 0) {
|
||||||
|
netdiag_api_error('Parameter id fehlt', 400);
|
||||||
|
}
|
||||||
|
$protocol = new NetDiagProtocol($db);
|
||||||
|
if ($protocol->fetch($id) <= 0) {
|
||||||
|
netdiag_api_error('Protokoll nicht gefunden', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$devObj = new NetDiagDevice($db);
|
||||||
|
$devices = array();
|
||||||
|
foreach ($devObj->fetchAllByProtocol($protocol->id) as $d) {
|
||||||
|
$devices[] = array(
|
||||||
|
'id' => (int) $d->id,
|
||||||
|
'ip' => $d->ip,
|
||||||
|
'mac' => $d->mac,
|
||||||
|
'hostname' => $d->hostname,
|
||||||
|
'vendor' => $d->vendor,
|
||||||
|
'deviceType' => $d->devicetype,
|
||||||
|
'note' => $d->note,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$measObj = new NetDiagMeasurement($db);
|
||||||
|
$measurements = array();
|
||||||
|
foreach ($measObj->fetchAllByProtocol($protocol->id) as $m) {
|
||||||
|
$measurements[] = array(
|
||||||
|
'id' => (int) $m->id,
|
||||||
|
'deviceId' => $m->fk_device ? (int) $m->fk_device : null,
|
||||||
|
'tool' => $m->tool,
|
||||||
|
'category' => $m->category,
|
||||||
|
'label' => $m->label,
|
||||||
|
'params' => $m->params ? json_decode($m->params, true) : null,
|
||||||
|
'result' => $m->result ? json_decode($m->result, true) : null,
|
||||||
|
'measureStatus' => (int) $m->measure_status,
|
||||||
|
'dateMeasure' => $db->jdate($m->date_measure),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
netdiag_api_respond(array(
|
||||||
|
'protocol' => array(
|
||||||
|
'id' => (int) $protocol->id,
|
||||||
|
'ref' => $protocol->ref,
|
||||||
|
'label' => $protocol->label,
|
||||||
|
'clientUuid' => $protocol->client_uuid,
|
||||||
|
'socId' => $protocol->fk_soc ? (int) $protocol->fk_soc : null,
|
||||||
|
'orderId' => $protocol->fk_commande ? (int) $protocol->fk_commande : null,
|
||||||
|
'dateDiag' => $db->jdate($protocol->date_diag),
|
||||||
|
'location' => $protocol->standort,
|
||||||
|
'subnet' => $protocol->subnet,
|
||||||
|
'status' => (int) $protocol->status,
|
||||||
|
'note' => $protocol->note,
|
||||||
|
),
|
||||||
|
'devices' => $devices,
|
||||||
|
'measurements' => $measurements,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// POST: Protokoll synchronisieren (anlegen oder aktualisieren)
|
||||||
|
// =========================================================================
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
netdiag_api_error('Methode nicht erlaubt', 405);
|
||||||
|
}
|
||||||
|
if (!$user->hasRight('netdiag', 'protocol', 'write')) {
|
||||||
|
netdiag_api_error('Keine Schreibberechtigung', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = netdiag_api_read_body();
|
||||||
|
if (($body['action'] ?? '') !== 'sync' || empty($body['protocol']) || !is_array($body['protocol'])) {
|
||||||
|
netdiag_api_error('Erwartet: { action: "sync", protocol: {...} }', 400);
|
||||||
|
}
|
||||||
|
$p = $body['protocol'];
|
||||||
|
|
||||||
|
$uuid = isset($p['clientUuid']) ? trim((string) $p['clientUuid']) : '';
|
||||||
|
if ($uuid === '') {
|
||||||
|
netdiag_api_error('protocol.clientUuid erforderlich', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->begin();
|
||||||
|
|
||||||
|
// Vorhandenes Protokoll über UUID suchen (idempotent)
|
||||||
|
$protocol = new NetDiagProtocol($db);
|
||||||
|
$exists = $protocol->fetchByClientUuid($uuid);
|
||||||
|
if ($exists < 0) {
|
||||||
|
$db->rollback();
|
||||||
|
netdiag_api_error('Datenbankfehler beim Laden', 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
$protocol->client_uuid = $uuid;
|
||||||
|
$protocol->label = isset($p['label']) ? (string) $p['label'] : '';
|
||||||
|
$protocol->fk_soc = !empty($p['socId']) ? (int) $p['socId'] : null;
|
||||||
|
$protocol->fk_commande = !empty($p['orderId']) ? (int) $p['orderId'] : null;
|
||||||
|
$protocol->date_diag = !empty($p['dateDiag']) ? (int) $p['dateDiag'] : dol_now();
|
||||||
|
$protocol->fk_user_techniker = (int) $user->id;
|
||||||
|
$protocol->standort = isset($p['location']) ? (string) $p['location'] : '';
|
||||||
|
$protocol->subnet = isset($p['subnet']) ? (string) $p['subnet'] : '';
|
||||||
|
$protocol->status = isset($p['status']) ? (int) $p['status'] : NetDiagProtocol::STATUS_DRAFT;
|
||||||
|
$protocol->note = isset($p['note']) ? (string) $p['note'] : '';
|
||||||
|
|
||||||
|
if ($exists > 0) {
|
||||||
|
$result = $protocol->update($user, 1);
|
||||||
|
} else {
|
||||||
|
$result = $protocol->create($user, 1);
|
||||||
|
}
|
||||||
|
if ($result <= 0) {
|
||||||
|
$db->rollback();
|
||||||
|
netdiag_api_error('Protokoll speichern fehlgeschlagen: '.$protocol->error, 500);
|
||||||
|
}
|
||||||
|
$protocolId = (int) $protocol->id;
|
||||||
|
|
||||||
|
// Alte Geräte und Messungen entfernen (Sync ersetzt komplett)
|
||||||
|
$db->query("DELETE FROM ".$db->prefix()."netdiag_measurement WHERE fk_protocol = ".$protocolId);
|
||||||
|
$db->query("DELETE FROM ".$db->prefix()."netdiag_device WHERE fk_protocol = ".$protocolId);
|
||||||
|
|
||||||
|
// Geräte einfügen, dabei clientId -> serverRowid merken
|
||||||
|
$deviceIdMap = array();
|
||||||
|
$devicesIn = (!empty($p['devices']) && is_array($p['devices'])) ? $p['devices'] : array();
|
||||||
|
foreach ($devicesIn as $d) {
|
||||||
|
$dev = new NetDiagDevice($db);
|
||||||
|
$dev->fk_protocol = $protocolId;
|
||||||
|
$dev->ip = isset($d['ip']) ? (string) $d['ip'] : '';
|
||||||
|
$dev->mac = isset($d['mac']) ? (string) $d['mac'] : '';
|
||||||
|
$dev->hostname = isset($d['hostname']) ? (string) $d['hostname'] : '';
|
||||||
|
$dev->vendor = isset($d['vendor']) ? (string) $d['vendor'] : '';
|
||||||
|
$dev->devicetype = isset($d['deviceType']) ? (string) $d['deviceType'] : '';
|
||||||
|
$dev->note = isset($d['note']) ? (string) $d['note'] : '';
|
||||||
|
if ($dev->create($user, 1) <= 0) {
|
||||||
|
$db->rollback();
|
||||||
|
netdiag_api_error('Gerät speichern fehlgeschlagen: '.$dev->error, 500);
|
||||||
|
}
|
||||||
|
if (isset($d['clientId'])) {
|
||||||
|
$deviceIdMap[(string) $d['clientId']] = (int) $dev->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messungen einfügen, deviceClientId auf serverRowid abbilden
|
||||||
|
$measIn = (!empty($p['measurements']) && is_array($p['measurements'])) ? $p['measurements'] : array();
|
||||||
|
foreach ($measIn as $m) {
|
||||||
|
$meas = new NetDiagMeasurement($db);
|
||||||
|
$meas->fk_protocol = $protocolId;
|
||||||
|
$dcid = isset($m['deviceClientId']) ? (string) $m['deviceClientId'] : '';
|
||||||
|
$meas->fk_device = ($dcid !== '' && isset($deviceIdMap[$dcid])) ? $deviceIdMap[$dcid] : null;
|
||||||
|
$meas->tool = isset($m['tool']) ? (string) $m['tool'] : 'unknown';
|
||||||
|
$meas->category = isset($m['category']) ? (string) $m['category'] : '';
|
||||||
|
$meas->label = isset($m['label']) ? (string) $m['label'] : '';
|
||||||
|
$meas->params = isset($m['params']) ? json_encode($m['params'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null;
|
||||||
|
$meas->result = isset($m['result']) ? json_encode($m['result'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null;
|
||||||
|
$meas->measure_status = isset($m['measureStatus']) ? (int) $m['measureStatus'] : 0;
|
||||||
|
$meas->date_measure = !empty($m['dateMeasure']) ? (int) $m['dateMeasure'] : dol_now();
|
||||||
|
if ($meas->create($user, 1) <= 0) {
|
||||||
|
$db->rollback();
|
||||||
|
netdiag_api_error('Messung speichern fehlgeschlagen: '.$meas->error, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->commit();
|
||||||
|
|
||||||
|
// PDF neu erzeugen, wenn das Protokoll abgeschlossen ist
|
||||||
|
$pdfgenerated = false;
|
||||||
|
if ($protocol->status == NetDiagProtocol::STATUS_DONE) {
|
||||||
|
require_once __DIR__.'/../lib/netdiag_pdf.lib.php';
|
||||||
|
$pdfgenerated = (netdiagGeneratePdf($db, $protocol, $langs) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
netdiag_api_respond(array(
|
||||||
|
'ok' => true,
|
||||||
|
'protocolId' => $protocolId,
|
||||||
|
'ref' => $protocol->ref,
|
||||||
|
'created' => ($exists == 0),
|
||||||
|
'pdfGenerated' => $pdfgenerated,
|
||||||
|
));
|
||||||
156
class/netdiagdevice.class.php
Normal file
156
class/netdiagdevice.class.php
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
<?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),
|
||||||
|
'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),
|
||||||
|
'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' => 1, 'position' => 501),
|
||||||
|
);
|
||||||
|
|
||||||
|
public $rowid;
|
||||||
|
public $entity;
|
||||||
|
public $fk_protocol;
|
||||||
|
public $ip;
|
||||||
|
public $mac;
|
||||||
|
public $hostname;
|
||||||
|
public $vendor;
|
||||||
|
public $devicetype;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
170
class/netdiagmeasurement.class.php
Normal file
170
class/netdiagmeasurement.class.php
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
<?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/netdiagmeasurement.class.php
|
||||||
|
* \ingroup netdiag
|
||||||
|
* \brief Klasse für eine Messung (ein Tool-Lauf) im Diagnose-Protokoll
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Klasse NetDiagMeasurement — Ergebnis eines Tool-Laufs.
|
||||||
|
*
|
||||||
|
* params/result sind generisches JSON: neue Tools brauchen kein Schema-Update.
|
||||||
|
*/
|
||||||
|
class NetDiagMeasurement extends CommonObject
|
||||||
|
{
|
||||||
|
/** @var string Modul-Name */
|
||||||
|
public $module = 'netdiag';
|
||||||
|
|
||||||
|
/** @var string Element-Typ */
|
||||||
|
public $element = 'netdiagmeasurement';
|
||||||
|
|
||||||
|
/** @var string Datenbanktabelle (ohne Präfix) */
|
||||||
|
public $table_element = 'netdiag_measurement';
|
||||||
|
|
||||||
|
/** @var string Icon */
|
||||||
|
public $picto = 'fa-wave-square';
|
||||||
|
|
||||||
|
/** @var int 1 = Tabelle hat ein 'entity'-Feld */
|
||||||
|
public $ismultientitymanaged = 1;
|
||||||
|
|
||||||
|
/** Ampel-Status der Messung */
|
||||||
|
const RESULT_OK = 0;
|
||||||
|
const RESULT_WARN = 1;
|
||||||
|
const RESULT_FAIL = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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),
|
||||||
|
'fk_device' => array('type' => 'integer', 'label' => 'Device', 'enabled' => 1, 'visible' => 0, 'index' => 1, 'position' => 15),
|
||||||
|
'tool' => array('type' => 'varchar(64)', 'label' => 'Tool', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'position' => 20),
|
||||||
|
'category' => array('type' => 'varchar(32)', 'label' => 'ToolCategory', 'enabled' => 1, 'visible' => 1, 'position' => 25),
|
||||||
|
'label' => array('type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'visible' => 1, 'position' => 30),
|
||||||
|
'params' => array('type' => 'text', 'label' => 'Params', 'enabled' => 1, 'visible' => 0, 'position' => 40),
|
||||||
|
'result' => array('type' => 'text', 'label' => 'Result', 'enabled' => 1, 'visible' => 0, 'position' => 45),
|
||||||
|
'measure_status' => array('type' => 'smallint', 'label' => 'MeasureStatus', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'default' => 0, 'position' => 50),
|
||||||
|
'date_measure' => array('type' => 'datetime', 'label' => 'DateMeasure', 'enabled' => 1, 'visible' => 1, 'position' => 55),
|
||||||
|
'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' => 1, 'position' => 501),
|
||||||
|
);
|
||||||
|
|
||||||
|
public $rowid;
|
||||||
|
public $entity;
|
||||||
|
public $fk_protocol;
|
||||||
|
public $fk_device;
|
||||||
|
public $tool;
|
||||||
|
public $category;
|
||||||
|
public $label;
|
||||||
|
public $params;
|
||||||
|
public $result;
|
||||||
|
public $measure_status;
|
||||||
|
public $date_measure;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (empty($this->date_measure)) {
|
||||||
|
$this->date_measure = dol_now();
|
||||||
|
}
|
||||||
|
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 Messungen eines Protokolls laden
|
||||||
|
*
|
||||||
|
* @param int $fk_protocol Protokoll-Rowid
|
||||||
|
* @return NetDiagMeasurement[] Liste der Messungen
|
||||||
|
*/
|
||||||
|
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 date_measure, rowid";
|
||||||
|
$resql = $this->db->query($sql);
|
||||||
|
if ($resql) {
|
||||||
|
while ($obj = $this->db->fetch_object($resql)) {
|
||||||
|
$m = new self($this->db);
|
||||||
|
if ($m->fetch((int) $obj->rowid) > 0) {
|
||||||
|
$result[] = $m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
233
class/netdiagprotocol.class.php
Normal file
233
class/netdiagprotocol.class.php
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
<?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/netdiagprotocol.class.php
|
||||||
|
* \ingroup netdiag
|
||||||
|
* \brief Klasse für das Diagnose-Protokoll (Kopfdatensatz)
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Klasse NetDiagProtocol — ein Netzwerk-Diagnose-Vorgang beim Kunden.
|
||||||
|
*
|
||||||
|
* Bündelt gefundene Geräte (NetDiagDevice) und Messungen (NetDiagMeasurement)
|
||||||
|
* und ist mit Kunde (fk_soc) sowie optional Auftrag (fk_commande) verknüpft.
|
||||||
|
*/
|
||||||
|
class NetDiagProtocol extends CommonObject
|
||||||
|
{
|
||||||
|
/** @var string Modul-Name */
|
||||||
|
public $module = 'netdiag';
|
||||||
|
|
||||||
|
/** @var string Element-Typ */
|
||||||
|
public $element = 'netdiagprotocol';
|
||||||
|
|
||||||
|
/** @var string Datenbanktabelle (ohne Präfix) */
|
||||||
|
public $table_element = 'netdiag_protocol';
|
||||||
|
|
||||||
|
/** @var string Icon */
|
||||||
|
public $picto = 'fa-network-wired';
|
||||||
|
|
||||||
|
/** @var int 1 = Tabelle hat ein 'entity'-Feld für Multicompany */
|
||||||
|
public $ismultientitymanaged = 1;
|
||||||
|
|
||||||
|
/** Status-Konstanten */
|
||||||
|
const STATUS_DRAFT = 0;
|
||||||
|
const STATUS_DONE = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string,array<string,mixed>> Felddefinitionen für die generischen CRUD-Methoden
|
||||||
|
*/
|
||||||
|
public $fields = array(
|
||||||
|
'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'index' => 1, 'position' => 1),
|
||||||
|
'ref' => array('type' => 'varchar(128)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 10),
|
||||||
|
'entity' => array('type' => 'integer', 'label' => 'Entity', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'default' => 1, 'index' => 1, 'position' => 5),
|
||||||
|
'label' => array('type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'visible' => 1, 'position' => 20),
|
||||||
|
'client_uuid' => array('type' => 'varchar(64)', 'label' => 'ClientUuid', 'enabled' => 1, 'visible' => 0, 'position' => 25),
|
||||||
|
'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 1, 'visible' => 1, 'index' => 1, 'position' => 30),
|
||||||
|
'fk_commande' => array('type' => 'integer:Commande:commande/class/commande.class.php', 'label' => 'Order', 'enabled' => 1, 'visible' => 1, 'index' => 1, 'position' => 35),
|
||||||
|
'date_diag' => array('type' => 'datetime', 'label' => 'DateDiag', 'enabled' => 1, 'visible' => 1, 'position' => 40),
|
||||||
|
'fk_user_techniker' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Technician', 'enabled' => 1, 'visible' => 1, 'position' => 45),
|
||||||
|
'standort' => array('type' => 'varchar(255)', 'label' => 'Location', 'enabled' => 1, 'visible' => 1, 'position' => 50),
|
||||||
|
'subnet' => array('type' => 'varchar(64)', 'label' => 'Subnet', 'enabled' => 1, 'visible' => 1, 'position' => 55),
|
||||||
|
'status' => array('type' => 'smallint', 'label' => 'Status', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'default' => 0, 'index' => 1, 'position' => 60, 'arrayofkeyval' => array(0 => 'Draft', 1 => 'Done')),
|
||||||
|
'note' => array('type' => 'text', 'label' => 'Note', 'enabled' => 1, 'visible' => 3, 'position' => 70),
|
||||||
|
'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' => 1, 'position' => 501),
|
||||||
|
'fk_user_creat' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 510),
|
||||||
|
'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => 0, 'position' => 511),
|
||||||
|
);
|
||||||
|
|
||||||
|
public $rowid;
|
||||||
|
public $ref;
|
||||||
|
public $entity;
|
||||||
|
public $label;
|
||||||
|
public $client_uuid;
|
||||||
|
public $fk_soc;
|
||||||
|
public $fk_commande;
|
||||||
|
public $date_diag;
|
||||||
|
public $fk_user_techniker;
|
||||||
|
public $standort;
|
||||||
|
public $subnet;
|
||||||
|
public $status;
|
||||||
|
public $note;
|
||||||
|
public $date_creation;
|
||||||
|
public $tms;
|
||||||
|
public $fk_user_creat;
|
||||||
|
public $fk_user_modif;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 (rowid), <=0 bei Fehler
|
||||||
|
*/
|
||||||
|
public function create(User $user, $notrigger = 0)
|
||||||
|
{
|
||||||
|
if (empty($this->ref) || $this->ref == '(PROV)') {
|
||||||
|
$this->ref = $this->getNextNumRef();
|
||||||
|
}
|
||||||
|
if (empty($this->date_diag)) {
|
||||||
|
$this->date_diag = dol_now();
|
||||||
|
}
|
||||||
|
return $this->createCommon($user, $notrigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Datensatz laden
|
||||||
|
*
|
||||||
|
* @param int $id Rowid
|
||||||
|
* @param string $ref Referenz statt Rowid
|
||||||
|
* @return int >0 wenn OK, 0 wenn nicht gefunden, <0 bei Fehler
|
||||||
|
*/
|
||||||
|
public function fetch($id, $ref = null)
|
||||||
|
{
|
||||||
|
return $this->fetchCommon($id, $ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protokoll anhand der Client-UUID laden (für idempotenten App-Sync)
|
||||||
|
*
|
||||||
|
* @param string $uuid Client-UUID
|
||||||
|
* @return int >0 wenn OK, 0 wenn nicht gefunden, <0 bei Fehler
|
||||||
|
*/
|
||||||
|
public function fetchByClientUuid($uuid)
|
||||||
|
{
|
||||||
|
$sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
|
||||||
|
$sql .= " WHERE client_uuid = '".$this->db->escape($uuid)."'";
|
||||||
|
$sql .= " AND entity IN (".getEntity($this->element).")";
|
||||||
|
$resql = $this->db->query($sql);
|
||||||
|
if (!$resql) {
|
||||||
|
$this->error = $this->db->lasterror();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ($obj = $this->db->fetch_object($resql)) {
|
||||||
|
return $this->fetch((int) $obj->rowid);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 (inkl. zugehöriger Geräte und Messungen)
|
||||||
|
*
|
||||||
|
* @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)
|
||||||
|
{
|
||||||
|
$this->db->begin();
|
||||||
|
$tables = array('netdiag_measurement', 'netdiag_device');
|
||||||
|
foreach ($tables as $table) {
|
||||||
|
$sql = "DELETE FROM ".$this->db->prefix().$table." WHERE fk_protocol = ".((int) $this->id);
|
||||||
|
if (!$this->db->query($sql)) {
|
||||||
|
$this->error = $this->db->lasterror();
|
||||||
|
$this->db->rollback();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$result = $this->deleteCommon($user, $notrigger);
|
||||||
|
if ($result <= 0) {
|
||||||
|
$this->db->rollback();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
$this->db->commit();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nächste freie Referenz ermitteln (NDjjjj-nnnn)
|
||||||
|
*
|
||||||
|
* @return string Neue Referenz
|
||||||
|
*/
|
||||||
|
public function getNextNumRef()
|
||||||
|
{
|
||||||
|
$prefix = 'ND'.dol_print_date(dol_now(), '%Y').'-';
|
||||||
|
$sql = "SELECT MAX(CAST(SUBSTRING(ref, ".(strlen($prefix) + 1).") AS UNSIGNED)) AS maxnum";
|
||||||
|
$sql .= " FROM ".$this->db->prefix().$this->table_element;
|
||||||
|
$sql .= " WHERE ref LIKE '".$this->db->escape($prefix)."%'";
|
||||||
|
$sql .= " AND entity IN (".getEntity($this->element).")";
|
||||||
|
$resql = $this->db->query($sql);
|
||||||
|
$num = 0;
|
||||||
|
if ($resql && ($obj = $this->db->fetch_object($resql))) {
|
||||||
|
$num = (int) $obj->maxnum;
|
||||||
|
}
|
||||||
|
return $prefix.sprintf('%04d', $num + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Beschriftung des Status zurückgeben
|
||||||
|
*
|
||||||
|
* @param int $mode 0=Langtext, 1=Kurztext, 3=Picto, ...
|
||||||
|
* @return string HTML-Label
|
||||||
|
*/
|
||||||
|
public function getLibStatut($mode = 0)
|
||||||
|
{
|
||||||
|
global $langs;
|
||||||
|
$labels = array(
|
||||||
|
self::STATUS_DRAFT => $langs->trans('Draft'),
|
||||||
|
self::STATUS_DONE => $langs->trans('NetDiagStatusDone'),
|
||||||
|
);
|
||||||
|
$label = isset($labels[$this->status]) ? $labels[$this->status] : '';
|
||||||
|
$statustype = ($this->status == self::STATUS_DONE) ? 'status4' : 'status0';
|
||||||
|
return dolGetStatus($label, $label, '', $statustype, $mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
240
core/modules/modNetDiag.class.php
Normal file
240
core/modules/modNetDiag.class.php
Normal file
|
|
@ -0,0 +1,240 @@
|
||||||
|
<?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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \defgroup netdiag Modul NetDiag
|
||||||
|
* \brief NetDiag Modul-Descriptor — Netzwerk-Diagnose-Protokolle.
|
||||||
|
* \file htdocs/custom/netdiag/core/modules/modNetDiag.class.php
|
||||||
|
* \ingroup netdiag
|
||||||
|
* \brief Beschreibungs- und Aktivierungsdatei für das Modul NetDiag
|
||||||
|
*/
|
||||||
|
include_once DOL_DOCUMENT_ROOT.'/core/modules/DolibarrModules.class.php';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Beschreibungs- und Aktivierungsklasse für das Modul NetDiag
|
||||||
|
*
|
||||||
|
* NetDiag speichert Netzwerk-Diagnose-Protokolle (IP-Scan, Port-Scan, Ping,
|
||||||
|
* WLAN, DHCP, SNMP, Durchsatz, Stresstest) und hängt sie an Kunde und Auftrag.
|
||||||
|
* Die JSON-API unter /custom/netdiag/api/ versorgt die mobile Diagnose-App.
|
||||||
|
*/
|
||||||
|
class modNetDiag extends DolibarrModules
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Konstruktor. Definiert Namen, Konstanten, Verzeichnisse, Rechte, Menüs.
|
||||||
|
*
|
||||||
|
* @param DoliDB $db Datenbank-Handler
|
||||||
|
*/
|
||||||
|
public function __construct($db)
|
||||||
|
{
|
||||||
|
global $conf, $langs;
|
||||||
|
|
||||||
|
$this->db = $db;
|
||||||
|
|
||||||
|
// Eindeutige Modul-ID (freier Bereich, siehe Wiki Liste der Modul-IDs)
|
||||||
|
$this->numero = 500100;
|
||||||
|
|
||||||
|
// Schlüsseltext zur Identifikation (Rechte, Menüs, ...)
|
||||||
|
$this->rights_class = 'netdiag';
|
||||||
|
|
||||||
|
// Familie: 'technic' = transversales/technisches Modul
|
||||||
|
$this->family = "technic";
|
||||||
|
$this->module_position = '90';
|
||||||
|
|
||||||
|
// Modul-Label (ohne Leerzeichen), Fallback wenn Übersetzung 'ModuleNetDiagName' fehlt
|
||||||
|
$this->name = preg_replace('/^mod/i', '', get_class($this));
|
||||||
|
|
||||||
|
// Modulbeschreibung, Fallback wenn Übersetzung 'ModuleNetDiagDesc' fehlt
|
||||||
|
$this->description = "ModuleNetDiagDesc";
|
||||||
|
$this->descriptionlong = "Netzwerk-Diagnose-Protokolle: erfasst per mobiler App Geräte, Ports, Messungen und hängt sie an Kunde und Auftrag.";
|
||||||
|
|
||||||
|
// Autor
|
||||||
|
$this->editor_name = 'Alles Watt läuft';
|
||||||
|
$this->editor_url = '';
|
||||||
|
$this->editor_squarred_logo = '';
|
||||||
|
|
||||||
|
$this->version = '1.0.0';
|
||||||
|
|
||||||
|
// Konstanten-Name in llx_const für Modul-Status
|
||||||
|
$this->const_name = 'MAIN_MODULE_'.strtoupper($this->name);
|
||||||
|
|
||||||
|
// Icon (Font-Awesome)
|
||||||
|
$this->picto = 'fa-network-wired';
|
||||||
|
|
||||||
|
// Vom Modul unterstützte Funktionen
|
||||||
|
$this->module_parts = array(
|
||||||
|
'triggers' => 0,
|
||||||
|
'login' => 0,
|
||||||
|
'substitutions' => 0,
|
||||||
|
'menus' => 0,
|
||||||
|
'tpl' => 0,
|
||||||
|
'barcode' => 0,
|
||||||
|
'models' => 1,
|
||||||
|
'printing' => 0,
|
||||||
|
'theme' => 0,
|
||||||
|
'css' => array(),
|
||||||
|
'js' => array(),
|
||||||
|
'hooks' => array(),
|
||||||
|
'moduleforexternal' => 0,
|
||||||
|
'websitetemplates' => 0,
|
||||||
|
'captcha' => 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Datenverzeichnisse, die beim Aktivieren angelegt werden
|
||||||
|
$this->dirs = array("/netdiag/temp");
|
||||||
|
|
||||||
|
// Konfigurationsseiten (in netdiag/admin)
|
||||||
|
$this->config_page_url = array("setup.php@netdiag");
|
||||||
|
|
||||||
|
// Abhängigkeiten
|
||||||
|
$this->hidden = false;
|
||||||
|
$this->depends = array();
|
||||||
|
$this->requiredby = array();
|
||||||
|
$this->conflictwith = array();
|
||||||
|
|
||||||
|
// Sprachdatei des Moduls
|
||||||
|
$this->langfiles = array("netdiag@netdiag");
|
||||||
|
|
||||||
|
// Voraussetzungen
|
||||||
|
$this->phpmin = array(7, 4);
|
||||||
|
$this->need_dolibarr_version = array(19, -3);
|
||||||
|
$this->need_javascript_ajax = 0;
|
||||||
|
|
||||||
|
$this->warnings_activation = array();
|
||||||
|
$this->warnings_activation_ext = array();
|
||||||
|
|
||||||
|
// Konstanten beim Aktivieren anlegen
|
||||||
|
// (Key, Typ, Wert, Beschreibung, sichtbar, current/allentities, deleteonunactive)
|
||||||
|
$this->const = array(
|
||||||
|
1 => array('NETDIAG_API_JWT_SECRET', 'chaine', dol_hash(dol_print_date(dol_now(), 'dayhourrfc').mt_rand(), 'md5'), 'Geheimer Schlüssel zum Signieren der API-JWT', 0, 'current', 1),
|
||||||
|
2 => array('NETDIAG_API_TOKEN_TTL', 'chaine', '604800', 'Gültigkeit des API-Tokens in Sekunden (Standard 7 Tage)', 1, 'current', 0),
|
||||||
|
3 => array('NETDIAG_APK_URL', 'chaine', 'https://git.data-it-solution.de/api/packages/data-it/generic/netdiag-apk/latest/NetDiag.apk', 'Download-URL der Android-App (für QR-Code im Admin)', 1, 'current', 0),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isModEnabled("netdiag")) {
|
||||||
|
$conf->netdiag = new stdClass();
|
||||||
|
$conf->netdiag->enabled = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neue Tabs an bestehenden Objekten
|
||||||
|
// Tab "Netzwerk-Diagnose" an Kunde (thirdparty) und Auftrag (order)
|
||||||
|
$this->tabs = array(
|
||||||
|
'thirdparty:+netdiag:NetDiagTab:netdiag@netdiag:$user->hasRight(\'netdiag\', \'protocol\', \'read\'):/custom/netdiag/netdiag_object_tab.php?socid=__ID__&objecttype=thirdparty',
|
||||||
|
'order:+netdiag:NetDiagTab:netdiag@netdiag:$user->hasRight(\'netdiag\', \'protocol\', \'read\'):/custom/netdiag/netdiag_object_tab.php?id=__ID__&objecttype=order',
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->dictionaries = array();
|
||||||
|
$this->boxes = array();
|
||||||
|
$this->cronjobs = array();
|
||||||
|
|
||||||
|
// Rechte
|
||||||
|
$this->rights = array();
|
||||||
|
$r = 0;
|
||||||
|
$o = 1;
|
||||||
|
|
||||||
|
$this->rights[$r][0] = $this->numero.sprintf("%02d", ($o * 10) + 1);
|
||||||
|
$this->rights[$r][1] = 'Diagnose-Protokolle lesen';
|
||||||
|
$this->rights[$r][4] = 'protocol';
|
||||||
|
$this->rights[$r][5] = 'read';
|
||||||
|
$r++;
|
||||||
|
|
||||||
|
$this->rights[$r][0] = $this->numero.sprintf("%02d", ($o * 10) + 2);
|
||||||
|
$this->rights[$r][1] = 'Diagnose-Protokolle anlegen/ändern';
|
||||||
|
$this->rights[$r][4] = 'protocol';
|
||||||
|
$this->rights[$r][5] = 'write';
|
||||||
|
$r++;
|
||||||
|
|
||||||
|
$this->rights[$r][0] = $this->numero.sprintf("%02d", ($o * 10) + 3);
|
||||||
|
$this->rights[$r][1] = 'Diagnose-Protokolle löschen';
|
||||||
|
$this->rights[$r][4] = 'protocol';
|
||||||
|
$this->rights[$r][5] = 'delete';
|
||||||
|
$r++;
|
||||||
|
|
||||||
|
// Top-Menü
|
||||||
|
$this->menu = array();
|
||||||
|
$r = 0;
|
||||||
|
|
||||||
|
$this->menu[$r++] = array(
|
||||||
|
'fk_menu' => '',
|
||||||
|
'type' => 'top',
|
||||||
|
'titre' => 'ModuleNetDiagName',
|
||||||
|
'prefix' => img_picto('', $this->picto, 'class="pictofixedwidth valignmiddle"'),
|
||||||
|
'mainmenu' => 'netdiag',
|
||||||
|
'leftmenu' => '',
|
||||||
|
'url' => '/custom/netdiag/netdiagindex.php',
|
||||||
|
'langs' => 'netdiag@netdiag',
|
||||||
|
'position' => 1000 + $r,
|
||||||
|
'enabled' => 'isModEnabled("netdiag")',
|
||||||
|
'perms' => '$user->hasRight("netdiag", "protocol", "read")',
|
||||||
|
'target' => '',
|
||||||
|
'user' => 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->menu[$r++] = array(
|
||||||
|
'fk_menu' => 'fk_mainmenu=netdiag',
|
||||||
|
'type' => 'left',
|
||||||
|
'titre' => 'NetDiagProtocolList',
|
||||||
|
'prefix' => img_picto('', $this->picto, 'class="pictofixedwidth valignmiddle paddingright"'),
|
||||||
|
'mainmenu' => 'netdiag',
|
||||||
|
'leftmenu' => 'netdiag_protocol_list',
|
||||||
|
'url' => '/custom/netdiag/netdiagindex.php',
|
||||||
|
'langs' => 'netdiag@netdiag',
|
||||||
|
'position' => 1000 + $r,
|
||||||
|
'enabled' => 'isModEnabled("netdiag")',
|
||||||
|
'perms' => '$user->hasRight("netdiag", "protocol", "read")',
|
||||||
|
'target' => '',
|
||||||
|
'user' => 2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wird beim Aktivieren des Moduls aufgerufen.
|
||||||
|
* Legt Konstanten, Rechte, Menüs und Datenbanktabellen an.
|
||||||
|
*
|
||||||
|
* @param string $options Optionen ('', 'noboxes')
|
||||||
|
* @return int<-1,1> 1 wenn OK, <=0 bei Fehler
|
||||||
|
*/
|
||||||
|
public function init($options = '')
|
||||||
|
{
|
||||||
|
global $conf, $langs;
|
||||||
|
|
||||||
|
// SQL-Tabellen beim Aktivieren laden
|
||||||
|
$result = $this->_load_tables('/netdiag/sql/');
|
||||||
|
if ($result < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rechte entfernen/neu setzen
|
||||||
|
$this->remove($options);
|
||||||
|
|
||||||
|
$sql = array();
|
||||||
|
|
||||||
|
return $this->_init($sql, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wird beim Deaktivieren des Moduls aufgerufen.
|
||||||
|
*
|
||||||
|
* @param string $options Optionen ('', 'noboxes')
|
||||||
|
* @return int<-1,1> 1 wenn OK, <=0 bei Fehler
|
||||||
|
*/
|
||||||
|
public function remove($options = '')
|
||||||
|
{
|
||||||
|
$sql = array();
|
||||||
|
return $this->_remove($sql, $options);
|
||||||
|
}
|
||||||
|
}
|
||||||
61
langs/de_DE/netdiag.lang
Normal file
61
langs/de_DE/netdiag.lang
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
||||||
|
# Sprachdatei NetDiag - Deutsch
|
||||||
|
|
||||||
|
#
|
||||||
|
# Allgemein
|
||||||
|
#
|
||||||
|
ModuleNetDiagName = NetDiag
|
||||||
|
ModuleNetDiagDesc = Netzwerk-Diagnose: erfasst per mobiler App Geräte, Ports und Messungen und hängt die Protokolle an Kunde und Auftrag.
|
||||||
|
NetDiagArea = NetDiag - Übersicht
|
||||||
|
NetDiagTab = Netzwerk-Diagnose
|
||||||
|
|
||||||
|
#
|
||||||
|
# Admin-Seite
|
||||||
|
#
|
||||||
|
NetDiagSetup = NetDiag Einrichtung
|
||||||
|
NetDiagSetupPage = NetDiag Einrichtungsseite
|
||||||
|
NetDiagAbout = Über NetDiag
|
||||||
|
NetDiagAboutPage = NetDiag Info-Seite
|
||||||
|
NETDIAG_API_TOKEN_TTL = Token-Gültigkeit (Sekunden)
|
||||||
|
NETDIAG_API_TOKEN_TTLTooltip = Wie lange ein API-Token der mobilen App gültig bleibt. Standard 604800 (7 Tage).
|
||||||
|
NETDIAG_APK_URL = Download-URL der App (APK)
|
||||||
|
NETDIAG_APK_URLTooltip = Adresse, unter der die Android-App (APK) liegt. Wird als QR-Code angezeigt.
|
||||||
|
NetDiagApiSecretInfo = Der JWT-Schlüssel wurde beim Aktivieren automatisch erzeugt und ist nicht editierbar.
|
||||||
|
NetDiagAppDownload = NetDiag-App herunterladen
|
||||||
|
NetDiagAppDownloadHint = Mit dem Handy scannen, um die Diagnose-App zu installieren.
|
||||||
|
|
||||||
|
#
|
||||||
|
# Protokoll
|
||||||
|
#
|
||||||
|
NetDiagProtocol = Diagnose-Protokoll
|
||||||
|
NetDiagProtocols = Diagnose-Protokolle
|
||||||
|
NetDiagProtocolList = Protokoll-Liste
|
||||||
|
NewNetDiagProtocol = Neues Diagnose-Protokoll
|
||||||
|
NetDiagStatusDraft = Entwurf
|
||||||
|
NetDiagStatusDone = Abgeschlossen
|
||||||
|
NetDiagNoProtocol = Noch kein Diagnose-Protokoll für dieses Objekt vorhanden
|
||||||
|
|
||||||
|
#
|
||||||
|
# Felder
|
||||||
|
#
|
||||||
|
DateDiag = Aufnahmedatum
|
||||||
|
DateMeasure = Messzeitpunkt
|
||||||
|
Technician = Techniker
|
||||||
|
Location = Standort
|
||||||
|
Subnet = Netzbereich
|
||||||
|
DeviceType = Gerätetyp
|
||||||
|
Protocol = Protokoll
|
||||||
|
Device = Gerät
|
||||||
|
Tool = Werkzeug
|
||||||
|
ToolCategory = Kategorie
|
||||||
|
MeasureStatus = Bewertung
|
||||||
|
ClientUuid = App-Kennung
|
||||||
|
IpAddress = IP-Adresse
|
||||||
|
MacAddress = MAC-Adresse
|
||||||
|
Hostname = Hostname
|
||||||
|
Vendor = Hersteller
|
||||||
|
NetDiagDevices = Gefundene Geräte
|
||||||
|
NetDiagMeasurements = Messungen
|
||||||
|
NetDiagMeasureOk = OK
|
||||||
|
NetDiagMeasureWarn = Warnung
|
||||||
|
NetDiagMeasureFail = Fehler
|
||||||
61
langs/en_US/netdiag.lang
Normal file
61
langs/en_US/netdiag.lang
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
||||||
|
# Translation file NetDiag - English
|
||||||
|
|
||||||
|
#
|
||||||
|
# Generic
|
||||||
|
#
|
||||||
|
ModuleNetDiagName = NetDiag
|
||||||
|
ModuleNetDiagDesc = Network diagnostics: capture devices, ports and measurements with a mobile app and attach the protocols to customer and order.
|
||||||
|
NetDiagArea = NetDiag - Overview
|
||||||
|
NetDiagTab = Network diagnostics
|
||||||
|
|
||||||
|
#
|
||||||
|
# Admin page
|
||||||
|
#
|
||||||
|
NetDiagSetup = NetDiag setup
|
||||||
|
NetDiagSetupPage = NetDiag setup page
|
||||||
|
NetDiagAbout = About NetDiag
|
||||||
|
NetDiagAboutPage = NetDiag about page
|
||||||
|
NETDIAG_API_TOKEN_TTL = Token validity (seconds)
|
||||||
|
NETDIAG_API_TOKEN_TTLTooltip = How long a mobile app API token stays valid. Default 604800 (7 days).
|
||||||
|
NETDIAG_APK_URL = App download URL (APK)
|
||||||
|
NETDIAG_APK_URLTooltip = Address where the Android app (APK) is hosted. Shown as a QR code.
|
||||||
|
NetDiagApiSecretInfo = The JWT secret was generated automatically on activation and is not editable.
|
||||||
|
NetDiagAppDownload = Download NetDiag app
|
||||||
|
NetDiagAppDownloadHint = Scan with your phone to install the diagnostics app.
|
||||||
|
|
||||||
|
#
|
||||||
|
# Protocol
|
||||||
|
#
|
||||||
|
NetDiagProtocol = Diagnostic protocol
|
||||||
|
NetDiagProtocols = Diagnostic protocols
|
||||||
|
NetDiagProtocolList = Protocol list
|
||||||
|
NewNetDiagProtocol = New diagnostic protocol
|
||||||
|
NetDiagStatusDraft = Draft
|
||||||
|
NetDiagStatusDone = Completed
|
||||||
|
NetDiagNoProtocol = No diagnostic protocol for this object yet
|
||||||
|
|
||||||
|
#
|
||||||
|
# Fields
|
||||||
|
#
|
||||||
|
DateDiag = Diagnostic date
|
||||||
|
DateMeasure = Measurement time
|
||||||
|
Technician = Technician
|
||||||
|
Location = Location
|
||||||
|
Subnet = Subnet
|
||||||
|
DeviceType = Device type
|
||||||
|
Protocol = Protocol
|
||||||
|
Device = Device
|
||||||
|
Tool = Tool
|
||||||
|
ToolCategory = Category
|
||||||
|
MeasureStatus = Rating
|
||||||
|
ClientUuid = App identifier
|
||||||
|
IpAddress = IP address
|
||||||
|
MacAddress = MAC address
|
||||||
|
Hostname = Hostname
|
||||||
|
Vendor = Vendor
|
||||||
|
NetDiagDevices = Discovered devices
|
||||||
|
NetDiagMeasurements = Measurements
|
||||||
|
NetDiagMeasureOk = OK
|
||||||
|
NetDiagMeasureWarn = Warning
|
||||||
|
NetDiagMeasureFail = Fail
|
||||||
134
lib/netdiag.lib.php
Normal file
134
lib/netdiag.lib.php
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
<?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/lib/netdiag.lib.php
|
||||||
|
* \ingroup netdiag
|
||||||
|
* \brief Hilfsfunktionen für das Modul NetDiag
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tabs für die Admin-Seiten des Moduls vorbereiten
|
||||||
|
*
|
||||||
|
* @return array<int,array<int,string>> Tab-Array
|
||||||
|
*/
|
||||||
|
function netdiagAdminPrepareHead()
|
||||||
|
{
|
||||||
|
global $langs, $conf;
|
||||||
|
|
||||||
|
$langs->load("netdiag@netdiag");
|
||||||
|
|
||||||
|
$h = 0;
|
||||||
|
$head = array();
|
||||||
|
|
||||||
|
$head[$h][0] = dol_buildpath("/netdiag/admin/setup.php", 1);
|
||||||
|
$head[$h][1] = $langs->trans("Settings");
|
||||||
|
$head[$h][2] = 'settings';
|
||||||
|
$h++;
|
||||||
|
|
||||||
|
$head[$h][0] = dol_buildpath("/netdiag/admin/about.php", 1);
|
||||||
|
$head[$h][1] = $langs->trans("About");
|
||||||
|
$head[$h][2] = 'about';
|
||||||
|
$h++;
|
||||||
|
|
||||||
|
complete_head_from_modules($conf, $langs, null, $head, $h, 'netdiag@netdiag');
|
||||||
|
complete_head_from_modules($conf, $langs, null, $head, $h, 'netdiag@netdiag', 'remove');
|
||||||
|
|
||||||
|
return $head;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tabs für die Detailansicht eines Diagnose-Protokolls vorbereiten
|
||||||
|
*
|
||||||
|
* @param NetDiagProtocol $object Protokoll-Objekt
|
||||||
|
* @return array<int,array<int,string>> Tab-Array
|
||||||
|
*/
|
||||||
|
function netdiagProtocolPrepareHead($object)
|
||||||
|
{
|
||||||
|
global $langs, $conf;
|
||||||
|
|
||||||
|
$langs->load("netdiag@netdiag");
|
||||||
|
|
||||||
|
$h = 0;
|
||||||
|
$head = array();
|
||||||
|
|
||||||
|
$head[$h][0] = dol_buildpath("/netdiag/netdiagprotocol_card.php", 1).'?id='.$object->id;
|
||||||
|
$head[$h][1] = $langs->trans("NetDiagProtocol");
|
||||||
|
$head[$h][2] = 'card';
|
||||||
|
$h++;
|
||||||
|
|
||||||
|
complete_head_from_modules($conf, $langs, $object, $head, $h, 'netdiagprotocol@netdiag');
|
||||||
|
complete_head_from_modules($conf, $langs, $object, $head, $h, 'netdiagprotocol@netdiag', 'remove');
|
||||||
|
|
||||||
|
return $head;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ausgabeverzeichnis des Moduls ermitteln (für PDF-Dokumente)
|
||||||
|
*
|
||||||
|
* @return string Absoluter Pfad zum Dokumentenverzeichnis
|
||||||
|
*/
|
||||||
|
function netdiagGetOutputDir()
|
||||||
|
{
|
||||||
|
global $conf;
|
||||||
|
if (!empty($conf->netdiag->dir_output)) {
|
||||||
|
return $conf->netdiag->dir_output;
|
||||||
|
}
|
||||||
|
return DOL_DATA_ROOT.'/netdiag';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ein Mess-Ergebnis (JSON) lesbar als HTML aufbereiten.
|
||||||
|
*
|
||||||
|
* Generisch: rendert flache Schlüssel/Wert-Paare und einfache Listen,
|
||||||
|
* damit auch künftige Tools ohne Code-Änderung dargestellt werden.
|
||||||
|
*
|
||||||
|
* @param string $json JSON-String des Ergebnisses
|
||||||
|
* @return string HTML-Schnipsel
|
||||||
|
*/
|
||||||
|
function netdiagFormatResult($json)
|
||||||
|
{
|
||||||
|
if (empty($json)) {
|
||||||
|
return '<span class="opacitymedium">-</span>';
|
||||||
|
}
|
||||||
|
$data = json_decode($json, true);
|
||||||
|
if ($data === null) {
|
||||||
|
return dol_escape_htmltag(dol_trunc($json, 120));
|
||||||
|
}
|
||||||
|
if (!is_array($data)) {
|
||||||
|
return dol_escape_htmltag((string) $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$out = '<div class="netdiag-result">';
|
||||||
|
foreach ($data as $key => $val) {
|
||||||
|
$label = dol_escape_htmltag(ucfirst((string) $key));
|
||||||
|
if (is_array($val)) {
|
||||||
|
$flat = array();
|
||||||
|
foreach ($val as $item) {
|
||||||
|
$flat[] = is_array($item) ? json_encode($item, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : (string) $item;
|
||||||
|
}
|
||||||
|
$valstr = implode(', ', $flat);
|
||||||
|
} elseif (is_bool($val)) {
|
||||||
|
$valstr = $val ? 'ja' : 'nein';
|
||||||
|
} else {
|
||||||
|
$valstr = (string) $val;
|
||||||
|
}
|
||||||
|
$out .= '<span class="netdiag-kv"><strong>'.$label.':</strong> '.dol_escape_htmltag(dol_trunc($valstr, 200)).'</span> ';
|
||||||
|
}
|
||||||
|
$out .= '</div>';
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
199
lib/netdiag_pdf.lib.php
Normal file
199
lib/netdiag_pdf.lib.php
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
<?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/lib/netdiag_pdf.lib.php
|
||||||
|
* \ingroup netdiag
|
||||||
|
* \brief PDF-Generator für Diagnose-Protokolle
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php';
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
|
||||||
|
require_once __DIR__.'/netdiag.lib.php';
|
||||||
|
require_once __DIR__.'/../class/netdiagdevice.class.php';
|
||||||
|
require_once __DIR__.'/../class/netdiagmeasurement.class.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ein Diagnose-Protokoll als PDF erzeugen und im Dokumentenverzeichnis ablegen.
|
||||||
|
*
|
||||||
|
* @param DoliDB $db Datenbank-Handler
|
||||||
|
* @param NetDiagProtocol $protocol Protokoll-Objekt (geladen)
|
||||||
|
* @param Translate $outputlangs Sprache der Ausgabe
|
||||||
|
* @return int >0 wenn OK, <=0 bei Fehler
|
||||||
|
*/
|
||||||
|
function netdiagGeneratePdf($db, $protocol, $outputlangs)
|
||||||
|
{
|
||||||
|
global $conf, $mysoc, $langs;
|
||||||
|
|
||||||
|
$outputlangs->loadLangs(array("netdiag@netdiag", "main", "companies", "orders"));
|
||||||
|
|
||||||
|
$dir = netdiagGetOutputDir().'/'.dol_sanitizeFileName($protocol->ref);
|
||||||
|
if (!dol_is_dir($dir)) {
|
||||||
|
if (dol_mkdir($dir) < 0) {
|
||||||
|
$protocol->error = 'Konnte Verzeichnis nicht anlegen: '.$dir;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$file = $dir.'/'.dol_sanitizeFileName($protocol->ref).'.pdf';
|
||||||
|
|
||||||
|
// Daten laden
|
||||||
|
$soc = new Societe($db);
|
||||||
|
if ($protocol->fk_soc > 0) {
|
||||||
|
$soc->fetch($protocol->fk_soc);
|
||||||
|
}
|
||||||
|
$devObj = new NetDiagDevice($db);
|
||||||
|
$devices = $devObj->fetchAllByProtocol($protocol->id);
|
||||||
|
$measObj = new NetDiagMeasurement($db);
|
||||||
|
$measurements = $measObj->fetchAllByProtocol($protocol->id);
|
||||||
|
|
||||||
|
// PDF aufbauen
|
||||||
|
$pdf = pdf_getInstance('A4', 'mm', 'P');
|
||||||
|
$default_font_size = pdf_getPDFFontSize($outputlangs);
|
||||||
|
$pdf->SetAutoPageBreak(true, 20);
|
||||||
|
$pdf->SetMargins(15, 15, 15);
|
||||||
|
$pdf->SetTitle($protocol->ref);
|
||||||
|
$pdf->SetSubject($outputlangs->transnoentities("NetDiagProtocol"));
|
||||||
|
$pdf->SetCreator("Dolibarr ".DOL_VERSION." - NetDiag");
|
||||||
|
$pdf->SetAuthor($mysoc->name);
|
||||||
|
$pdf->AddPage();
|
||||||
|
|
||||||
|
// Kopf
|
||||||
|
$pdf->SetFont('', 'B', 16);
|
||||||
|
$pdf->SetTextColor(0, 70, 130);
|
||||||
|
$pdf->Cell(0, 8, $outputlangs->transnoentities("NetDiagProtocol").' '.$protocol->ref, 0, 1, 'L');
|
||||||
|
$pdf->SetTextColor(0, 0, 0);
|
||||||
|
$pdf->SetFont('', '', 9);
|
||||||
|
$pdf->Cell(0, 5, $mysoc->name, 0, 1, 'L');
|
||||||
|
$pdf->Ln(3);
|
||||||
|
|
||||||
|
// Stammdaten
|
||||||
|
$pdf->SetFont('', '', 10);
|
||||||
|
$infos = array(
|
||||||
|
$outputlangs->transnoentities("ThirdParty") => ($soc->id > 0 ? $soc->name : '-'),
|
||||||
|
$outputlangs->transnoentities("DateDiag") => dol_print_date($protocol->date_diag, 'dayhour', false, $outputlangs),
|
||||||
|
$outputlangs->transnoentities("Location") => ($protocol->standort ? $protocol->standort : '-'),
|
||||||
|
$outputlangs->transnoentities("Subnet") => ($protocol->subnet ? $protocol->subnet : '-'),
|
||||||
|
);
|
||||||
|
foreach ($infos as $k => $v) {
|
||||||
|
$pdf->SetFont('', 'B', 10);
|
||||||
|
$pdf->Cell(40, 6, $k.':', 0, 0, 'L');
|
||||||
|
$pdf->SetFont('', '', 10);
|
||||||
|
$pdf->Cell(0, 6, $v, 0, 1, 'L');
|
||||||
|
}
|
||||||
|
if (!empty($protocol->note)) {
|
||||||
|
$pdf->Ln(1);
|
||||||
|
$pdf->SetFont('', 'I', 9);
|
||||||
|
$pdf->MultiCell(0, 5, $outputlangs->transnoentities("Note").': '.$protocol->note, 0, 'L');
|
||||||
|
}
|
||||||
|
$pdf->Ln(4);
|
||||||
|
|
||||||
|
// Geräteliste
|
||||||
|
$pdf->SetFont('', 'B', 12);
|
||||||
|
$pdf->Cell(0, 7, $outputlangs->transnoentities("NetDiagDevices").' ('.count($devices).')', 0, 1, 'L');
|
||||||
|
$pdf->SetFillColor(0, 70, 130);
|
||||||
|
$pdf->SetTextColor(255, 255, 255);
|
||||||
|
$pdf->SetFont('', 'B', 8);
|
||||||
|
$cols = array(35, 40, 50, 40, 15);
|
||||||
|
$heads = array("IpAddress", "MacAddress", "Hostname", "Vendor", "DeviceType");
|
||||||
|
foreach ($heads as $idx => $h) {
|
||||||
|
$pdf->Cell($cols[$idx], 6, $outputlangs->transnoentities($h), 1, 0, 'L', true);
|
||||||
|
}
|
||||||
|
$pdf->Ln();
|
||||||
|
$pdf->SetTextColor(0, 0, 0);
|
||||||
|
$pdf->SetFont('', '', 8);
|
||||||
|
foreach ($devices as $dev) {
|
||||||
|
$pdf->Cell($cols[0], 5, dol_trunc($dev->ip, 22), 1, 0, 'L');
|
||||||
|
$pdf->Cell($cols[1], 5, dol_trunc($dev->mac, 24), 1, 0, 'L');
|
||||||
|
$pdf->Cell($cols[2], 5, dol_trunc($dev->hostname, 32), 1, 0, 'L');
|
||||||
|
$pdf->Cell($cols[3], 5, dol_trunc($dev->vendor, 26), 1, 0, 'L');
|
||||||
|
$pdf->Cell($cols[4], 5, dol_trunc($dev->devicetype, 10), 1, 1, 'L');
|
||||||
|
}
|
||||||
|
if (empty($devices)) {
|
||||||
|
$pdf->Cell(array_sum($cols), 5, '-', 1, 1, 'C');
|
||||||
|
}
|
||||||
|
$pdf->Ln(4);
|
||||||
|
|
||||||
|
// Messungen
|
||||||
|
$pdf->SetFont('', 'B', 12);
|
||||||
|
$pdf->Cell(0, 7, $outputlangs->transnoentities("NetDiagMeasurements").' ('.count($measurements).')', 0, 1, 'L');
|
||||||
|
$statuslabels = array(0 => 'OK', 1 => $outputlangs->transnoentities("NetDiagMeasureWarn"), 2 => $outputlangs->transnoentities("NetDiagMeasureFail"));
|
||||||
|
foreach ($measurements as $m) {
|
||||||
|
$st = (int) $m->measure_status;
|
||||||
|
if ($st == 2) {
|
||||||
|
$pdf->SetFillColor(230, 130, 130);
|
||||||
|
} elseif ($st == 1) {
|
||||||
|
$pdf->SetFillColor(245, 215, 130);
|
||||||
|
} else {
|
||||||
|
$pdf->SetFillColor(170, 215, 170);
|
||||||
|
}
|
||||||
|
$pdf->SetFont('', 'B', 9);
|
||||||
|
$title = ($m->category ? '['.$m->category.'] ' : '').$m->tool.($m->label ? ' — '.$m->label : '');
|
||||||
|
$pdf->Cell(150, 6, dol_trunc($title, 80), 1, 0, 'L');
|
||||||
|
$pdf->Cell(30, 6, $statuslabels[$st], 1, 1, 'C', true);
|
||||||
|
$pdf->SetFont('', '', 8);
|
||||||
|
$pdf->MultiCell(180, 5, netdiagPdfFlattenResult($m->result), 1, 'L');
|
||||||
|
}
|
||||||
|
if (empty($measurements)) {
|
||||||
|
$pdf->SetFont('', '', 9);
|
||||||
|
$pdf->Cell(180, 5, '-', 1, 1, 'C');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fuß
|
||||||
|
$pdf->Ln(6);
|
||||||
|
$pdf->SetFont('', 'I', 8);
|
||||||
|
$pdf->SetTextColor(120, 120, 120);
|
||||||
|
$pdf->Cell(0, 5, $outputlangs->transnoentities("NetDiagProtocol").' '.$protocol->ref.' — '.dol_print_date(dol_now(), 'dayhour', false, $outputlangs), 0, 1, 'L');
|
||||||
|
|
||||||
|
// Speichern
|
||||||
|
$pdf->Output($file, 'F');
|
||||||
|
dolChmod($file);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mess-Ergebnis (JSON) als einzeiligen Text für PDF-Ausgabe.
|
||||||
|
*
|
||||||
|
* @param string $json JSON-String
|
||||||
|
* @return string Lesbarer Text
|
||||||
|
*/
|
||||||
|
function netdiagPdfFlattenResult($json)
|
||||||
|
{
|
||||||
|
if (empty($json)) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
$data = json_decode($json, true);
|
||||||
|
if (!is_array($data)) {
|
||||||
|
return (string) $json;
|
||||||
|
}
|
||||||
|
$parts = array();
|
||||||
|
foreach ($data as $key => $val) {
|
||||||
|
if (is_array($val)) {
|
||||||
|
$flat = array();
|
||||||
|
foreach ($val as $item) {
|
||||||
|
$flat[] = is_array($item) ? json_encode($item, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : (string) $item;
|
||||||
|
}
|
||||||
|
$val = implode(', ', $flat);
|
||||||
|
} elseif (is_bool($val)) {
|
||||||
|
$val = $val ? 'ja' : 'nein';
|
||||||
|
}
|
||||||
|
$parts[] = ucfirst((string) $key).': '.$val;
|
||||||
|
}
|
||||||
|
return implode(' | ', $parts);
|
||||||
|
}
|
||||||
170
netdiag_object_tab.php
Normal file
170
netdiag_object_tab.php
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
<?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/netdiag_object_tab.php
|
||||||
|
* \ingroup netdiag
|
||||||
|
* \brief Tab "Netzwerk-Diagnose" an Kunde (thirdparty) und Auftrag (order)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 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.'/core/lib/company.lib.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
$objecttype = GETPOST('objecttype', 'aZ09');
|
||||||
|
$id = GETPOSTINT('id');
|
||||||
|
$socid = GETPOSTINT('socid');
|
||||||
|
|
||||||
|
// Parent-Objekt laden und passenden Filter aufbauen
|
||||||
|
$parent = null;
|
||||||
|
$head = array();
|
||||||
|
$picto = 'company';
|
||||||
|
$filtersql = '';
|
||||||
|
|
||||||
|
if ($objecttype == 'order') {
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/core/lib/order.lib.php';
|
||||||
|
$parent = new Commande($db);
|
||||||
|
if ($id <= 0 || $parent->fetch($id) <= 0) {
|
||||||
|
accessforbidden('Order not found');
|
||||||
|
}
|
||||||
|
$parent->fetch_thirdparty();
|
||||||
|
$head = commande_prepare_head($parent);
|
||||||
|
$picto = 'order';
|
||||||
|
$filtersql = " AND p.fk_commande = ".((int) $parent->id);
|
||||||
|
} else {
|
||||||
|
$parent = new Societe($db);
|
||||||
|
if ($socid <= 0 || $parent->fetch($socid) <= 0) {
|
||||||
|
accessforbidden('ThirdParty not found');
|
||||||
|
}
|
||||||
|
$head = societe_prepare_head($parent);
|
||||||
|
$picto = 'company';
|
||||||
|
$filtersql = " AND p.fk_soc = ".((int) $parent->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ansicht
|
||||||
|
*/
|
||||||
|
|
||||||
|
llxHeader('', $langs->trans("NetDiagTab"), '', '', 0, 0, '', '', '', 'mod-netdiag');
|
||||||
|
|
||||||
|
print dol_get_fiche_head($head, 'netdiag', $langs->trans($objecttype == 'order' ? 'CustomerOrder' : 'ThirdParty'), -1, $picto);
|
||||||
|
|
||||||
|
if ($objecttype == 'order') {
|
||||||
|
$linkback = '<a href="'.DOL_URL_ROOT.'/commande/list.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
|
||||||
|
dol_banner_tab($parent, 'ref', $linkback, 1, 'ref', 'ref');
|
||||||
|
} else {
|
||||||
|
$linkback = '<a href="'.DOL_URL_ROOT.'/societe/list.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
|
||||||
|
dol_banner_tab($parent, 'socid', $linkback, ($user->socid ? 0 : 1), 'rowid', 'nom');
|
||||||
|
}
|
||||||
|
|
||||||
|
print dol_get_fiche_end();
|
||||||
|
|
||||||
|
// Protokolle des Objekts auflisten
|
||||||
|
$sql = "SELECT p.rowid, p.ref, p.label, p.date_diag, p.standort, p.subnet, p.status,";
|
||||||
|
$sql .= " (SELECT COUNT(*) FROM ".$db->prefix()."netdiag_device d WHERE d.fk_protocol = p.rowid) as devcount,";
|
||||||
|
$sql .= " (SELECT COUNT(*) FROM ".$db->prefix()."netdiag_measurement m WHERE m.fk_protocol = p.rowid) as meascount";
|
||||||
|
$sql .= " FROM ".$db->prefix()."netdiag_protocol as p";
|
||||||
|
$sql .= " WHERE p.entity IN (".getEntity('netdiagprotocol').")";
|
||||||
|
$sql .= $filtersql;
|
||||||
|
$sql .= " ORDER BY p.date_diag DESC, p.rowid DESC";
|
||||||
|
|
||||||
|
$resql = $db->query($sql);
|
||||||
|
|
||||||
|
print '<div class="fichecenter"><br>';
|
||||||
|
print load_fiche_titre($langs->trans("NetDiagProtocols"), '', 'fa-network-wired');
|
||||||
|
|
||||||
|
print '<div class="div-table-responsive-no-min">';
|
||||||
|
print '<table class="noborder centpercent">';
|
||||||
|
print '<tr class="liste_titre">';
|
||||||
|
print '<th>'.$langs->trans("Ref").'</th>';
|
||||||
|
print '<th>'.$langs->trans("DateDiag").'</th>';
|
||||||
|
print '<th>'.$langs->trans("Location").'</th>';
|
||||||
|
print '<th>'.$langs->trans("Subnet").'</th>';
|
||||||
|
print '<th class="right">'.$langs->trans("NetDiagDevices").'</th>';
|
||||||
|
print '<th class="right">'.$langs->trans("NetDiagMeasurements").'</th>';
|
||||||
|
print '<th class="center">'.$langs->trans("Status").'</th>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
$proto = new NetDiagProtocol($db);
|
||||||
|
$nb = 0;
|
||||||
|
if ($resql) {
|
||||||
|
while ($obj = $db->fetch_object($resql)) {
|
||||||
|
$nb++;
|
||||||
|
$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>';
|
||||||
|
print '<td>'.dol_print_date($db->jdate($obj->date_diag), 'dayhour').'</td>';
|
||||||
|
print '<td>'.dol_escape_htmltag($obj->standort).'</td>';
|
||||||
|
print '<td>'.dol_escape_htmltag($obj->subnet).'</td>';
|
||||||
|
print '<td class="right">'.((int) $obj->devcount).'</td>';
|
||||||
|
print '<td class="right">'.((int) $obj->meascount).'</td>';
|
||||||
|
print '<td class="center">'.$proto->getLibStatut(3).'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$nb) {
|
||||||
|
print '<tr><td colspan="7" class="opacitymedium center">'.$langs->trans("NetDiagNoProtocol").'</td></tr>';
|
||||||
|
}
|
||||||
|
print '</table>';
|
||||||
|
print '</div>';
|
||||||
|
print '</div>';
|
||||||
|
|
||||||
|
llxFooter();
|
||||||
|
$db->close();
|
||||||
158
netdiagindex.php
Normal file
158
netdiagindex.php
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
<?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');
|
||||||
|
$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";
|
||||||
|
$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);
|
||||||
|
}
|
||||||
|
$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="4" 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("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>';
|
||||||
|
print '<td class="center">'.$proto->getLibStatut(3).'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
}
|
||||||
|
if (empty($rows)) {
|
||||||
|
print '<tr><td colspan="6" class="opacitymedium center">'.$langs->trans("NoRecordFound").'</td></tr>';
|
||||||
|
}
|
||||||
|
|
||||||
|
print '</table>';
|
||||||
|
print '</div>';
|
||||||
|
print '</form>';
|
||||||
|
|
||||||
|
llxFooter();
|
||||||
|
$db->close();
|
||||||
231
netdiagprotocol_card.php
Normal file
231
netdiagprotocol_card.php
Normal file
|
|
@ -0,0 +1,231 @@
|
||||||
|
<?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("Vendor").'</th><th>'.$langs->trans("DeviceType").'</th>';
|
||||||
|
print '</tr>';
|
||||||
|
foreach ($devices as $dev) {
|
||||||
|
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($dev->hostname).'</td>';
|
||||||
|
print '<td>'.dol_escape_htmltag($dev->vendor).'</td>';
|
||||||
|
print '<td>'.dol_escape_htmltag($dev->devicetype).'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
}
|
||||||
|
if (empty($devices)) {
|
||||||
|
print '<tr><td colspan="5" 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');
|
||||||
|
$statuscss = array(0 => 'badge-status4', 1 => 'badge-status1', 2 => 'badge-status8');
|
||||||
|
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;
|
||||||
|
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($m->tool).'</td>';
|
||||||
|
print '<td>'.dol_escape_htmltag($m->label).'</td>';
|
||||||
|
print '<td>'.netdiagFormatResult($m->result).'</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();
|
||||||
6
sql/llx_netdiag_device.key.sql
Normal file
6
sql/llx_netdiag_device.key.sql
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
-- Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
||||||
|
--
|
||||||
|
-- Schlüssel und Indizes für llx_netdiag_device
|
||||||
|
|
||||||
|
ALTER TABLE llx_netdiag_device ADD INDEX idx_netdiag_device_fk_protocol(fk_protocol);
|
||||||
|
ALTER TABLE llx_netdiag_device ADD CONSTRAINT fk_netdiag_device_protocol FOREIGN KEY (fk_protocol) REFERENCES llx_netdiag_protocol(rowid);
|
||||||
22
sql/llx_netdiag_device.sql
Normal file
22
sql/llx_netdiag_device.sql
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
-- 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.
|
||||||
|
--
|
||||||
|
-- Tabelle: gefundenes Gerät innerhalb eines Diagnose-Protokolls
|
||||||
|
|
||||||
|
CREATE TABLE llx_netdiag_device(
|
||||||
|
rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL,
|
||||||
|
entity integer DEFAULT 1 NOT NULL,
|
||||||
|
fk_protocol integer NOT NULL,
|
||||||
|
ip varchar(45),
|
||||||
|
mac varchar(17),
|
||||||
|
hostname varchar(255),
|
||||||
|
vendor varchar(128),
|
||||||
|
devicetype varchar(64),
|
||||||
|
note text,
|
||||||
|
date_creation datetime NOT NULL,
|
||||||
|
tms timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||||
|
) ENGINE=innodb;
|
||||||
8
sql/llx_netdiag_measurement.key.sql
Normal file
8
sql/llx_netdiag_measurement.key.sql
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
-- Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
||||||
|
--
|
||||||
|
-- Schlüssel und Indizes für llx_netdiag_measurement
|
||||||
|
|
||||||
|
ALTER TABLE llx_netdiag_measurement ADD INDEX idx_netdiag_measurement_fk_protocol(fk_protocol);
|
||||||
|
ALTER TABLE llx_netdiag_measurement ADD INDEX idx_netdiag_measurement_fk_device(fk_device);
|
||||||
|
ALTER TABLE llx_netdiag_measurement ADD INDEX idx_netdiag_measurement_tool(tool);
|
||||||
|
ALTER TABLE llx_netdiag_measurement ADD CONSTRAINT fk_netdiag_measurement_protocol FOREIGN KEY (fk_protocol) REFERENCES llx_netdiag_protocol(rowid);
|
||||||
25
sql/llx_netdiag_measurement.sql
Normal file
25
sql/llx_netdiag_measurement.sql
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
-- 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.
|
||||||
|
--
|
||||||
|
-- Tabelle: ein Tool-Lauf (Messung) innerhalb eines Diagnose-Protokolls.
|
||||||
|
-- params/result sind generisches JSON -> neue Tools brauchen kein Schema-Update.
|
||||||
|
|
||||||
|
CREATE TABLE llx_netdiag_measurement(
|
||||||
|
rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL,
|
||||||
|
entity integer DEFAULT 1 NOT NULL,
|
||||||
|
fk_protocol integer NOT NULL,
|
||||||
|
fk_device integer,
|
||||||
|
tool varchar(64) NOT NULL,
|
||||||
|
category varchar(32),
|
||||||
|
label varchar(255),
|
||||||
|
params text,
|
||||||
|
result longtext,
|
||||||
|
measure_status smallint DEFAULT 0 NOT NULL,
|
||||||
|
date_measure datetime,
|
||||||
|
date_creation datetime NOT NULL,
|
||||||
|
tms timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||||
|
) ENGINE=innodb;
|
||||||
8
sql/llx_netdiag_protocol.key.sql
Normal file
8
sql/llx_netdiag_protocol.key.sql
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
-- Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
||||||
|
--
|
||||||
|
-- Schlüssel und Indizes für llx_netdiag_protocol
|
||||||
|
|
||||||
|
ALTER TABLE llx_netdiag_protocol ADD UNIQUE INDEX uk_netdiag_protocol_ref(ref, entity);
|
||||||
|
ALTER TABLE llx_netdiag_protocol ADD UNIQUE INDEX uk_netdiag_protocol_uuid(client_uuid);
|
||||||
|
ALTER TABLE llx_netdiag_protocol ADD INDEX idx_netdiag_protocol_fk_soc(fk_soc);
|
||||||
|
ALTER TABLE llx_netdiag_protocol ADD INDEX idx_netdiag_protocol_fk_commande(fk_commande);
|
||||||
28
sql/llx_netdiag_protocol.sql
Normal file
28
sql/llx_netdiag_protocol.sql
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
-- 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.
|
||||||
|
--
|
||||||
|
-- Tabelle: Diagnose-Protokoll (Kopfdatensatz eines Netzwerk-Einsatzes)
|
||||||
|
|
||||||
|
CREATE TABLE llx_netdiag_protocol(
|
||||||
|
rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL,
|
||||||
|
ref varchar(128) NOT NULL,
|
||||||
|
entity integer DEFAULT 1 NOT NULL,
|
||||||
|
label varchar(255),
|
||||||
|
client_uuid varchar(64),
|
||||||
|
fk_soc integer,
|
||||||
|
fk_commande integer,
|
||||||
|
date_diag datetime,
|
||||||
|
fk_user_techniker integer,
|
||||||
|
standort varchar(255),
|
||||||
|
subnet varchar(64),
|
||||||
|
status smallint DEFAULT 0 NOT NULL,
|
||||||
|
note text,
|
||||||
|
date_creation datetime NOT NULL,
|
||||||
|
tms timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
fk_user_creat integer NOT NULL,
|
||||||
|
fk_user_modif integer
|
||||||
|
) ENGINE=innodb;
|
||||||
Loading…
Reference in a new issue