| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- <?php
- ini_set('display_errors', 1);
- ini_set('display_startup_errors', 1);
- error_reporting(E_ALL);
- /**
- * pdf-files/headlessChrome_pdf.php
- *
- * Generic headless Chrome PDF generator.
- * Renders any registered report type to a native Chrome PDF.
- *
- * GET params:
- * type string Report type key (see $reportTypes below)
- * rid int Primary record ID
- * rand string Security token (soil_records.rand / plant_records.rand etc.)
- * cid int Client ID (optional, passed through to print page)
- * stid string Soil/crop type (optional, passed through)
- *
- * Usage examples:
- * /pdf-files/headlessChrome_pdf.php?type=soil&rid=123&rand=abc
- * /pdf-files/headlessChrome_pdf.php?type=soil-combined&rid=123&rand=abc
- * /pdf-files/headlessChrome_pdf.php?type=plant&rid=456&rand=def&cid=12
- *
- * To add a new report type: add an entry to $reportTypes below.
- */
- require_once __DIR__ . '/../config/database.php';
- require_once __DIR__ . '/../lib/auth.php';
- require_once __DIR__ . '/../vendor/autoload.php';
- use daandesmedt\PHPHeadlessChrome\HeadlessChrome;
- if (session_status() === PHP_SESSION_NONE) {
- session_start();
- }
- requireLogin();
- // ── Input ─────────────────────────────────────────────────────────────────────
- $type = trim($_GET['type'] ?? 'soil');
- $recordId = (int) ($_GET['rid'] ?? 0);
- $randId = trim( $_GET['rand'] ?? '');
- $clientId = (int) ($_GET['cid'] ?? 0);
- $stid = trim( $_GET['stid'] ?? '');
- // ── Report type registry ──────────────────────────────────────────────────────
- //
- // 'print_page' — path relative to site root; {rid}, {rand}, {cid}, {stid}
- // placeholders are substituted at runtime
- // 'verify_table' — DB table used to verify the rand token before generating
- // 'filename' — prefix for the downloaded PDF filename
- // 'label' — human-readable name (for error messages)
- //
- $reportTypes = [
- // Individual pages
- 'soil-analysis' => [
- 'print_page' => '/dashboard/crop-analysis/soil-test-data/soil-analysis.php?rid={rid}&rand={rand}&cid={cid}&stid={stid}&print=1',
- 'verify_table' => 'soil_records',
- 'filename' => 'soil-analysis',
- 'label' => 'Soil Analysis',
- ],
- 'soil-report' => [
- 'print_page' => '/dashboard/crop-analysis/soil-test-data/soil-report-pdf.php?rid={rid}&rand={rand}',
- 'verify_table' => 'soil_records',
- 'filename' => 'soil-report',
- 'label' => 'Soil Report',
- ],
- // Combined: analysis + AI report in one PDF
- 'soil' => [
- 'print_page' => '/dashboard/crop-analysis/soil-test-data/soil-print-combined.php?rid={rid}&rand={rand}&cid={cid}&stid={stid}',
- 'verify_table' => 'soil_records',
- 'filename' => 'soil-analysis-report',
- 'label' => 'Soil Analysis & Report',
- ],
- // Plant
- 'plant-analysis' => [
- 'print_page' => '/dashboard/crop-analysis/plant-test-data/plant-analysis.php?rid={rid}&rand={rand}&cid={cid}',
- 'verify_table' => 'plant_records',
- 'filename' => 'plant-analysis',
- 'label' => 'Plant Analysis',
- ],
- 'plant-report' => [
- 'print_page' => '/dashboard/crop-analysis/plant-test-data/plant-report-pdf.php?rid={rid}&rand={rand}',
- 'verify_table' => 'plant_records',
- 'filename' => 'plant-report',
- 'label' => 'Plant Report',
- ],
- 'plant' => [
- 'print_page' => '/dashboard/crop-analysis/plant-test-data/plant-print-combined.php?rid={rid}&rand={rand}&cid={cid}',
- 'verify_table' => 'plant_records',
- 'filename' => 'plant-analysis-report',
- 'label' => 'Plant Analysis & Report',
- ],
- // Water
- 'water-analysis' => [
- 'print_page' => '/dashboard/crop-analysis/water-test-data/water-analysis-pdf.php?rid={rid}&rand={rand}&cid={cid}',
- 'verify_table' => 'water_records',
- 'filename' => 'water-analysis',
- 'label' => 'Water Analysis',
- ],
- 'water-report' => [
- 'print_page' => '/dashboard/crop-analysis/water-test-data/water-report-pdf.php?rid={rid}&rand={rand}',
- 'verify_table' => 'water_records',
- 'filename' => 'water-report',
- 'label' => 'Water Report',
- ],
- 'water' => [
- 'print_page' => '/dashboard/crop-analysis/water-test-data/water-print-combined.php?rid={rid}&rand={rand}&cid={cid}',
- 'verify_table' => 'water_records',
- 'filename' => 'water-analysis-report',
- 'label' => 'Water Analysis & Report',
- ],
- // Animal
- 'animal-analysis' => [
- 'print_page' => '/dashboard/crop-analysis/animal-dietary-balance/animal-dietary-balance.php?rid={rid}&rand={rand}&cid={cid}',
- 'verify_table' => 'animal_records',
- 'filename' => 'animal-analysis',
- 'label' => 'Animal Dietary Analysis',
- ],
- 'animal-report' => [
- 'print_page' => '/dashboard/crop-analysis/animal-dietary-balance/animal-report-pdf.php?rid={rid}&rand={rand}',
- 'verify_table' => 'animal_records',
- 'filename' => 'animal-report',
- 'label' => 'Animal Dietary Report',
- ],
- 'animal' => [
- 'print_page' => '/dashboard/crop-analysis/animal-dietary-balance/animal-print-combined.php?rid={rid}&rand={rand}&cid={cid}',
- 'verify_table' => 'animal_records',
- 'filename' => 'animal-dietary-report',
- 'label' => 'Animal Dietary Analysis & Report',
- ],
- ];
- if (!isset($reportTypes[$type])) {
- http_response_code(400);
- $valid = implode(', ', array_keys($reportTypes));
- die("Unknown report type \"" . htmlspecialchars($type, ENT_QUOTES, 'UTF-8') . "\". Valid types: $valid");
- }
- $config = $reportTypes[$type];
- if ($recordId <= 0 || $randId === '') {
- http_response_code(400);
- die('Missing required parameters: rid, rand');
- }
- // ── Verify record exists via its table ───────────────────────────────────────
- $pdo = getDBConnection();
- $table = $config['verify_table'];
- // Allowlist the table name against known tables to prevent SQL injection
- $allowedTables = ['soil_records', 'plant_records', 'water_records', 'animal_records'];
- if (!in_array($table, $allowedTables, true)) {
- http_response_code(500);
- die('Invalid verify_table in report type config.');
- }
- $stmt = $pdo->prepare("SELECT lab_no FROM `{$table}` WHERE id = ? AND rand = ? LIMIT 1");
- $stmt->execute([$recordId, $randId]);
- $record = $stmt->fetch();
- if (!$record) {
- http_response_code(404);
- die('Record not found or access denied.');
- }
- // ── Write one-time print token ────────────────────────────────────────────────
- $token = bin2hex(random_bytes(16));
- $expires = time() + 120;
- $tokenDir = __DIR__ . '/tokens';
- if (!is_dir($tokenDir)) {
- mkdir($tokenDir, 0750, true);
- }
- file_put_contents($tokenDir . '/' . $token . '.tmp', json_encode([
- 'rid' => $recordId,
- 'rand' => $randId,
- 'expires' => $expires,
- 'type' => $type,
- ]));
- // ── Build print page URL ──────────────────────────────────────────────────────
- $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
- $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
- $baseUrl = $scheme . '://' . $host;
- $printPath = str_replace(
- ['{rid}', '{rand}', '{cid}', '{stid}'],
- [$recordId, urlencode($randId), $clientId, urlencode($stid)],
- $config['print_page']
- );
- $printUrl = $baseUrl . $printPath . '&ptoken=' . urlencode($token);
- // ── Output directory + filename ───────────────────────────────────────────────
- $outputDir = __DIR__;
- $today = date('Y-m-d');
- $labNo = preg_replace('/[^A-Za-z0-9\-_]/', '_', $record['lab_no'] ?? $recordId);
- $filename = $config['filename'] . '-' . $labNo . '-' . $today;
- // ── Headless Chrome ───────────────────────────────────────────────────────────
- $chromeBinary = '/usr/bin/google-chrome';
- foreach (['/usr/bin/google-chrome', '/usr/bin/chromium-browser', '/usr/bin/chromium'] as $bin) {
- if (file_exists($bin)) { $chromeBinary = $bin; break; }
- }
- $arguments = [
- '--headless' => '',
- '--disable-gpu' => '',
- '--hide-scrollbars' => '',
- '--enable-viewport' => '',
- '--timeout=' => '6000',
- '--disable-web-security' => '',
- '--run-all-compositor-stages-before-draw' => '',
- '--virtual-time-budget' => '40000',
- ];
- try {
- $chrome = new HeadlessChrome();
- $chrome->disablePDFHeader();
- $chrome->setUrl($printUrl);
- $chrome->setBinaryPath($chromeBinary);
- $chrome->setOutputDirectory($outputDir);
- $chrome->toPDF($filename . '.pdf');
- $pdfPath = $chrome->getFilePath();
- } catch (Exception $e) {
- error_log('HeadlessChrome PDF error [' . $type . ']: ' . $e->getMessage());
- @unlink($tokenDir . '/' . $token . '.tmp');
- http_response_code(500);
- die('PDF generation failed: ' . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8'));
- }
- @unlink($tokenDir . '/' . $token . '.tmp');
- // ── Stream PDF ────────────────────────────────────────────────────────────────
- if (!file_exists($pdfPath)) {
- http_response_code(500);
- die('PDF file was not created. Ensure Chrome is installed at: ' . $chromeBinary);
- }
- chmod($pdfPath, 0644);
- header('Content-Type: application/pdf');
- header('Content-Disposition: attachment; filename="' . $filename . '.pdf"');
- header('Content-Length: ' . filesize($pdfPath));
- header('Expires: 0');
- header('Cache-Control: must-revalidate');
- header('Pragma: public');
- readfile($pdfPath);
- @unlink($pdfPath);
- exit;
|