diff --git a/ChangeLog.md b/ChangeLog.md index de7d018..c8627b8 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,49 @@ # Changelog +## 1.2.0 — 2026-05-27 + +### Phase 1.8 Lieferschein-Bestätigung in der PWA + +**Neuer element_type='shipment'** +- Bericht-Modul unterstützt jetzt `element_type='shipment'` mit `fk_element = llx_expedition.rowid` +- Tab "Bericht" erscheint auf der Lieferung-Card (Konstante `BERICHT_TAB_ON_SHIPMENT`) +- `bericht_fetch_parent()` und `bericht_fetch_shipment_with_order()` laden Expedition + verknüpften Auftrag + +**Signatur auf Lieferschein-PDF stempeln** +- Neue Tabelle `llx_bericht_signature_box`: Pro PDF-Template (merou/rouget/espadon/...) konfigurierbare Box-Geometrie (x/y/w/h in mm, Seite first/last, Label) +- Konstante `BERICHT_SIGNATURE_BOX_DEFAULT` als JSON-Default (`{"page":"last","x_mm":120,"y_mm":230,"w_mm":70,"h_mm":35,"label":"Unterschrift Kunde"}`) +- `bericht_stamp_signature_on_pdf()`: FPDI importiert Original-Lieferschein, TCPDF stempelt PNG + Name + Datum + GPS in die konfigurierte Box + +**Visueller Admin-Editor (`admin/signature_box_editor.php`)** +- PDF.js rendert ein Beispiel-Lieferschein-PDF +- Fabric.js-Rechteck zum Drag&Resize der Signatur-Box auf dem Canvas +- Eingabefelder für X/Y/W/H/Label/Seite werden live synchronisiert +- "Speichern" persistiert via AJAX (`ajax/save_signature_box.php`) in `llx_bericht_signature_box` (UPSERT auf `template_name`) +- Reset-Button setzt zurück auf Default-Konstante + +**Neue PWA-Route (Baustelle-App)** +- Order-Detail bekommt Button "🚚 Lieferungen" +- `#/orders/:id/shipments` zeigt Liste aller verknüpften Expeditionen mit Status- und Signed-Badge +- `#/shipments/:id` zeigt Lieferschein-PDF inline (PDF.js Canvas) + großen Button "Lieferung unterschreiben lassen" + +**Vollbild-Querformat-Signatur (`openShipmentSignatureModal`)** +- `requestFullscreen()` + `screen.orientation.lock('landscape')` (Best-Effort) +- 2-Spalten-Layout: links Lieferschein-Info + Namens-Input + GPS-Toggle, rechts Vollbild-Canvas +- HiDPI-aware Canvas (`devicePixelRatio`), Pointer/Touch-Events mit quadratischer Glättung +- Beim Bestätigen: PNG → `POST /api/shipments.php?action=confirm` → Backend stempelt in PDF und legt es in `documents/expedition//-signed.pdf` + +**Auto-Workflow auf der Expedition** +- `signed_status=1` (via `CommonSignedObject::setSignedStatus`) +- Wenn Expedition noch Draft (Status 0): erst `valid()`, dann `setClosed()` +- Bericht-Record (`element_type='shipment'`) wird angelegt/aktualisiert mit `final_pdf_path` zum signierten PDF +- `.meta.json` neben der PNG mit voller Audit-Spur (signer, user, GPS, IP, Box, Template, signed_at) + +**Neue API-Endpoints** +- `GET /api/shipments.php?order_id=` — Liste der Lieferungen zu einem Auftrag +- `GET /api/shipments.php?id=` — Detail einer Lieferung + Bericht-ID falls vorhanden +- `GET /api/shipments.php?id=&action=pdf` — PDF-Stream (Original oder gestempelt) +- `POST /api/shipments.php?id=&action=confirm` — Unterschrift einstempeln + Status-Workflow + ## 1.1.0 — 2026-04-08 ### Phase 1 Bericht-Modul Erweiterungen diff --git a/admin/setup.php b/admin/setup.php index eb6ee86..4b0ef44 100644 --- a/admin/setup.php +++ b/admin/setup.php @@ -65,6 +65,8 @@ if ($action === 'save_const') { 'BERICHT_TAB_ON_INVOICE' => GETPOST('tab_invoice', 'int') ? '1' : '0', 'BERICHT_TAB_ON_ORDER' => GETPOST('tab_order', 'int') ? '1' : '0', 'BERICHT_TAB_ON_PROPAL' => GETPOST('tab_propal', 'int') ? '1' : '0', + 'BERICHT_TAB_ON_SHIPMENT' => GETPOST('tab_shipment', 'int') ? '1' : '0', + 'BERICHT_SIGNATURE_IMAGE_RATIO' => str_replace(',', '.', (string) GETPOST('sig_ratio', 'alphanohtml')) ?: '0.35', 'BERICHT_BURN_ANNOTATIONS' => GETPOST('burn', 'int') ? '1' : '0', 'BERICHT_LIBREOFFICE_BIN' => GETPOST('lobin', 'alphanohtml'), 'BERICHT_WHISPER_URL' => GETPOST('whisper_url', 'alphanohtml'), @@ -105,6 +107,14 @@ print '

Mit dem Handy print ''; print ''; +// Lieferschein-Unterschrift Konfigurator +print '

'; +print '

'.$langs->trans("BerichtSignatureBoxConfig").'

'; +print '

'.$langs->trans("BerichtSignatureBoxConfigDesc").'

'; +$editor_url = dol_buildpath('/bericht/admin/signature_box_editor.php', 1); +print '✍️ '.$langs->trans("BerichtOpenSignatureBoxEditor").''; +print '
'; + // Batch-Modus Link print '
'; print '

📦 Batch-Modus

'; @@ -211,6 +221,12 @@ $cb = function ($name, $key, $default) { print ''.$langs->trans("BerichtSetupTabInvoice").''.$cb('tab_invoice', 'BERICHT_TAB_ON_INVOICE', '1').''; print ''.$langs->trans("BerichtSetupTabOrder").''.$cb('tab_order', 'BERICHT_TAB_ON_ORDER', '1').''; print ''.$langs->trans("BerichtSetupTabPropal").''.$cb('tab_propal', 'BERICHT_TAB_ON_PROPAL', '1').''; +print ''.$langs->trans("BerichtSetupTabShipment").''.$cb('tab_shipment', 'BERICHT_TAB_ON_SHIPMENT', '1').''; +$sig_ratio = getDolGlobalString('BERICHT_SIGNATURE_IMAGE_RATIO', '0.35'); +print ''.$langs->trans("BerichtSetupSignatureRatio").''; +print ''; +print '
'.$langs->trans("BerichtSetupSignatureRatioDesc").''; +print ''; print ''.$langs->trans("BerichtSetupBurnAnnotations").''.$cb('burn', 'BERICHT_BURN_ANNOTATIONS', '1').''; print ''.$langs->trans("BerichtSetupLibreOfficeBin").''; diff --git a/admin/signature_box_editor.php b/admin/signature_box_editor.php new file mode 100644 index 0000000..bc840fc --- /dev/null +++ b/admin/signature_box_editor.php @@ -0,0 +1,406 @@ + + * GPL v3+ + * + * Visueller Editor fuer die Unterschriftsbox auf Lieferschein-PDFs. + * Laedt ein Beispiel-Lieferschein-PDF (PDF.js), zeigt die aktuelle Box als + * Fabric.js-Rechteck und speichert die mm-Geometrie in llx_bericht_signature_box. + */ + +$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 __DIR__.'/../lib/bericht.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php'; + +if (!$user->admin && !$user->hasRight('bericht', 'admin')) accessforbidden(); +$langs->loadLangs(array("admin", "bericht@bericht")); + +// Verfuegbare Templates: PDF-Module + ODT-Module + ODT-Dateien aus doctemplates/shipments/ +$tpl_dir = DOL_DOCUMENT_ROOT.'/core/modules/expedition/doc'; +$pdf_modules = array(); // ['espadon' => 'espadon', ...] +$odt_modules = array(); // ['generic_shipment_odt' => 'generic_shipment_odt'] +if (is_dir($tpl_dir)) { + foreach (glob($tpl_dir.'/pdf_*.modules.php') as $f) { + $name = basename($f); + if (preg_match('/^pdf_(.+)\.modules\.php$/', $name, $m)) { + $pdf_modules[$m[1]] = $m[1]; + } + } + foreach (glob($tpl_dir.'/doc_*.modules.php') as $f) { + $name = basename($f); + if (preg_match('/^doc_(.+)\.modules\.php$/', $name, $m)) { + $odt_modules[$m[1]] = $m[1]; + } + } +} + +// ODT-Dateien aus doctemplates/shipments/ — pro Datei eine eigene Box konfigurierbar +// EXPEDITION_ADDON_PDF_ODT_PATH enthaelt 'DOL_DATA_ROOT' woertlich + ist komma/zeilengetrennt +$odt_files = array(); +$raw_dirs = getDolGlobalString('EXPEDITION_ADDON_PDF_ODT_PATH', 'DOL_DATA_ROOT/doctemplates/shipments'); +foreach (explode(',', preg_replace('/[\r\n]+/', ',', trim($raw_dirs))) as $d) { + $d = trim($d); + if (!$d) continue; + $d = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $d); + if (!is_dir($d)) continue; + foreach (glob($d.'/*.odt') as $f) { + $name = basename($f); + $key = 'odt:'.preg_replace('/\.odt$/i', '', $name); + $odt_files[$key] = $name; + } +} + +$active_template = getDolGlobalString('EXPEDITION_ADDON_PDF', 'merou'); +$selected = GETPOST('template', 'alphanohtml') ?: $active_template; + +// Wenn nichts uebergeben + es gibt ODTs + Aktives Modul ist generic_shipment_odt → erstes ODT vorauswaehlen +if (!GETPOST('template', 'alphanohtml') && $active_template === 'generic_shipment_odt' && !empty($odt_files)) { + $selected = array_key_first($odt_files); +} + +// Auswahl validieren +$all_choices = array_merge($pdf_modules, $odt_modules, $odt_files); +if (!isset($all_choices[$selected])) { + $selected = $active_template; + if (!isset($all_choices[$selected]) && !empty($all_choices)) { + $selected = array_key_first($all_choices); + } +} + +// Aktuelle Box-Geometrie fuer das ausgewaehlte Template laden (oder Default) +$box = bericht_get_signature_box($db, $selected); + +// Letzte vorhandene Expedition fuer Preview suchen — sonst Hinweis ausgeben +$preview_shipment_id = 0; +$r = $db->query("SELECT rowid FROM ".$db->prefix()."expedition" + ." WHERE entity IN (".getEntity('expedition').")" + ." ORDER BY rowid DESC LIMIT 1"); +if ($r && ($o = $db->fetch_object($r))) $preview_shipment_id = (int) $o->rowid; + +llxHeader('', $langs->trans("BerichtSignatureBoxConfig")); + +$linkback = ''.$langs->trans("BackToSetup").''; +print load_fiche_titre($langs->trans("BerichtSignatureBoxConfig"), $linkback, 'title_setup'); +print '

'.$langs->trans("BerichtSignatureBoxConfigDesc").'

'; + +// Template-Wahl mit Gruppen +print '
'; +print ''; +print ''; +print ' ★ = aktuell in Dolibarr aktiv'; +print '
'; + +// Hinweis bei ODT-Auswahl: mm-Editor ist hier NICHT relevant. +// Stattdessen: Bild-Platzhalter "signature" im ODT einbauen — die Position waechst dann automatisch mit dem PDF-Inhalt mit. +$is_odt = (strpos($selected, 'odt:') === 0); +if ($is_odt) { + $sig_ratio = getDolGlobalString('BERICHT_SIGNATURE_IMAGE_RATIO', '0.35'); + print '
'; + print '

📄 ODT-Template — Platzhalter-Methode (wie EPCQR/{qrcode})

'; + print '

Bei ODT-Vorlagen ist die mm-Box nicht sinnvoll: das PDF wird je nach Positionsmenge unterschiedlich lang — fixe Position würde mitten in den Zeilen landen.

'; + print '

Stattdessen:

'; + print '
    '; + print '
  1. In deinem ODT ('.dol_escape_htmltag($odt_files[$selected] ?? '').') an die gewünschte Stelle den Text-Platzhalter {signature} schreiben — irgendwo, wo die Unterschrift erscheinen soll.
  2. '; + print '
  3. Drumherum optional Text-Variablen einsetzen: {signer_name}, {signed_at}, {gps}, {kunde_name}, {kunde_adresse}, {shipment_ref}, {order_ref}, {auftragsnummer}, {datum}.
  4. '; + print '
  5. Speichern. Fertig.
  6. '; + print '
'; + print '

Beim Bestätigen in der PWA ersetzt setImage() den {signature}-Text durch ein <draw:frame> mit der Kunden-Unterschrift. Die Größe wird durch den Faktor BERICHT_SIGNATURE_IMAGE_RATIO (aktuell '.dol_escape_htmltag($sig_ratio).') gesteuert — bei 0.35 ergibt das ca. 7×3.5 cm.

'; + print '

Die mm-Felder rechts sind hier ohne Wirkung — sie gelten nur für reine PDF-Module (espadon/merou/rouget).

'; + print '
'; +} + +if (!$preview_shipment_id) { + print '
'.$langs->trans("BerichtSignatureBoxNoPreviewShipment").'
'; +} else { + // PDF-URL fuer die Vorschau: nutzt den shipments.php-Endpoint mit JWT + // Hier brauchen wir keinen JWT, weil wir admin sind und das PDF lokal lesen. + // Stattdessen: Vorschau-Endpoint nimmt nur die Expedition-ID und laedt das PDF. + $preview_url = dol_buildpath('/bericht/admin/signature_box_preview.php', 1) + .'?id='.$preview_shipment_id + .'&template='.urlencode($selected); + + print '
'; + print '
'; + + // Linke Spalte: PDF-Vorschau mit Box + print '
'; + print '

'.$langs->trans("BerichtPreviewWithBox").'

'; + print ''; + print '
'; + print '
'; + print '
'; + print '
'; + print ''; + print ''; + print ''; + print '
'; + print '
'; + + // Rechte Spalte: Werte editierbar + Speichern (bei ODT optisch gedimmt) + $right_attr = $is_odt ? ' style="flex:0 0 280px;opacity:0.5;pointer-events:none;"' : ' style="flex:0 0 280px;"'; + print ''; + print '

'.$langs->trans("BerichtBoxGeometry").($is_odt ? ' (bei ODT inaktiv)' : '').'

'; + print '
'; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print '
'.$langs->trans("BerichtBoxPage").'
X (mm)
Y (mm)
'.$langs->trans("Width").' (mm)
'.$langs->trans("Height").' (mm)
'.$langs->trans("BerichtBoxLabel").'
'; + print '
'; + print ''; + print ''; + print '
'; + print '
'; + print '
'; // rechte Spalte + print '
'; // Flex + print '
'; // section + + // PDF.js einbinden + Editor-JS + $pdfjs_url = dol_buildpath('/bericht/js/lib/pdf.min.js', 1); + $pdfjs_worker_url = dol_buildpath('/bericht/js/lib/pdf.worker.min.js', 1); + $fabric_url = dol_buildpath('/bericht/js/lib/fabric.min.js', 1); + $save_url = dol_buildpath('/bericht/ajax/save_signature_box.php', 1); + ?> + + + + close(); diff --git a/admin/signature_box_preview.php b/admin/signature_box_preview.php new file mode 100644 index 0000000..8202813 --- /dev/null +++ b/admin/signature_box_preview.php @@ -0,0 +1,82 @@ + + * GPL v3+ + * + * Liefert ein Beispiel-Lieferschein-PDF fuer den signature_box_editor. + * Nur fuer eingeloggte Admins / bericht-admin Rechte. + */ + +$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 __DIR__.'/../lib/bericht.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php'; + +if (!$user->admin && !$user->hasRight('bericht', 'admin')) accessforbidden(); + +$id = (int) GETPOST('id', 'int'); +$template = GETPOST('template', 'alphanohtml'); +if (!$id) { http_response_code(400); exit('id fehlt'); } + +$shipment = new Expedition($db); +if ($shipment->fetch($id) <= 0) { http_response_code(404); exit('Lieferung nicht gefunden'); } +$shipment->fetch_thirdparty(); + +// Vorschau-PDF generieren mit dem ausgewaehlten Template — Overrides nur request-lokal +// (keine dolibarr_set_const, sonst persistieren wir DB-Konstanten ungewollt). +$pdf_path = null; +if ($template) { + if (strpos($template, 'odt:') === 0) { + // ODT-Datei aus doctemplates/shipments/ — generic_shipment_odt nimmt das erste ODT im konfigurierten Pfad. + // Wir kopieren das gewuenschte ODT in einen tempo-Pfad und stellen den ODT_PATH request-lokal um. + $odt_filename = substr($template, 4).'.odt'; + $odt_full = null; + $raw_dirs = getDolGlobalString('EXPEDITION_ADDON_PDF_ODT_PATH', 'DOL_DATA_ROOT/doctemplates/shipments'); + foreach (explode(',', preg_replace('/[\r\n]+/', ',', trim($raw_dirs))) as $d) { + $d = trim($d); + if (!$d) continue; + $d = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $d); + if (file_exists($d.'/'.$odt_filename)) { $odt_full = $d.'/'.$odt_filename; break; } + } + if ($odt_full) { + $tmp_tpl_dir = DOL_DATA_ROOT.'/bericht/temp/sigbox_tpl_'.uniqid(); + dol_mkdir($tmp_tpl_dir); + @copy($odt_full, $tmp_tpl_dir.'/'.$odt_filename); + + // Nur in-memory overriden — schreibt NICHT in llx_const + $old_path = $conf->global->EXPEDITION_ADDON_PDF_ODT_PATH ?? ''; + $conf->global->EXPEDITION_ADDON_PDF_ODT_PATH = $tmp_tpl_dir; + try { + $shipment->generateDocument('generic_shipment_odt', $langs); + } catch (Throwable $e) { + dol_syslog('signature_box_preview ODT: '.$e->getMessage(), LOG_ERR); + } + $conf->global->EXPEDITION_ADDON_PDF_ODT_PATH = $old_path; + @unlink($tmp_tpl_dir.'/'.$odt_filename); + @rmdir($tmp_tpl_dir); + + $pdf_path = bericht_get_shipment_pdf($db, $shipment); + } + } else { + // PDF/ODT-Modul direkt + try { $shipment->generateDocument($template, $langs); } catch (Throwable $e) {} + $pdf_path = bericht_get_shipment_pdf($db, $shipment); + } +} else { + $pdf_path = bericht_get_shipment_pdf($db, $shipment); +} + +if (!$pdf_path || !file_exists($pdf_path)) { http_response_code(404); exit('Lieferschein-PDF nicht verfuegbar'); } + +header('Content-Type: application/pdf'); +header('Content-Disposition: inline; filename="'.basename($pdf_path).'"'); +header('Content-Length: '.filesize($pdf_path)); +readfile($pdf_path); +exit; diff --git a/ajax/save_signature_box.php b/ajax/save_signature_box.php new file mode 100644 index 0000000..26c2bd4 --- /dev/null +++ b/ajax/save_signature_box.php @@ -0,0 +1,67 @@ + + * GPL v3+ + * + * Speichert die Geometrie der Unterschriftsbox in llx_bericht_signature_box. + * UNIQUE-Index (entity, template_name) → ON DUPLICATE KEY UPDATE. + */ + +require_once __DIR__.'/_inc.php'; + +if (!$user->admin && !$user->hasRight('bericht', 'admin')) { + http_response_code(403); + echo json_encode(array('error' => 'Forbidden')); + exit; +} + +// Token-Check (Dolibarr-Standard: Session-Token im POST muss matchen) +$posted_token = GETPOST('token', 'alpha'); +if (!$posted_token || !isset($_SESSION['token']) || $posted_token !== $_SESSION['token']) { + http_response_code(403); + echo json_encode(array('error' => 'CSRF-Token ungueltig')); + exit; +} + +$template = trim((string) GETPOST('template_name', 'alphanohtml')); +$page = trim((string) GETPOST('page', 'alphanohtml')) ?: 'last'; +$x_mm = (float) GETPOST('x_mm', 'alpha'); +$y_mm = (float) GETPOST('y_mm', 'alpha'); +$w_mm = (float) GETPOST('w_mm', 'alpha'); +$h_mm = (float) GETPOST('h_mm', 'alpha'); +$label = trim((string) GETPOST('label', 'restricthtml')) ?: 'Unterschrift Kunde'; + +if ($template === '' || $w_mm <= 0 || $h_mm <= 0) { + echo json_encode(array('error' => 'Ungueltige Werte')); + exit; +} +$allowed_pages = array('first', 'last'); +if (!in_array($page, $allowed_pages, true) && !ctype_digit((string) $page)) { + $page = 'last'; +} + +$sql = "INSERT INTO ".$db->prefix()."bericht_signature_box" + ." (entity, template_name, page, x_mm, y_mm, w_mm, h_mm, label, fk_user_modif)" + ." VALUES (" + .((int) $conf->entity)."," + ."'".$db->escape($template)."'," + ."'".$db->escape($page)."'," + .((float) $x_mm)."," + .((float) $y_mm)."," + .((float) $w_mm)."," + .((float) $h_mm)."," + ."'".$db->escape($label)."'," + .((int) $user->id) + .") ON DUPLICATE KEY UPDATE " + ."page='".$db->escape($page)."'," + ."x_mm=".((float) $x_mm)."," + ."y_mm=".((float) $y_mm)."," + ."w_mm=".((float) $w_mm)."," + ."h_mm=".((float) $h_mm)."," + ."label='".$db->escape($label)."'," + ."fk_user_modif=".((int) $user->id); + +if (!$db->query($sql)) { + echo json_encode(array('error' => $db->lasterror())); + exit; +} +echo json_encode(array('ok' => true)); diff --git a/api/_jwt.php b/api/_jwt.php index f54c869..142fcd7 100644 --- a/api/_jwt.php +++ b/api/_jwt.php @@ -56,11 +56,19 @@ function bericht_jwt_from_request() $hdr = ''; if (isset($_SERVER['HTTP_AUTHORIZATION'])) { $hdr = $_SERVER['HTTP_AUTHORIZATION']; + } elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) { + $hdr = $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; } elseif (function_exists('apache_request_headers')) { $h = apache_request_headers(); if (isset($h['Authorization'])) $hdr = $h['Authorization']; } - if (!$hdr || stripos($hdr, 'bearer ') !== 0) return null; - $token = trim(substr($hdr, 7)); + $token = ''; + if ($hdr && stripos($hdr, 'bearer ') === 0) { + $token = trim(substr($hdr, 7)); + } elseif (!empty($_GET['jwt'])) { + // Fallback: JWT als Query-Param (fuer , ,