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 | easy-invoice.php +134 -10 2.1.21 → 2.4.2 View file →
@@ -2,9 +2,9 @@
2 2 /**
3 3 * Plugin Name: Easy Invoice
4 4 * Plugin URI: https://matrixaddons.com/plugins/easy-invoice
5 5 * Description: A beautiful, full-featured invoicing solution for WordPress. Create professional invoices, quotes, and manage payments with ease.
6 - * Version: 2.1.21
6 + * Version: 2.4.2
7 7 * Author: MatrixAddons
8 8 * Author URI: https://matrixaddons.com
9 9 * License: GPL v2 or later
10 10 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -9,12 +9,11 @@
9 9 * License: GPL v2 or later
10 10 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
11 11 * Text Domain: easy-invoice
12 12 * Domain Path: /languages
13 - * Requires at least: 5.6
14 - * Tested up to: 6.9
13 + * Requires at least: 6.0
14 + * Tested up to: 7.1
15 15 * Requires PHP: 7.4
16 - * Network: false
17 16 *
18 17 * @package EasyInvoice
19 18 */
20 19
@@ -24,9 +23,9 @@
24 23 }
25 24
26 25
27 26 // Define plugin constants.
28 -define( 'EASY_INVOICE_VERSION', '2.1.21' );
27 +define( 'EASY_INVOICE_VERSION', '2.4.2' );
29 28 define( 'EASY_INVOICE_PLUGIN_FILE', __FILE__ );
30 29 define( 'EASY_INVOICE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
31 30 define( 'EASY_INVOICE_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
32 31
@@ -39,15 +38,34 @@
39 38 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/NotificationHelper.php';
40 39 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Admin/Helpers/Utility.php';
41 40 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/GeneralHelper.php';
42 41 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/ToastHelper.php';
42 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/CapabilityHelper.php';
43 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/LogoHelper.php';
44 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/AuditHelper.php';
45 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/AutoloadOptimizer.php';
46 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/BrandHelper.php';
43 47 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/TaxHelper.php';
44 48 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/FormattingHelper.php';
45 49 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/EmailHelper.php';
50 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/MoneyHelper.php';
46 51 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/EnqueueHelper.php';
47 52 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/PdfHelper.php';
48 53 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/QuoteInvoiceHelper.php';
54 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/TemplateLocator.php';
49 55
56 +// Addons system — Free plugin owns the listing UI + license gate;
57 +// Pro plugin owns the executable addon code.
58 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Addons/LicensePlanResolver.php';
59 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Addons/AddonRegistry.php';
60 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Addons/AddonManager.php';
61 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Addons/ProAddonMigration.php';
62 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Controllers/AddonsController.php';
63 +
64 +// Register the Pro→addon migration. Only fires inside admin (admin_init
65 +// hook), only when Pro is active, only once per MIGRATION_VERSION bump.
66 +\EasyInvoice\Addons\ProAddonMigration::register();
67 +
50 68 /**
51 69 * Initialize the plugin.
52 70 *
53 71 * @since 1.0.0
@@ -79,12 +97,8 @@
79 97
80 98 // Initialize EmailManager
81 99 $email_manager = \EasyInvoice\Services\EmailManager::getInstance();
82 100
83 - // Initialize form manager
84 - //$form_manager = \EasyInvoice\Forms\FormManager::getInstance();
85 - // $form_manager->setInvoiceNumberServiceProvider($invoice_number_service_provider);
86 -
87 101 // Initialize controllers.
88 102 $payment_controller = new \EasyInvoice\Controllers\PaymentController();
89 103 $payment_controller->init();
90 104
@@ -99,22 +113,129 @@
99 113
100 114 // Initialize review notice service
101 115 \EasyInvoice\Services\ReviewNoticeService::init();
102 116
117 + // One-time notice explaining that document links now require an access key.
118 + // Registered here rather than on activation because WordPress updates a plugin
119 + // by replacing files, without firing the activation hook — so an upgrading site
120 + // would otherwise get no warning that previously-emailed links stop working.
121 + \EasyInvoice\Services\DocumentAccessNoticeService::init();
122 + \EasyInvoice\Services\ProCompatibilityNotice::init();
123 + \EasyInvoice\Services\TaxTreatment::init();
124 + \EasyInvoice\Rest\RestController::init();
125 + \EasyInvoice\Services\PdfDownload::init();
126 + \EasyInvoice\Services\DocumentPage::init();
127 + \EasyInvoice\Services\DocumentViews::init();
128 + \EasyInvoice\Services\DocumentAttachments::init();
129 + \EasyInvoice\Services\InvoiceRetention::init();
130 + \EasyInvoice\Controllers\CreditNoteController::init();
131 + \EasyInvoice\Controllers\VatCheckController::init();
132 + \EasyInvoice\Controllers\StatementController::init();
133 + \EasyInvoice\Controllers\ImportController::init();
134 +
103 135 // Initialize promotion service
104 136 \EasyInvoice\Services\PromotionService::init();
105 137
138 + // Initialize Addons controller (admin page + AJAX toggle)
139 + (new \EasyInvoice\Controllers\AddonsController())->init();
140 +
106 141 do_action('easy_invoice_loaded');
107 142
108 143 }
109 144 add_action( 'init', 'easy_invoice_init' );
110 145
146 +// Any plugin option change invalidates the per-request settings cache the
147 +// money formatter reads (see SettingsController::getSettings()).
148 +foreach ( [ 'updated_option', 'added_option', 'deleted_option' ] as $ei_opt_hook ) {
149 + add_action( $ei_opt_hook, static function ( $option ) {
150 + if ( is_string( $option ) && 0 === strpos( $option, 'easy_invoice_' ) ) {
151 + \EasyInvoice\Controllers\SettingsController::flushSharedCache();
152 + // The builder field set depends on settings (tax fields are only registered
153 + // while tax is on), so the models' cached definitions go with it.
154 + \EasyInvoice\Models\Invoice::flushFieldDefinitionsCache();
155 + \EasyInvoice\Models\Quote::flushFieldDefinitionsCache();
156 + }
157 + } );
158 +}
159 +unset( $ei_opt_hook );
160 +
161 +// Payment and credit-note writes invalidate the balance totals primed for
162 +// dashboard and report screens (see InvoiceBalance::prime()).
163 +add_action( 'save_post', static function ( $post_id, $post ) {
164 + if ( $post instanceof WP_Post && in_array( $post->post_type, [ 'easy_invoice_payment', 'easy_invoice_credit', 'easy_invoice', 'easy_invoice_quote' ], true ) ) {
165 + \EasyInvoice\Services\InvoiceBalance::forget();
166 + \EasyInvoice\Services\InvoiceTotalsCache::bumpVersion();
167 + }
168 +}, 10, 2 );
169 +add_action( 'updated_post_meta', static function ( $meta_id, $post_id, $meta_key ) {
170 + if ( in_array( $meta_key, [ '_amount', '_status', '_easy_invoice_total', '_invoice_id', '_easy_invoice_status', '_easy_invoice_due_date', '_easy_invoice_issue_date', '_easy_invoice_client_id', '_easy_invoice_currency_code', '_easy_invoice_quote_status', '_easy_invoice_quote_currency_code' ], true ) ) {
171 + \EasyInvoice\Services\InvoiceBalance::forget();
172 + \EasyInvoice\Services\InvoiceTotalsCache::bumpVersion();
173 + }
174 +}, 10, 3 );
175 +add_action( 'added_post_meta', static function ( $meta_id, $post_id, $meta_key ) {
176 + if ( in_array( $meta_key, [ '_amount', '_status', '_easy_invoice_total', '_invoice_id', '_easy_invoice_status', '_easy_invoice_due_date', '_easy_invoice_issue_date', '_easy_invoice_client_id', '_easy_invoice_currency_code', '_easy_invoice_quote_status', '_easy_invoice_quote_currency_code' ], true ) ) {
177 + \EasyInvoice\Services\InvoiceBalance::forget();
178 + \EasyInvoice\Services\InvoiceTotalsCache::bumpVersion();
179 + }
180 +}, 10, 3 );
181 +
182 +// Persisted invoice totals (InvoiceTotalsCache): written on save, backfilled by cron,
183 +// dropped when a total's inputs change.
184 +add_action( 'easy_invoice_totals_backfill', [ \EasyInvoice\Services\InvoiceTotalsCache::class, 'cronBackfill' ] );
185 +add_action( \EasyInvoice\Services\QuoteTotals::CRON_HOOK, [ \EasyInvoice\Services\QuoteTotals::class, 'cronBackfill' ] );
186 +foreach ( [ 'updated_post_meta', 'added_post_meta', 'deleted_post_meta' ] as $ei_meta_hook ) {
187 + add_action( $ei_meta_hook, static function ( $meta_id, $post_id, $meta_key ) {
188 + if ( in_array( $meta_key, \EasyInvoice\Services\InvoiceTotalsCache::DEPENDENT_META, true ) && 'easy_invoice' === get_post_type( $post_id ) ) {
189 + \EasyInvoice\Services\InvoiceTotalsCache::invalidate( (int) $post_id );
190 + }
191 + if ( in_array( $meta_key, \EasyInvoice\Services\QuoteTotals::DEPENDENT_META, true ) && 'easy_invoice_quote' === get_post_type( $post_id ) ) {
192 + \EasyInvoice\Services\QuoteTotals::invalidate( (int) $post_id );
193 + }
194 + }, 10, 3 );
195 +}
196 +unset( $ei_meta_hook );
197 +foreach ( [ 'trashed_post', 'untrashed_post', 'deleted_post', 'before_delete_post' ] as $ei_post_hook ) {
198 + add_action( $ei_post_hook, static function ( $post_id ) {
199 + if ( in_array( get_post_type( $post_id ), [ 'easy_invoice', 'easy_invoice_payment', 'easy_invoice_credit', 'easy_invoice_quote' ], true ) ) {
200 + \EasyInvoice\Services\InvoiceBalance::forget();
201 + \EasyInvoice\Services\InvoiceTotalsCache::bumpVersion();
202 + }
203 + } );
204 +}
205 +unset( $ei_post_hook );
206 +foreach ( [ 'updated_option', 'added_option', 'deleted_option' ] as $ei_opt_hook ) {
207 + add_action( $ei_opt_hook, static function ( $option ) {
208 + if ( in_array( $option, \EasyInvoice\Services\InvoiceTotalsCache::DEPENDENT_OPTIONS, true ) ) {
209 + \EasyInvoice\Services\InvoiceTotalsCache::invalidateAll();
210 + }
211 + } );
212 +}
213 +unset( $ei_opt_hook );
214 +add_action( 'easy_invoice_loaded', static function () {
215 + $want = \EasyInvoice\Services\InvoiceTotalsCache::CACHE_VERSION;
216 + if ( $want !== get_option( 'easy_invoice_totals_cache_version' ) ) {
217 + update_option( 'easy_invoice_totals_cache_version', $want, false );
218 + \EasyInvoice\Services\InvoiceTotalsCache::invalidateAll(); // drops stale totals and queues the backfill
219 + }
220 + if ( '1' !== get_option( 'easy_invoice_quote_totals_version' ) ) {
221 + update_option( 'easy_invoice_quote_totals_version', '1', false );
222 + \EasyInvoice\Services\QuoteTotals::scheduleBackfill();
223 + }
224 + // Versioned memo keys from the first 2.4.0 builds left one transient per write behind.
225 + if ( '1' !== get_option( 'easy_invoice_stats_memo_keys' ) ) {
226 + global $wpdb;
227 + $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_ei_stats_%' OR option_name LIKE '_transient_timeout_ei_stats_%'" );
228 + update_option( 'easy_invoice_stats_memo_keys', '1', false );
229 + }
230 +} );
231 +
111 232 /**
112 233 * Handle payment gateway logging
113 234 */
114 235 function easy_invoice_payment_gateway_log_handler($message, $level, $gateway_name) {
115 236 $log_message = sprintf('[%s] Easy Invoice %s Gateway: %s',
116 - date('Y-m-d H:i:s'),
237 + gmdate('Y-m-d H:i:s'),
117 238 ucfirst($gateway_name),
118 239 $message
119 240 );
120 241
@@ -202,8 +323,11 @@
202 323 flush_rewrite_rules();
203 324
204 325 // Clear scheduled events
205 326 \EasyInvoice\Services\QuoteExpirationService::clear_schedule();
327 + foreach ( [ 'easy_invoice_payment_reminder', 'easy_invoice_send_payment_reminders', 'easy_invoice_quote_expiration_check' ] as $hook ) {
328 + wp_clear_scheduled_hook( $hook );
329 + }
206 330 }
207 331
208 332 /**
209 333 * Toast notification helper functions