diff --git a/app/Controllers/Ipd_discharge.php b/app/Controllers/Ipd_discharge.php index 701ef68..496032b 100644 --- a/app/Controllers/Ipd_discharge.php +++ b/app/Controllers/Ipd_discharge.php @@ -87,6 +87,10 @@ class Ipd_discharge extends BaseController template_html LONGTEXT NOT NULL, is_default TINYINT(1) NOT NULL DEFAULT 0, is_audit_only TINYINT(1) NOT NULL DEFAULT 0, + watermark_type VARCHAR(10) NOT NULL DEFAULT 'none', + watermark_text VARCHAR(255) NULL, + watermark_image VARCHAR(255) NULL, + watermark_alpha DECIMAL(4,2) NOT NULL DEFAULT 0.12, status TINYINT(1) NOT NULL DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, @@ -117,6 +121,10 @@ class Ipd_discharge extends BaseController 'footer_html' => 'ALTER TABLE ipd_discharge_templates ADD COLUMN footer_html LONGTEXT NULL AFTER header_html', 'template_css' => 'ALTER TABLE ipd_discharge_templates ADD COLUMN template_css LONGTEXT NULL AFTER footer_html', 'is_audit_only' => 'ALTER TABLE ipd_discharge_templates ADD COLUMN is_audit_only TINYINT(1) NOT NULL DEFAULT 0 AFTER is_default', + 'watermark_type' => "ALTER TABLE ipd_discharge_templates ADD COLUMN watermark_type VARCHAR(10) NOT NULL DEFAULT 'none' AFTER is_audit_only", + 'watermark_text' => 'ALTER TABLE ipd_discharge_templates ADD COLUMN watermark_text VARCHAR(255) NULL AFTER watermark_type', + 'watermark_image' => 'ALTER TABLE ipd_discharge_templates ADD COLUMN watermark_image VARCHAR(255) NULL AFTER watermark_text', + 'watermark_alpha' => 'ALTER TABLE ipd_discharge_templates ADD COLUMN watermark_alpha DECIMAL(4,2) NOT NULL DEFAULT 0.12 AFTER watermark_image', ]; foreach ($columns as $column => $sql) { @@ -191,6 +199,10 @@ class Ipd_discharge extends BaseController 'header_html' => '', 'footer_html' => '', 'template_css' => '', + 'watermark_type' => 'none', + 'watermark_text' => '', + 'watermark_image' => '', + 'watermark_alpha' => 0.12, ]; } @@ -292,7 +304,7 @@ class Ipd_discharge extends BaseController } return $this->db->table('ipd_discharge_templates') - ->select('id,template_name,page_size,custom_width_mm,custom_height_mm,page_margin_top_cm,page_margin_bottom_cm,page_margin_left_cm,page_margin_right_cm,margin_header_cm,margin_footer_cm,header_html,footer_html,template_css,template_html,is_default,is_audit_only,status') + ->select('id,template_name,page_size,custom_width_mm,custom_height_mm,page_margin_top_cm,page_margin_bottom_cm,page_margin_left_cm,page_margin_right_cm,margin_header_cm,margin_footer_cm,header_html,footer_html,template_css,template_html,is_default,is_audit_only,watermark_type,watermark_text,watermark_image,watermark_alpha,status') ->where('status', 1) ->orderBy('is_default', 'DESC') ->orderBy('id', 'ASC') @@ -1174,6 +1186,10 @@ class Ipd_discharge extends BaseController 'header_html' => (string) ($selectedTemplate['header_html'] ?? ''), 'footer_html' => (string) ($selectedTemplate['footer_html'] ?? ''), 'template_css' => (string) ($selectedTemplate['template_css'] ?? ''), + 'watermark_type' => (string) ($selectedTemplate['watermark_type'] ?? 'none'), + 'watermark_text' => (string) ($selectedTemplate['watermark_text'] ?? ''), + 'watermark_image' => (string) ($selectedTemplate['watermark_image'] ?? ''), + 'watermark_alpha' => (float) ($selectedTemplate['watermark_alpha'] ?? 0.12), ]); $rendered = $this->applyDischargeTemplateTokens($templateHtml, $tokenVars); @@ -7405,6 +7421,23 @@ class Ipd_discharge extends BaseController $mpdf->SetHTMLFooter($footerHtml, 'E'); } + $watermarkType = (string) ($templateSettings['watermark_type'] ?? 'none'); + $watermarkAlpha = max(0.01, min(1.00, (float) ($templateSettings['watermark_alpha'] ?? 0.12))); + if ($watermarkType === 'text') { + $watermarkText = trim((string) ($templateSettings['watermark_text'] ?? '')); + if ($watermarkText !== '') { + $mpdf->SetWatermarkText($watermarkText, $watermarkAlpha); + $mpdf->showWatermarkText = true; + } + } elseif ($watermarkType === 'image') { + $watermarkImage = trim((string) ($templateSettings['watermark_image'] ?? '')); + $watermarkPath = $watermarkImage !== '' ? FCPATH . ltrim(str_replace('\\', '/', $watermarkImage), '/') : ''; + if ($watermarkPath !== '' && is_file($watermarkPath)) { + $mpdf->SetWatermarkImage($watermarkPath, $watermarkAlpha); + $mpdf->showWatermarkImage = true; + } + } + if ($withHeader || $footerHtml !== '') { $mpdf->AddPage(); if ($withHeader && $headerHtml !== '') { diff --git a/app/Controllers/Setting/Template.php b/app/Controllers/Setting/Template.php index b7e3b78..b92ab02 100644 --- a/app/Controllers/Setting/Template.php +++ b/app/Controllers/Setting/Template.php @@ -2779,6 +2779,10 @@ HTML; template_html LONGTEXT NOT NULL, is_default TINYINT(1) NOT NULL DEFAULT 0, is_audit_only TINYINT(1) NOT NULL DEFAULT 0, + watermark_type VARCHAR(10) NOT NULL DEFAULT 'none', + watermark_text VARCHAR(255) NULL, + watermark_image VARCHAR(255) NULL, + watermark_alpha DECIMAL(4,2) NOT NULL DEFAULT 0.12, status TINYINT(1) NOT NULL DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, @@ -2809,6 +2813,10 @@ HTML; 'footer_html' => "ALTER TABLE ipd_discharge_templates ADD COLUMN footer_html LONGTEXT NULL AFTER header_html", 'template_css' => "ALTER TABLE ipd_discharge_templates ADD COLUMN template_css LONGTEXT NULL AFTER footer_html", 'is_audit_only' => "ALTER TABLE ipd_discharge_templates ADD COLUMN is_audit_only TINYINT(1) NOT NULL DEFAULT 0 AFTER is_default", + 'watermark_type' => "ALTER TABLE ipd_discharge_templates ADD COLUMN watermark_type VARCHAR(10) NOT NULL DEFAULT 'none' AFTER is_audit_only", + 'watermark_text' => "ALTER TABLE ipd_discharge_templates ADD COLUMN watermark_text VARCHAR(255) NULL AFTER watermark_type", + 'watermark_image' => "ALTER TABLE ipd_discharge_templates ADD COLUMN watermark_image VARCHAR(255) NULL AFTER watermark_text", + 'watermark_alpha' => "ALTER TABLE ipd_discharge_templates ADD COLUMN watermark_alpha DECIMAL(4,2) NOT NULL DEFAULT 0.12 AFTER watermark_image", ]; foreach ($columns as $col => $sql) { @@ -2954,6 +2962,31 @@ HTML; $isDefault = (int) ($this->request->getPost('is_default') ?? 0) === 1 ? 1 : 0; $isAuditOnly = (int) ($this->request->getPost('is_audit_only') ?? 0) === 1 ? 1 : 0; $status = (int) ($this->request->getPost('status') ?? 1) === 1 ? 1 : 0; + $watermarkType = (string) ($this->request->getPost('watermark_type') ?? 'none'); + if (! in_array($watermarkType, ['none', 'text', 'image'], true)) { + $watermarkType = 'none'; + } + $watermarkText = trim((string) ($this->request->getPost('watermark_text') ?? '')); + $watermarkAlpha = max(0.01, min(1.00, (float) ($this->request->getPost('watermark_alpha') ?? 0.12))); + $watermarkImage = ''; + if ($id > 0) { + $watermarkImage = (string) (($this->db->table('ipd_discharge_templates')->select('watermark_image')->where('id', $id)->get(1)->getRowArray() ?? [])['watermark_image'] ?? ''); + } + $watermarkUpload = $this->request->getFile('watermark_image'); + if ($watermarkUpload && $watermarkUpload->isValid() && ! $watermarkUpload->hasMoved()) { + $extension = strtolower((string) $watermarkUpload->getExtension()); + if (in_array($extension, ['png', 'jpg', 'jpeg', 'webp'], true)) { + $directory = FCPATH . 'assets' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'discharge_watermarks'; + if (! is_dir($directory)) { + mkdir($directory, 0755, true); + } + $fileName = 'discharge_wm_' . date('YmdHis') . '_' . bin2hex(random_bytes(4)) . '.' . $extension; + $watermarkUpload->move($directory, $fileName); + $watermarkImage = 'assets/images/discharge_watermarks/' . $fileName; + } else { + $watermarkType = 'none'; + } + } // In SPA/AJAX reload scenarios, CKEditor can occasionally post an empty // template field even when editing an existing row. Preserve existing @@ -3004,6 +3037,10 @@ HTML; 'template_html' => $templateHtml, 'is_default' => $isDefault, 'is_audit_only' => $isAuditOnly, + 'watermark_type' => $watermarkType, + 'watermark_text' => $watermarkText, + 'watermark_image' => $watermarkImage, + 'watermark_alpha' => $watermarkAlpha, 'status' => $status, ]; diff --git a/app/Views/ipd_discharge/discharge_template_edit.php b/app/Views/ipd_discharge/discharge_template_edit.php index 691d46f..11f3009 100644 --- a/app/Views/ipd_discharge/discharge_template_edit.php +++ b/app/Views/ipd_discharge/discharge_template_edit.php @@ -17,6 +17,10 @@ $marginHeader= (string) ($edit['margin_header_cm'] ?? '1.00'); $marginFooter= (string) ($edit['margin_footer_cm'] ?? '0.50'); $isDefault = (int) ($edit['is_default'] ?? 0); $isAuditOnly = (int) ($edit['is_audit_only'] ?? 0); +$watermarkType = (string) ($edit['watermark_type'] ?? 'none'); +$watermarkText = (string) ($edit['watermark_text'] ?? ''); +$watermarkAlpha = (string) ($edit['watermark_alpha'] ?? '0.12'); +$watermarkImage = (string) ($edit['watermark_image'] ?? ''); $status = (int) ($edit['status'] ?? 1); $placeholders = [ @@ -122,20 +126,48 @@ $placeholders = [
- +
- +
- +
+
+
Document Watermark
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
Current image:
+
+
+
+
> @@ -188,6 +220,15 @@ $placeholders = [ (function () { var editId = parseInt(document.getElementById('tpl_edit_id').value || '0', 10); var msg = document.getElementById('tpl_edit_msg'); + var editorIds = ['tpl_header_html', 'tpl_footer_html', 'tpl_html']; + + if (typeof CKEDITOR !== 'undefined') { + editorIds.forEach(function (id) { + if (!CKEDITOR.instances[id]) { + CKEDITOR.replace(id, {height: id === 'tpl_html' ? 280 : 140}); + } + }); + } function getCsrf() { var el = document.querySelector('input[name^="csrf_"]'); @@ -205,7 +246,13 @@ $placeholders = [ msg.innerHTML = '
' + text + '
'; } - function val(id) { var el = document.getElementById(id); return el ? el.value : ''; } + function val(id) { + if (typeof CKEDITOR !== 'undefined' && CKEDITOR.instances[id]) { + return CKEDITOR.instances[id].getData(); + } + var el = document.getElementById(id); + return el ? el.value : ''; + } function checked(id) { var el = document.getElementById(id); return el ? (el.checked ? 1 : 0) : 0; } document.getElementById('btn_save_tpl').addEventListener('click', function () { @@ -232,6 +279,13 @@ $placeholders = [ fd.append('status', val('tpl_status')); fd.append('is_default', String(checked('tpl_is_default'))); fd.append('is_audit_only', String(checked('tpl_is_audit'))); + fd.append('watermark_type', val('tpl_watermark_type')); + fd.append('watermark_text', val('tpl_watermark_text')); + fd.append('watermark_alpha', val('tpl_watermark_alpha')); + var watermarkImage = document.getElementById('tpl_watermark_image'); + if (watermarkImage && watermarkImage.files.length) { + fd.append('watermark_image', watermarkImage.files[0]); + } fetch('', { method: 'POST', @@ -278,6 +332,15 @@ $placeholders = [ document.querySelectorAll('.ph-chip').forEach(function (chip) { chip.addEventListener('click', function () { var token = chip.getAttribute('data-token'); + if (typeof CKEDITOR !== 'undefined') { + var focusedEditor = editorIds.map(function (id) { return CKEDITOR.instances[id]; }).find(function (editor) { + return editor && editor.focusManager && editor.focusManager.hasFocus; + }); + if (focusedEditor) { + focusedEditor.insertHtml(token); + return; + } + } var active = document.activeElement; var targets = ['tpl_header_html', 'tpl_footer_html', 'tpl_html', 'tpl_css']; var target = null;