PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.2
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.2
2.4.3 2.4.4 2.4.2 2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 All 60 releases
← All changes | templates/invoices/live-preview.php +119 -24 2.1.1 → 2.4.2 View file →
@@ -1,7 +1,10 @@
1 - <!-- Right Panel - Live Preview -->
2 - <div class="bg-white shadow rounded-lg" style="padding: 10px;">
3 -
1 +<?php
2 +if ( ! defined( 'ABSPATH' ) ) {
3 + exit;
4 +}
5 +?> <div class="bg-white shadow rounded-lg" style="padding: 10px;">
6 +
4 7 <div class="px-6 py-5 border-b border-gray-200" style="margin-bottom: 10px;">
5 8 <div class="flex justify-between items-center">
6 9 <div class="flex items-center">
7 10 <div class="flex-shrink-0 bg-indigo-100 rounded-lg p-3">
@@ -7,14 +10,14 @@
7 10 <div class="flex-shrink-0 bg-indigo-100 rounded-lg p-3">
8 11 <i class="fas fa-eye text-indigo-600 text-xl"></i>
9 12 </div>
10 13 <div class="ml-4">
11 - <h2 class="text-lg font-medium text-gray-900">Invoice Preview</h2>
12 - <p class="mt-1 text-sm text-gray-500">Live preview of your invoice</p>
14 + <h2 class="text-lg font-medium text-gray-900"><?php esc_html_e('Invoice Preview', 'easy-invoice'); ?></h2>
15 + <p class="mt-1 text-sm text-gray-500"><?php esc_html_e('Live preview of your invoice', 'easy-invoice'); ?></p>
13 16 </div>
14 17 </div>
15 18 <?php if($invoice_id > 0 ){ ?>
16 - <a href="<?php echo get_permalink($invoice_id); ?>" target="_blank" class="inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
19 + <a href="<?php echo esc_url(get_permalink($invoice_id)); ?>" target="_blank" class="inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
17 20 <i class="fas fa-external-link-alt mr-2"></i>
18 21 Full Preview
19 22 </a>
20 23 <?php } ?>
@@ -27,18 +30,70 @@
27 30 if (empty($current_template)) {
28 31 $current_template = 'standard';
29 32 }
30 33 $template_class = 'template-' . $current_template;
31 -
34 +
32 35 // Initialize formatter for the invoice templates
33 36 $formatter = new \EasyInvoice\Helpers\InvoiceFormatter($invoice);
34 37 ?>
35 -
38 +
36 39 <div id="invoice-container" class="invoice-container">
37 40 <?php
41 + // Add custom template builder CSS if using a custom template
42 + if ($invoice && method_exists($invoice, 'getId')) {
43 + $template_id = get_post_meta($invoice->getId(), '_easy_invoice_invoice_template', true);
44 + if ($template_id && class_exists('\EasyInvoicePro\TemplateParser') && class_exists('\EasyInvoicePro\Addons\CustomTemplates\Controllers\TemplateBuilderController')) {
45 + $templateData = \EasyInvoicePro\TemplateParser::parseTemplateFromDatabase($template_id);
46 + if ($templateData && isset($templateData['elements'])) {
47 + echo '<style>';
48 + // Canvas settings
49 + $canvas = $templateData['canvas'] ?? [];
50 + $canvasWidth = $canvas['width'] ?? 794;
51 + $canvasHeight = $canvas['height'] ?? 1123;
52 + $canvasDesignCSS = \EasyInvoicePro\Addons\CustomTemplates\Controllers\TemplateBuilderController::generateCanvasCSS($templateData);
53 +
54 + echo "#canvas { position: relative; width: " . floatval($canvasWidth) . "px; height: " . floatval($canvasHeight) . "px; margin: 0; overflow: visible; ";
55 + if ($canvasDesignCSS) {
56 + echo wp_strip_all_tags(str_replace('!important;', ';', $canvasDesignCSS)); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS inside a <style> element; tags stripped, entities are not decoded there.
57 + }
58 + echo " }\n";
59 +
60 + // Element styles
61 + foreach ($templateData['elements'] as $element) {
62 + $elementId = $element['id'] ?? '';
63 + if (!$elementId) continue;
64 +
65 + echo '#' . esc_attr($elementId) . ' { position: absolute; ';
66 + if (isset($element['position']['left'])) echo "left: " . floatval($element['position']['left']) . "px; ";
67 + if (isset($element['position']['top'])) echo "top: " . floatval($element['position']['top']) . "px; ";
68 + if (isset($element['dimensions']['width'])) echo "width: " . floatval($element['dimensions']['width']) . "px; ";
69 + if (isset($element['dimensions']['height'])) echo "min-height: " . floatval($element['dimensions']['height']) . "px; ";
70 +
71 + if (isset($element['styles']) && is_array($element['styles'])) {
72 + foreach ($element['styles'] as $property => $value) {
73 + if ($value !== '' && !in_array($property, ['position', 'dimensions'])) {
74 + echo esc_attr($property) . ": " . esc_attr($value) . "; ";
75 + }
76 + }
77 + }
78 + echo "}\n";
79 + }
80 + echo '</style>';
81 + }
82 + }
83 + }
84 +
38 85 // Dynamically load the selected template
39 - $template_file = EASY_INVOICE_PLUGIN_DIR . 'templates/invoice-templates/' . $current_template . '.php';
40 - if (file_exists($template_file)) {
86 + $template_file = easy_invoice_design_template( 'invoice', (string) $current_template );
87 + if ( ! $invoice instanceof \EasyInvoice\Models\Invoice ) {
88 + // Nothing to render yet. The designs read the model
89 + // directly, so including one here would be fatal.
90 + ?>
91 + <div class="ei-preview-empty" style="text-align:center;padding:48px 24px;color:#64748b;">
92 + <p style="margin:0;font-size:14px;"><?php esc_html_e( 'Your invoice will appear here as you fill in the form.', 'easy-invoice' ); ?></p>
93 + </div>
94 + <?php
95 + } elseif (file_exists($template_file)) {
41 96 include_once $template_file;
42 97 } else {
43 98 // Show template not found message
44 99 ?>
@@ -45,11 +100,11 @@
45 100 <div class="template-not-found">
46 101 <div class="template-not-found-icon">
47 102 <i class="fas fa-exclamation-circle"></i>
48 103 </div>
49 - <h3>Template Not Found</h3>
104 + <h3><?php esc_html_e('Template Not Found', 'easy-invoice'); ?></h3>
50 105 <p>The selected template "<?php echo esc_html($current_template); ?>" does not exist.</p>
51 - <p>Please select a different template or contact support if you believe this is an error.</p>
106 + <p><?php esc_html_e('Please select a different template or contact support if you believe this is an error.', 'easy-invoice'); ?></p>
52 107 </div>
53 108 <style>
54 109 .template-not-found {
55 110 text-align: center;
@@ -108,10 +163,10 @@
108 163 <script>
109 164 // Localize easyInvoice object for this page
110 165 window.easyInvoice = window.easyInvoice || {};
111 166 window.easyInvoice.isPro = <?php echo easy_invoice_has_pro() ? 'true' : 'false'; ?>;
112 -window.easyInvoice.ajaxUrl = '<?php echo admin_url('admin-ajax.php'); ?>';
113 -window.easyInvoice.nonce = '<?php echo wp_create_nonce('easy_invoice_nonce'); ?>';
167 +window.easyInvoice.ajaxUrl = '<?php echo esc_url(admin_url('admin-ajax.php')); ?>';
168 +window.easyInvoice.nonce = '<?php echo esc_attr(wp_create_nonce('easy_invoice_nonce')); ?>';
114 169
115 170 jQuery(document).ready(function($) {
116 171 // Function to update the live preview
117 172 function updateLivePreview(templateId) {
@@ -116,9 +171,9 @@
116 171 // Function to update the live preview
117 172 function updateLivePreview(templateId) {
118 173 // Show loading state
119 174 $('#invoice-container').addClass('loading');
120 -
175 +
121 176 // Make AJAX call to load the template
122 177 $.ajax({
123 178 url: window.easyInvoice.ajaxUrl,
124 179 type: 'POST',
@@ -124,8 +179,9 @@
124 179 type: 'POST',
125 180 data: {
126 181 action: 'easy_invoice_load_template',
127 182 template: templateId,
183 + form_data: jQuery('#invoice-form').length ? jQuery('#invoice-form').serialize() : '',
128 184 invoice_id: '<?php echo esc_js($invoice_id); ?>',
129 185 nonce: window.easyInvoice.nonce
130 186 },
131 187 success: function(response) {
@@ -138,11 +194,11 @@
138 194 <div class="template-not-found">
139 195 <div class="template-not-found-icon">
140 196 <i class="fas fa-exclamation-circle"></i>
141 197 </div>
142 - <h3>Template Not Found</h3>
198 + <h3><?php esc_html_e('Template Not Found', 'easy-invoice'); ?></h3>
143 199 <p>The selected template "${templateId}" does not exist.</p>
144 - <p>Please select a different template or contact support if you believe this is an error.</p>
200 + <p><?php esc_html_e('Please select a different template or contact support if you believe this is an error.', 'easy-invoice'); ?></p>
145 201 </div>
146 202 `);
147 203 }
148 204 },
@@ -152,10 +208,10 @@
152 208 <div class="template-not-found">
153 209 <div class="template-not-found-icon">
154 210 <i class="fas fa-exclamation-circle"></i>
155 211 </div>
156 - <h3>Error Loading Template</h3>
157 - <p>There was an error loading the template. Please try again.</p>
212 + <h3><?php esc_html_e('Error Loading Template', 'easy-invoice'); ?></h3>
213 + <p><?php esc_html_e('There was an error loading the template. Please try again.', 'easy-invoice'); ?></p>
158 214 </div>
159 215 `);
160 216 },
161 217 complete: function() {
@@ -167,9 +223,9 @@
167 223
168 224 // Listen for template changes from the template cards
169 225 $('.template-card').on('click', function(e) {
170 226 const templateId = $(this).data('template-id');
171 -
227 +
172 228 // Check if this is a premium template in free version
173 229 const isPremium = $(this).attr('data-is-premium') === 'true';
174 230 const isFreeVersion = !window.easyInvoice || !window.easyInvoice.isPro;
175 231 if (isPremium && isFreeVersion) {
@@ -174,15 +230,15 @@
174 230 const isFreeVersion = !window.easyInvoice || !window.easyInvoice.isPro;
175 231 if (isPremium && isFreeVersion) {
176 232 // Show premium upgrade modal instead of selecting the template
177 233 if (typeof EasyInvoiceConfirmation !== 'undefined' && EasyInvoiceConfirmation.showFeatureUpgrade) {
178 - EasyInvoiceConfirmation.showFeatureUpgrade('<?php _e('Premium Templates', 'easy-invoice'); ?>', '<?php _e('Access all premium templates including Modern, Professional, Classic, and more to give your invoices a unique and professional look.', 'easy-invoice'); ?>');
234 + EasyInvoiceConfirmation.showFeatureUpgrade('<?php echo esc_js(__('Premium Templates', 'easy-invoice')); ?>', '<?php echo esc_js(__('Access all premium templates including Modern, Professional, Classic, and more to give your invoices a unique and professional look.', 'easy-invoice')); ?>');
179 235 } else {
180 - alert('<?php _e('This is a premium feature. Please upgrade to Pro to access all templates.', 'easy-invoice'); ?>');
236 + alert('<?php echo esc_js(__('This is a premium feature. Please upgrade to Pro to access all templates.', 'easy-invoice')); ?>');
181 237 }
182 238 return;
183 239 }
184 -
240 +
185 241 updateLivePreview(templateId);
186 242 });
187 243
188 244 // Listen for template changes from the gallery
@@ -191,6 +247,45 @@
191 247 updateLivePreview(templateId);
192 248 });
193 249
194 250
251 + // Live means live: re-render as the builder changes, not only when the
252 + // template changes. Debounced so typing a description does not fire a
253 + // request per keystroke.
254 + (function () {
255 + var $form = $('#invoice-form'); if (!$form.length) { return; }
256 + var timer = null;
257 + var current = function () { return $form.find('input[name="invoice_template"]').val() || $('.template-card.selected, .template-card.active').first().data('template-id') || 'standard'; };
258 + var schedule = function () { clearTimeout(timer); timer = setTimeout(function () { updateLivePreview(current()); }, 700); };
259 + $form.on('input change', 'input, textarea, select', schedule);
260 + $(document).on('click', '.add-item-button, .remove-item, .client-option, #remove-selected-client, .template-card', schedule);
261 + $(document).on('easy_invoice_items_changed easy_invoice_client_changed', schedule);
262 + })();
263 +
195 264 });
196 -</script>
265 +</script>
266 +
267 +<script>
268 +// Scale the A4 design to the preview pane instead of letting it reflow: the
269 +// preview then shows the sheet the client will see, only smaller.
270 +(function () {
271 + var box = document.getElementById('invoice-container');
272 + if (!box || !window.ResizeObserver) { return; }
273 + var SHEET = 794;
274 + function fit() {
275 + var sheet = box.querySelector('.template, [class*="template-"]');
276 + if (!sheet) { box.style.height = ''; return; }
277 + var avail = box.clientWidth;
278 + if (avail <= 0) { return; }
279 + var k = Math.min(1, avail / SHEET);
280 + sheet.style.width = SHEET + 'px';
281 + sheet.style.maxWidth = 'none';
282 + sheet.style.transformOrigin = 'top left';
283 + sheet.style.transform = k < 1 ? 'scale(' + k + ')' : '';
284 + box.style.overflow = 'hidden';
285 + box.style.height = k < 1 ? Math.ceil(sheet.offsetHeight * k) + 'px' : '';
286 + }
287 + new ResizeObserver(fit).observe(box);
288 + new MutationObserver(fit).observe(box, { childList: true, subtree: true });
289 + fit();
290 +})();
291 +</script>