Passed
Push — master ( fc57ae...6f34c7 )
by Brian
08:52
created

GetPaid_Admin::send_customer_payment_reminder()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
c 0
b 0
f 0
dl 0
loc 11
rs 10
cc 2
nc 2
nop 1
1
<?php
2
/**
3
 * Contains the admin class.
4
 *
5
 */
6
7
defined( 'ABSPATH' ) || exit;
8
9
/**
10
 * The main admin class.
11
 *
12
 * @since       1.0.19
13
 */
14
class GetPaid_Admin {
15
16
    /**
17
	 * Local path to this plugins admin directory
18
	 *
19
	 * @var         string
20
	 */
21
	public $admin_path;
22
23
	/**
24
	 * Web path to this plugins admin directory
25
	 *
26
	 * @var         string
27
	 */
28
	public $admin_url;
29
	
30
	/**
31
	 * Reports components.
32
	 *
33
	 * @var GetPaid_Reports
34
	 */
35
    public $reports;
36
37
    /**
38
	 * Class constructor.
39
	 */
40
	public function __construct(){
41
42
        $this->admin_path  = plugin_dir_path( __FILE__ );
43
		$this->admin_url   = plugins_url( '/', __FILE__ );
44
		$this->reports     = new GetPaid_Reports();
45
46
        if ( is_admin() ) {
47
			$this->init_admin_hooks();
48
        }
49
50
    }
51
52
    /**
53
	 * Init action and filter hooks
54
	 *
55
	 */
56
	private function init_admin_hooks() {
57
        add_action( 'admin_enqueue_scripts', array( $this, 'enqeue_scripts' ), 9 );
58
        add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
59
        add_action( 'admin_init', array( $this, 'init_ayecode_connect_helper' ) );
60
        add_action( 'admin_init', array( $this, 'activation_redirect') );
61
        add_action( 'admin_init', array( $this, 'maybe_do_admin_action') );
62
		add_action( 'admin_notices', array( $this, 'show_notices' ) );
63
		add_action( 'getpaid_authenticated_admin_action_rate_plugin', array( $this, 'redirect_to_wordpress_rating_page' ) );
64
		add_action( 'getpaid_authenticated_admin_action_duplicate_form', array( $this, 'duplicate_payment_form' ) );
65
		add_action( 'getpaid_authenticated_admin_action_duplicate_invoice', array( $this, 'duplicate_invoice' ) );
66
		add_action( 'getpaid_authenticated_admin_action_send_invoice', array( $this, 'send_customer_invoice' ) );
67
		add_action( 'getpaid_authenticated_admin_action_send_invoice_reminder', array( $this, 'send_customer_payment_reminder' ) );
68
        add_action( 'getpaid_authenticated_admin_action_reset_tax_rates', array( $this, 'admin_reset_tax_rates' ) );
69
		add_action( 'getpaid_authenticated_admin_action_create_missing_pages', array( $this, 'admin_create_missing_pages' ) );
70
		add_action( 'getpaid_authenticated_admin_action_create_missing_tables', array( $this, 'admin_create_missing_tables' ) );
71
		add_action( 'getpaid_authenticated_admin_action_migrate_old_invoices', array( $this, 'admin_migrate_old_invoices' ) );
72
		add_action( 'getpaid_authenticated_admin_action_download_customers', array( $this, 'admin_download_customers' ) );
73
		add_action( 'getpaid_authenticated_admin_action_recalculate_discounts', array( $this, 'admin_recalculate_discounts' ) );
74
		add_action( 'getpaid_authenticated_admin_action_install_plugin', array( $this, 'admin_install_plugin' ) );
75
		add_action( 'getpaid_authenticated_admin_action_connect_gateway', array( $this, 'admin_connect_gateway' ) );
76
		add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ) );
77
		do_action( 'getpaid_init_admin_hooks', $this );
78
79
		// Setup/welcome
80
		if ( ! empty( $_GET['page'] ) ) {
81
			switch ( $_GET['page'] ) {
82
				case 'gp-setup' :
83
					include_once( dirname( __FILE__ ) . '/class-getpaid-admin-setup-wizard.php' );
84
					break;
85
			}
86
		}
87
88
    }
89
90
    /**
91
	 * Register admin scripts
92
	 *
93
	 */
94
	public function enqeue_scripts() {
95
        global $current_screen, $pagenow;
96
97
		$page    = isset( $_GET['page'] ) ? $_GET['page'] : '';
98
		$editing = $pagenow == 'post.php' || $pagenow == 'post-new.php';
99
100
        if ( ! empty( $current_screen->post_type ) ) {
101
			$page = $current_screen->post_type;
102
        }
103
104
        // General styles.
105
        if ( false !== stripos( $page, 'wpi' ) || false !== stripos( $page, 'getpaid' ) || 'gp-setup' == $page ) {
106
107
            // Styles.
108
            $version = filemtime( WPINV_PLUGIN_DIR . 'assets/css/admin.css' );
109
            wp_enqueue_style( 'wpinv_admin_style', WPINV_PLUGIN_URL . 'assets/css/admin.css', array( 'wp-color-picker' ), $version );
110
            wp_enqueue_style( 'select2', WPINV_PLUGIN_URL . 'assets/css/select2/select2.min.css', array(), '4.0.13', 'all' );
111
112
            // Scripts.
113
            wp_enqueue_script('select2', WPINV_PLUGIN_URL . 'assets/js/select2/select2.full.min.js', array( 'jquery' ), WPINV_VERSION );
114
115
            $version = filemtime( WPINV_PLUGIN_DIR . 'assets/js/admin.js' );
116
            wp_enqueue_script( 'wpinv-admin-script', WPINV_PLUGIN_URL . 'assets/js/admin.js', array( 'jquery', 'wp-color-picker', 'jquery-ui-tooltip' ),  $version );
117
            wp_localize_script( 'wpinv-admin-script', 'WPInv_Admin', apply_filters( 'wpinv_admin_js_localize', $this->get_admin_i18() ) );
118
119
        }
120
121
        // Payment form scripts.
122
		if ( 'wpi_payment_form' == $page && $editing ) {
123
            $this->load_payment_form_scripts();
124
        }
125
126
		if ( $page == 'wpinv-subscriptions' ) {
127
			wp_enqueue_script( 'postbox' );
128
		}
129
130
    }
131
132
    /**
133
	 * Returns admin js translations.
134
	 *
135
	 */
136
	protected function get_admin_i18() {
137
        global $post;
138
139
		$date_range = array(
140
			'period' => isset( $_GET['date_range'] ) ? sanitize_text_field( $_GET['date_range'] ) : '7_days'
141
		);
142
143
		if ( $date_range['period'] == 'custom' ) {
144
			
145
			if ( isset( $_GET['from'] ) ) {
146
				$date_range[ 'after' ] = date( 'Y-m-d', strtotime( sanitize_text_field( $_GET['from'] ), current_time( 'timestamp' ) ) - DAY_IN_SECONDS );
147
			}
148
149
			if ( isset( $_GET['to'] ) ) {
150
				$date_range[ 'before' ] = date( 'Y-m-d', strtotime( sanitize_text_field( $_GET['to'] ), current_time( 'timestamp' ) ) + DAY_IN_SECONDS );
151
			}
152
153
		}
154
155
        $i18n = array(
156
            'ajax_url'                  => admin_url( 'admin-ajax.php' ),
157
            'post_ID'                   => isset( $post->ID ) ? $post->ID : '',
158
			'wpinv_nonce'               => wp_create_nonce( 'wpinv-nonce' ),
159
			'rest_nonce'                => wp_create_nonce( 'wp_rest' ),
160
			'rest_root'                 => esc_url_raw( rest_url() ),
161
			'date_range'                => $date_range,
162
            'add_invoice_note_nonce'    => wp_create_nonce( 'add-invoice-note' ),
163
            'delete_invoice_note_nonce' => wp_create_nonce( 'delete-invoice-note' ),
164
            'invoice_item_nonce'        => wp_create_nonce( 'invoice-item' ),
165
            'billing_details_nonce'     => wp_create_nonce( 'get-billing-details' ),
166
            'tax'                       => wpinv_tax_amount(),
167
            'discount'                  => 0,
168
			'currency_symbol'           => wpinv_currency_symbol(),
169
			'currency'                  => wpinv_get_currency(),
170
            'currency_pos'              => wpinv_currency_position(),
171
            'thousand_sep'              => wpinv_thousands_separator(),
172
            'decimal_sep'               => wpinv_decimal_separator(),
173
            'decimals'                  => wpinv_decimals(),
174
            'save_invoice'              => __( 'Save Invoice', 'invoicing' ),
175
            'status_publish'            => wpinv_status_nicename( 'publish' ),
176
            'status_pending'            => wpinv_status_nicename( 'wpi-pending' ),
177
            'delete_tax_rate'           => __( 'Are you sure you wish to delete this tax rate?', 'invoicing' ),
178
            'status_pending'            => wpinv_status_nicename( 'wpi-pending' ),
179
            'FillBillingDetails'        => __( 'Fill the user\'s billing information? This will remove any currently entered billing information', 'invoicing' ),
180
            'confirmCalcTotals'         => __( 'Recalculate totals? This will recalculate totals based on the user billing country. If no billing country is set it will use the base country.', 'invoicing' ),
181
            'AreYouSure'                => __( 'Are you sure?', 'invoicing' ),
182
            'errDeleteItem'             => __( 'This item is in use! Before delete this item, you need to delete all the invoice(s) using this item.', 'invoicing' ),
183
            'delete_subscription'       => __( 'Are you sure you want to delete this subscription?', 'invoicing' ),
184
            'action_edit'               => __( 'Edit', 'invoicing' ),
185
            'action_cancel'             => __( 'Cancel', 'invoicing' ),
186
            'item_description'          => __( 'Item Description', 'invoicing' ),
187
            'invoice_description'       => __( 'Invoice Description', 'invoicing' ),
188
            'discount_description'      => __( 'Discount Description', 'invoicing' ),
189
			'searching'                 => __( 'Searching', 'invoicing' ),
190
			'loading'                   => __( 'Loading...', 'invoicing' ),
191
			'search_customers'          => __( 'Enter customer name or email', 'invoicing' ),
192
			'search_items'              => __( 'Enter item name', 'invoicing' ),
193
        );
194
195
		if ( ! empty( $post ) && getpaid_is_invoice_post_type( $post->post_type ) ) {
196
197
			$invoice              = new WPInv_Invoice( $post );
198
			$i18n['save_invoice'] = sprintf(
199
				__( 'Save %s', 'invoicing' ),
200
				ucfirst( $invoice->get_invoice_quote_type() )
201
			);
202
203
			$i18n['invoice_description'] = sprintf(
204
				__( '%s Description', 'invoicing' ),
205
				ucfirst( $invoice->get_invoice_quote_type() )
206
			);
207
208
		}
209
		return $i18n;
210
	}
211
212
	/**
213
	 * Change the admin footer text on GetPaid admin pages.
214
	 *
215
	 * @since  2.0.0
216
	 * @param  string $footer_text
217
	 * @return string
218
	 */
219
	public function admin_footer_text( $footer_text ) {
220
		global $current_screen;
221
222
		$page    = isset( $_GET['page'] ) ? $_GET['page'] : '';
223
224
        if ( ! empty( $current_screen->post_type ) ) {
225
			$page = $current_screen->post_type;
226
        }
227
228
        // General styles.
229
        if ( apply_filters( 'getpaid_display_admin_footer_text', wpinv_current_user_can_manage_invoicing() ) && false !== stripos( $page, 'wpi' ) ) {
230
231
			// Change the footer text
232
			if ( ! get_user_meta( get_current_user_id(), 'getpaid_admin_footer_text_rated', true ) ) {
233
234
				$rating_url  = esc_url(
235
					wp_nonce_url(
236
						admin_url( 'admin.php?page=wpinv-reports&getpaid-admin-action=rate_plugin' ),
237
						'getpaid-nonce',
238
						'getpaid-nonce'
239
						)
240
				);
241
242
				$footer_text = sprintf(
243
					/* translators: %s: five stars */
244
					__( 'If you like <strong>GetPaid</strong>, please leave us a %s rating. A huge thanks in advance!', 'invoicing' ),
245
					"<a href='$rating_url'>&#9733;&#9733;&#9733;&#9733;&#9733;</a>"
246
				);
247
248
			} else {
249
250
				$footer_text = sprintf(
251
					/* translators: %s: GetPaid */
252
					__( 'Thank you for using %s!', 'invoicing' ),
253
					"<a href='https://wpgetpaid.com/' target='_blank'><strong>GetPaid</strong></a>"
254
				);
255
256
			}
257
258
		}
259
260
		return $footer_text;
261
	}
262
263
	/**
264
	 * Redirects to wp.org to rate the plugin.
265
	 *
266
	 * @since  2.0.0
267
	 */
268
	public function redirect_to_wordpress_rating_page() {
269
		update_user_meta( get_current_user_id(), 'getpaid_admin_footer_text_rated', 1 );
270
		wp_redirect( 'https://wordpress.org/support/plugin/invoicing/reviews?rate=5#new-post' );
271
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
272
	}
273
274
    /**
275
	 * Loads payment form js.
276
	 *
277
	 */
278
	protected function load_payment_form_scripts() {
279
        global $post;
280
281
        wp_enqueue_script( 'vue', WPINV_PLUGIN_URL . 'assets/js/vue/vue.min.js', array(), WPINV_VERSION );
282
		wp_enqueue_script( 'sortable', WPINV_PLUGIN_URL . 'assets/js/sortable.min.js', array(), WPINV_VERSION );
283
		wp_enqueue_script( 'vue_draggable', WPINV_PLUGIN_URL . 'assets/js/vue/vuedraggable.min.js', array( 'sortable', 'vue' ), WPINV_VERSION );
284
285
		$version = filemtime( WPINV_PLUGIN_DIR . 'assets/js/admin-payment-forms.js' );
286
		wp_register_script( 'wpinv-admin-payment-form-script', WPINV_PLUGIN_URL . 'assets/js/admin-payment-forms.js', array( 'wpinv-admin-script', 'vue_draggable', 'wp-hooks' ),  $version );
287
288
		wp_localize_script(
289
            'wpinv-admin-payment-form-script',
290
            'wpinvPaymentFormAdmin',
291
            array(
292
				'elements'      => wpinv_get_data( 'payment-form-elements' ),
293
				'form_elements' => getpaid_get_payment_form_elements( $post->ID ),
294
				'currency'      => wpinv_currency_symbol(),
295
				'position'      => wpinv_currency_position(),
296
				'decimals'      => (int) wpinv_decimals(),
297
				'thousands_sep' => wpinv_thousands_separator(),
298
				'decimals_sep'  => wpinv_decimal_separator(),
299
				'form_items'    => gepaid_get_form_items( $post->ID ),
300
				'is_default'    => $post->ID == wpinv_get_default_payment_form(),
301
            )
302
        );
303
304
        wp_enqueue_script( 'wpinv-admin-payment-form-script' );
305
306
    }
307
308
    /**
309
	 * Add our classes to admin pages.
310
     *
311
     * @param string $classes
312
     * @return string
313
	 *
314
	 */
315
    public function admin_body_class( $classes ) {
316
		global $pagenow, $post, $current_screen;
317
318
319
        $page = isset( $_GET['page'] ) ? $_GET['page'] : '';
320
321
        if ( ! empty( $current_screen->post_type ) ) {
322
			$page = $current_screen->post_type;
323
        }
324
325
        if ( false !== stripos( $page, 'wpi' ) ) {
326
            $classes .= ' wpi-' . sanitize_key( $page );
327
        }
328
329
        if ( in_array( $page, wpinv_parse_list( 'wpi_invoice wpi_payment_form wpi_quote' ) ) ) {
330
            $classes .= ' wpinv-cpt wpinv';
331
		}
332
		
333
		if ( getpaid_is_invoice_post_type( $page ) ) {
334
            $classes .= ' getpaid-is-invoice-cpt';
335
        }
336
337
		return $classes;
338
    }
339
340
    /**
341
	 * Maybe show the AyeCode Connect Notice.
342
	 */
343
	public function init_ayecode_connect_helper(){
344
345
		// Register with the deactivation survey class.
346
		AyeCode_Deactivation_Survey::instance(array(
347
			'slug'		        => 'invoicing',
348
			'version'	        => WPINV_VERSION,
349
			'support_url'       => 'https://wpgetpaid.com/support/',
350
			'documentation_url' => 'https://docs.wpgetpaid.com/',
351
			'activated'         => (int) get_option( 'gepaid_installed_on' ),
352
		));
353
354
        new AyeCode_Connect_Helper(
355
            array(
356
				'connect_title' => __("WP Invoicing - an AyeCode product!","invoicing"),
357
				'connect_external'  => __( "Please confirm you wish to connect your site?","invoicing" ),
358
				'connect'           => sprintf( __( "<strong>Have a license?</strong> Forget about entering license keys or downloading zip files, connect your site for instant access. %slearn more%s","invoicing" ),"<a href='https://ayecode.io/introducing-ayecode-connect/' target='_blank'>","</a>" ),
359
				'connect_button'    => __("Connect Site","invoicing"),
360
				'connecting_button'    => __("Connecting...","invoicing"),
361
				'error_localhost'   => __( "This service will only work with a live domain, not a localhost.","invoicing" ),
362
				'error'             => __( "Something went wrong, please refresh and try again.","invoicing" ),
363
            ),
364
            array( 'wpi-addons' )
365
        );
366
367
    }
368
369
	/**
370
	 * Redirect users to settings on activation.
371
	 *
372
	 * @return void
373
	 */
374
	public function activation_redirect() {
375
376
		$redirected = get_option( 'wpinv_redirected_to_settings' );
377
378
		if ( ! empty( $redirected ) || wp_doing_ajax() || ! current_user_can( 'manage_options' ) ) {
379
			return;
380
		}
381
382
		// Bail if activating from network, or bulk
383
		if ( is_network_admin() || isset( $_GET['activate-multi'] ) ) {
384
			return;
385
		}
386
387
	    update_option( 'wpinv_redirected_to_settings', 1 );
388
389
        wp_safe_redirect( admin_url( 'index.php?page=gp-setup' ) );
390
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
391
392
	}
393
394
    /**
395
     * Fires an admin action after verifying that a user can fire them.
396
     */
397
    public function maybe_do_admin_action() {
398
399
        if ( wpinv_current_user_can_manage_invoicing() && isset( $_REQUEST['getpaid-admin-action'] ) && isset( $_REQUEST['getpaid-nonce'] ) && wp_verify_nonce( $_REQUEST['getpaid-nonce'], 'getpaid-nonce' ) ) {
400
            $key = sanitize_key( $_REQUEST['getpaid-admin-action'] );
401
            do_action( "getpaid_authenticated_admin_action_$key", $_REQUEST );
402
        }
403
404
    }
405
406
	/**
407
     * Duplicate invoice.
408
	 * 
409
	 * @param array $args
410
     */
411
    public function duplicate_invoice( $args ) {
412
413
		if ( empty( $args['invoice_id'] ) ) {
414
			return;
415
		}
416
417
		$invoice = new WPInv_Invoice( (int) $args['invoice_id'] );
418
419
		if ( ! $invoice->exists() ) {
420
			return;
421
		}
422
423
		$new_invoice = getpaid_duplicate_invoice( $invoice );
424
		$new_invoice->save();
425
426
		if ( $new_invoice->exists() ) {
427
428
			getpaid_admin()->show_success( __( 'Invoice duplicated successfully.', 'newsletter-optin-box' ) );
429
430
			wp_safe_redirect(
431
				add_query_arg(
432
					array(
433
						'action' => 'edit',
434
						'post' => $new_invoice->get_id(),
435
					),
436
					admin_url( 'post.php' )
437
				)
438
			);
439
			exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
440
441
		}
442
443
		getpaid_admin()->show_error( __( 'There was an error duplicating this invoice. Please try again.', 'newsletter-optin-box' ) );
444
445
	}
446
447
	/**
448
     * Sends a payment reminder to a customer.
449
	 * 
450
	 * @param array $args
451
     */
452
    public function duplicate_payment_form( $args ) {
453
454
		if ( empty( $args['form_id'] ) ) {
455
			return;
456
		}
457
458
		$form = new GetPaid_Payment_Form( (int) $args['form_id'] );
459
460
		if ( ! $form->exists() ) {
461
			return;
462
		}
463
464
		$new_form = new GetPaid_Payment_Form();
465
		$new_form->set_author( $form->get_author( 'edit' ) );
466
		$new_form->set_name( $form->get_name( 'edit' ) . __( '(copy)', 'invoicing' ) );
467
		$new_form->set_elements( $form->get_elements( 'edit' ) );
468
		$new_form->set_items( $form->get_items( 'edit' ) );
469
		$new_form->save();
470
471
		if ( $new_form->exists() ) {
472
			$this->show_success( __( 'Form duplicated successfully', 'invoicing' ) );
473
			$url = get_edit_post_link( $new_form->get_id(), 'edit' );
474
		} else {
475
			$this->show_error( __( 'Unable to duplicate form', 'invoicing' ) );
476
			$url = remove_query_arg( array( 'getpaid-admin-action', 'form_id', 'getpaid-nonce' ) );
477
		}
478
479
		wp_redirect( $url );
480
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
481
	}
482
483
	/**
484
     * Sends a payment reminder to a customer.
485
	 * 
486
	 * @param array $args
487
     */
488
    public function send_customer_invoice( $args ) {
489
		$sent = getpaid()->get( 'invoice_emails' )->user_invoice( new WPInv_Invoice( $args['invoice_id'] ), true );
490
491
		if ( $sent ) {
492
			$this->show_success( __( 'Invoice was successfully sent to the customer', 'invoicing' ) );
493
		} else {
494
			$this->show_error( __( 'Could not send the invoice to the customer', 'invoicing' ) );
495
		}
496
497
		wp_safe_redirect( remove_query_arg( array( 'getpaid-admin-action', 'getpaid-nonce', 'invoice_id' ) ) );
498
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
499
	}
500
501
	/**
502
     * Sends a payment reminder to a customer.
503
	 * 
504
	 * @param array $args
505
     */
506
    public function send_customer_payment_reminder( $args ) {
507
		$sent = getpaid()->get( 'invoice_emails' )->force_send_overdue_notice( new WPInv_Invoice( $args['invoice_id'] ) );
508
509
		if ( $sent ) {
510
			$this->show_success( __( 'Payment reminder was successfully sent to the customer', 'invoicing' ) );
511
		} else {
512
			$this->show_error( __( 'Could not sent payment reminder to the customer', 'invoicing' ) );
513
		}
514
515
		wp_safe_redirect( remove_query_arg( array( 'getpaid-admin-action', 'getpaid-nonce', 'invoice_id' ) ) );
516
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
517
	}
518
519
	/**
520
     * Resets tax rates.
521
	 * 
522
     */
523
    public function admin_reset_tax_rates() {
524
525
		update_option( 'wpinv_tax_rates', wpinv_get_data( 'tax-rates' ) );
526
		wp_safe_redirect( remove_query_arg( array( 'getpaid-admin-action', 'getpaid-nonce' ) ) );
527
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
528
529
	}
530
531
	/**
532
     * Resets admin pages.
533
	 * 
534
     */
535
    public function admin_create_missing_pages() {
536
		$installer = new GetPaid_Installer();
537
		$installer->create_pages();
538
		$this->show_success( __( 'GetPaid pages updated.', 'invoicing' ) );
539
		wp_safe_redirect( remove_query_arg( array( 'getpaid-admin-action', 'getpaid-nonce' ) ) );
540
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
541
	}
542
543
	/**
544
     * Creates an missing admin tables.
545
	 * 
546
     */
547
    public function admin_create_missing_tables() {
548
		global $wpdb;
549
		$installer = new GetPaid_Installer();
550
551
		if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}wpinv_subscriptions'" ) != $wpdb->prefix . 'wpinv_subscriptions' ) {
552
			$installer->create_subscriptions_table();
553
554
			if ( $wpdb->last_error !== '' ) {
555
				$this->show_error( __( 'Your GetPaid tables have been updated:', 'invoicing' ) . ' ' . $wpdb->last_error );
556
			}
557
		}
558
559
		if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}getpaid_invoices'" ) != $wpdb->prefix . 'getpaid_invoices' ) {
560
			$installer->create_invoices_table();
561
562
			if ( $wpdb->last_error !== '' ) {
563
				$this->show_error( __( 'Your GetPaid tables have been updated:', 'invoicing' ) . ' ' . $wpdb->last_error );
564
			}
565
		}
566
567
		if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}getpaid_invoice_items'" ) != $wpdb->prefix . 'getpaid_invoice_items' ) {
568
			$installer->create_invoice_items_table();
569
570
			if ( $wpdb->last_error !== '' ) {
571
				$this->show_error( __( 'Your GetPaid tables have been updated:', 'invoicing' ) . ' ' . $wpdb->last_error );
572
			}
573
		}
574
575
		if ( ! $this->has_notices() ) {
576
			$this->show_success( __( 'Your GetPaid tables have been updated.', 'invoicing' ) );
577
		}
578
579
		wp_safe_redirect( remove_query_arg( array( 'getpaid-admin-action', 'getpaid-nonce' ) ) );
580
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
581
	}
582
583
	/**
584
     * Migrates old invoices to the new database tables.
585
	 * 
586
     */
587
    public function admin_migrate_old_invoices() {
588
589
		// Migrate the invoices.
590
		$installer = new GetPaid_Installer();
591
		$installer->migrate_old_invoices();
592
593
		// Show an admin message.
594
		$this->show_success( __( 'Your invoices have been migrated.', 'invoicing' ) );
595
596
		// Redirect the admin.
597
		wp_safe_redirect( remove_query_arg( array( 'getpaid-admin-action', 'getpaid-nonce' ) ) );
598
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
599
600
	}
601
602
	/**
603
     * Download customers.
604
	 * 
605
     */
606
    public function admin_download_customers() {
607
		global $wpdb;
608
609
		$output = fopen( 'php://output', 'w' ) or die( __( 'Unsupported server', 'invoicing' ) );
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
610
611
		header( "Content-Type:text/csv" );
612
		header( "Content-Disposition:attachment;filename=customers.csv" );
613
614
		$post_types = '';
615
616
		foreach ( array_keys( getpaid_get_invoice_post_types() ) as $post_type ) {
617
			$post_types .= $wpdb->prepare( "post_type=%s OR ", $post_type );
618
		}
619
620
		$post_types = rtrim( $post_types, ' OR' );
621
622
		$customers = $wpdb->get_col(
623
			$wpdb->prepare(
624
				"SELECT DISTINCT( post_author ) FROM $wpdb->posts WHERE $post_types"
625
			)
626
		);
627
628
		$columns = array(
629
			'name'     => __( 'Name', 'invoicing' ),
630
			'email'    => __( 'Email', 'invoicing' ),
631
			'country'  => __( 'Country', 'invoicing' ),
632
			'state'    => __( 'State', 'invoicing' ),
633
			'city'     => __( 'City', 'invoicing' ),
634
			'zip'      => __( 'ZIP', 'invoicing' ),
635
			'address'  => __( 'Address', 'invoicing' ),
636
			'phone'    => __( 'Phone', 'invoicing' ),
637
			'company'  => __( 'Company', 'invoicing' ),
638
			'company_id'  => __( 'Company ID', 'invoicing' ),
639
			'invoices' => __( 'Invoices', 'invoicing' ),
640
			'total_raw' => __( 'Total Spend', 'invoicing' ),
641
			'signup'   => __( 'Date created', 'invoicing' ),
642
		);
643
644
		// Output the csv column headers.
645
		fputcsv( $output, array_values( $columns ) );
646
647
		// Loop through
648
		$table = new WPInv_Customers_Table();
649
		foreach ( $customers as $customer_id ) {
650
651
			$user = get_user_by( 'id', $customer_id );
652
			$row  = array();
653
			if ( empty( $user ) ) {
654
				continue;
655
			}
656
657
			foreach ( array_keys( $columns ) as $column ) {
658
659
				$method = 'column_' . $column;
660
661
				if ( 'name' == $column ) {
662
					$value = esc_html( $user->display_name );
663
				} else if( 'email' == $column ) {
664
					$value = sanitize_email( $user->user_email );
665
				} else if ( is_callable( array( $table, $method ) ) ) {
666
					$value = strip_tags( $table->$method( $user ) );
667
				}
668
669
				if ( empty( $value ) ) {
670
					$value = esc_html( get_user_meta( $user->ID, '_wpinv_' . $column, true ) );
0 ignored issues
show
Bug introduced by
It seems like get_user_meta($user->ID,...pinv_' . $column, true) can also be of type false; however, parameter $text of esc_html() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

670
					$value = esc_html( /** @scrutinizer ignore-type */ get_user_meta( $user->ID, '_wpinv_' . $column, true ) );
Loading history...
671
				}
672
673
				$row[] = $value;
674
675
			}
676
677
			fputcsv( $output, $row );
678
		}
679
680
		fclose( $output );
681
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
682
683
	}
684
685
	/**
686
     * Installs a plugin.
687
	 *
688
	 * @param array $data
689
     */
690
    public function admin_install_plugin( $data ) {
691
692
		if ( ! empty( $data['plugins'] ) ) {
693
			include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
694
			wp_cache_flush();
695
696
			foreach ( $data['plugins'] as $slug => $file ) {
697
				$plugin_zip = esc_url( 'https://downloads.wordpress.org/plugin/' . $slug . '.latest-stable.zip' );
698
				$upgrader   = new Plugin_Upgrader( new Automatic_Upgrader_Skin() );
699
				$installed  = $upgrader->install( $plugin_zip );
700
701
				if ( ! is_wp_error( $installed ) && $installed ) {
702
					activate_plugin( $file, '', false, true );
703
				} else {
704
					wpinv_error_log( $upgrader->skin->get_upgrade_messages(), false );
0 ignored issues
show
Bug introduced by
The method get_upgrade_messages() does not exist on WP_Upgrader_Skin. It seems like you code against a sub-type of WP_Upgrader_Skin such as Automatic_Upgrader_Skin. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

704
					wpinv_error_log( $upgrader->skin->/** @scrutinizer ignore-call */ get_upgrade_messages(), false );
Loading history...
Bug introduced by
$upgrader->skin->get_upgrade_messages() of type string[] is incompatible with the type string expected by parameter $log of wpinv_error_log(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

704
					wpinv_error_log( /** @scrutinizer ignore-type */ $upgrader->skin->get_upgrade_messages(), false );
Loading history...
705
				}
706
707
			}
708
709
		}
710
711
		$redirect = isset( $data['redirect'] ) ? esc_url_raw( $data['redirect'] ) : admin_url( 'plugins.php' );
712
		wp_safe_redirect( $redirect );
713
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
714
715
	}
716
717
	/**
718
     * Connects a gateway.
719
	 *
720
	 * @param array $data
721
     */
722
    public function admin_connect_gateway( $data ) {
723
724
		if ( ! empty( $data['plugin'] ) ) {
725
726
			$gateway     = sanitize_key( $data['plugin'] );
727
			$connect_url = apply_filters( "getpaid_get_{$gateway}_connect_url", false, $data );
728
729
			if ( ! empty( $connect_url ) ) {
730
				wp_redirect( $connect_url );
731
				exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
732
			}
733
734
			if ( 'stripe' == $data['plugin'] ) {
735
				require_once ABSPATH . 'wp-admin/includes/plugin.php';
736
				include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
737
				wp_cache_flush();
738
739
				if ( ! array_key_exists( 'getpaid-stripe-payments/getpaid-stripe-payments.php', get_plugins() ) ) {
740
					$plugin_zip = esc_url( 'https://downloads.wordpress.org/plugin/getpaid-stripe-payments.latest-stable.zip' );
741
					$upgrader   = new Plugin_Upgrader( new Automatic_Upgrader_Skin() );
742
					$upgrader->install( $plugin_zip );
743
				}
744
745
				activate_plugin( 'getpaid-stripe-payments/getpaid-stripe-payments.php', '', false, true );
746
			}
747
748
			$connect_url = apply_filters( "getpaid_get_{$gateway}_connect_url", false, $data );
749
			if ( ! empty( $connect_url ) ) {
750
				wp_redirect( $connect_url );
751
				exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
752
			}
753
754
		}
755
756
		$redirect = isset( $data['redirect'] ) ? esc_url_raw( urldecode( $data['redirect'] ) ) : admin_url( 'admin.php?page=wpinv-settings&tab=gateways' );
757
		wp_safe_redirect( $redirect );
758
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
759
760
	}
761
762
	/**
763
     * Recalculates discounts.
764
	 * 
765
     */
766
    public function admin_recalculate_discounts() {
767
		global $wpdb;
768
769
		// Fetch all invoices that have discount codes.
770
		$table    = $wpdb->prefix . 'getpaid_invoices';
771
		$invoices = $wpdb->get_col( "SELECT `post_id` FROM `$table` WHERE `discount` = 0 && `discount_code` <> ''" );
772
773
		foreach ( $invoices as $invoice ) {
774
775
			$invoice = new WPInv_Invoice( $invoice );
776
777
			if ( ! $invoice->exists() ) {
778
				continue;
779
			}
780
781
			// Abort if the discount does not exist or does not apply here.
782
			$discount = new WPInv_Discount( $invoice->get_discount_code() );
783
			if ( ! $discount->exists() ) {
784
				continue;
785
			}
786
787
			$invoice->add_discount( getpaid_calculate_invoice_discount( $invoice, $discount ) );
788
			$invoice->recalculate_total();
789
790
			if ( $invoice->get_total_discount() > 0 ) {
791
				$invoice->save();
792
			}
793
794
		}
795
796
		// Show an admin message.
797
		$this->show_success( __( 'Discounts have been recalculated.', 'invoicing' ) );
798
799
		// Redirect the admin.
800
		wp_safe_redirect( remove_query_arg( array( 'getpaid-admin-action', 'getpaid-nonce' ) ) );
801
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
802
803
	}
804
805
    /**
806
	 * Returns an array of admin notices.
807
	 *
808
	 * @since       1.0.19
809
     * @return array
810
	 */
811
	public function get_notices() {
812
		$notices = get_option( 'wpinv_admin_notices' );
813
        return is_array( $notices ) ? $notices : array();
814
	}
815
816
	/**
817
	 * Checks if we have any admin notices.
818
	 *
819
	 * @since       2.0.4
820
     * @return array
821
	 */
822
	public function has_notices() {
823
		return count( $this->get_notices() ) > 0;
0 ignored issues
show
Bug Best Practice introduced by
The expression return count($this->get_notices()) > 0 returns the type boolean which is incompatible with the documented return type array.
Loading history...
824
	}
825
826
	/**
827
	 * Clears all admin notices
828
	 *
829
	 * @access      public
830
	 * @since       1.0.19
831
	 */
832
	public function clear_notices() {
833
		delete_option( 'wpinv_admin_notices' );
834
	}
835
836
	/**
837
	 * Saves a new admin notice
838
	 *
839
	 * @access      public
840
	 * @since       1.0.19
841
	 */
842
	public function save_notice( $type, $message ) {
843
		$notices = $this->get_notices();
844
845
		if ( empty( $notices[ $type ] ) || ! is_array( $notices[ $type ]) ) {
846
			$notices[ $type ] = array();
847
		}
848
849
		$notices[ $type ][] = $message;
850
851
		update_option( 'wpinv_admin_notices', $notices );
852
	}
853
854
	/**
855
	 * Displays a success notice
856
	 *
857
	 * @param       string $msg The message to qeue.
858
	 * @access      public
859
	 * @since       1.0.19
860
	 */
861
	public function show_success( $msg ) {
862
		$this->save_notice( 'success', $msg );
863
	}
864
865
	/**
866
	 * Displays a error notice
867
	 *
868
	 * @access      public
869
	 * @param       string $msg The message to qeue.
870
	 * @since       1.0.19
871
	 */
872
	public function show_error( $msg ) {
873
		$this->save_notice( 'error', $msg );
874
	}
875
876
	/**
877
	 * Displays a warning notice
878
	 *
879
	 * @access      public
880
	 * @param       string $msg The message to qeue.
881
	 * @since       1.0.19
882
	 */
883
	public function show_warning( $msg ) {
884
		$this->save_notice( 'warning', $msg );
885
	}
886
887
	/**
888
	 * Displays a info notice
889
	 *
890
	 * @access      public
891
	 * @param       string $msg The message to qeue.
892
	 * @since       1.0.19
893
	 */
894
	public function show_info( $msg ) {
895
		$this->save_notice( 'info', $msg );
896
	}
897
898
	/**
899
	 * Show notices
900
	 *
901
	 * @access      public
902
	 * @since       1.0.19
903
	 */
904
	public function show_notices() {
905
906
        $notices = $this->get_notices();
907
        $this->clear_notices();
908
909
		foreach ( $notices as $type => $messages ) {
910
911
			if ( ! is_array( $messages ) ) {
912
				continue;
913
			}
914
915
            $type  = sanitize_key( $type );
916
			foreach ( $messages as $message ) {
917
                $message = wp_kses_post( $message );
918
				echo "<div class='notice notice-$type is-dismissible'><p>$message</p></div>";
919
            }
920
921
        }
922
923
		foreach ( array( 'checkout_page', 'invoice_history_page', 'success_page', 'failure_page', 'invoice_subscription_page' ) as $page ) {
924
925
			if ( ! is_numeric( wpinv_get_option( $page, false ) ) ) {
926
				$url     = wp_nonce_url(
927
					add_query_arg( 'getpaid-admin-action', 'create_missing_pages' ),
928
					'getpaid-nonce',
929
					'getpaid-nonce'
930
				);
931
				$message  = __( 'Some GetPaid pages are missing. To use GetPaid without any issues, click the button below to generate the missing pages.', 'invoicing' );
932
				$message2 = __( 'Generate Pages', 'invoicing' );
933
				echo "<div class='notice notice-warning is-dismissible'><p>$message<br><br><a href='$url' class='button button-primary'>$message2</a></p></div>";
934
				break;
935
			}
936
937
		}
938
939
	}
940
941
}
942