PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.4
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.4
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
easy-invoice / easy-invoice.php

easy-invoice.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.4.4, at easy-invoice.php

336 lines 14.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Easy Invoice
4 * Plugin URI: https://matrixaddons.com/plugins/easy-invoice
5 * Description: A beautiful, full-featured invoicing solution for WordPress. Create professional invoices, quotes, and manage payments with ease.
6 * Version: 2.4.4
7 * Author: MatrixAddons
8 * Author URI: https://matrixaddons.com
9 * License: GPL v2 or later
10 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
11 * Text Domain: easy-invoice
12 * Domain Path: /languages
13 * Requires at least: 6.0
14 * Tested up to: 7.1
15 * Requires PHP: 7.4
16 *
17 * @package EasyInvoice
18 */
19
20 // Exit if accessed directly.
21 if ( ! defined( 'ABSPATH' ) ) {
22 exit;
23 }
24
25
26 // Define plugin constants.
27 define( 'EASY_INVOICE_VERSION', '2.4.4' );
28 define( 'EASY_INVOICE_PLUGIN_FILE', __FILE__ );
29 define( 'EASY_INVOICE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
30 define( 'EASY_INVOICE_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
31
32 // Load composer autoloader if it exists.
33 if ( file_exists( EASY_INVOICE_PLUGIN_DIR . 'vendor/autoload.php' ) ) {
34 require_once EASY_INVOICE_PLUGIN_DIR . 'vendor/autoload.php';
35 }
36
37 // Include standalone function files (not autoloaded by Composer)
38 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/NotificationHelper.php';
39 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Admin/Helpers/Utility.php';
40 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/GeneralHelper.php';
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';
47 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/TaxHelper.php';
48 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/FormattingHelper.php';
49 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/EmailHelper.php';
50 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/MoneyHelper.php';
51 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/EnqueueHelper.php';
52 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/PdfHelper.php';
53 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/QuoteInvoiceHelper.php';
54 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/TemplateLocator.php';
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
68 /**
69 * Initialize the plugin.
70 *
71 * @since 1.0.0
72 * @return void
73 */
74 function easy_invoice_init() {
75 // Initialize the main plugin class.
76 $plugin = \EasyInvoice\EasyInvoice::getInstance();
77 $plugin->init();
78
79 // Initialize service providers.
80 $invoice_service_provider = new \EasyInvoice\Providers\InvoiceServiceProvider();
81 $invoice_service_provider->register();
82
83 $invoice_item_service_provider = new \EasyInvoice\Providers\InvoiceItemServiceProvider();
84 $invoice_item_service_provider->register();
85
86 $client_service_provider = new \EasyInvoice\Providers\ClientServiceProvider();
87 $client_service_provider->register();
88
89 $invoice_number_service_provider = new \EasyInvoice\Providers\InvoiceNumberServiceProvider();
90 $invoice_number_service_provider->register();
91
92 $quote_number_service_provider = new \EasyInvoice\Providers\QuoteNumberServiceProvider();
93 $quote_number_service_provider->register();
94
95 $quote_service_provider = new \EasyInvoice\Providers\QuoteServiceProvider();
96 $quote_service_provider->register();
97
98 // Initialize EmailManager
99 $email_manager = \EasyInvoice\Services\EmailManager::getInstance();
100
101 // Initialize controllers.
102 $payment_controller = new \EasyInvoice\Controllers\PaymentController();
103 $payment_controller->init();
104
105 $settings_controller = new \EasyInvoice\Controllers\SettingsController();
106 $settings_controller->init();
107
108 $quote_controller = new \EasyInvoice\Controllers\QuoteController();
109 $quote_controller->init();
110
111 // Initialize shortcode manager
112 $shortcode_manager = new \EasyInvoice\Shortcodes\ShortcodeManager();
113
114 // Initialize review notice service
115 \EasyInvoice\Services\ReviewNoticeService::init();
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
135 // Initialize promotion service
136 \EasyInvoice\Services\PromotionService::init();
137
138 // Initialize Addons controller (admin page + AJAX toggle)
139 (new \EasyInvoice\Controllers\AddonsController())->init();
140
141 do_action('easy_invoice_loaded');
142
143 }
144 add_action( 'init', 'easy_invoice_init' );
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
232 /**
233 * Handle payment gateway logging
234 */
235 function easy_invoice_payment_gateway_log_handler($message, $level, $gateway_name) {
236 $log_message = sprintf('[%s] Easy Invoice %s Gateway: %s',
237 gmdate('Y-m-d H:i:s'),
238 ucfirst($gateway_name),
239 $message
240 );
241
242 if (defined('WP_DEBUG') && WP_DEBUG) {
243 error_log($log_message);
244 }
245 }
246 add_action('easy_invoice_payment_gateway_log', 'easy_invoice_payment_gateway_log_handler', 10, 3);
247
248 // Initialize migration system
249 if ( class_exists( 'EasyInvoice\Migration\MigrationInit' ) ) {
250
251 EasyInvoice\Migration\MigrationInit::init();
252 }
253 /**
254 * Register post types early to ensure they're available
255 */
256 function easy_invoice_register_post_types() {
257 // Initialize the main plugin class and register post types
258 $plugin = \EasyInvoice\EasyInvoice::getInstance();
259 $plugin->registerPostTypes();
260 $plugin->registerCustomStatuses();
261 }
262 add_action( 'init', 'easy_invoice_register_post_types', 0 ); // Priority 0 to run early
263
264 /**
265 * Load textdomain early to avoid translation loading warnings.
266 *
267 * @since 1.0.0
268 * @return void
269 */
270 function easy_invoice_load_textdomain() {
271 load_plugin_textdomain('easy-invoice', false, dirname(plugin_basename(__FILE__)) . '/languages');
272 }
273 add_action( 'init', 'easy_invoice_load_textdomain', 1 );
274
275
276
277
278
279 /**
280 * Plugin activation hook.
281 *
282 * @since 1.0.0
283 * @return void
284 */
285 register_activation_hook( __FILE__, 'easy_invoice_activate' );
286 function easy_invoice_activate() {
287 // Initialization of post types so rewrite rules can be flushed properly.
288 \EasyInvoice\EasyInvoice::getInstance()->registerPostTypes();
289
290 // Initialize default settings
291 $settings_controller = new \EasyInvoice\Controllers\SettingsController();
292 $settings_controller->initializeSettings();
293
294 // Record first install timestamp (do not overwrite if it already exists)
295 if (!get_option('easy_invoice_installed')) {
296 update_option('easy_invoice_installed', current_time('mysql'));
297 }
298
299 // Check if this is a fresh installation or an upgrade
300 $stored_version = get_option('easy_invoice_version', '');
301
302 // If no version is stored, this is a fresh installation
303 if (empty($stored_version)) {
304 update_option('easy_invoice_version', EASY_INVOICE_VERSION);
305 update_option('easy_invoice_migration_completed', true);
306 } else {
307 // This is an upgrade. Do NOT bump version here; migration will set it when completed.
308 }
309
310 // Flush rewrite rules.
311 flush_rewrite_rules();
312 }
313
314 /**
315 * Plugin deactivation hook.
316 *
317 * @since 1.0.0
318 * @return void
319 */
320 register_deactivation_hook( __FILE__, 'easy_invoice_deactivate' );
321 function easy_invoice_deactivate() {
322 // Flush rewrite rules.
323 flush_rewrite_rules();
324
325 // Clear scheduled events
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 }
330 }
331
332 /**
333 * Toast notification helper functions
334 */
335
336