PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.0
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.0
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 2.1.15 2.1.16 2.1.2 All 57 releases
← All changes | includes/Admin/AdminAssets.php +218 -28 2.1.142.4.0 View file →
@@ -48,25 +48,67 @@
48 48 * Enqueue stylesheets
49 49 */
50 50 private function enqueueStyles() {
51 51
52 + // Tailwind: the purged ~47 KB build is the default; the full 2.8 MB
53 + // dump stays on disk as the fallback.
54 + //
55 + // The purged file is the shipped v2 bundle with unused rules removed --
56 + // NOT a v3 rebuild, which would rename utilities and change the palette.
57 + // Every rule it keeps is byte-identical to the full file, so anything
58 + // still on a page renders exactly as before. It was verified by
59 + // pixel-diffing 41 admin screens (every core page, every addon page,
60 + // three breakpoints, the drawer, a modal and toasts) against the full
61 + // file: zero differing pixels. See tools/tailwind-build/README.md.
62 + //
63 + // A site that adds markup using utilities the plugin never uses --
64 + // a custom template, a child theme -- can go back to the full file:
65 + // update_option('easy_invoice_tailwind_purged', '0');
66 + // or
67 + // add_filter('easy_invoice_use_purged_tailwind', '__return_false');
68 + //
69 + // The option was previously opt-IN ('1' enabled the purged file). It is
70 + // now opt-OUT: only an explicit '0' disables it. A site that had set
71 + // '1' keeps working; a site that never set it gets the win.
72 + //
73 + // The file_exists check below means a deployment that lost the purged
74 + // file falls back to the full one rather than 404-ing the stylesheet.
75 + $purged_option = get_option('easy_invoice_tailwind_purged', '1');
76 + $use_purged = apply_filters(
77 + 'easy_invoice_use_purged_tailwind',
78 + '0' !== (string) $purged_option && false !== $purged_option
79 + );
80 + $purged_rel = 'assets/lib/tailwind/tailwind.purged.min.css';
81 + $shipped_rel = 'assets/lib/tailwind/tailwind.min.css';
82 + $tailwind_rel = ($use_purged && file_exists(EASY_INVOICE_PLUGIN_DIR . $purged_rel))
83 + ? $purged_rel
84 + : $shipped_rel;
85 +
52 86 wp_enqueue_style(
53 87 'easy-invoice-tailwind',
54 - EASY_INVOICE_PLUGIN_URL . 'assets/lib/tailwind/tailwind.min.css',
88 + EASY_INVOICE_PLUGIN_URL . $tailwind_rel,
55 89 array(),
56 90 EASY_INVOICE_VERSION
57 91 );
58 92
93 + // Colour names the views use that Tailwind v2 never had (amber, emerald,
94 + // teal …) — without these every amber notice and emerald badge was unstyled.
95 + wp_enqueue_style(
96 + 'easy-invoice-palette-ext',
97 + EASY_INVOICE_PLUGIN_URL . 'assets/css/palette-ext.css',
98 + array( 'easy-invoice-tailwind' ),
99 + EASY_INVOICE_VERSION
100 + );
101 +
59 102 $page = isset($_GET['page']) ? sanitize_text_field($_GET['page']) : '';
60 103
61 - if(strpos($page, 'easy_invoice') === false) {
62 - wp_enqueue_style(
63 - 'easy-invoice-builder',
64 - EASY_INVOICE_PLUGIN_URL . 'assets/css/easy-invoice.css',
65 - array(),
66 - EASY_INVOICE_VERSION
67 - );
68 - }
104 + // Main plugin layout + header strip alignment (always on Easy Invoice admin screens).
105 + wp_enqueue_style(
106 + 'easy-invoice-main',
107 + EASY_INVOICE_PLUGIN_URL . 'assets/css/easy-invoice.css',
108 + array(),
109 + EASY_INVOICE_VERSION
110 + );
69 111
70 112 if($page === 'easy-invoice-builder') {
71 113 // Main admin styles
72 114
@@ -77,18 +119,47 @@
77 119 EASY_INVOICE_VERSION
78 120 );
79 121 }
80 122
81 - wp_enqueue_style(
82 - 'font-awesome',
83 - EASY_INVOICE_PLUGIN_URL . 'assets/lib/font-awesome/css/all.min.css',
84 - array(),
85 - EASY_INVOICE_VERSION
86 - );
123 + // Font Awesome (~87 KB) is used by the heavyweight templates
124 + // (clients, settings, reports, invoices, quotes, payments) but
125 + // NOT by the lightweight admin pages — Dashboard, Addons grid,
126 + // License, Free-vs-Pro, Join Community, and every addon-settings
127 + // sub-page (those use dashicons). Skip enqueueing FA on the
128 + // deny-listed pages to save the bandwidth and the parse cost.
129 + // Filterable so site-specific custom templates can opt back in.
130 + $fa_deny = (array) apply_filters('easy_invoice_font_awesome_skip_pages', [
131 + 'easy-invoice-dashboard',
132 + 'easy-invoice-addons',
133 + 'easy-invoice-license',
134 + 'easy-invoice-free-vs-pro',
135 + 'easy-invoice-join-community',
136 + ]);
137 + $is_addon_subpage = ($page !== '' && strpos($page, 'easy-invoice-addon-') === 0);
138 + if (!in_array($page, $fa_deny, true) && !$is_addon_subpage) {
139 + wp_enqueue_style(
140 + 'font-awesome',
141 + EASY_INVOICE_PLUGIN_URL . 'assets/lib/font-awesome/css/all.min.css',
142 + array(),
143 + EASY_INVOICE_VERSION
144 + );
145 + }
87 146
88 147
89 - // Add RTL support
90 - wp_style_add_data('easy-invoice-admin', 'rtl', 'replace');
148 + // RTL support.
149 + //
150 + // Strategy: 'append' (not 'replace') because Tailwind utility
151 + // classes generated into easy-invoice.css remain valid LTR — only
152 + // the small set of custom directional rules (sidebar borders,
153 + // page-header negative margins, drawer offsets) need mirroring.
154 + // WordPress loads assets/css/easy-invoice-rtl.css AFTER
155 + // easy-invoice.css when is_rtl() is true; that file holds only the
156 + // directional overrides.
157 + //
158 + // 'easy-invoice-main' is the handle of the file we want to extend
159 + // (line 87 above). The previous handle 'easy-invoice-admin' did
160 + // not exist and so the call was a no-op.
161 + wp_style_add_data('easy-invoice-main', 'rtl', 'append');
91 162 }
92 163
93 164 /**
94 165 * Enqueue scripts
@@ -114,12 +185,27 @@
114 185 wp_enqueue_script('wp-hooks');
115 186
116 187 // Settings page script
117 188 if (isset($_GET['page']) && $_GET['page'] === 'easy-invoice-settings') {
189 + // settings.js calls .sortable() and .disableSelection() on the
190 + // payment-gateway list, so jQuery UI Sortable must be present and
191 + // printed first. SettingsController::enqueueAdminScripts() also
192 + // registers this same handle; WordPress keeps whichever call runs
193 + // first and silently drops the other's dependency array, and this
194 + // one runs first (AdminAssets is wired up during EasyInvoice::init,
195 + // before SettingsController::init). Declaring the jQuery UI handles
196 + // here lets wp_scripts resolve the order rather than leaving it to
197 + // the sequence the two enqueue callbacks happen to fire in.
198 + //
199 + // Only core handles are listed. select2 / easy-invoice-toast are
200 + // also used by settings.js but are registered by SettingsController
201 + // later in the request; naming them here would mean settings.js is
202 + // dropped entirely if that callback ever stops running, so they are
203 + // deliberately left out.
118 204 wp_enqueue_script(
119 205 'easy-invoice-settings',
120 206 EASY_INVOICE_PLUGIN_URL . 'assets/js/settings.js',
121 - array('jquery', 'wp-util', 'wp-api-fetch', 'wp-i18n'),
207 + array('jquery', 'jquery-ui-core', 'jquery-ui-sortable', 'wp-util', 'wp-api-fetch', 'wp-i18n'),
122 208 EASY_INVOICE_VERSION,
123 209 true
124 210 );
125 211
@@ -167,10 +253,46 @@
167 253 // Toast notification system - global across the plugin
168 254 wp_register_script('easy-invoice-toast', EASY_INVOICE_PLUGIN_URL . 'assets/js/easy-invoice-toast.js', array('jquery', 'wp-api-fetch', 'wp-i18n'), EASY_INVOICE_VERSION, true);
169 255 wp_enqueue_script('easy-invoice-toast');
170 256
171 - // jsPDF for PDF generation - available on all Easy Invoice pages
172 - wp_register_script('jspdf', 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js', array(), '2.5.1', true);
257 + // Bulk "Send Email" teaser — only on the invoice / quote listings.
258 + // Always injects the option (even without Pro) so users discover the
259 + // feature; when Pro is inactive, clicking Apply opens the upgrade
260 + // dialog instead of doing the work.
261 + $current_page = isset($_GET['page']) ? sanitize_key($_GET['page']) : '';
262 + if (in_array($current_page, ['easy-invoice-all', 'easy-quote-all'], true)) {
263 + wp_register_script(
264 + 'easy-invoice-bulk-send-email-teaser',
265 + EASY_INVOICE_PLUGIN_URL . 'assets/js/bulk-send-email-teaser.js',
266 + array('jquery', 'easy-invoice-confirmation-modal', 'easy-invoice-toast'),
267 + EASY_INVOICE_VERSION,
268 + true
269 + );
270 + wp_localize_script('easy-invoice-bulk-send-email-teaser', 'easyInvoiceBulkSendTeaser', array(
271 + 'hasPro' => function_exists('easy_invoice_has_pro') && easy_invoice_has_pro(),
272 + 'upgradeUrl' => 'https://matrixaddons.com/plugins/easy-invoice/#pricing',
273 + 'i18n' => array(
274 + // Send Email
275 + 'send_email_label' => __('Send Email (Pro)', 'easy-invoice'),
276 + 'send_email_feature_name' => __('Bulk Send Email', 'easy-invoice'),
277 + 'send_email_feature_description'=> __('Select multiple invoices or quotes and send the configured "Available" email to every selected document\'s client in a single click. Includes a per-row success / failure report so you can spot deliverability problems immediately.', 'easy-invoice'),
278 + // Export
279 + 'export_feature_name' => __('Bulk Export Selected', 'easy-invoice'),
280 + 'export_feature_description' => __('Export your selected invoices or quotes to a clean, accounting-ready CSV — perfect for QuickBooks, Xero, audit trails, or migrating to a new system.', 'easy-invoice'),
281 + // Generic
282 + 'upgrade_required' => __('This action requires Easy Invoice Pro.', 'easy-invoice'),
283 + ),
284 + ));
285 + wp_enqueue_script('easy-invoice-bulk-send-email-teaser');
286 + }
287 +
288 + // jsPDF + html2canvas for PDF generation - available on all Easy Invoice pages.
289 + // Both are bundled in assets/js/vendors/ rather than loaded from a CDN:
290 + // WordPress.org requires every asset to ship inside the plugin, and a remote
291 + // CDN is a hard runtime dependency for a core feature.
292 + wp_register_script('html2canvas', EASY_INVOICE_PLUGIN_URL . 'assets/js/vendors/html2canvas.min.js', array(), '1.4.1', true);
293 + wp_register_script('jspdf', EASY_INVOICE_PLUGIN_URL . 'assets/js/vendors/jspdf.umd.min.js', array(), '2.5.1', true);
294 + wp_enqueue_script('html2canvas');
173 295 wp_enqueue_script('jspdf');
174 296
175 297
176 298 // Conditionally load invoice-specific scripts only on invoice pages
@@ -183,13 +305,36 @@
183 305 wp_register_script('easy-invoice-save', EASY_INVOICE_PLUGIN_URL . 'assets/js/invoice-save.js', array('jquery', 'easy-invoice-scripts', 'easy-invoice-payment-manager', 'wp-api-fetch', 'wp-i18n'), EASY_INVOICE_VERSION, true);
184 306 wp_enqueue_script('easy-invoice-save');
185 307 }
186 308
187 - // Conditionally load quote-specific scripts only on quote pages
188 - if (isset($_GET['page']) && $_GET['page'] === 'easy-invoice-quotes' && isset($_GET['action']) && $_GET['action'] === 'edit') {
189 - // Quote builder scripts - these are loaded directly in the quote builder template
190 - // to avoid conflicts with invoice scripts
309 + // Quote builder (full-screen editor)
310 + if (isset($_GET['page']) && $_GET['page'] === 'easy-invoice-quote-builder') {
311 + wp_register_script(
312 + 'easy-quote-save',
313 + EASY_INVOICE_PLUGIN_URL . 'assets/js/quote-save.js',
314 + array('jquery', 'easy-invoice-scripts', 'easy-invoice-payment-manager', 'wp-api-fetch', 'wp-i18n'),
315 + EASY_INVOICE_VERSION,
316 + true
317 + );
318 + wp_enqueue_script('easy-quote-save');
191 319 }
320 +
321 + if (isset($_GET['page']) && in_array($_GET['page'], array('easy-invoice-builder', 'easy-invoice-quote-builder'), true)) {
322 + wp_enqueue_script(
323 + 'easy-invoice-builder-mobile-tabs',
324 + EASY_INVOICE_PLUGIN_URL . 'assets/js/builder-mobile-tabs.js',
325 + array('jquery'),
326 + EASY_INVOICE_VERSION,
327 + true
328 + );
329 + wp_enqueue_script(
330 + 'easy-invoice-builder-ux',
331 + EASY_INVOICE_PLUGIN_URL . 'assets/js/builder-ux.js',
332 + array('jquery', 'easy-invoice-scripts'),
333 + EASY_INVOICE_VERSION,
334 + true
335 + );
336 + }
192 337 }
193 338
194 339 /**
195 340 * Localize script data
@@ -205,8 +350,10 @@
205 350 $currency_code = $settings['easy_invoice_currency_code'] ?? 'USD';
206 351 $currency_position = $settings['easy_invoice_currency_position'] ?? 'left';
207 352 $currency_symbol = easy_invoice_get_currency_symbol();
208 353
354 + $current_admin_page = isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : '';
355 +
209 356 // Default data
210 357 $script_data = array(
211 358 'ajaxUrl' => admin_url('admin-ajax.php'),
212 359 'nonce' => wp_create_nonce('easy_invoice_nonce'),
@@ -222,8 +369,49 @@
222 369 'add_item' => __('Add Item', 'easy-invoice'),
223 370 'delete_item' => __('Delete Item', 'easy-invoice'),
224 371 'error' => __('An error occurred', 'easy-invoice'),
225 372 ),
373 + );
374 +
375 + if (in_array($current_admin_page, array('easy-invoice-builder', 'easy-invoice-quote-builder'), true)) {
376 + $script_data['i18n'] = array_merge(
377 + $script_data['i18n'],
378 + array(
379 + 'saving' => __('Saving…', 'easy-invoice'),
380 + 'sending' => __('Sending…', 'easy-invoice'),
381 + 'save_invoice' => __('Save Invoice', 'easy-invoice'),
382 + 'update_invoice' => __('Update Invoice', 'easy-invoice'),
383 + 'save_quote' => __('Save Quote', 'easy-invoice'),
384 + 'update_quote' => __('Update Quote', 'easy-invoice'),
385 + 'send_invoice' => __('Send Invoice', 'easy-invoice'),
386 + 'send_quote' => __('Send Quote', 'easy-invoice'),
387 + 'save_first_invoice' => __('Please save the invoice before sending.', 'easy-invoice'),
388 + 'save_first_quote' => __('Please save the quote before sending.', 'easy-invoice'),
389 + 'email_sent_invoice' => __('Invoice email sent successfully.', 'easy-invoice'),
390 + 'email_sent_quote' => __('Quote email sent successfully.', 'easy-invoice'),
391 + 'email_error' => __('Error sending email.', 'easy-invoice'),
392 + 'network_error' => __('Error connecting to server.', 'easy-invoice'),
393 + 'tab_editor' => __('Editor', 'easy-invoice'),
394 + 'tab_preview' => __('Preview', 'easy-invoice'),
395 + 'confirm_send_invoice_title' => __('Send invoice by email?', 'easy-invoice'),
396 + 'confirm_send_invoice_message' => __('This will email the invoice to the client using your configured template.', 'easy-invoice'),
397 + 'confirm_send_quote_title' => __('Send quote by email?', 'easy-invoice'),
398 + 'confirm_send_quote_message' => __('This will email the quote to the client using your configured template.', 'easy-invoice'),
399 + 'confirm_send_confirm' => __('Send', 'easy-invoice'),
400 + 'confirm_cancel' => __('Cancel', 'easy-invoice'),
401 + 'builder_unsaved_warning' => __('You have unsaved changes. If you leave this page, your changes may be lost.', 'easy-invoice'),
402 + 'builder_title_hint' => __('The title above matches the document title field in the editor.', 'easy-invoice'),
403 + 'builder_shell_hint' => __('On smaller screens, use the tabs to switch between the editor and the live preview.', 'easy-invoice'),
404 + 'builder_shell_hint_dismiss' => __('Got it', 'easy-invoice'),
405 + 'send_invoice_confirm_browser' => __('Send this invoice by email?', 'easy-invoice'),
406 + 'send_quote_confirm_browser' => __('Send this quote by email?', 'easy-invoice'),
407 + )
408 + );
409 + }
410 +
411 + $script_data = array_merge(
412 + $script_data,
413 + array(
226 414 'urls' => array(
227 415 'preview' => admin_url('admin.php?action=easy_invoice_preview&invoice_id='),
228 416 'edit' => admin_url('admin.php?page=easy-invoice-new&id='),
229 417 ),
@@ -235,8 +423,10 @@
235 423 'thousand_separator' => $settings['easy_invoice_thousands_separator'] ?? ',',
236 424 'decimal_places' => $settings['easy_invoice_decimal_precision'] ?? 2,
237 425 'date_format' => get_option('date_format', 'F j, Y'),
238 426 ),
427 + 'showAdjustField' => \EasyInvoice\Controllers\SettingsController::shouldShowInvoiceAdjustField(),
428 + )
239 429 );
240 430
241 431 // Localize common scripts
242 432 wp_localize_script('easy-invoice-scripts', 'easyInvoice', $script_data);
@@ -324,11 +514,11 @@
324 514 wp_localize_script('easy-invoice-builder', 'easyInvoice', $script_data);
325 515 wp_localize_script('easy-invoice-save', 'easyInvoice', $script_data);
326 516 }
327 517
328 - // Conditionally localize quote-specific scripts
329 - if (isset($_GET['page']) && $_GET['page'] === 'easy-invoice-quotes' && isset($_GET['action']) && $_GET['action'] === 'edit') {
330 - // Quote-specific data is handled in the quote builder template
331 - // to avoid conflicts with invoice scripts
518 + if (isset($_GET['page']) && $_GET['page'] === 'easy-invoice-quote-builder') {
519 + // Override showAdjustField for quote pages
520 + $script_data['showAdjustField'] = \EasyInvoice\Controllers\SettingsController::shouldShowQuoteAdjustField();
521 + wp_localize_script('easy-quote-save', 'easyInvoice', $script_data);
332 522 }
333 523 }
334 524 }