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/listing.php +260 -229 2.2.0 → 2.4.2 View file →
@@ -38,17 +38,17 @@
38 38 $text_class = 'text-blue-800';
39 39 $icon = '<svg class="h-5 w-5 text-blue-400" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>';
40 40 }
41 41
42 - echo '<div class="rounded-md ' . $bg_class . ' p-4 mb-4">';
42 + echo '<div class="rounded-md ' . esc_html($bg_class) . ' p-4 mb-4">';
43 43 echo '<div class="flex">';
44 - echo '<div class="flex-shrink-0">' . $icon . '</div>';
44 + echo '<div class="flex-shrink-0">' . wp_kses_post($icon) . '</div>';
45 45 echo '<div class="ml-3">';
46 - echo '<p class="text-sm font-medium ' . $text_class . '">' . $message . '</p>';
46 + echo '<p class="text-sm font-medium ' . esc_html($text_class) . '">' . esc_html($message) . '</p>';
47 47 echo '</div>';
48 48 echo '<div class="ml-auto pl-3">';
49 49 echo '<div class="-mx-1.5 -my-1.5">';
50 - echo '<button type="button" class="inline-flex rounded-md p-1.5 ' . $text_class . ' hover:bg-green-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 notification-dismiss">';
50 + echo '<button type="button" class="inline-flex rounded-md p-1.5 ' . esc_html($text_class) . ' hover:bg-green-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 notification-dismiss">';
51 51 echo '<span class="sr-only">Dismiss</span>';
52 52 echo '<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">';
53 53 echo '<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />';
54 54 echo '</svg>';
@@ -68,9 +68,10 @@
68 68 if ($updated_count > 0) {
69 69 // Show a one-time notification
70 70 add_action('admin_notices', function() use ($updated_count) {
71 71 echo '<div class="notice notice-success is-dismissible">';
72 - echo '<p>' . sprintf(__('%d draft invoices updated to published status for proper permalinks.', 'easy-invoice'), $updated_count) . '</p>';
72 + /* translators: %d: number updated. */
73 + echo '<p>' . sprintf(esc_html__('%d draft invoices updated to published status for proper permalinks.', 'easy-invoice'), (int) $updated_count) . '</p>';
73 74 echo '</div>';
74 75 });
75 76 }
76 77
@@ -95,8 +96,9 @@
95 96 $notification = false;
96 97
97 98 if (isset($_GET['bulk_trashed']) && $_GET['bulk_trashed'] > 0) {
98 99 $count = intval($_GET['bulk_trashed']);
100 + /* translators: %s: number of items. */
99 101 easy_invoice_display_notification('success', sprintf(_n('%s invoice moved to trash.', '%s invoices moved to trash.', $count, 'easy-invoice'), $count));
100 102 $notification = true;
101 103 }
102 104
@@ -101,8 +103,9 @@
101 103 }
102 104
103 105 if (isset($_GET['bulk_restored']) && $_GET['bulk_restored'] > 0) {
104 106 $count = intval($_GET['bulk_restored']);
107 + /* translators: %s: number of items. */
105 108 easy_invoice_display_notification('success', sprintf(_n('%s invoice restored from trash.', '%s invoices restored from trash.', $count, 'easy-invoice'), $count));
106 109 $notification = true;
107 110 }
108 111
@@ -107,8 +110,9 @@
107 110 }
108 111
109 112 if (isset($_GET['bulk_deleted']) && $_GET['bulk_deleted'] > 0) {
110 113 $count = intval($_GET['bulk_deleted']);
114 + /* translators: %s: number of items. */
111 115 easy_invoice_display_notification('success', sprintf(_n('%s invoice permanently deleted.', '%s invoices permanently deleted.', $count, 'easy-invoice'), $count));
112 116 $notification = true;
113 117 }
114 118
@@ -113,8 +117,9 @@
113 117 }
114 118
115 119 if (isset($_GET['bulk_drafted']) && $_GET['bulk_drafted'] > 0) {
116 120 $count = intval($_GET['bulk_drafted']);
121 + /* translators: %s: number of items. */
117 122 easy_invoice_display_notification('success', sprintf(_n('%s invoice set to draft status.', '%s invoices set to draft status.', $count, 'easy-invoice'), $count));
118 123 $notification = true;
119 124 }
120 125
@@ -119,8 +124,9 @@
119 124 }
120 125
121 126 if (isset($_GET['bulk_published']) && $_GET['bulk_published'] > 0) {
122 127 $count = intval($_GET['bulk_published']);
128 + /* translators: %s: number of items. */
123 129 easy_invoice_display_notification('success', sprintf(_n('%s invoice published successfully.', '%s invoices published successfully.', $count, 'easy-invoice'), $count));
124 130 $notification = true;
125 131 }
126 132
@@ -160,64 +166,98 @@
160 166 // Get stats from all invoices, not just the current page
161 167 $invoice_controller = new \EasyInvoice\Controllers\InvoiceController();
162 168 $stats = $invoice_controller->getInvoiceStats();
163 169
164 -// Add total invoices count from current page for display purposes
165 -$stats['total_invoices'] = count($formatted_invoices);
170 +// The controller passes the real counts (found_posts and SQL counts); the
171 +// template used to overwrite the total with the size of the current page and
172 +// load every trashed / draft invoice as a model just to count them.
173 +$all_count = (int) ($stats['total_invoices'] ?? 0);
174 +if (!isset($trash_count)) {
175 + $trash_count = (int) $repository->count(['post_status' => 'trash']);
176 +}
177 +if (!isset($draft_count)) {
178 + $draft_count = (int) $repository->count(['meta_key' => '_easy_invoice_status', 'meta_value' => 'draft']); // phpcs:ignore WordPress.DB.SlowDBQuery
179 +}
166 180
167 -// Get trash count for tab display
168 -$trash_args = ['post_status' => 'trash'];
169 -$trash_invoices = $repository->all($trash_args);
170 -$trash_count = count($trash_invoices);
171 -
172 -// Get draft count for tab display
173 -$draft_args = ['post_status' => 'draft'];
174 -$draft_invoices = $repository->all($draft_args);
175 -$draft_count = count($draft_invoices);
176 -
177 181 // Define available status filters
182 +// The statuses an invoice can actually hold — "Pending" was offered but no
183 +// invoice is ever in that state, while "Available" (every new invoice) was not.
178 184 $status_filters = array(
179 - 'paid' => 'Paid',
180 - 'unpaid' => 'Unpaid',
181 - 'pending' => 'Pending',
182 - 'draft' => 'Draft',
183 - 'overdue' => 'Overdue',
184 - 'cancelled' => 'Cancelled'
185 + 'available' => __('Available', 'easy-invoice'),
186 + 'paid' => __('Paid', 'easy-invoice'),
187 + 'partial' => __('Partially paid', 'easy-invoice'),
188 + 'unpaid' => __('Unpaid', 'easy-invoice'),
189 + 'overdue' => __('Overdue', 'easy-invoice'),
190 + 'draft' => __('Draft', 'easy-invoice'),
191 + 'cancelled' => __('Cancelled', 'easy-invoice'),
185 192 );
186 193
187 194 ?>
188 195 <div class="p-8">
189 - <!-- Page Header -->
190 196 <div class="ei-app-page-header bg-white border-b border-gray-200 px-6" style="margin-left: -2rem; margin-top: -2rem; padding-left:2rem; padding-right:2rem; margin-right: -2rem;">
191 197 <div class="flex items-center justify-between">
192 198 <div>
193 - <h1 class="text-2xl font-bold text-gray-900"><?php _e('Invoices', 'easy-invoice'); ?></h1>
194 - <p class="mt-1 text-sm text-gray-500"><?php _e('Manage and track your invoices', 'easy-invoice'); ?></p>
199 + <h1 class="text-2xl font-bold text-gray-900"><?php esc_html_e('Invoices', 'easy-invoice'); ?></h1>
200 + <p class="mt-1 text-sm text-gray-500"><?php esc_html_e('Manage and track your invoices', 'easy-invoice'); ?></p>
195 201 </div>
196 202 <div class="flex items-center space-x-3">
197 203 <?php if (isset($_GET['rewrite_flushed'])): ?>
198 204 <div class="bg-green-100 border border-green-400 text-green-700 px-4 py-2 rounded-md text-sm">
199 - <?php _e('Rewrite rules flushed successfully!', 'easy-invoice'); ?>
205 + <?php esc_html_e('Rewrite rules flushed successfully!', 'easy-invoice'); ?>
200 206 </div>
201 207 <?php endif; ?>
202 208
203 209 <?php if (isset($_GET['post_types_registered'])): ?>
204 210 <div class="bg-green-100 border border-green-400 text-green-700 px-4 py-2 rounded-md text-sm">
205 - <?php _e('Post types registered successfully!', 'easy-invoice'); ?>
211 + <?php esc_html_e('Post types registered successfully!', 'easy-invoice'); ?>
206 212 </div>
207 213 <?php endif; ?>
208 214
209 - <a href="<?php echo wp_nonce_url(admin_url('admin-post.php?action=flush_easy_invoice_rewrite_rules'), 'flush_easy_invoice_rewrite_rules'); ?>"
215 + <?php
216 + // "Flush Rewrite Rules" is a troubleshooting action, not something a
217 + // user does as part of their job — but it rendered unconditionally as
218 + // a prominent button right beside the primary "Create New Invoice" CTA
219 + // on every admin's screen. It is now hidden by default and shown when
220 + // WP_DEBUG is on, or when a site opts in via the filter.
221 + //
222 + // Kept rather than deleted because it is the manual escape hatch if
223 + // invoice permalinks ever 404.
224 + $ei_show_maintenance_tools = apply_filters(
225 + 'easy_invoice_show_maintenance_tools',
226 + defined('WP_DEBUG') && WP_DEBUG
227 + );
228 + if ($ei_show_maintenance_tools) : ?>
229 + <a href="<?php echo esc_url(wp_nonce_url(admin_url('admin-post.php?action=flush_easy_invoice_rewrite_rules'), 'flush_easy_invoice_rewrite_rules')); ?>"
210 230 class="bg-yellow-100 text-yellow-700 hover:bg-yellow-200 px-4 py-2 rounded-md text-sm font-medium transition-colors duration-200">
211 - <?php _e('Flush Rewrite Rules', 'easy-invoice'); ?>
231 + <?php esc_html_e('Flush Rewrite Rules', 'easy-invoice'); ?>
212 232 </a>
233 + <?php endif; ?>
213 234
214 - <?php if (!easy_invoice_has_pro()): ?>
235 + <?php
236 + // "Export All" is part of the bulk_operations addon
237 + // (Personal tier). Gate three states:
238 + // addon loaded → working POST form
239 + // free user (no Pro) → upsell button + modal
240 + // Pro w/o addon active → hidden (enable in Addons grid)
241 + $ei_bulk_ops_loaded = \EasyInvoice\Addons\AddonManager::shouldLoad('bulk_operations');
242 + ?>
243 + <?php if ($ei_bulk_ops_loaded): ?>
244 + <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="display:inline;">
245 + <input type="hidden" name="action" value="easy_invoice_export_all_invoices" />
246 + <input type="hidden" name="_wpnonce" value="<?php echo esc_attr(wp_create_nonce('easy_invoice_export_all_invoices')); ?>" />
247 + <button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-indigo-700 bg-indigo-100 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
248 + <svg class="w-4 h-4 mr-2 text-indigo-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
249 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
250 + </svg>
251 + <?php esc_html_e('Export All', 'easy-invoice'); ?>
252 + </button>
253 + </form>
254 + <?php elseif (!easy_invoice_has_pro()): ?>
215 255 <button type="button" id="export-all-invoices-btn" class="premium-export-btn inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition-colors duration-200 relative">
216 256 <svg class="w-4 h-4 mr-2 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
217 257 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
218 258 </svg>
219 - <?php _e('Export All', 'easy-invoice'); ?>
259 + <?php esc_html_e('Export All', 'easy-invoice'); ?>
220 260 <svg class="ml-2 w-5 h-5 text-purple-600 crown-icon" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
221 261 <path d="M12 8L15 13.2L18 10.5L17.3 14H6.7L6 10.5L9 13.2L12 8M12 4L8.5 10L3 5L5 16H19L21 5L15.5 10L12 4Z"/>
222 262 </svg>
223 263 </button>
@@ -224,41 +264,29 @@
224 264 <script>
225 265 jQuery(document).ready(function($) {
226 266 $('#export-all-invoices-btn').on('click', function(e) {
227 267 e.preventDefault();
228 - // Show premium popup using the proper confirmation modal
229 268 if (typeof EasyInvoiceConfirmation !== 'undefined') {
230 269 EasyInvoiceConfirmation.showFeatureUpgrade('Export All Invoices', 'Export all your invoices to CSV, Excel, or PDF format for backup, analysis, or sharing with your team.');
231 270 } else {
232 - // Fallback to alert if confirmation modal is not available
233 271 alert('<?php echo esc_js(__('Exporting all invoices is a premium feature. Upgrade to Easy Invoice Pro to unlock this feature.', 'easy-invoice')); ?>');
234 272 }
235 273 });
236 274 });
237 275 </script>
238 - <?php else: ?>
239 - <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="display:inline;">
240 - <input type="hidden" name="action" value="easy_invoice_export_all_invoices" />
241 - <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce('easy_invoice_export_all_invoices'); ?>" />
242 - <button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-indigo-700 bg-indigo-100 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
243 - <svg class="w-4 h-4 mr-2 text-indigo-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
244 - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
245 - </svg>
246 - <?php _e('Export All', 'easy-invoice'); ?>
247 - </button>
248 - </form>
249 276 <?php endif; ?>
250 - <a href="#" id="create-new-invoice-btn" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
277 + <?php if (easy_invoice_user_can('ei_create_invoice')) : ?>
278 +<a href="#" id="create-new-invoice-btn" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
251 279 <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
252 280 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
253 281 </svg>
254 - <?php _e('Create New Invoice', 'easy-invoice'); ?>
282 + <?php esc_html_e('Create New Invoice', 'easy-invoice'); ?>
255 283 </a>
284 +<?php endif; ?>
256 285 </div>
257 286 </div>
258 287 </div>
259 288
260 - <!-- Stats Cards -->
261 289 <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6 mb-8 mt-8">
262 290 <div class="bg-white rounded-lg shadow p-6 transition-transform duration-300 hover:shadow-md hover:-translate-y-1">
263 291 <div class="flex items-center">
264 292 <div class="rounded-full bg-indigo-100 p-3 mr-4">
@@ -266,10 +294,10 @@
266 294 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
267 295 </svg>
268 296 </div>
269 297 <div>
270 - <h3 class="text-sm font-medium text-gray-500">Total Invoices</h3>
271 - <p class="mt-1 text-2xl font-bold text-gray-900"><?php echo $stats['total_invoices']; ?></p>
298 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Total Invoices', 'easy-invoice'); ?></h3>
299 + <p class="mt-1 text-2xl font-bold text-gray-900"><?php echo esc_html($stats['total_invoices']); ?></p>
272 300 </div>
273 301 </div>
274 302 </div>
275 303 <div class="bg-white rounded-lg shadow p-6 transition-transform duration-300 hover:shadow-md hover:-translate-y-1">
@@ -279,9 +307,9 @@
279 307 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
280 308 </svg>
281 309 </div>
282 310 <div>
283 - <h3 class="text-sm font-medium text-gray-500">Total Revenue</h3>
311 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Total Revenue', 'easy-invoice'); ?></h3>
284 312 <?php if (is_array($stats['total_revenue']) && !empty($stats['total_revenue'])): ?>
285 313 <div class="mt-1">
286 314 <?php foreach ($stats['total_revenue'] as $currency => $data): ?>
287 315 <?php
@@ -290,10 +318,10 @@
290 318 $temp_invoice->setCurrencyCode($currency);
291 319 $formatter = new \EasyInvoice\Helpers\InvoiceFormatter($temp_invoice);
292 320 ?>
293 321 <p class="text-lg font-bold text-gray-900">
294 - <?php echo $formatter->format($data['amount']); ?>
295 - <span class="text-sm font-normal text-gray-500"><?php echo $currency; ?></span>
322 + <?php echo esc_html($formatter->format($data['amount'])); ?>
323 + <span class="text-sm font-normal text-gray-500"><?php echo esc_html($currency); ?></span>
296 324 </p>
297 325 <?php endforeach; ?>
298 326 </div>
299 327 <?php else: ?>
@@ -309,19 +337,18 @@
309 337 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
310 338 </svg>
311 339 </div>
312 340 <div>
313 - <h3 class="text-sm font-medium text-gray-500">Total Value</h3>
341 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Total Value', 'easy-invoice'); ?></h3>
314 342 <?php if (is_array($stats['total_value']) && !empty($stats['total_value'])): ?>
315 343 <div class="mt-1">
316 344 <?php foreach ($stats['total_value'] as $currency => $data): ?>
317 345 <?php
318 - $formatter = new \EasyInvoice\Helpers\InvoiceFormatter($data['invoice_object']);
319 - $formatted_amount = $formatter->format($data['amount']);
346 + $formatted_amount = easy_invoice_format_money((float) $data['amount'], null, (string) $currency);
320 347 ?>
321 348 <div class="text-lg font-bold text-gray-900">
322 - <?php echo $formatted_amount; ?>
323 - <span class="text-xs font-normal text-gray-500 ml-1">(<?php echo $data['invoices']; ?> <?php echo $data['invoices'] > 1 ? 'invoices' : 'invoice'; ?>)</span>
349 + <?php echo esc_html($formatted_amount); ?>
350 + <span class="text-xs font-normal text-gray-500 ml-1">(<?php echo esc_html($data['invoices']); ?> <?php echo $data['invoices'] > 1 ? 'invoices' : 'invoice'; ?>)</span>
324 351 </div>
325 352 <?php endforeach; ?>
326 353 </div>
327 354 <?php else: ?>
@@ -337,10 +364,10 @@
337 364 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
338 365 </svg>
339 366 </div>
340 367 <div>
341 - <h3 class="text-sm font-medium text-gray-500">Paid Invoices</h3>
342 - <p class="mt-1 text-2xl font-bold text-green-600"><?php echo $stats['paid_invoices']; ?></p>
368 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Paid Invoices', 'easy-invoice'); ?></h3>
369 + <p class="mt-1 text-2xl font-bold text-green-600"><?php echo esc_html($stats['paid_invoices']); ?></p>
343 370 </div>
344 371 </div>
345 372 </div>
346 373 <div class="bg-white rounded-lg shadow p-6 transition-transform duration-300 hover:shadow-md hover:-translate-y-1">
@@ -350,16 +377,15 @@
350 377 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
351 378 </svg>
352 379 </div>
353 380 <div>
354 - <h3 class="text-sm font-medium text-gray-500">Pending Invoices</h3>
355 - <p class="mt-1 text-2xl font-bold text-yellow-600"><?php echo $stats['pending_invoices']; ?></p>
381 + <h3 class="text-sm font-medium text-gray-500"><?php esc_html_e('Outstanding', 'easy-invoice'); ?></h3>
382 + <p class="mt-1 text-2xl font-bold text-yellow-600"><?php echo esc_html($stats['pending_invoices']); ?></p>
356 383 </div>
357 384 </div>
358 385 </div>
359 386 </div>
360 387
361 - <!-- Top Pagination -->
362 388 <?php if ($total_invoices > 0): ?>
363 389 <div class="mb-4">
364 390 <div class="px-4 py-3 flex items-center justify-between">
365 391 <div class="flex items-center space-x-2">
@@ -417,9 +443,9 @@
417 443 if ($link && strpos($link, 'bg-indigo-50') === false && strpos($link, 'bg-white') === false) {
418 444 $link = easy_invoice_str_replace('border-gray-300', 'border-gray-300 bg-white text-gray-500 hover:bg-gray-50', $link);
419 445 }
420 446
421 - echo $link;
447 + echo wp_kses_post($link);
422 448 }
423 449 echo '</div>';
424 450 }
425 451 ?>
@@ -428,17 +454,15 @@
428 454 </div>
429 455 </div>
430 456 <?php endif; ?>
431 457
432 - <!-- View Tabs and Filters -->
433 458 <div class="bg-white rounded-lg shadow mb-6 overflow-hidden">
434 - <!-- Tabs with more modern styling -->
435 459 <div class="border-b border-gray-200 bg-gradient-to-r from-indigo-50 to-white">
436 460 <nav class="flex">
437 461 <?php
438 462 // View Tabs (All, Draft, Trash)
439 463 $views = array(
440 - 'all' => 'All (' . $stats['total_invoices'] . ')',
464 + 'all' => 'All (' . $all_count . ')',
441 465 'draft' => 'Draft (' . $draft_count . ')',
442 466 'trash' => 'Trash (' . $trash_count . ')'
443 467 );
444 468
@@ -452,9 +476,9 @@
452 476 ? 'border-b-2 border-indigo-500 text-indigo-600 bg-white'
453 477 : 'border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300';
454 478 ?>
455 479 <a href="?page=easy-invoice-all&view=<?php echo esc_attr($view . $status_param); ?>"
456 - class="flex items-center whitespace-nowrap py-4 px-6 font-medium text-sm transition-colors duration-200 <?php echo $class; ?>">
480 + class="flex items-center whitespace-nowrap py-4 px-6 font-medium text-sm transition-colors duration-200 <?php echo esc_attr($class); ?>">
457 481 <?php if ($view === 'all'): ?>
458 482 <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
459 483 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
460 484 </svg>
@@ -466,9 +490,9 @@
466 490 <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
467 491 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
468 492 </svg>
469 493 <?php endif; ?>
470 - <?php echo $label; ?>
494 + <?php echo esc_html($label); ?>
471 495 </a>
472 496 <?php
473 497 }
474 498 ?>
@@ -474,27 +498,26 @@
474 498 ?>
475 499 </nav>
476 500 </div>
477 501
478 - <!-- Filters and Bulk Actions with Search -->
479 502 <div class="p-4 flex flex-wrap items-center justify-between bg-white border-b border-gray-100">
480 - <!-- Bulk Actions (Left Side) -->
481 503 <div class="flex items-center space-x-2 my-2">
482 - <form id="bulk-action-form" method="post" class="flex items-center gap-2">
504 + <?php if (easy_invoice_user_can('ei_create_invoice')) : ?>
505 +<form id="bulk-action-form" method="post" class="flex items-center gap-2">
483 506 <?php wp_nonce_field('easy_invoice_bulk_action', 'easy_invoice_bulk_nonce'); ?>
484 507 <input type="hidden" name="action" value="easy_invoice_bulk_action">
485 508
486 - <select name="bulk_action" class="block w-full px-3 py-2 border border-gray-300 rounded-md leading-5 bg-white text-gray-700 placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
487 - <option value="">Bulk Actions</option>
509 + <select name="bulk_action" aria-label="Bulk action for selected invoices" class="block w-full px-3 py-2 border border-gray-300 rounded-md leading-5 bg-white text-gray-700 placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
510 + <option value=""><?php esc_html_e('Bulk Actions', 'easy-invoice'); ?></option>
488 511 <?php if ($current_view === 'trash'): ?>
489 - <option value="restore">Restore</option>
490 - <option value="delete">Delete Permanently</option>
512 + <option value="restore"><?php esc_html_e('Restore', 'easy-invoice'); ?></option>
513 + <option value="delete"><?php esc_html_e('Delete Permanently', 'easy-invoice'); ?></option>
491 514 <?php elseif ($current_view === 'draft'): ?>
492 - <option value="publish">Publish</option>
493 - <option value="trash">Move to Trash</option>
515 + <option value="publish"><?php esc_html_e('Publish', 'easy-invoice'); ?></option>
516 + <option value="trash"><?php esc_html_e('Move to Trash', 'easy-invoice'); ?></option>
494 517 <?php else: ?>
495 - <option value="trash">Move to Trash</option>
496 - <option value="draft">Set to Draft</option>
518 + <option value="trash"><?php esc_html_e('Move to Trash', 'easy-invoice'); ?></option>
519 + <option value="draft"><?php esc_html_e('Set to Draft', 'easy-invoice'); ?></option>
497 520 <?php endif; ?>
498 521 <option value="export" data-requires-pro="1"><?php esc_html_e('Export Selected (Pro)', 'easy-invoice'); ?></option>
499 522 </select>
500 523
@@ -501,12 +524,12 @@
501 524 <button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 whitespace-nowrap">
502 525 Apply
503 526 </button>
504 527 </form>
528 +<?php endif; ?>
505 529 </div>
506 530
507 531 <?php if ($current_view !== 'trash'): ?>
508 - <!-- Search Form (Right Side) -->
509 532 <div class="flex items-center gap-2">
510 533 <form method="get" class="flex items-center gap-2">
511 534 <input type="hidden" name="page" value="easy-invoice-all">
512 535 <input type="hidden" name="view" value="<?php echo esc_attr($current_view); ?>">
@@ -538,19 +561,19 @@
538 561 </div>
539 562 <input type="text"
540 563 name="search"
541 564 value="<?php echo esc_attr($search_query); ?>"
542 - placeholder="<?php _e('Search invoices, numbers, or clients...', 'easy-invoice'); ?>"
565 + placeholder="<?php echo esc_js(__('Search invoices, numbers, or clients...', 'easy-invoice')); ?>"
543 566 class="block w-64 pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
544 567 </div>
545 568
546 569 <button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
547 - <?php _e('Search', 'easy-invoice'); ?>
570 + <?php esc_html_e('Search', 'easy-invoice'); ?>
548 571 </button>
549 572
550 573 <?php if (!empty($search_query)): ?>
551 574 <a href="<?php echo esc_url(remove_query_arg('search', $_SERVER['REQUEST_URI'])); ?>" class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
552 - <?php _e('Clear', 'easy-invoice'); ?>
575 + <?php esc_html_e('Clear', 'easy-invoice'); ?>
553 576 </a>
554 577 <?php endif; ?>
555 578 </form>
556 579 </div>
@@ -556,13 +579,12 @@
556 579 </div>
557 580 <?php endif; ?>
558 581 </div>
559 582
560 - <!-- Status Filters -->
561 583 <?php if ($current_view !== 'trash'): ?>
562 584 <div class="p-4 bg-white border-b border-gray-100">
563 585 <div class="flex flex-wrap items-center gap-2 my-2">
564 - <span class="text-sm text-gray-500 mr-2">Filter by status:</span>
586 + <span class="text-sm text-gray-500 mr-2"><?php esc_html_e('Filter by status:', 'easy-invoice'); ?></span>
565 587 <div class="flex flex-wrap gap-1">
566 588 <?php
567 589 // Build base URL with current view, recurring filter, client filter and search query
568 590 $base_url = '?page=easy-invoice-all&view=' . $current_view;
@@ -581,9 +603,11 @@
581 603 </a>
582 604
583 605 <?php
584 606 $status_colors = [
607 + 'available' => 'bg-blue-100 text-blue-800 ring-1 ring-blue-300',
585 608 'paid' => 'bg-green-100 text-green-800 ring-1 ring-green-300',
609 + 'partial' => 'bg-yellow-100 text-yellow-800 ring-1 ring-yellow-300',
586 610 'unpaid' => 'bg-yellow-100 text-yellow-800 ring-1 ring-yellow-300',
587 611 'pending' => 'bg-blue-100 text-blue-800 ring-1 ring-blue-300',
588 612 'draft' => 'bg-gray-100 text-gray-800 ring-1 ring-gray-300',
589 613 'overdue' => 'bg-red-100 text-red-800 ring-1 ring-red-300',
@@ -603,10 +627,10 @@
603 627 // Build URL with status filter
604 628 $status_url = $base_url . '&status=' . urlencode($status_key);
605 629 ?>
606 630 <a href="<?php echo esc_url($status_url); ?>"
607 - class="<?php echo $base_class . ' ' . $color_class; ?>">
608 - <?php echo $status_label; ?>
631 + class="<?php echo esc_html($base_class) . ' ' . esc_html($color_class); ?>">
632 + <?php echo esc_html($status_label); ?>
609 633 </a>
610 634 <?php endforeach; ?>
611 635 </div>
612 636 <?php do_action('easy_invoice_inline_type_filter'); ?>
@@ -611,37 +635,35 @@
611 635 </div>
612 636 <?php do_action('easy_invoice_inline_type_filter'); ?>
613 637 </div>
614 638
615 - <!-- Hook for Pro plugin to add additional filters -->
616 639 <?php do_action('easy_invoice_before_invoice_listing'); ?>
617 640 <?php endif; ?>
618 641 </div>
619 642 </div>
620 643
621 - <!-- Invoices Table -->
622 644 <div class="bg-white rounded-lg shadow overflow-hidden">
623 645 <div class="overflow-x-auto">
624 - <table class="min-w-full divide-y divide-gray-200">
646 + <table class="min-w-full divide-y divide-gray-200 ei-invoices-table">
625 647 <thead>
626 648 <tr class="bg-gray-50">
627 649 <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-10 border-b border-gray-200">
628 - <input type="checkbox" id="select-all" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
650 + <input type="checkbox" id="select-all" aria-label="Select all invoices on this page" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
629 651 </th>
630 652 <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider border-b border-gray-200">
631 - <?php _e('Invoice', 'easy-invoice'); ?>
653 + <?php esc_html_e('Invoice', 'easy-invoice'); ?>
632 654 </th>
633 655 <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider border-b border-gray-200">
634 - <?php _e('Client', 'easy-invoice'); ?>
656 + <?php esc_html_e('Client', 'easy-invoice'); ?>
635 657 </th>
636 658 <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider border-b border-gray-200">
637 - <?php _e('Status', 'easy-invoice'); ?>
659 + <?php esc_html_e('Status', 'easy-invoice'); ?>
638 660 </th>
639 661 <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider border-b border-gray-200">
640 - <?php _e('Amount', 'easy-invoice'); ?>
662 + <?php esc_html_e('Amount', 'easy-invoice'); ?>
641 663 </th>
642 664 <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider border-b border-gray-200">
643 - <?php _e('Due Date', 'easy-invoice'); ?>
665 + <?php esc_html_e('Due Date', 'easy-invoice'); ?>
644 666 </th>
645 667 <?php do_action('easy_invoice_after_invoice_listing_header'); ?>
646 668 </tr>
647 669 </thead>
@@ -688,22 +710,20 @@
688 710
689 711 // Format the display title
690 712 $display_title = $invoice_title ?: 'Untitled Invoice';
691 713
692 - // Determine classes for status badge
693 - $status_class = 'bg-gray-100 text-gray-800 ring-1 ring-gray-200';
694 - if ($status === 'paid') {
695 - $status_class = 'bg-green-100 text-green-800 ring-1 ring-green-200';
696 - } elseif ($status === 'pending') {
697 - $status_class = 'bg-yellow-100 text-yellow-800 ring-1 ring-yellow-200';
698 - } elseif ($status === 'overdue') {
699 - $status_class = 'bg-red-100 text-red-800 ring-1 ring-red-200';
700 - }
714 + // An invoice still owed after its due date reads as overdue
715 + // whatever its stored status says (nothing flips the status
716 + // by itself); the underlying status stays for the filters.
717 + $status = \EasyInvoice\Helpers\InvoiceStatus::displayKey((string) $status, $due_date ? (string) $due_date : null);
718 + $status_class = 'available' === $status
719 + ? 'bg-gray-100 text-gray-800 ring-1 ring-gray-200'
720 + : \EasyInvoice\Helpers\InvoiceStatus::badgeClass($status);
701 721 ?>
702 722 <?php do_action('easy_invoice_before_invoice_listing_row', $invoice); ?>
703 - <tr class="hover:bg-gray-50 transition-colors duration-150" data-invoice-id="<?php echo $invoice_id; ?>">
723 + <tr class="hover:bg-gray-50 transition-colors duration-150" data-invoice-id="<?php echo (int) $invoice_id; ?>">
704 724 <td class="px-6 py-4 whitespace-nowrap">
705 - <input type="checkbox" name="invoice_ids[]" value="<?php echo $invoice_id; ?>" form="bulk-action-form" class="invoice-checkbox rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
725 + <input type="checkbox" name="invoice_ids[]" value="<?php echo (int) $invoice_id; ?>" form="bulk-action-form" aria-label="<?php /* translators: %s: invoice number. */ echo esc_attr(sprintf(__('Select invoice %s', 'easy-invoice'), $invoice_number)); ?>" class="invoice-checkbox rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
706 726 </td>
707 727 <td class="px-6 py-4 whitespace-nowrap">
708 728 <?php if ($current_view === 'trash'): ?>
709 729 <div class="text-sm font-medium text-gray-900">
@@ -709,9 +729,15 @@
709 729 <div class="text-sm font-medium text-gray-900">
710 730 <?php echo esc_html($display_title); ?>
711 731 </div>
712 732 <div class="text-xs text-gray-500 mt-1">
713 - <?php echo apply_filters('easy_invoice_invoice_number_display', esc_html($invoice_number), $invoice); ?>
733 + <?php echo wp_kses_post(apply_filters('easy_invoice_invoice_number_display', esc_html($invoice_number), $invoice)); ?>
734 + <?php if ('draft' !== get_post_meta($invoice_id, '_easy_invoice_status', true)) : ?>
735 + <span class="ei-viewed ml-2 inline-flex items-center gap-1 <?php echo \EasyInvoice\Services\DocumentViews::summary($invoice_id)['count'] ? 'text-emerald-600' : 'text-gray-400'; ?>" title="<?php echo esc_attr(\EasyInvoice\Services\DocumentViews::summary($invoice_id)['from'] ? sprintf(/* translators: %s: anonymised IP. */ __('Last opened from %s', 'easy-invoice'), \EasyInvoice\Services\DocumentViews::summary($invoice_id)['from']) : ''); ?>">
736 + <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>
737 + <?php echo esc_html(\EasyInvoice\Services\DocumentViews::label($invoice_id)); ?>
738 + </span>
739 + <?php endif; ?>
714 740 <?php
715 741 // Check if this invoice was converted from a quote
716 742 $quote_info = \EasyInvoice\Helpers\QuoteInvoiceHelper::getQuoteInfoFromInvoice($invoice_id);
717 743 if ($quote_info): ?>
@@ -726,19 +752,25 @@
726 752 </span>
727 753 <?php endif; ?>
728 754 </div>
729 755 <div class="row-actions mt-1">
730 - <span class="restore"><a href="#" class="restore-invoice" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"></path></svg><?php _e('Restore', 'easy-invoice'); ?></a></span>
731 - <span class="delete"><a href="#" class="delete-permanently text-red-600" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg><?php _e('Delete Permanently', 'easy-invoice'); ?></a></span>
756 + <span class="restore"><a href="#" class="restore-invoice" data-invoice-id="<?php echo (int) $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"></path></svg><?php esc_html_e('Restore', 'easy-invoice'); ?></a></span>
757 + <span class="delete"><a href="#" class="delete-permanently text-red-600" data-invoice-id="<?php echo (int) $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg><?php esc_html_e('Delete Permanently', 'easy-invoice'); ?></a></span>
732 758 </div>
733 759 <?php elseif ($current_view === 'draft'): ?>
734 760 <div class="text-sm font-medium text-gray-900">
735 - <a href="<?php echo get_permalink($invoice_id); ?>" class="text-indigo-600 hover:text-indigo-900">
736 - <?php echo apply_filters('easy_invoice_invoice_title', esc_html($display_title), $invoice); ?>
761 + <a href="<?php echo esc_url(get_permalink($invoice_id)); ?>" class="text-indigo-600 hover:text-indigo-900">
762 + <?php echo wp_kses_post(apply_filters('easy_invoice_invoice_title', esc_html($display_title), $invoice)); ?>
737 763 </a>
738 764 </div>
739 765 <div class="text-xs text-gray-500 mt-1">
740 - <?php echo apply_filters('easy_invoice_invoice_number_display', esc_html($invoice_number), $invoice); ?>
766 + <?php echo wp_kses_post(apply_filters('easy_invoice_invoice_number_display', esc_html($invoice_number), $invoice)); ?>
767 + <?php if ('draft' !== get_post_meta($invoice_id, '_easy_invoice_status', true)) : ?>
768 + <span class="ei-viewed ml-2 inline-flex items-center gap-1 <?php echo \EasyInvoice\Services\DocumentViews::summary($invoice_id)['count'] ? 'text-emerald-600' : 'text-gray-400'; ?>" title="<?php echo esc_attr(\EasyInvoice\Services\DocumentViews::summary($invoice_id)['from'] ? sprintf(/* translators: %s: anonymised IP. */ __('Last opened from %s', 'easy-invoice'), \EasyInvoice\Services\DocumentViews::summary($invoice_id)['from']) : ''); ?>">
769 + <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>
770 + <?php echo esc_html(\EasyInvoice\Services\DocumentViews::label($invoice_id)); ?>
771 + </span>
772 + <?php endif; ?>
741 773 <?php
742 774 // Check if this invoice was converted from a quote
743 775 $quote_info = \EasyInvoice\Helpers\QuoteInvoiceHelper::getQuoteInfoFromInvoice($invoice_id);
744 776 if ($quote_info): ?>
@@ -753,18 +785,18 @@
753 785 </span>
754 786 <?php endif; ?>
755 787 </div>
756 788 <div class="row-actions mt-1">
757 - <span class="edit"><a href="<?php echo admin_url('admin.php?page=easy-invoice-builder&invoice_id=' . $invoice_id); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path></svg><?php _e('Edit', 'easy-invoice'); ?></a></span>
758 - <span class="publish"><a href="#" class="publish-invoice" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg><?php _e('Publish', 'easy-invoice'); ?></a></span>
759 - <span class="trash"><a href="#" class="trash-invoice text-red-600" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg><?php _e('Trash', 'easy-invoice'); ?></a></span>
760 - <span class="email"><a href="#" class="email-invoice" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path></svg><?php _e('Send Email', 'easy-invoice'); ?></a></span>
761 - <span class="pdf"><a href="<?php echo get_permalink($invoice_id); ?>?auto_download_pdf=1" target="_blank" class="download-pdf" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg><?php _e('Download PDF', 'easy-invoice'); ?></a></span>
789 + <span class="edit"><a href="<?php echo esc_url(admin_url('admin.php?page=easy-invoice-builder&invoice_id=' . $invoice_id)); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path></svg><?php esc_html_e('Edit', 'easy-invoice'); ?></a></span>
790 + <span class="publish"><a href="#" class="publish-invoice" data-invoice-id="<?php echo (int) $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg><?php esc_html_e('Publish', 'easy-invoice'); ?></a></span>
791 + <span class="trash"><a href="#" class="trash-invoice text-red-600" data-invoice-id="<?php echo (int) $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg><?php esc_html_e('Trash', 'easy-invoice'); ?></a></span>
792 + <span class="email"><a href="#" class="email-invoice" data-invoice-id="<?php echo (int) $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path></svg><?php esc_html_e('Send Email', 'easy-invoice'); ?></a></span>
793 + <span class="pdf"><a href="<?php echo esc_url(get_permalink($invoice_id)); ?>?auto_download_pdf=1" target="_blank" class="download-pdf" data-invoice-id="<?php echo (int) $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg><?php esc_html_e('Download PDF', 'easy-invoice'); ?></a></span>
762 794 <?php if (!easy_invoice_has_pro()): ?>
763 - <span class="export premium-feature"><a href="#" class="export-invoice-pro-placeholder text-yellow-600 font-semibold" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>Export</a></span>
795 + <span class="export premium-feature"><a href="#" class="export-invoice-pro-placeholder text-yellow-600 font-semibold" data-invoice-id="<?php echo (int) $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>Export</a></span>
764 796 <?php endif; ?>
765 797 <?php if (!easy_invoice_has_pro()): ?>
766 - <span class="duplicate premium-feature"><a href="#" class="duplicate-invoice-pro-placeholder text-yellow-600 font-semibold" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 7l2 4 4 .5-3 2.5.5 4-3.5-2-3.5 2 .5-4-3-2.5 4-.5 2-4z"/></svg>Duplicate</a></span>
798 + <span class="duplicate premium-feature"><a href="#" class="duplicate-invoice-pro-placeholder text-yellow-600 font-semibold" data-invoice-id="<?php echo (int) $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 7l2 4 4 .5-3 2.5.5 4-3.5-2-3.5 2 .5-4-3-2.5 4-.5 2-4z"/></svg>Duplicate</a></span>
767 799 <?php endif; ?>
768 800
769 801 <?php
770 802 // Apply filter for Pro plugin to add secure link actions
@@ -769,20 +801,26 @@
769 801 <?php
770 802 // Apply filter for Pro plugin to add secure link actions
771 803 $secure_actions = apply_filters('easy_invoice_invoice_row_actions', [], $invoice);
772 804 foreach ($secure_actions as $action_key => $action_html) {
773 - echo '<span class="' . esc_attr($action_key) . '">' . $action_html . '</span>';
805 + echo '<span class="' . esc_attr($action_key) . '">' . wp_kses_post($action_html) . '</span>';
774 806 }
775 807 ?>
776 808 </div>
777 809 <?php else: ?>
778 810 <div class="text-sm font-medium text-gray-900">
779 - <a href="<?php echo admin_url('admin.php?page=easy-invoice-builder&invoice_id=' . $invoice_id); ?>" class="text-indigo-600 hover:text-indigo-900">
780 - <?php echo apply_filters('easy_invoice_invoice_title', esc_html($display_title), $invoice); ?>
811 + <a href="<?php echo esc_url(admin_url('admin.php?page=easy-invoice-builder&invoice_id=' . $invoice_id)); ?>" class="text-indigo-600 hover:text-indigo-900">
812 + <?php echo wp_kses_post(apply_filters('easy_invoice_invoice_title', esc_html($display_title), $invoice)); ?>
781 813 </a>
782 814 </div>
783 815 <div class="text-xs text-gray-500 mt-1">
784 - <?php echo apply_filters('easy_invoice_invoice_number_display', esc_html($invoice_number), $invoice); ?>
816 + <?php echo wp_kses_post(apply_filters('easy_invoice_invoice_number_display', esc_html($invoice_number), $invoice)); ?>
817 + <?php if ('draft' !== get_post_meta($invoice_id, '_easy_invoice_status', true)) : ?>
818 + <span class="ei-viewed ml-2 inline-flex items-center gap-1 <?php echo \EasyInvoice\Services\DocumentViews::summary($invoice_id)['count'] ? 'text-emerald-600' : 'text-gray-400'; ?>" title="<?php echo esc_attr(\EasyInvoice\Services\DocumentViews::summary($invoice_id)['from'] ? sprintf(/* translators: %s: anonymised IP. */ __('Last opened from %s', 'easy-invoice'), \EasyInvoice\Services\DocumentViews::summary($invoice_id)['from']) : ''); ?>">
819 + <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>
820 + <?php echo esc_html(\EasyInvoice\Services\DocumentViews::label($invoice_id)); ?>
821 + </span>
822 + <?php endif; ?>
785 823 <?php
786 824 // Check if this invoice was converted from a quote
787 825 $quote_info = \EasyInvoice\Helpers\QuoteInvoiceHelper::getQuoteInfoFromInvoice($invoice_id);
788 826 if ($quote_info): ?>
@@ -797,19 +835,23 @@
797 835 </span>
798 836 <?php endif; ?>
799 837 </div>
800 838 <div class="row-actions mt-1">
801 - <span class="view"><a href="<?php echo apply_filters('easy_invoice_invoice_view_url', get_permalink($invoice_id), $invoice_id); ?>" target="_blank"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path></svg><?php _e('View', 'easy-invoice'); ?></a></span>
802 - <span class="edit"><a href="<?php echo admin_url('admin.php?page=easy-invoice-builder&invoice_id=' . $invoice_id); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path></svg><?php _e('Edit', 'easy-invoice'); ?></a></span>
803 - <span class="draft"><a href="#" class="draft-invoice" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg><?php _e('Draft', 'easy-invoice'); ?></a></span>
804 - <span class="trash"><a href="#" class="trash-invoice text-red-600" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg><?php _e('Trash', 'easy-invoice'); ?></a></span>
805 - <span class="email"><a href="#" class="email-invoice" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path></svg><?php _e('Send Email', 'easy-invoice'); ?></a></span>
806 - <span class="pdf"><a href="<?php echo get_permalink($invoice_id); ?>?auto_download_pdf=1" target="_blank" class="download-pdf" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg><?php _e('Download PDF', 'easy-invoice'); ?></a></span>
839 + <span class="view"><a href="<?php echo esc_url(apply_filters('easy_invoice_invoice_view_url', get_permalink($invoice_id), $invoice_id)); ?>" target="_blank"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path></svg><?php esc_html_e('View', 'easy-invoice'); ?></a></span>
840 + <span class="edit"><a href="<?php echo esc_url(admin_url('admin.php?page=easy-invoice-builder&invoice_id=' . $invoice_id)); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path></svg><?php esc_html_e('Edit', 'easy-invoice'); ?></a></span>
841 + <?php if (in_array((string) $invoice->getStatus(), ['available', 'unpaid', 'overdue'], true)) : // paid, part-paid and cancelled invoices are not un-issued ?>
842 + <?php if (is_object($invoice) && in_array(strtolower((string) $invoice->getStatus()), ['available', 'unpaid', 'overdue'], true)) : // paid, part-paid and cancelled invoices are not un-issued ?>
843 + <span class="draft"><a href="#" class="draft-invoice" data-invoice-id="<?php echo (int) $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg><?php esc_html_e('Draft', 'easy-invoice'); ?></a></span>
844 + <?php endif; ?>
845 + <?php endif; ?>
846 + <span class="trash"><a href="#" class="trash-invoice text-red-600" data-invoice-id="<?php echo (int) $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg><?php esc_html_e('Trash', 'easy-invoice'); ?></a></span>
847 + <span class="email"><a href="#" class="email-invoice" data-invoice-id="<?php echo (int) $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path></svg><?php esc_html_e('Send Email', 'easy-invoice'); ?></a></span>
848 + <span class="pdf"><a href="<?php echo esc_url(get_permalink($invoice_id)); ?>?auto_download_pdf=1" target="_blank" class="download-pdf" data-invoice-id="<?php echo (int) $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg><?php esc_html_e('Download PDF', 'easy-invoice'); ?></a></span>
807 849 <?php if (!easy_invoice_has_pro()): ?>
808 - <span class="export premium-feature"><a href="#" class="export-invoice-pro-placeholder text-yellow-600 font-semibold" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>Export</a></span>
850 + <span class="export premium-feature"><a href="#" class="export-invoice-pro-placeholder text-yellow-600 font-semibold" data-invoice-id="<?php echo (int) $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>Export</a></span>
809 851 <?php endif; ?>
810 852 <?php if (!easy_invoice_has_pro()): ?>
811 - <span class="duplicate premium-feature"><a href="#" class="duplicate-invoice-pro-placeholder text-yellow-600 font-semibold" data-invoice-id="<?php echo $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 7l2 4 4 .5-3 2.5.5 4-3.5-2-3.5 2 .5-4-3-2.5 4-.5 2-4z"/></svg>Duplicate</a></span>
853 + <span class="duplicate premium-feature"><a href="#" class="duplicate-invoice-pro-placeholder text-yellow-600 font-semibold" data-invoice-id="<?php echo (int) $invoice_id; ?>" data-invoice-number="<?php echo esc_attr($invoice_number); ?>"><svg class="w-3.5 h-3.5 mr-1 inline-block text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 7l2 4 4 .5-3 2.5.5 4-3.5-2-3.5 2 .5-4-3-2.5 4-.5 2-4z"/></svg>Duplicate</a></span>
812 854 <?php endif; ?>
813 855
814 856 <?php
815 857 // Apply filter for Pro plugin to add secure link actions
@@ -814,9 +856,9 @@
814 856 <?php
815 857 // Apply filter for Pro plugin to add secure link actions
816 858 $secure_actions = apply_filters('easy_invoice_invoice_row_actions', [], $invoice);
817 859 foreach ($secure_actions as $action_key => $action_html) {
818 - echo '<span class="' . esc_attr($action_key) . '">' . $action_html . '</span>';
860 + echo '<span class="' . esc_attr($action_key) . '">' . wp_kses_post($action_html) . '</span>';
819 861 }
820 862 ?>
821 863 </div>
822 864 <?php endif; ?>
@@ -825,10 +867,10 @@
825 867 <div class="text-sm text-gray-900"><?php echo esc_html($client_name ?: 'No Client'); ?></div>
826 868 <div class="text-sm text-gray-500"><?php echo esc_html($client_email ?: ''); ?></div>
827 869 </td>
828 870 <td class="px-6 py-4 whitespace-nowrap">
829 - <span class="px-3 py-1 inline-flex text-xs leading-5 font-medium rounded-full <?php echo $status_class; ?>">
830 - <?php echo ucfirst(esc_html($status)); ?>
871 + <span class="px-3 py-1 inline-flex text-xs leading-5 font-medium rounded-full <?php echo esc_attr($status_class); ?>">
872 + <?php echo esc_html(\EasyInvoice\Helpers\InvoiceStatus::label($status)); ?>
831 873 </span>
832 874 </td>
833 875 <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-700 font-medium">
834 876 <?php echo esc_html($amount); ?>
@@ -842,14 +884,28 @@
842 884
843 885 <?php if (empty($invoices)): ?>
844 886 <tr>
845 887 <td colspan="7" class="px-6 py-4 text-center text-sm text-gray-500">
888 + <?php
889 + // These three strings were hardcoded English with no
890 + // __() wrapper, so they could never be translated even
891 + // though the rest of the screen is fully localised.
892 + $ei_create_link = '<a href="#" class="text-indigo-600 hover:text-indigo-900 create-first-invoice">'
893 + . esc_html__('Create your first invoice', 'easy-invoice')
894 + . '</a>';
895 + ?>
846 896 <?php if ($current_view === 'trash'): ?>
847 - No invoices found in trash.
897 + <?php esc_html_e('No invoices found in trash.', 'easy-invoice'); ?>
848 898 <?php elseif ($current_view === 'draft'): ?>
849 - No invoices found in draft. <a href="#" class="text-indigo-600 hover:text-indigo-900 create-first-invoice">Create your first invoice</a>.
899 + <?php
900 + /* translators: %s: link reading "Create your first invoice". */
901 + printf(esc_html__('No invoices found in draft. %s.', 'easy-invoice'), wp_kses_post($ei_create_link));
902 + ?>
850 903 <?php else: ?>
851 - No invoices found. <a href="#" class="text-indigo-600 hover:text-indigo-900 create-first-invoice">Create your first invoice</a>.
904 + <?php
905 + /* translators: %s: link reading "Create your first invoice". */
906 + printf(esc_html__('No invoices found. %s.', 'easy-invoice'), wp_kses_post($ei_create_link));
907 + ?>
852 908 <?php endif; ?>
853 909 </td>
854 910 </tr>
855 911 <?php endif; ?>
@@ -857,9 +913,8 @@
857 913 </table>
858 914 </div>
859 915 </div>
860 916
861 - <!-- Pagination -->
862 917 <?php if ($total_invoices > 0): ?>
863 918 <div class="mt-4">
864 919 <div class="px-4 py-3 flex items-center justify-between">
865 920 <div class="flex items-center space-x-2">
@@ -916,9 +971,9 @@
916 971 if ($link && strpos($link, 'bg-indigo-50') === false && strpos($link, 'bg-white') === false) {
917 972 $link = easy_invoice_str_replace('border-gray-300', 'border-gray-300 bg-white text-gray-500 hover:bg-gray-50', $link);
918 973 }
919 974
920 - echo $link;
975 + echo wp_kses_post($link);
921 976 }
922 977 echo '</div>';
923 978 }
924 979 ?>
@@ -928,15 +983,14 @@
928 983 </div>
929 984 <?php endif; ?>
930 985 </div>
931 986
932 -<!-- Delete Invoice Confirmation Modal -->
933 987 <div id="trash-invoice-modal" class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center hidden z-50">
934 988 <div class="bg-white rounded-lg shadow-xl max-w-md w-full p-6 relative">
935 989 <button class="close-modal absolute top-4 right-4 text-gray-400 hover:text-gray-500">
936 990 <i class="fas fa-times"></i>
937 991 </button>
938 - <h2 class="text-xl font-medium text-gray-900 mb-4">Move to Trash</h2>
992 + <h2 class="text-xl font-medium text-gray-900 mb-4"><?php esc_html_e('Move to Trash', 'easy-invoice'); ?></h2>
939 993 <p class="text-sm text-gray-500 mb-4">Are you sure you want to move invoice <span id="trash-invoice-number" class="font-medium"></span> to trash?</p>
940 994
941 995 <div class="mt-5 flex justify-end space-x-3">
942 996 <button type="button" class="cancel-modal bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
@@ -948,15 +1002,14 @@
948 1002 </div>
949 1003 </div>
950 1004 </div>
951 1005
952 -<!-- Restore Invoice Confirmation Modal -->
953 1006 <div id="restore-invoice-modal" class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center hidden z-50">
954 1007 <div class="bg-white rounded-lg shadow-xl max-w-md w-full p-6 relative">
955 1008 <button class="close-modal absolute top-4 right-4 text-gray-400 hover:text-gray-500">
956 1009 <i class="fas fa-times"></i>
957 1010 </button>
958 - <h2 class="text-xl font-medium text-gray-900 mb-4">Restore Invoice</h2>
1011 + <h2 class="text-xl font-medium text-gray-900 mb-4"><?php esc_html_e('Restore Invoice', 'easy-invoice'); ?></h2>
959 1012 <p class="text-sm text-gray-500 mb-4">Are you sure you want to restore invoice <span id="restore-invoice-number" class="font-medium"></span>?</p>
960 1013
961 1014 <div class="mt-5 flex justify-end space-x-3">
962 1015 <button type="button" class="cancel-modal bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
@@ -968,15 +1021,14 @@
968 1021 </div>
969 1022 </div>
970 1023 </div>
971 1024
972 -<!-- Permanently Delete Invoice Confirmation Modal -->
973 1025 <div id="delete-permanently-modal" class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center hidden z-50">
974 1026 <div class="bg-white rounded-lg shadow-xl max-w-md w-full p-6 relative">
975 1027 <button class="close-modal absolute top-4 right-4 text-gray-400 hover:text-gray-500">
976 1028 <i class="fas fa-times"></i>
977 1029 </button>
978 - <h2 class="text-xl font-medium text-gray-900 mb-4">Delete Invoice Permanently</h2>
1030 + <h2 class="text-xl font-medium text-gray-900 mb-4"><?php esc_html_e('Delete Invoice Permanently', 'easy-invoice'); ?></h2>
979 1031 <p class="text-sm text-gray-500 mb-4">Are you sure you want to permanently delete invoice <span id="delete-permanently-number" class="font-medium"></span>? This action cannot be undone.</p>
980 1032
981 1033 <div class="mt-5 flex justify-end space-x-3">
982 1034 <button type="button" class="cancel-modal bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
@@ -988,15 +1040,14 @@
988 1040 </div>
989 1041 </div>
990 1042 </div>
991 1043
992 -<!-- Publish Invoice Confirmation Modal -->
993 1044 <div id="publish-invoice-modal" class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center hidden z-50">
994 1045 <div class="bg-white rounded-lg shadow-xl max-w-md w-full p-6 relative">
995 1046 <button class="close-modal absolute top-4 right-4 text-gray-400 hover:text-gray-500">
996 1047 <i class="fas fa-times"></i>
997 1048 </button>
998 - <h2 class="text-xl font-medium text-gray-900 mb-4">Publish Invoice</h2>
1049 + <h2 class="text-xl font-medium text-gray-900 mb-4"><?php esc_html_e('Publish Invoice', 'easy-invoice'); ?></h2>
999 1050 <p class="text-sm text-gray-500 mb-4">Are you sure you want to publish invoice <span id="publish-invoice-number" class="font-medium"></span>?</p>
1000 1051
1001 1052 <div class="mt-5 flex justify-end space-x-3">
1002 1053 <button type="button" class="cancel-modal bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
@@ -1008,16 +1059,15 @@
1008 1059 </div>
1009 1060 </div>
1010 1061 </div>
1011 1062
1012 -<!-- Draft Invoice Confirmation Modal -->
1013 1063 <div id="draft-invoice-modal" class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center hidden z-50">
1014 1064 <div class="bg-white rounded-lg shadow-xl max-w-md w-full p-6 relative">
1015 1065 <button class="close-modal absolute top-4 right-4 text-gray-400 hover:text-gray-500">
1016 1066 <i class="fas fa-times"></i>
1017 1067 </button>
1018 - <h2 class="text-xl font-medium text-gray-900 mb-4">Set Invoice to Draft</h2>
1019 - <p class="text-sm text-gray-500 mb-4">Are you sure you want to set invoice <span id="draft-invoice-number" class="font-medium"></span> to draft status?</p>
1068 + <h2 class="text-xl font-medium text-gray-900 mb-4"><?php esc_html_e('Set Invoice to Draft', 'easy-invoice'); ?></h2>
1069 + <p class="text-sm text-gray-500 mb-4"><?php esc_html_e('Set invoice', 'easy-invoice'); ?> <span id="draft-invoice-number" class="font-medium"></span> <?php esc_html_e('back to draft? It stays in your list and keeps its number, but reads as not yet issued until you send it again.', 'easy-invoice'); ?></p>
1020 1070
1021 1071 <div class="mt-5 flex justify-end space-x-3">
1022 1072 <button type="button" class="cancel-modal bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
1023 1073 Cancel
@@ -1030,20 +1080,48 @@
1030 1080 </div>
1031 1081
1032 1082 <style>
1033 1083 /* Modern row actions styling */
1084 +/* Team roles: controls a member may not use are not drawn (the endpoints check again). */
1085 +<?php if (!easy_invoice_user_can('ei_create_invoice')) : ?>.row-actions .edit, .row-actions .publish, .row-actions .draft { display: none; }<?php endif; ?>
1086 +<?php if (!easy_invoice_user_can('ei_delete_invoice')) : ?>.row-actions .trash, .row-actions .delete, .row-actions .restore { display: none; }<?php endif; ?>
1087 +<?php if (!easy_invoice_user_can('ei_send_invoice')) : ?>.row-actions .email { display: none; }<?php endif; ?>
1088 +.ei-invoices-table td:nth-child(2) { white-space: normal; }
1089 +.ei-invoices-table td:nth-child(2) .ei-viewed { white-space: nowrap; }
1034 1090 .row-actions {
1035 1091 visibility: hidden;
1036 1092 font-size: 12px;
1037 1093 padding-top: 4px;
1038 1094 display: flex;
1039 - gap: 8px;
1095 + flex-wrap: wrap; /* a long action list must not widen the column past the screen */
1096 + gap: 4px 8px;
1040 1097 }
1041 1098
1042 -tr:hover .row-actions {
1099 +tr:hover .row-actions,
1100 +tr:focus-within .row-actions {
1043 1101 visibility: visible;
1044 1102 }
1045 1103
1104 +/*
1105 + * Row actions were revealed on `tr:hover` only, which made them unreachable for
1106 + * two whole groups of users:
1107 + *
1108 + * - Keyboard users. `visibility: hidden` also removes elements from the tab
1109 + * order, so View / Edit / Trash / Send Email / Download PDF / Duplicate could
1110 + * never be focused at all. `:focus-within` above fixes that — tabbing into the
1111 + * row now reveals them.
1112 + * - Touch users. There is no hover on a phone or tablet, so every row action was
1113 + * permanently invisible and there was no other way to reach them.
1114 + *
1115 + * On pointer-less devices, and on the narrow widths WordPress itself treats as
1116 + * mobile admin, the actions are simply always visible.
1117 + */
1118 +@media (hover: none), (max-width: 782px) {
1119 + .row-actions {
1120 + visibility: visible;
1121 + }
1122 +}
1123 +
1046 1124 .row-actions span a {
1047 1125 color: #6366f1; /* indigo-500 */
1048 1126 text-decoration: none;
1049 1127 transition: all 0.2s;
@@ -1302,46 +1380,30 @@
1302 1380 }
1303 1381 }
1304 1382 });
1305 1383
1306 - // Handle Download PDF click
1307 - $(".download-pdf").on("click", function(e) {
1308 - e.preventDefault();
1309 - var invoiceId = $(this).data("invoice-id");
1310 - var invoiceNumber = $(this).data("invoice-number");
1311 -
1312 - // Show loading state
1313 - var $link = $(this);
1314 - var originalText = $link.text();
1315 - $link.text("Generating PDF...").css("opacity", "0.7");
1316 -
1317 - // Get invoice data via AJAX and generate PDF
1318 - $.ajax({
1319 - url: easyInvoice.ajaxUrl,
1320 - type: "POST",
1321 - data: {
1322 - action: "easy_invoice_download_invoice_pdf",
1323 - invoice_id: invoiceId,
1324 - nonce: easyInvoice.nonce
1325 - },
1326 - success: function(response) {
1327 - if (response.success && response.data.download_url) {
1328 - // Open the download URL in a new tab
1329 - window.open(response.data.download_url, '_blank');
1330 - EasyInvoiceToast.success("PDF download started");
1331 - } else {
1332 - EasyInvoiceToast.error(response.data.message || "Error preparing PDF data");
1333 - }
1334 - // Reset link text
1335 - $link.text(originalText).css("opacity", "1");
1336 - },
1337 - error: function() {
1338 - EasyInvoiceToast.error("Error connecting to server");
1339 - // Reset link text
1340 - $link.text(originalText).css("opacity", "1");
1341 - }
1342 - });
1343 - });
1384 + // Download PDF: let the anchor's native href do the job.
1385 + //
1386 + // Historically this handler intercepted the click and did an
1387 + // admin-ajax round-trip that minted a per-user nonce and opened
1388 + // the resulting admin-ajax URL in a new tab. That extra hop was
1389 + // fragile in production — page-cache layers, security plugins /
1390 + // WAFs, cross-tab session cookie drops (Safari ITP, SameSite),
1391 + // and PHP output-buffering interactions all caused the target
1392 + // handler's redirect to be swallowed, leaving the user on a
1393 + // blank admin-ajax page with no download.
1394 + //
1395 + // The anchor's href is already `<permalink>?auto_download_pdf=1`
1396 + // (see line ~756). The single-invoice page JS picks up that
1397 + // query arg and triggers the PDF render directly. Removing the
1398 + // interceptor collapses three server round-trips into one and
1399 + // sidesteps every failure mode above. The
1400 + // `easy_invoice_download_invoice_pdf` /
1401 + // `easy_invoice_generate_pdf` handlers are still registered on
1402 + // the server for any external integration that used them (they
1403 + // now have their own robust 3-path auth + JS redirect fallback
1404 + // — see EasyInvoiceAjax::generateInvoicePdf), but this button
1405 + // no longer depends on them.
1344 1406
1345 1407 // Close modals when Close button or Cancel is clicked
1346 1408 $(".close-modal, .cancel-modal").on("click", function() {
1347 1409 $(".fixed").addClass("hidden");
@@ -1551,42 +1613,11 @@
1551 1613 e.preventDefault();
1552 1614 EasyInvoiceConfirmation.showFeatureUpgrade('Invoice Export', 'Export your invoices to CSV, Excel, or PDF format for backup, analysis, or sharing with your team.');
1553 1615 });
1554 1616
1555 - // Handle "Create Sample Invoice" button click
1556 - $('#easy-invoice-create-sample').on('click', function() {
1557 - var $button = $(this);
1558 - var original_text = $button.html();
1559 - $button.html('Creating...').prop('disabled', true);
1560 -
1561 - $.ajax({
1562 - url: ajaxurl, // WordPress AJAX URL
1563 - type: 'POST',
1564 - data: {
1565 - action: 'easy_invoice_create_sample_invoice',
1566 - nonce: '<?php echo wp_create_nonce("easy_invoice_admin_nonce"); ?>' // Make sure this nonce matches the one checked in PHP
1567 - },
1568 - success: function(response) {
1569 - if (response.success) {
1570 - EasyInvoiceToast.success(response.data.message + ' You can edit it at: ' + response.data.edit_link);
1571 - // Optionally, refresh the page or redirect
1572 - window.location.reload();
1573 - } else {
1574 - EasyInvoiceToast.error('Error: ' + response.data.message);
1575 - }
1576 - },
1577 - error: function(jqXHR, textStatus, errorThrown) {
1578 - EasyInvoiceToast.error('AJAX Error: ' + textStatus + ' - ' + errorThrown);
1579 - },
1580 - complete: function() {
1581 - $button.html(original_text).prop('disabled', false);
1582 - }
1583 - });
1584 - });
1585 1617 });
1586 1618 </script>
1587 1619
1588 -<!-- New Invoice Modal -->
1589 1620 <div id="new-invoice-modal" class="fixed inset-0 bg-gray-600 bg-opacity-75 overflow-y-auto h-full w-full z-50 hidden">
1590 1621 <div class="relative mx-auto p-6 border w-96 shadow-xl rounded-lg bg-white my-20">
1591 1622 <div class="flex justify-between items-center mb-6">
1592 1623 <h3 class="text-xl font-semibold text-gray-900"><?php esc_html_e('Create New Invoice', 'easy-invoice'); ?></h3>
@@ -1605,9 +1636,9 @@
1605 1636 id="new_invoice_title"
1606 1637 required
1607 1638 placeholder="Enter invoice title..."
1608 1639 class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-3 px-4 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm transition-colors duration-200">
1609 - <p class="mt-2 text-sm text-gray-500">This will be the title of your invoice</p>
1640 + <p class="mt-2 text-sm text-gray-500"><?php esc_html_e('This will be the title of your invoice', 'easy-invoice'); ?></p>
1610 1641 </div>
1611 1642
1612 1643 <div class="flex justify-end space-x-3 pt-4">
1613 1644 <button type="button" id="cancel-new-invoice"
@@ -1628,10 +1659,10 @@
1628 1659 <script>
1629 1660 jQuery(document).ready(function($) {
1630 1661 // Initialize easyInvoice object for AJAX calls
1631 1662 window.easyInvoice = {
1632 - ajaxUrl: '<?php echo admin_url('admin-ajax.php'); ?>',
1633 - nonce: '<?php echo wp_create_nonce('easy_invoice_nonce'); ?>',
1663 + ajaxUrl: '<?php echo esc_url(admin_url('admin-ajax.php')); ?>',
1664 + nonce: '<?php echo esc_attr(wp_create_nonce('easy_invoice_nonce')); ?>',
1634 1665 isPro: <?php echo easy_invoice_has_pro() ? 'true' : 'false'; ?>
1635 1666 };
1636 1667
1637 1668 // New Invoice Modal Functionality
@@ -1672,9 +1703,9 @@
1672 1703 var $message = $('#new-invoice-message');
1673 1704 var title = $('#new_invoice_title').val().trim();
1674 1705
1675 1706 if (!title) {
1676 - $message.removeClass('hidden').html('<div class="text-red-600 text-sm">Please enter an invoice title.</div>');
1707 + $message.removeClass('hidden').html('<div class="text-red-600 text-sm"><?php esc_html_e('Please enter an invoice title.', 'easy-invoice'); ?></div>');
1677 1708 return;
1678 1709 }
1679 1710
1680 1711 // Show loading state
@@ -1691,9 +1722,9 @@
1691 1722 },
1692 1723 success: function(response) {
1693 1724 if (response.success) {
1694 1725 // Show success message
1695 - $message.removeClass('hidden').html('<div class="text-green-600 text-sm">Invoice created successfully! Redirecting...</div>');
1726 + $message.removeClass('hidden').html('<div class="text-green-600 text-sm"><?php esc_html_e('Invoice created successfully! Redirecting...', 'easy-invoice'); ?></div>');
1696 1727
1697 1728 // Redirect to the builder page with the new invoice ID
1698 1729 setTimeout(function() {
1699 1730 window.location.href = response.data.redirect_url;
@@ -1703,9 +1734,9 @@
1703 1734 $submitBtn.prop('disabled', false).html('Create Invoice');
1704 1735 }
1705 1736 },
1706 1737 error: function() {
1707 - $message.removeClass('hidden').html('<div class="text-red-600 text-sm">Error connecting to server. Please try again.</div>');
1738 + $message.removeClass('hidden').html('<div class="text-red-600 text-sm"><?php esc_html_e('Error connecting to server. Please try again.', 'easy-invoice'); ?></div>');
1708 1739 $submitBtn.prop('disabled', false).html('Create Invoice');
1709 1740 }
1710 1741 });
1711 1742 });