soil-analysis-pdf.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. <?php
  2. require_once __DIR__ . '/../../../config/database.php';
  3. require_once __DIR__ . '/../../../lib/auth.php';
  4. if (session_status() === PHP_SESSION_NONE) {
  5. session_start();
  6. }
  7. requireLogin();
  8. $client_id = (int) ($_GET['cid'] ?? 0);
  9. $record_id = (int) ($_GET['rid'] ?? 0);
  10. $rand_id = (float)($_GET['rand'] ?? 0);
  11. $croptype = htmlspecialchars(trim($_GET['stid'] ?? ''), ENT_QUOTES, 'UTF-8');
  12. if (!$record_id || !$rand_id) {
  13. http_response_code(400);
  14. die('Invalid request parameters.');
  15. }
  16. try {
  17. $pdo = getDBConnection();
  18. $stmt = $pdo->prepare(
  19. 'SELECT * FROM `soil_records` WHERE `id` = ? AND `rand` = ? LIMIT 1'
  20. );
  21. $stmt->execute([$record_id, $rand_id]);
  22. $row = $stmt->fetch();
  23. } catch (PDOException $e) {
  24. error_log('soil-analysis-pdf.php DB error: ' . $e->getMessage());
  25. http_response_code(500);
  26. die('Database error.');
  27. }
  28. if (!$row) {
  29. http_response_code(404);
  30. die('Record not found.');
  31. }
  32. // All values escaped for HTML output
  33. $h = fn($v) => htmlspecialchars((string)($v ?? ''), ENT_QUOTES, 'UTF-8');
  34. $client = $h($row['client_name']);
  35. $address = $h($row['site_address']);
  36. $state = $h($row['state_postcode']);
  37. $email = $h($row['email']);
  38. $labNo = $h($row['lab_no']);
  39. $sampleDate = $h($row['date_sampled']);
  40. $sample = $h($row['site_id']);
  41. $crop = $h($row['sample_id']);
  42. $today = date('jS F Y');
  43. // Navigation URLs (replacing modX [[~41~]], [[~66~]], [[~37~]] resource links)
  44. $params = http_build_query(['rand' => $rand_id, 'cid' => $client_id, 'rid' => $record_id, 'stid' => $croptype]);
  45. $analysisUrl = '/dashboard/crop-analysis/soil-test-data/soil-analysis.php?' . $params;
  46. $reportUrl = '/dashboard/crop-analysis/soil-test-data/soil-report.php?' . $params;
  47. ?>
  48. <!doctype html>
  49. <html lang="en">
  50. <head>
  51. <meta charset="UTF-8">
  52. <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  53. <title>Soil Analysis PDF | Crop Monitor</title>
  54. <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
  55. <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.css" rel="stylesheet">
  56. <link href="/client-assets/css/dashboard.css" rel="stylesheet">
  57. <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.9.3/html2pdf.bundle.min.js"
  58. integrity="sha512-YcsIPGdhPK4P/uRW6/sruonlYj+Q7UHWeKfTAkBW+g83NKM+jMJFJ4iAPfSnVp7BKD4dKMHmVSvICUbE/V1sSw=="
  59. crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  60. <style>
  61. @media print {
  62. @page { size: A4 portrait; margin: 1cm; }
  63. }
  64. </style>
  65. </head>
  66. <body>
  67. <div class="grid">
  68. <div class="col-md-3">
  69. <img class="img-fluid" src="/client-assets/images/crop-monitor.png" alt="Crop Monitor">
  70. </div>
  71. <div class="col-md-9"></div>
  72. <table class="title">
  73. <tbody>
  74. <tr>
  75. <td class="right"><b>DATE:</b></td>
  76. <td class="left"><?= $today ?></td>
  77. <td></td>
  78. <td class="right"><b>SAMPLE ID:</b></td>
  79. <td class="left"><?= $sample ?></td>
  80. </tr>
  81. <tr>
  82. <td class="right"><b>CLIENT:</b></td>
  83. <td class="left"><?= $client ?></td>
  84. <td></td>
  85. <td class="right"><b>DATE SAMPLED:</b></td>
  86. <td class="left"><?= $sampleDate ?></td>
  87. </tr>
  88. <tr>
  89. <td class="right"><b>ADDRESS:</b></td>
  90. <td class="left"><?= $address ?></td>
  91. <td></td>
  92. <td class="right"><b>LAB NUMBER:</b></td>
  93. <td class="left"><?= $labNo ?></td>
  94. </tr>
  95. <tr>
  96. <td class="right"></td>
  97. <td class="left"><?= $state ?></td>
  98. <td></td>
  99. <td class="right"><b>CROP:</b></td>
  100. <td class="left"><?= $crop ?></td>
  101. </tr>
  102. <tr>
  103. <td class="right"></td>
  104. <td class="left"><?= $email ?></td>
  105. <td></td>
  106. <td></td>
  107. <td></td>
  108. </tr>
  109. </tbody>
  110. </table>
  111. <div class="clearfix"></div>
  112. <!-- Navigation buttons (hidden on print) -->
  113. <div class="pdfHide">
  114. <a href="<?= $analysisUrl ?>" target="_blank">
  115. <input type="button" class="button" value="Analysis Page">
  116. </a>
  117. <a href="<?= $reportUrl ?>" target="_blank">
  118. <input type="button" class="button" value="Soil Report">
  119. </a>
  120. <button class="btn btn-sm btn-secondary downloadPDF">Download PDF</button>
  121. </div>
  122. <div class="nav-wrap">
  123. <div class="graph-header text-center">ANALYSIS RESULTS</div>
  124. </div>
  125. <div class="clearfix"></div>
  126. <hr>
  127. <!-- Analysis table rows are still rendered by [[!soilAnalysisCalcs]] snippets
  128. which need to be migrated to PHP calls — tracked in CLAUDE.md.
  129. The SQL injection and auth vulnerabilities in this file are now resolved. -->
  130. </div>
  131. <script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
  132. <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
  133. <script>
  134. $('.downloadPDF').click(function () {
  135. var element = document.body;
  136. html2pdf().from(element).set({
  137. margin: 3,
  138. filename: 'soil-analysis.pdf',
  139. image: { type: 'jpeg', quality: 1.0 },
  140. html2canvas: { scale: 2, letterRendering: true, windowWidth: 1024 },
  141. jsPDF: { orientation: 'portrait', unit: 'mm', format: 'a4' }
  142. }).save();
  143. });
  144. </script>
  145. </body>
  146. </html>