From dd76b3601a24f01b54e77d517bd0731cbbcb44f7 Mon Sep 17 00:00:00 2001 From: Daniel Kastl Date: Wed, 5 Jun 2024 16:01:21 +0900 Subject: [PATCH 01/22] Create de.yml --- config/locales/de.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 config/locales/de.yml diff --git a/config/locales/de.yml b/config/locales/de.yml new file mode 100644 index 0000000..346523b --- /dev/null +++ b/config/locales/de.yml @@ -0,0 +1 @@ +de: From 54273bf01c0a112e9c319dbfeebed617dd244e51 Mon Sep 17 00:00:00 2001 From: Daniel Kastl Date: Mon, 24 Jun 2024 17:17:08 +0900 Subject: [PATCH 02/22] Upgrading PDFme Signed-off-by: Daniel Kastl --- app/views/print_templates/_form.html.erb | 2 +- .../print_templates_pdfme/designer.html.erb | 2 +- app/views/print_templates_pdfme/form.html.erb | 4 +- config/locales/en.yml | 2 + package.json | 17 +- src/designerUtils.ts | 2 +- src/form.ts | 4 +- src/locales.ts | 11 +- yarn.lock | 495 ++++++++---------- 9 files changed, 253 insertions(+), 286 deletions(-) diff --git a/app/views/print_templates/_form.html.erb b/app/views/print_templates/_form.html.erb index 811fccb..df589b9 100644 --- a/app/views/print_templates/_form.html.erb +++ b/app/views/print_templates/_form.html.erb @@ -9,7 +9,7 @@

<%= content_tag(:label, l(:label_print_template_basepdf), for: 'pdf-upload') %> - + <%= link_to l(:link_print_template_basepdf_reset), '#', id: 'use-blank-pdf', style: ('display: none;' unless @print_template.basepdf.present?) %>

diff --git a/app/views/print_templates_pdfme/designer.html.erb b/app/views/print_templates_pdfme/designer.html.erb index ef3addf..4e27815 100644 --- a/app/views/print_templates_pdfme/designer.html.erb +++ b/app/views/print_templates_pdfme/designer.html.erb @@ -8,7 +8,7 @@
-
Reload page to start PDFme Designer...
+
<%= l(:reload_page_for_designer) %>
<%= javascript_include_tag 'pdfme-designer', plugin: :redmine_print_templates %> diff --git a/app/views/print_templates_pdfme/form.html.erb b/app/views/print_templates_pdfme/viewer.html.erb similarity index 62% rename from app/views/print_templates_pdfme/form.html.erb rename to app/views/print_templates_pdfme/viewer.html.erb index f3006a9..f4767cd 100644 --- a/app/views/print_templates_pdfme/form.html.erb +++ b/app/views/print_templates_pdfme/viewer.html.erb @@ -2,7 +2,7 @@ <% plugin_name = Redmine::Plugin.find(:redmine_print_templates).name rescue 'Print Templates' %> - <%= "#{plugin_name} - Form" %> + <%= "#{plugin_name} - Viewer" %> <%= stylesheet_link_tag('print_pdfme', plugin: 'redmine_print_templates') %> @@ -10,11 +10,11 @@
<%= l(:reload_page_for_designer) %>
- <%= javascript_include_tag 'pdfme-form', plugin: :redmine_print_templates %> + <%= javascript_include_tag 'pdfme-viewer', plugin: :redmine_print_templates %> diff --git a/assets/javascripts/print_templates_designer.js b/assets/javascripts/print_templates_designer.js index 7f449f5..ac17bc0 100644 --- a/assets/javascripts/print_templates_designer.js +++ b/assets/javascripts/print_templates_designer.js @@ -1,216 +1,163 @@ -document.addEventListener("DOMContentLoaded", function() { - const basepdfField = document.getElementById('print_template_basepdf'); - const schemasField = document.getElementById('print_template_schemas'); - const inputsField = document.getElementById('print_template_inputs'); - const trackerIdSelect = document.getElementById('print_template_tracker_id'); - const openBtn = document.getElementById('open-designer-fullscreen-btn'); - const closeBtn = document.getElementById('close-designer-fullscreen-btn'); - const designerOverlay = document.getElementById('designer-fullscreen'); - const iframe = document.getElementById('pdfme-designer-iframe'); - const uploadField = document.getElementById('pdf-upload'); - const useBlankPdfLink = document.getElementById('use-blank-pdf'); - const templateDownloadBtn = document.getElementById('template_download-designer-fullscreen-btn'); - const templateUploadBtn = document.getElementById('template_upload-designer-fullscreen-btn'); - const templateFileInput = document.getElementById('template-file-input'); - - // Function to update fields dropdown - function loadTrackerData() { - if (trackerIdSelect) { +document.addEventListener("DOMContentLoaded", () => { + const elements = { + basepdfField: document.getElementById('print_template_basepdf'), + schemasField: document.getElementById('print_template_schemas'), + trackerIdSelect: document.getElementById('print_template_tracker_id'), + openBtn: document.getElementById('open-designer-fullscreen-btn'), + closeBtn: document.getElementById('close-designer-fullscreen-btn'), + designerOverlay: document.getElementById('designer-fullscreen'), + iframe: document.getElementById('pdfme-designer-iframe'), + uploadField: document.getElementById('pdf-upload'), + useBlankPdfLink: document.getElementById('use-blank-pdf'), + templateDownloadBtn: document.getElementById('template_download-designer-fullscreen-btn'), + templateUploadBtn: document.getElementById('template_upload-designer-fullscreen-btn'), + templateFileInput: document.getElementById('template-file-input'), + basepdfIcon: document.getElementById('basepdf-ok-icon') + }; + + console.log('Print Templates Designer loaded!'); + + const showError = (message) => { + console.error(message); + alert('An error occurred. Please try again.'); + }; + + const loadTrackerData = () => { + if (elements.trackerIdSelect) { Rails.ajax({ - url: trackerIdSelect.getAttribute('data-url') + "?tracker_id=" + trackerIdSelect.value, + url: `${elements.trackerIdSelect.getAttribute('data-url')}?tracker_id=${elements.trackerIdSelect.value}`, type: 'GET', dataType: 'json', - success: function(response) { - const coreFields = response.coreFields; - const customFields = response.customFields; - const specialFields = response.specialFields; - - const fieldKeyOptions = createFieldKeyOptions(coreFields, customFields, specialFields); - sessionStorage.setItem('fieldKeyOptions', JSON.stringify(fieldKeyOptions)); + success: (response) => { + sessionStorage.setItem('fieldKeyOptions', JSON.stringify(response.fieldKeyOptions)); + sessionStorage.setItem('fieldFormatOptions', JSON.stringify(response.fieldFormatOptions)); } }); } - } - - // Event listener for tracker dropdown change - if (trackerIdSelect) { - trackerIdSelect.addEventListener('change', function() { - loadTrackerData(); - }); - - // Trigger the loadTrackerData request on page load - loadTrackerData(); - } - - // Function to create fieldKeyOptions from tracker data - function createFieldKeyOptions(coreFields, customFields, specialFields) { - return [ - { - label: 'Core Fields', - options: coreFields.map(field => ({ label: field.label, value: field.key })), - }, - { - label: 'Custom Fields', - options: customFields.map(field => ({ label: field.label, value: field.key })), - }, - { - label: 'Special Fields', - options: specialFields.map(field => ({ label: field.label, value: field.key })), - }, - ]; - } - - if (templateDownloadBtn) { - templateDownloadBtn.addEventListener('click', function() { - const iframeWindow = document.getElementById('pdfme-designer-iframe').contentWindow; - const trackerSelect = document.getElementById('print_template_tracker_id'); - const trackerName = trackerSelect.options[trackerSelect.selectedIndex].text; - - iframeWindow.postMessage({ - type: 'downloadTemplate', - data: { trackerName: trackerName } // Enclose trackerName within a data object - }, window.location.origin); - }); - } - - if (templateUploadBtn && templateFileInput) { - templateUploadBtn.addEventListener('click', function() { - // Trigger the hidden file input when the button is clicked - document.getElementById('template-file-input').click(); - }); - - templateFileInput.addEventListener('change', function(event) { - const file = event.target.files[0]; - if (file && file.type === "application/json") { - const reader = new FileReader(); - reader.onload = function(e) { - try { - const templateData = JSON.parse(e.target.result); - - if (templateData.basePdf) { - basepdfField.value = templateData.basePdf; - } else { - basepdfField.value = ''; // Reset if 'basePdf' is not provided - } - - // Toggle visibility of basepdf controls - toggleBasePDFControls(); - - // Post the remaining data to the iframe - const iframeWindow = document.getElementById('pdfme-designer-iframe').contentWindow; - iframeWindow.postMessage({ - type: 'uploadTemplate', - data: { templateData: templateData } - }, window.location.origin); - } catch (error) { - console.error('Failed to parse template file:', error); - alert('Invalid JSON template file.'); + }; + + const handleTemplateDownloadClick = () => { + const iframeWindow = elements.iframe.contentWindow; + const trackerName = elements.trackerIdSelect.options[elements.trackerIdSelect.selectedIndex].text; + + iframeWindow.postMessage({ + type: 'downloadTemplate', + data: { trackerName: trackerName } + }, window.location.origin); + }; + + const handleTemplateUploadClick = () => { + elements.templateFileInput.click(); + }; + + const handleTemplateFileChange = (event) => { + const file = event.target.files[0]; + if (file && file.type === "application/json") { + const reader = new FileReader(); + reader.onload = (e) => { + try { + const templateData = JSON.parse(e.target.result); + + if (templateData.basePdf) { + elements.basepdfField.value = templateData.basePdf; + } else { + elements.basepdfField.value = ''; // Reset if 'basePdf' is not provided } - }; - reader.readAsText(file); - } else { - alert('Please upload a valid JSON template file.'); - } - }); - } - - // Function to encode a PDF file in base64 - function encodeBasePDF(input) { + + toggleBasePDFControls(); + + const iframeWindow = elements.iframe.contentWindow; + iframeWindow.postMessage({ + type: 'uploadTemplate', + data: { templateData: templateData } + }, window.location.origin); + } catch (error) { + showError('Failed to parse template file: ' + error); + } + }; + reader.readAsText(file); + } else { + alert('Please upload a valid JSON template file.'); + } + }; + + const encodeBasePDF = (input) => { if (input.files && input.files[0]) { const reader = new FileReader(); - reader.onload = function(e) { + reader.onload = (e) => { const encodedPDF = e.target.result; - if (basepdfField) { - basepdfField.value = encodedPDF; + if (elements.basepdfField) { + elements.basepdfField.value = encodedPDF; } toggleBasePDFControls(); }; reader.readAsDataURL(input.files[0]); } - } - - // Function to toggle the visibility of the basepdf controls - function toggleBasePDFControls() { - const basepdfIcon = document.getElementById('basepdf-ok-icon'); + }; - if (basepdfField && basepdfField.value) { - basepdfIcon.style.display = 'inline'; - useBlankPdfLink.style.display = 'inline'; + const toggleBasePDFControls = () => { + if (elements.basepdfField && elements.basepdfField.value) { + elements.basepdfIcon.style.display = 'inline'; + elements.useBlankPdfLink.style.display = 'inline'; } else { - basepdfIcon.style.display = 'none'; - useBlankPdfLink.style.display = 'none'; + elements.basepdfIcon.style.display = 'none'; + elements.useBlankPdfLink.style.display = 'none'; } - } - - // Event listener for file upload - if (uploadField) { - uploadField.addEventListener('change', function() { - encodeBasePDF(this); - }); - } - - // Event listener for resetting the PDF - if (useBlankPdfLink) { - useBlankPdfLink.addEventListener('click', function(event) { - event.preventDefault(); - if (basepdfField) { - basepdfField.value = ''; - uploadField.value = ''; - toggleBasePDFControls(); - } - }); - } - - // Event listeners for iframe communication - if (basepdfField && schemasField && inputsField && openBtn && closeBtn && designerOverlay && iframe) { - openBtn.addEventListener('click', function() { - designerOverlay.style.display = 'block'; - const iframeWindow = iframe.contentWindow; - - const data = {}; - basepdfField.value ? data.basePdf = basepdfField.value : null; - schemasField.value ? data.schemas = JSON.parse(schemasField.value) : null; - inputsField.value ? data.inputs = JSON.parse(inputsField.value) : null; - - data.fieldKeyOptions = JSON.parse(sessionStorage.getItem('fieldKeyOptions')); - data.fieldFormatOptions = [ - { label: 'Boolean', value: 'bool' }, - { label: 'Date', value: 'date' }, - // { label: 'File', value: 'attachment' }, - { label: 'Float', value: 'float' }, - { label: 'Integer', value: 'int' }, - // { label: 'Key/value list', value: 'enumeration' }, - // { label: 'Link', value: 'link' }, - // { label: 'List', value: 'list' }, - // { label: 'Long text', value: 'text' }, - { label: 'Text', value: 'string' }, - // { label: 'User', value: 'user' }, - // { label: 'Version', value: 'version' }, - ]; - - iframeWindow.postMessage({ - type: 'openDesigner', - data: data - }, window.location.origin); - }); - - closeBtn.addEventListener('click', function() { - designerOverlay.style.display = 'none'; - iframe.src = iframe.src; // Refresh the iframe - }); - - window.addEventListener('message', function(event) { - if (event.origin !== window.location.origin) { - return; - } - - if (event.data.type === 'updateData') { - // Only updating schemas and inputs - const { schemas, inputs } = event.data.data; - schemasField.value = JSON.stringify(schemas); - inputsField.value = JSON.stringify(inputs); - } - }); - } + }; + + const handleOpenBtnClick = () => { + elements.designerOverlay.style.display = 'block'; + const iframeWindow = elements.iframe.contentWindow; + + const data = {}; + elements.basepdfField.value ? data.basePdf = elements.basepdfField.value : null; + elements.schemasField.value ? data.schemas = JSON.parse(elements.schemasField.value) : null; + + data.fieldKeyOptions = JSON.parse(sessionStorage.getItem('fieldKeyOptions')); + data.fieldFormatOptions = JSON.parse(sessionStorage.getItem('fieldFormatOptions')); + + iframeWindow.postMessage({ + type: 'openDesigner', + data: data + }, window.location.origin); + }; + + const handleCloseBtnClick = () => { + elements.designerOverlay.style.display = 'none'; + elements.iframe.src = elements.iframe.src; // Refresh the iframe + }; + + const handleMessageEvent = (event) => { + if (event.origin !== window.location.origin) return; + + if (event.data.type === 'updateData') { + const { schemas } = event.data.data; + elements.schemasField.value = JSON.stringify(schemas); + } + }; + + elements.trackerIdSelect?.addEventListener('change', loadTrackerData); + loadTrackerData(); + + elements.templateDownloadBtn?.addEventListener('click', handleTemplateDownloadClick); + elements.templateUploadBtn?.addEventListener('click', handleTemplateUploadClick); + elements.templateFileInput?.addEventListener('change', handleTemplateFileChange); + + elements.uploadField?.addEventListener('change', function() { + encodeBasePDF(this); + }); + + elements.useBlankPdfLink?.addEventListener('click', (event) => { + event.preventDefault(); + if (elements.basepdfField) { + elements.basepdfField.value = ''; + elements.uploadField.value = ''; + toggleBasePDFControls(); + } + }); + + elements.openBtn?.addEventListener('click', handleOpenBtnClick); + elements.closeBtn?.addEventListener('click', handleCloseBtnClick); + window.addEventListener('message', handleMessageEvent); }); diff --git a/assets/javascripts/print_templates_font.js b/assets/javascripts/print_templates_font.js index 17d4c8e..95f6bc9 100644 --- a/assets/javascripts/print_templates_font.js +++ b/assets/javascripts/print_templates_font.js @@ -66,6 +66,8 @@ function clearFontFields() { document.addEventListener('DOMContentLoaded', function() { const container = document.querySelector('#settings.plugin.plugin-redmine_print_templates'); + console.log('Print Templates Fonts loaded!'); + if (container) { const form = container.querySelector('form'); diff --git a/assets/javascripts/print_templates_form.js b/assets/javascripts/print_templates_form.js deleted file mode 100644 index e9704fb..0000000 --- a/assets/javascripts/print_templates_form.js +++ /dev/null @@ -1,53 +0,0 @@ -document.addEventListener("DOMContentLoaded", function() { - const viewPdfButton = document.getElementById('open-form-fullscreen-btn'); - const printTemplateSelect = document.getElementById('print_template_select'); - - const formOverlay = document.getElementById('form-fullscreen'); - const downloadPdfButton = document.getElementById('download-pdf-button'); - const closeFormBtn = document.getElementById('close-form-fullscreen-btn'); - const formIframe = document.getElementById('pdfme-form-iframe'); - - if (viewPdfButton && printTemplateSelect) { - viewPdfButton.addEventListener('click', function() { - const selectedTemplateId = printTemplateSelect.value; - if (selectedTemplateId) { - // Fetch the template data from the server - Rails.ajax({ - url: `/print_templates/show/${selectedTemplateId}.json`, - type: 'GET', - dataType: 'json', - success: function(response) { - // Open the form overlay and send the fetched template data - formOverlay.style.display = 'block'; - formIframe.contentWindow.postMessage({ - type: 'loadSelectedTemplate', - templateData: response - }, window.location.origin); - }, - error: function(error) { - console.error('Error fetching template data:', error); - } - }); - } else { - alert('Please select a print template.'); - } - }); - } - - if (downloadPdfButton) { - downloadPdfButton.addEventListener('click', function() { - if (formIframe && formIframe.contentWindow) { - // Send a message to the iframe to trigger PDF generation - formIframe.contentWindow.postMessage({ type: 'generatePdf' }, window.location.origin); - } - }); - } - - // Close button logic - if (closeFormBtn) { - closeFormBtn.addEventListener('click', function() { - formOverlay.style.display = 'none'; - formIframe.src = formIframe.src; // Refresh the iframe - }); - } -}); diff --git a/assets/javascripts/print_templates_viewer.js b/assets/javascripts/print_templates_viewer.js new file mode 100644 index 0000000..3febc3b --- /dev/null +++ b/assets/javascripts/print_templates_viewer.js @@ -0,0 +1,65 @@ +document.addEventListener("DOMContentLoaded", () => { + const elements = { + viewPdfButton: document.getElementById('open-form-fullscreen-btn'), + printTemplateSelect: document.getElementById('print_template_select'), + formOverlay: document.getElementById('form-fullscreen'), + downloadPdfButton: document.getElementById('download-pdf-button'), + closeFormBtn: document.getElementById('close-form-fullscreen-btn'), + formIframe: document.getElementById('pdfme-form-iframe') + }; + + console.log('Print Templates Viewer loaded!'); + + const showError = (message) => { + console.error(message); + alert('An error occurred. Please try again.'); + }; + + const handleViewPdfClick = () => { + const selectedTemplateId = elements.printTemplateSelect.value; + if (!selectedTemplateId) { + alert('Please select a print template.'); + return; + } + + Rails.ajax({ + url: `/print_templates/show/${selectedTemplateId}.json`, + type: 'GET', + dataType: 'json', + success: (response) => { + const data = {}; + data.basePdf = response.basepdf; + data.schemas = response.schemas; + data.fieldKeyOptions = response.fieldKeyOptions; + data.fieldFormatOptions = response.fieldFormatOptions; + + elements.formOverlay.style.display = 'block'; + elements.formIframe.contentWindow.postMessage({ + type: 'openViewer', + data: data + }, window.location.origin); + }, + error: (error) => { + showError('Error fetching template data: ' + error); + } + }); + }; + + const handleDownloadPdfClick = () => { + if (elements.formIframe?.contentWindow) { + elements.formIframe.contentWindow.postMessage( + { type: 'generatePdf' }, + window.location.origin + ); + } + }; + + const handleCloseFormClick = () => { + elements.formOverlay.style.display = 'none'; + elements.formIframe.src = elements.formIframe.src; // Refresh the iframe + }; + + elements.viewPdfButton?.addEventListener('click', handleViewPdfClick); + elements.downloadPdfButton?.addEventListener('click', handleDownloadPdfClick); + elements.closeFormBtn?.addEventListener('click', handleCloseFormClick); +}); diff --git a/config/locales/en.yml b/config/locales/en.yml index 9dec774..b2b49b1 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -31,6 +31,10 @@ en: print_templates_settings_general_placeholder_empty: "Empty field placeholder text" print_templates_settings_general_placeholder_note: "Enter text to display in empty fields, or leave this blank to keep fields empty." + label_core_fields: "Core Fields" + label_custom_fields: "Custom Fields" + label_special_fields: "Special Fields" + pdf_properties_print_templates_settings: "PDF Properties" print_templates_settings_general_pdf_author: "Author" print_templates_settings_general_pdf_creator: "Creator" diff --git a/config/routes.rb b/config/routes.rb index 818d291..70e801f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,7 +4,7 @@ resources :print_templates, only: %i(index new create edit update destroy) do # Nested routes for PDFme functionalities get 'designer', to: 'print_templates_pdfme#designer', on: :collection - get 'form', to: 'print_templates_pdfme#form', on: :collection + get 'viewer', to: 'print_templates_pdfme#viewer', on: :collection end # Route to fetch fields for a tracker (AJAX) diff --git a/db/migrate/20240626160439_remove_inputs_from_print_templates.rb b/db/migrate/20240626160439_remove_inputs_from_print_templates.rb new file mode 100644 index 0000000..b178657 --- /dev/null +++ b/db/migrate/20240626160439_remove_inputs_from_print_templates.rb @@ -0,0 +1,5 @@ +class RemoveInputsFromPrintTemplates < ActiveRecord::Migration[6.1] + def change + remove_column :print_templates, :inputs, :jsonb + end +end diff --git a/docs/ReferenceTemplate.json b/docs/ReferenceTemplate.json index 5ea7de4..caf3432 100644 --- a/docs/ReferenceTemplate.json +++ b/docs/ReferenceTemplate.json @@ -1,9 +1,14 @@ { - "basePdf": "data:application/pdf;base64,JVBERi0xLjcKJeLjz9MKNSAwIG9iago8PAovRmlsdGVyIC9GbGF0ZURlY29kZQovTGVuZ3RoIDM4Cj4+CnN0cmVhbQp4nCvkMlAwUDC1NNUzMVGwMDHUszRSKErlCtfiyuMK5AIAXQ8GCgplbmRzdHJlYW0KZW5kb2JqCjQgMCBvYmoKPDwKL1R5cGUgL1BhZ2UKL01lZGlhQm94IFswIDAgNTk1LjQ0IDg0MS45Ml0KL1Jlc291cmNlcyA8PAo+PgovQ29udGVudHMgNSAwIFIKL1BhcmVudCAyIDAgUgo+PgplbmRvYmoKMiAwIG9iago8PAovVHlwZSAvUGFnZXMKL0tpZHMgWzQgMCBSXQovQ291bnQgMQo+PgplbmRvYmoKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovUGFnZXMgMiAwIFIKPj4KZW5kb2JqCjMgMCBvYmoKPDwKL3RyYXBwZWQgKGZhbHNlKQovQ3JlYXRvciAoU2VyaWYgQWZmaW5pdHkgRGVzaWduZXIgMS4xMC40KQovVGl0bGUgKFVudGl0bGVkLnBkZikKL0NyZWF0aW9uRGF0ZSAoRDoyMDIyMDEwNjE0MDg1OCswOScwMCcpCi9Qcm9kdWNlciAoaUxvdmVQREYpCi9Nb2REYXRlIChEOjIwMjIwMTA2MDUwOTA5WikKPj4KZW5kb2JqCjYgMCBvYmoKPDwKL1NpemUgNwovUm9vdCAxIDAgUgovSW5mbyAzIDAgUgovSUQgWzwyODhCM0VENTAyOEU0MDcyNERBNzNCOUE0Nzk4OUEwQT4gPEY1RkJGNjg4NkVERDZBQUNBNDRCNEZDRjBBRDUxRDlDPl0KL1R5cGUgL1hSZWYKL1cgWzEgMiAyXQovRmlsdGVyIC9GbGF0ZURlY29kZQovSW5kZXggWzAgN10KL0xlbmd0aCAzNgo+PgpzdHJlYW0KeJxjYGD4/5+RUZmBgZHhFZBgDAGxakAEP5BgEmFgAABlRwQJCmVuZHN0cmVhbQplbmRvYmoKc3RhcnR4cmVmCjUzMgolJUVPRgo=", + "basePdf": { + "width": 210, + "height": 297, + "padding": [0, 0, 0, 0] + }, "schemas": [ { - "$(standard#author.name)#1e69": { - "type": "text", + "author.name": { + "type": "extendedText", + "content": "Author", "position": { "x": 73.25, "y": 45.77 @@ -14,8 +19,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#status.name)#a1a2": { - "type": "text", + "status.name": { + "type": "extendedText", + "content": "Status", "position": { "x": 73.25, "y": 58.11 @@ -26,8 +32,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#priority.name)#075f": { - "type": "text", + "priority.name": { + "type": "extendedText", + "content": "Priority", "position": { "x": 73.25, "y": 70.45 @@ -38,8 +45,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#assigned_to.name)#5b13": { - "type": "text", + "assigned_to.name": { + "type": "extendedText", + "content": "Assigned to", "position": { "x": 73.25, "y": 82.78 @@ -50,8 +58,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#category.name)#4bf9": { - "type": "text", + "category.name": { + "type": "extendedText", + "content": "Category", "position": { "x": 73.25, "y": 95.12 @@ -62,8 +71,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#fixed_version.name)#0a2d": { - "type": "text", + "fixed_version.name": { + "type": "extendedText", + "content": "Fixed version", "position": { "x": 73.25, "y": 107.46 @@ -74,8 +84,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#subject)#e114": { - "type": "text", + "subject": { + "type": "extendedText", + "content": "Subject", "position": { "x": 73.25, "y": 119.79 @@ -86,8 +97,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#start_date)#800c": { - "type": "text", + "start_date": { + "type": "extendedText", + "content": "Start date", "position": { "x": 73.25, "y": 132.13 @@ -98,8 +110,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#due_date)#ecc1": { - "type": "text", + "due_date": { + "type": "extendedText", + "content": "Due date", "position": { "x": 73.25, "y": 144.47 @@ -110,8 +123,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#done_ratio)#ab49": { - "type": "text", + "done_ratio": { + "type": "extendedText", + "content": "Done ratio", "position": { "x": 73.25, "y": 156.81 @@ -122,8 +136,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#estimated_hours)#e114": { - "type": "text", + "estimated_hours": { + "type": "extendedText", + "content": "Estimated hours", "position": { "x": 73.25, "y": 169.15 @@ -134,8 +149,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#total_estimated_hours)#a814": { - "type": "text", + "total_estimated_hours": { + "type": "extendedText", + "content": "Total estimated hours", "position": { "x": 73.25, "y": 181.48 @@ -146,8 +162,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#spent_hours)#4995": { - "type": "text", + "spent_hours": { + "type": "extendedText", + "content": "Spent hours", "position": { "x": 73.25, "y": 193.82 @@ -158,8 +175,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#total_spent_hours)#32c2": { - "type": "text", + "total_spent_hours": { + "type": "extendedText", + "content": "Total spent hours", "position": { "x": 73.25, "y": 206.16 @@ -170,8 +188,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#created_on)#83d2": { - "type": "text", + "created_on": { + "type": "extendedText", + "content": "Created on", "position": { "x": 73.25, "y": 218.5 @@ -182,8 +201,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#updated_on)#ca96": { - "type": "text", + "updated_on": { + "type": "extendedText", + "content": "Updated on", "position": { "x": 73.25, "y": 230.83 @@ -194,8 +214,9 @@ "verticalAlignment": "middle", "fontName": "Roboto" }, - "$(standard#closed_on)#0b1a": { - "type": "text", + "closed_on": { + "type": "extendedText", + "content": "Closed on", "position": { "x": 73.25, "y": 243.17 @@ -208,8 +229,9 @@ } }, { - "$(standard#description)#62b3": { - "type": "text", + "description": { + "type": "extendedText", + "content": "Description", "position": { "x": 74.51, "y": 30.06 @@ -223,53 +245,6 @@ }, {}, {}, - { - "$(special#issue_url)#dc83": { - "type": "qrcode", - "position": { - "x": 153.75, - "y": 35.86 - }, - "width": 30, - "height": 30, - "alignment": "left", - "verticalAlignment": "middle" - }, - "$(special#issue_map)#1722": { - "type": "image", - "position": { - "x": 25.75, - "y": 70.99 - }, - "width": 158, - "height": 100, - "alignment": "left", - "verticalAlignment": "middle" - } - } - ], - "sampledata": [ - { - "$(standard#assigned_to.name)#5b13": "$(standard#assigned_to.name)", - "$(standard#author.name)#1e69": "$(standard#author.name)", - "$(standard#category.name)#4bf9": "$(standard#category.name)", - "$(standard#closed_on)#0b1a": "$(standard#closed_on)", - "$(standard#created_on)#83d2": "$(standard#created_on)", - "$(standard#description)#62b3": "$(standard#description)", - "$(standard#done_ratio)#ab49": "$(standard#done_ratio)", - "$(standard#due_date)#ecc1": "$(standard#due_date)", - "$(standard#estimated_hours)#e114": "$(standard#estimated_hours)", - "$(standard#fixed_version.name)#0a2d": "$(standard#fixed_version.name)", - "$(standard#priority.name)#075f": "$(standard#priority.name)", - "$(standard#spent_hours)#4995": "$(standard#spent_hours)", - "$(standard#start_date)#800c": "$(standard#start_date)", - "$(standard#status.name)#a1a2": "$(standard#status.name)", - "$(standard#subject)#e114": "$(standard#subject)", - "$(standard#total_estimated_hours)#a814": "$(standard#total_estimated_hours)", - "$(standard#total_spent_hours)#32c2": "$(standard#total_spent_hours)", - "$(standard#updated_on)#ca96": "$(standard#updated_on)", - "$(special#issue_map)#1722": "", - "$(special#issue_url)#dc83": "localhost:3000" - } + {} ] } diff --git a/lib/redmine_print_templates/view_hooks.rb b/lib/redmine_print_templates/view_hooks.rb index 1432d28..0ed75f5 100644 --- a/lib/redmine_print_templates/view_hooks.rb +++ b/lib/redmine_print_templates/view_hooks.rb @@ -3,13 +3,21 @@ class ViewHooks < Redmine::Hook::ViewListener def view_layouts_base_body_bottom(context={}) tags = [] - if User.current.admin? - tags << javascript_include_tag('print_templates_font.js', plugin: 'redmine_print_templates') + controller = context[:controller] + + # Load specific JS for admin in print_templates menu + if User.current.admin? && controller.controller_name == 'print_templates' && ['index', 'new', 'edit'].include?(controller.action_name) tags << javascript_include_tag('print_templates_designer.js', plugin: 'redmine_print_templates') + + # Load print_templates_font.js in plugin settings + elsif User.current.admin? && controller.controller_name == 'settings' && controller.action_name == 'plugin' && controller.params[:id] == 'redmine_print_templates' + tags << javascript_include_tag('print_templates_font.js', plugin: 'redmine_print_templates') + + # Load viewer JS only if designer JS is not loaded + elsif User.current.allowed_to?(:view_print_templates, context[:project], global: true) + tags << javascript_include_tag('print_templates_viewer.js', plugin: 'redmine_print_templates') end - if User.current.allowed_to?(:view_print_templates, context[:project], global: true) - tags << javascript_include_tag('print_templates_form.js', plugin: 'redmine_print_templates') - end + tags.join("\n") end diff --git a/src/designer.ts b/src/designer.ts index 6de0d4b..896ac77 100644 --- a/src/designer.ts +++ b/src/designer.ts @@ -1,5 +1,5 @@ import { Designer } from '@pdfme/ui'; -import { openDesigner, downloadTemplate, uploadTemplate } from './helper'; +import { openDesigner, downloadTemplate, uploadTemplate } from '.'; document.addEventListener("DOMContentLoaded", function() { @@ -23,7 +23,6 @@ document.addEventListener("DOMContentLoaded", function() { template: { basePdf: data.basePdf, schemas: data.schemas, - sampledata: data.inputs, }, locale: locale, fieldKeyOptions: data.fieldKeyOptions, diff --git a/src/form.ts b/src/form.ts deleted file mode 100644 index e4cdc30..0000000 --- a/src/form.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { Template, BLANK_PDF, getDefaultFont } from '@pdfme/common'; -import { Form } from '@pdfme/ui'; -import { generate } from '@pdfme/generator'; - -import { getPlugins } from './schemas'; - -interface FontData { - name: string; - url: string; -} - -declare const embeddedFonts: FontData[]; -declare const issueData: any; -declare const pluginSettings: any; - -const urlParams = new URLSearchParams(window.location.search); -const issueId = urlParams.get('issue_id'); - -document.addEventListener("DOMContentLoaded", function() { - const container = document.getElementById('pdfme-container'); - let form: Form | undefined; - - window.addEventListener('message', async function(event) { - if (event.origin !== window.location.origin) { - return; - } - - const { type, templateData } = event.data; - - switch (type) { - case 'loadSelectedTemplate': - if (container && templateData) { - // Parse schemas and inputs from JSON strings to JavaScript arrays - const schemas = JSON.parse(templateData.schemas || '[]'); - const inputs = [mapIssueDataToTemplate(issueData, schemas)] || [{}]; - - // Use BLANK_PDF as a fallback if basePdf is not provided - const basePdf = templateData.basepdf || BLANK_PDF; - - // Define the template structure - const template: Template = { - basePdf: basePdf, - schemas: schemas - }; - - async function initializeForm() { - // Set the default fonts - const availableFonts = getDefaultFont(); - - for (const font of embeddedFonts) { - const response = await fetch(font.url); - const arrayBuffer = await response.arrayBuffer(); - - availableFonts[font.name] = { - data: arrayBuffer, - }; - } - - // Recreate the Form instance with the new template and inputs - form = new Form({ - domContainer: container, - template: template as Template | any, - inputs: inputs, - // plugins: getPlugins({ fieldKeyOptions, fieldFormatOptions }), - options: { - theme: { - token: { - colorPrimary: '#f1515c' - }, - }, - font: availableFonts, - }, - }); - } - - initializeForm(); - } - break; - - case 'generatePdf': - if (form) { - const currentInputs = form.getInputs(); - - async function initializeGenerator() { - // Set the default fonts - const availableFonts = getDefaultFont(); - - for (const font of embeddedFonts) { - const response = await fetch(font.url); - const arrayBuffer = await response.arrayBuffer(); - - availableFonts[font.name] = { - data: arrayBuffer, - // include fallback and subset options if necessary - }; - } - - generate({ - template: form.getTemplate() as Template | any, - inputs: currentInputs, - // plugins: getPlugins({ fieldKeyOptions, fieldFormatOptions }), - options: { - font: availableFonts, - author: pluginSettings.default_pdf_author || "", - creator: pluginSettings.default_pdf_creator || "", - keywords: [], - language: "en-US", - producer: pluginSettings.default_pdf_producer || "", - subject: "Redmine Issue Report", - title: `Issue #${issueId}: Feature Implementation`, - }, - }).then((pdf) => { - const blob = new Blob([pdf.buffer], { type: 'application/pdf' }); - const timestamp = new Date().toISOString().replace(/[-:.TZ]/g, ''); - - // Create and trigger download link - const downloadLink = document.createElement("a"); - downloadLink.href = URL.createObjectURL(blob); - downloadLink.download = `${timestamp}_issue_${issueId}.pdf`; - document.body.appendChild(downloadLink); - downloadLink.click(); - document.body.removeChild(downloadLink); - URL.revokeObjectURL(downloadLink.href); - }).catch((error) => { - console.error('Error generating PDF:', error); - }); - } - - initializeGenerator(); - } - break; - // Handle other cases if necessary - } - }); -}); - -function mapIssueDataToTemplate(issueData: any, template: any) { - const mappedInputs: any = {}; - - template.forEach((page: any) => { - Object.keys(page).forEach(key => { - const match = key.match(/\$\(([^#]+)#([^)]+)\)/); - if (match && match.length >= 3) { - const type = match[1]; - const field = match[2]; - let value = ''; - - switch (type) { - case 'standard': - const fields = field.split('.'); - value = fields.reduce((acc, curr) => acc && acc[curr], issueData.issue); - break; - case 'custom': - const customField = issueData.issue.custom_fields.find((f: any) => f.name === field); - value = customField ? customField.value : ''; - break; - case 'special': - // Handle special cases here - break; - default: - // Handle unknown type - break; - } - - if (value !== undefined) { - mappedInputs[key] = String(value); - } - } - }); - }); - - return mappedInputs; -} diff --git a/src/helper.ts b/src/helper.ts deleted file mode 100644 index b355eb5..0000000 --- a/src/helper.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { Template, getDefaultFont,PDFME_VERSION } from '@pdfme/common'; -import { Designer } from '@pdfme/ui'; -import { getPlugins } from './schemas'; -import { validateLocale } from './types'; - -import type { DesignerOptions, SupportedLocale } from './types'; - -declare const embeddedFonts: any[]; - -const defaultTemplate: Template = { - basePdf: { width: 210, height: 297, padding: [0, 0, 0, 0] }, - sampledata: [], - schemas: [{} as Record], - pdfmeVersion: PDFME_VERSION -}; - -/** - * Checks if a value is valid. - * @param value - The value to check. - * @returns Whether the value is valid or not. - */ -function isValidValue(value: T | null | undefined | ''): value is T { - return value !== undefined && value !== null && value !== ''; -} - -/** - * Creates a template object based on the provided partial template. - * @param template - The partial template object. - * @returns The final template object. - */ -function createTemplate(template: Partial