All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Dolibarr-Modul für Arbeitsberichte aus Rechnungs-Anhängen mit Browser-PDF-Editor. - Reiter "Bericht" auf Rechnungen, Aufträgen und Angeboten - Anhänge-Browser inkl. verknüpfter Objekte (Auftrag → Rechnung) - PDF.js + Fabric.js Browser-Editor: Pfeile, Kreise, Rechtecke, Freihand, Text - SortableJS Seiten-Verwaltung mit Drag&Drop - ODT-Deckblatt mit Platzhaltern, Templates im Admin verwaltbar - TCPDF + FPDI Finalisierung mit eingebrannten Annotationen - ECM-Verknüpfung: PDF erscheint unter Verknüpfte Dokumente - Auftragsnummer aus existierendem Extrafield options_auftragsnummer - Mehrere Berichte pro Dokument - Beim Aktivieren werden vorhandene Extrafields nicht überschrieben Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
85 lines
2.8 KiB
PHP
85 lines
2.8 KiB
PHP
<?php
|
|
/* Fügt einen vorhandenen Anhang als neue Seite zum Bericht hinzu.
|
|
* POST: berichtid, relpath, mime, token
|
|
*/
|
|
require_once __DIR__.'/_inc.php';
|
|
|
|
global $db, $user;
|
|
|
|
if (!$user->hasRight('bericht', 'write')) bericht_ajax_fail('Permission denied', 403);
|
|
|
|
$berichtid = (int) ($_POST['berichtid'] ?? 0);
|
|
$relpath = (string) ($_POST['relpath'] ?? '');
|
|
$mime = (string) ($_POST['mime'] ?? '');
|
|
|
|
$bericht = new Bericht($db);
|
|
if ($bericht->fetch($berichtid) <= 0) bericht_ajax_fail('Bericht nicht gefunden', 404);
|
|
|
|
$fullpath = bericht_resolve_data_path($relpath);
|
|
if (!$fullpath || !file_exists($fullpath)) bericht_ajax_fail('Datei nicht gefunden', 404);
|
|
|
|
// Source-Type bestimmen
|
|
if (strpos($mime, 'image') === 0) $type = 'image';
|
|
elseif (strpos($mime, 'pdf') !== false) $type = 'pdf';
|
|
else bericht_ajax_fail('Dateityp nicht unterstützt: '.$mime);
|
|
|
|
// Höchste page_order ermitteln
|
|
$res = $db->query("SELECT COALESCE(MAX(page_order),0) AS m FROM ".$db->prefix()."bericht_page WHERE fk_bericht = ".((int) $berichtid));
|
|
$next_order = ($res && ($o = $db->fetch_object($res))) ? ((int) $o->m) + 1 : 1;
|
|
|
|
$created = array();
|
|
|
|
if ($type === 'pdf') {
|
|
// Bei mehrseitigen PDFs: pro Seite einen Eintrag — Seitenanzahl ermitteln
|
|
$pagecount = bericht_pdf_pagecount($fullpath);
|
|
if ($pagecount < 1) $pagecount = 1;
|
|
for ($p = 1; $p <= $pagecount; $p++) {
|
|
$page = new BerichtPage($db);
|
|
$page->fk_bericht = $berichtid;
|
|
$page->page_order = $next_order++;
|
|
$page->source_type = 'pdf';
|
|
$page->source_path = $relpath;
|
|
$page->source_page = $p;
|
|
if ($page->create() > 0) $created[] = $page->id;
|
|
}
|
|
} else {
|
|
$page = new BerichtPage($db);
|
|
$page->fk_bericht = $berichtid;
|
|
$page->page_order = $next_order++;
|
|
$page->source_type = 'image';
|
|
$page->source_path = $relpath;
|
|
$page->source_page = null;
|
|
if ($page->create() > 0) $created[] = $page->id;
|
|
}
|
|
|
|
bericht_ajax_ok(array('created' => $created));
|
|
|
|
/**
|
|
* Liefert die Seitenanzahl eines PDFs (best-effort).
|
|
*/
|
|
function bericht_pdf_pagecount($path)
|
|
{
|
|
// Versuch 1: Imagick
|
|
if (class_exists('Imagick')) {
|
|
try {
|
|
$im = new Imagick();
|
|
$im->pingImage($path);
|
|
$n = $im->getNumberImages();
|
|
$im->clear();
|
|
return $n;
|
|
} catch (Throwable $e) {}
|
|
}
|
|
// Versuch 2: pdfinfo
|
|
$out = @shell_exec('pdfinfo '.escapeshellarg($path).' 2>/dev/null');
|
|
if ($out && preg_match('/^Pages:\s+(\d+)/m', $out, $m)) {
|
|
return (int) $m[1];
|
|
}
|
|
// Versuch 3: roher PDF-Count (sehr grob)
|
|
$content = @file_get_contents($path);
|
|
if ($content !== false) {
|
|
if (preg_match_all('/\/Type\s*\/Page[^s]/', $content, $m)) {
|
|
return count($m[0]);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|