GetPaid_Invoice_Notification_Emails   F
last analyzed

Complexity

Total Complexity 62

Size/Duplication

Total Lines 487
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 62
eloc 178
c 1
b 0
f 0
dl 0
loc 487
rs 3.44

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 19 1
A get_date_query() 0 20 2
A cancelled_invoice() 0 6 1
A new_invoice() 0 11 4
A failed_invoice() 0 6 1
A refunded_invoice() 0 6 1
A completed_invoice() 0 11 3
B user_invoice() 0 15 8
A onhold_invoice() 0 6 1
A processing_invoice() 0 6 1
A force_send_overdue_notice() 0 3 1
A init_email_type_hook() 0 19 4
A is_payment_form_invoice() 0 5 3
A filter_email_recipients() 0 18 4
B get_invoice_merge_tags() 0 46 8
A user_note() 0 6 1
A overdue() 0 35 6
A init_hooks() 0 7 2
A invoice_merge_tags() 0 17 3
B send_email() 0 52 7

How to fix   Complexity   

Complex Class

Complex classes like GetPaid_Invoice_Notification_Emails often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use GetPaid_Invoice_Notification_Emails, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Contains the invoice notification emails management class.
4
 *
5
 */
6
7
defined( 'ABSPATH' ) || exit;
8
9
/**
10
 * This class handles invoice notificaiton emails.
11
 *
12
 */
13
class GetPaid_Invoice_Notification_Emails {
14
15
	/**
16
	 * The array of invoice email actions.
17
	 *
18
	 * @param array
19
	 */
20
	public $invoice_actions;
21
22
	/**
23
	 * Class constructor
24
	 *
25
	 */
26
	public function __construct() {
27
28
		$this->invoice_actions = apply_filters(
29
			'getpaid_notification_email_invoice_triggers',
30
			array(
31
				'getpaid_new_invoice'                   => array( 'new_invoice', 'user_invoice' ),
32
				'getpaid_invoice_status_wpi-cancelled'  => 'cancelled_invoice',
33
				'getpaid_invoice_status_wpi-failed'     => 'failed_invoice',
34
				'getpaid_invoice_status_wpi-onhold'     => 'onhold_invoice',
35
				'getpaid_invoice_status_wpi-processing' => 'processing_invoice',
36
				'getpaid_invoice_status_publish'        => 'completed_invoice',
37
				'getpaid_invoice_status_wpi-renewal'    => 'completed_invoice',
38
				'getpaid_invoice_status_wpi-refunded'   => 'refunded_invoice',
39
				'getpaid_new_customer_note'             => 'user_note',
40
				'getpaid_daily_maintenance'             => 'overdue',
41
			)
42
		);
43
44
		add_action( 'init', array( $this, 'init_hooks' ) );
45
	}
46
47
	/**
48
	 * Registers email hooks.
49
	 */
50
	public function init_hooks() {
51
52
		add_filter( 'getpaid_get_email_merge_tags', array( $this, 'invoice_merge_tags' ), 10, 2 );
53
		add_filter( 'getpaid_invoice_email_recipients', array( $this, 'filter_email_recipients' ), 10, 2 );
54
55
		foreach ( $this->invoice_actions as $hook => $email_type ) {
56
			$this->init_email_type_hook( $hook, $email_type );
57
		}
58
	}
59
60
	/**
61
	 * Registers an email hook for an invoice action.
62
	 *
63
	 * @param string $hook
64
	 * @param string|array $email_type
65
	 */
66
	public function init_email_type_hook( $hook, $email_type ) {
67
68
		$email_type = wpinv_parse_list( $email_type );
69
70
		foreach ( $email_type as $type ) {
71
72
			$email = new GetPaid_Notification_Email( $type );
73
74
			// Abort if it is not active.
75
			if ( ! $email->is_active() ) {
76
				continue;
77
			}
78
79
			if ( method_exists( $this, $type ) ) {
80
				add_action( $hook, array( $this, $type ), 100, 2 );
81
				continue;
82
			}
83
84
			do_action( 'getpaid_invoice_init_email_type_hook', $type, $hook );
85
		}
86
87
	}
88
89
	/**
90
	 * Filters invoice merge tags.
91
	 *
92
	 * @param array $merge_tags
93
	 * @param mixed|WPInv_Invoice|WPInv_Subscription $object
94
	 */
95
	public function invoice_merge_tags( $merge_tags, $object ) {
96
97
		if ( is_a( $object, 'WPInv_Invoice' ) ) {
98
			return array_merge(
99
				$merge_tags,
100
				$this->get_invoice_merge_tags( $object )
101
			);
102
		}
103
104
		if ( is_a( $object, 'WPInv_Subscription' ) ) {
105
			return array_merge(
106
				$merge_tags,
107
				$this->get_invoice_merge_tags( $object->get_parent_payment() )
108
			);
109
		}
110
111
		return $merge_tags;
112
113
	}
114
115
	/**
116
	 * Generates invoice merge tags.
117
	 *
118
	 * @param WPInv_Invoice $invoice
119
	 * @return array
120
	 */
121
	public function get_invoice_merge_tags( $invoice ) {
122
123
		// Abort if it does not exist.
124
		if ( ! $invoice->get_id() ) {
125
			return array();
126
		}
127
128
		$due_date   = $invoice->get_due_date();
129
		$due_date   = empty( $due_date ) ? time() + MINUTE_IN_SECONDS : strtotime( $due_date ) + ( (int) get_option( 'gmt_offset' ) * HOUR_IN_SECONDS );
130
		$merge_tags = array(
131
			'{name}'                 => sanitize_text_field( $invoice->get_user_full_name() ),
132
			'{full_name}'            => sanitize_text_field( $invoice->get_user_full_name() ),
133
			'{first_name}'           => sanitize_text_field( $invoice->get_first_name() ),
134
			'{last_name}'            => sanitize_text_field( $invoice->get_last_name() ),
135
			'{email}'                => sanitize_email( $invoice->get_email() ),
136
			'{invoice_number}'       => sanitize_text_field( $invoice->get_number() ),
137
			'{invoice_currency}'     => sanitize_text_field( $invoice->get_currency() ),
138
			'{invoice_total}'        => sanitize_text_field( wpinv_price( $invoice->get_total(), $invoice->get_currency() ) ),
139
			'{invoice_link}'         => esc_url( $invoice->get_view_url() ),
140
			'{invoice_pay_link}'     => esc_url( $invoice->get_checkout_payment_url() ),
141
			'{invoice_receipt_link}' => esc_url( $invoice->get_receipt_url() ),
142
			'{invoice_date}'         => getpaid_format_date_value( $invoice->get_date_created() ),
143
			'{invoice_due_date}'     => getpaid_format_date_value( $invoice->get_due_date(), __( 'on receipt', 'invoicing' ) ),
144
			'{invoice_quote}'        => sanitize_text_field( strtolower( $invoice->get_label() ) ),
145
			'{invoice_label}'        => sanitize_text_field( ucfirst( $invoice->get_label() ) ),
146
			'{invoice_description}'  => wp_kses_post( $invoice->get_description() ),
147
			'{subscription_name}'    => wp_kses_post( $invoice->get_subscription_name() ),
148
			'{is_was}'               => $due_date < time() ? __( 'was', 'invoicing' ) : __( 'is', 'invoicing' ),
149
		);
150
151
		$payment_form_data = $invoice->get_meta( 'payment_form_data', true );
152
153
		if ( is_array( $payment_form_data ) ) {
154
155
			foreach ( $payment_form_data as $label => $value ) {
156
157
				$label = preg_replace( '/[^a-z0-9]+/', '_', strtolower( $label ) );
158
				$value = is_array( $value ) ? implode( ', ', $value ) : $value;
159
160
				if ( is_scalar( $value ) ) {
161
					$merge_tags[ "{{$label}}" ] = wp_kses_post( $value );
162
				}
163
			}
164
		}
165
166
		return apply_filters( 'getpaid_invoice_email_merge_tags', $merge_tags, $invoice );
167
	}
168
169
	/**
170
	 * Helper function to send an email.
171
	 *
172
	 * @param WPInv_Invoice $invoice
173
	 * @param GetPaid_Notification_Email $email
174
	 * @param string $type
175
	 * @param string|array $recipients
176
	 * @param array $extra_args Extra template args.
177
	 */
178
	public function send_email( $invoice, $email, $type, $recipients, $extra_args = array() ) {
179
180
		do_action( 'getpaid_before_send_invoice_notification', $type, $invoice, $email );
181
182
		$skip = $invoice->is_free() && wpinv_get_option( 'skip_email_free_invoice' );
183
		if ( apply_filters( 'getpaid_skip_invoice_email', $skip, $type, $invoice ) ) {
184
			return;
185
		}
186
187
		$mailer     = new GetPaid_Notification_Email_Sender();
188
		$merge_tags = $email->get_merge_tags();
189
190
		$result = $mailer->send(
191
			apply_filters( 'getpaid_invoice_email_recipients', wpinv_parse_list( $recipients ), $email ),
192
			$email->add_merge_tags( $email->get_subject(), $merge_tags ),
193
			$email->get_content( $merge_tags, $extra_args ),
194
			$email->get_attachments()
195
		);
196
197
		// Maybe send a copy to the admin.
198
		if ( $email->include_admin_bcc() ) {
199
			$mailer->send(
200
				wpinv_get_admin_email(),
0 ignored issues
show
Bug introduced by
It seems like wpinv_get_admin_email() can also be of type false; however, parameter $to of GetPaid_Notification_Email_Sender::send() does only seem to accept array|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

200
				/** @scrutinizer ignore-type */ wpinv_get_admin_email(),
Loading history...
201
				$email->add_merge_tags( $email->get_subject() . __( ' - ADMIN BCC COPY', 'invoicing' ), $merge_tags ),
202
				$email->get_content( $merge_tags ),
203
				$email->get_attachments()
204
			);
205
		}
206
207
		if ( $result ) {
208
			$invoice->add_system_note(
209
				sprintf(
210
					// translators: %1 is the email type, %2 is the invoice recipient.
211
					__( 'Successfully sent %1$s notification email to %2$s.', 'invoicing' ),
212
					sanitize_key( $type ),
213
					$email->is_admin_email() ? __( 'admin', 'invoicing' ) : __( 'the customer', 'invoicing' )
214
				)
215
			);
216
		} else {
217
			$invoice->add_system_note(
218
				sprintf(
219
					// translators: %1 is the email type, %2 is the invoice recipient.
220
					__( 'Failed sending %1$s notification email to %2$s.', 'invoicing' ),
221
					sanitize_key( $type ),
222
					$email->is_admin_email() ? __( 'admin', 'invoicing' ) : __( 'the customer', 'invoicing' )
223
				)
224
			);
225
		}
226
227
		do_action( 'getpaid_after_send_invoice_notification', $type, $invoice, $email );
228
229
		return $result;
230
	}
231
232
	/**
233
	 * Also send emails to any cc users.
234
	 *
235
	 * @param array $recipients
236
	 * @param GetPaid_Notification_Email $email
237
	 */
238
	public function filter_email_recipients( $recipients, $email ) {
239
240
		if ( ! $email->is_admin_email() ) {
241
			$cc   = $email->object->get_email_cc();
0 ignored issues
show
Bug introduced by
The method get_email_cc() does not exist on WPInv_Subscription. ( Ignorable by Annotation )

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

241
			/** @scrutinizer ignore-call */ 
242
   $cc   = $email->object->get_email_cc();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method get_email_cc() does not exist on WPInv_Item. ( Ignorable by Annotation )

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

241
			/** @scrutinizer ignore-call */ 
242
   $cc   = $email->object->get_email_cc();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
242
			$cc_2 = get_user_meta( $email->object->get_user_id(), '_wpinv_email_cc', true );
0 ignored issues
show
Bug introduced by
The method get_user_id() does not exist on WPInv_Item. ( Ignorable by Annotation )

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

242
			$cc_2 = get_user_meta( $email->object->/** @scrutinizer ignore-call */ get_user_id(), '_wpinv_email_cc', true );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method get_user_id() does not exist on WPInv_Subscription. Did you maybe mean get_customer_id()? ( Ignorable by Annotation )

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

242
			$cc_2 = get_user_meta( $email->object->/** @scrutinizer ignore-call */ get_user_id(), '_wpinv_email_cc', true );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
243
244
			if ( ! empty( $cc ) ) {
245
				$cc = array_map( 'sanitize_email', wpinv_parse_list( $cc ) );
246
				$recipients = array_filter( array_unique( array_merge( $recipients, $cc ) ) );
247
			}
248
249
			if ( ! empty( $cc_2 ) ) {
250
				$cc_2 = array_map( 'sanitize_email', wpinv_parse_list( $cc_2 ) );
251
				$recipients = array_filter( array_unique( array_merge( $recipients, $cc_2 ) ) );
252
			}
253
		}
254
255
		return $recipients;
256
257
	}
258
259
	/**
260
	 * Sends a new invoice notification.
261
	 *
262
	 * @param WPInv_Invoice $invoice
263
	 */
264
	public function new_invoice( $invoice ) {
265
266
		// Only send this email for invoices created via the admin page.
267
		if ( ! $invoice->is_type( 'invoice' ) || $invoice->is_paid() || $this->is_payment_form_invoice( $invoice->get_id() ) ) {
268
			return;
269
		}
270
271
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
272
		$recipient = wpinv_get_admin_email();
273
274
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
0 ignored issues
show
Bug introduced by
It seems like $recipient can also be of type false; however, parameter $recipients of GetPaid_Invoice_Notification_Emails::send_email() does only seem to accept array|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

274
		return $this->send_email( $invoice, $email, __FUNCTION__, /** @scrutinizer ignore-type */ $recipient );
Loading history...
275
276
	}
277
278
	/**
279
	 * Sends a cancelled invoice notification.
280
	 *
281
	 * @param WPInv_Invoice $invoice
282
	 */
283
	public function cancelled_invoice( $invoice ) {
284
285
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
286
		$recipient = $invoice->get_email();
287
288
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
289
	}
290
291
	/**
292
	 * Sends a failed invoice notification.
293
	 *
294
	 * @param WPInv_Invoice $invoice
295
	 */
296
	public function failed_invoice( $invoice ) {
297
298
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
299
		$recipient = wpinv_get_admin_email();
300
301
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
0 ignored issues
show
Bug introduced by
It seems like $recipient can also be of type false; however, parameter $recipients of GetPaid_Invoice_Notification_Emails::send_email() does only seem to accept array|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

301
		return $this->send_email( $invoice, $email, __FUNCTION__, /** @scrutinizer ignore-type */ $recipient );
Loading history...
302
303
	}
304
305
	/**
306
	 * Sends a notification whenever an invoice is put on hold.
307
	 *
308
	 * @param WPInv_Invoice $invoice
309
	 */
310
	public function onhold_invoice( $invoice ) {
311
312
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
313
		$recipient = $invoice->get_email();
314
315
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
316
317
	}
318
319
	/**
320
	 * Sends a notification whenever an invoice is marked as processing payment.
321
	 *
322
	 * @param WPInv_Invoice $invoice
323
	 */
324
	public function processing_invoice( $invoice ) {
325
326
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
327
		$recipient = $invoice->get_email();
328
329
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
330
331
	}
332
333
	/**
334
	 * Sends a notification whenever an invoice is paid.
335
	 *
336
	 * @param WPInv_Invoice $invoice
337
	 */
338
	public function completed_invoice( $invoice ) {
339
340
		// (Maybe) abort if it is a renewal invoice.
341
		if ( $invoice->is_renewal() && ! wpinv_get_option( 'email_completed_invoice_renewal_active', false ) ) {
342
			return;
343
		}
344
345
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
346
		$recipient = $invoice->get_email();
347
348
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
349
350
	}
351
352
	/**
353
	 * Sends a notification whenever an invoice is refunded.
354
	 *
355
	 * @param WPInv_Invoice $invoice
356
	 */
357
	public function refunded_invoice( $invoice ) {
358
359
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
360
		$recipient = $invoice->get_email();
361
362
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
363
364
	}
365
366
	/**
367
	 * Notifies a user about new invoices
368
	 *
369
	 * @param WPInv_Invoice $invoice
370
	 * @param bool $force
371
	 */
372
	public function user_invoice( $invoice, $force = false ) {
373
374
		if ( ! $force && ! empty( $GLOBALS['wpinv_skip_invoice_notification'] ) ) {
375
			return;
376
		}
377
378
		// Only send this email for invoices created via the admin page.
379
		if ( ! $invoice->is_type( 'invoice' ) || ( empty( $force ) && $invoice->is_paid() ) || ( empty( $force ) && $this->is_payment_form_invoice( $invoice->get_id() ) ) ) {
380
			return;
381
		}
382
383
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
384
		$recipient = $invoice->get_email();
385
386
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
387
388
	}
389
390
	/**
391
	 * Checks if an invoice is a payment form invoice.
392
	 *
393
	 * @param int $invoice
394
	 * @return bool
395
	 */
396
	public function is_payment_form_invoice( $invoice ) {
397
		$created_via             = get_post_meta( $invoice, 'wpinv_created_via', true );
398
		$is_payment_form_invoice = 'payment_form' === $created_via || 'geodirectory' === $created_via;
399
		$is_payment_form_invoice = apply_filters( 'getpaid_invoice_notifications_is_payment_form_invoice', $is_payment_form_invoice, $invoice );
400
		return empty( $_GET['getpaid-admin-action'] ) && $is_payment_form_invoice;
401
	}
402
403
	/**
404
	 * Notifies admin about new invoice notes
405
	 *
406
	 * @param WPInv_Invoice $invoice
407
	 * @param string $note
408
	 */
409
	public function user_note( $invoice, $note ) {
410
411
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
412
		$recipient = $invoice->get_email();
413
414
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient, array( 'customer_note' => $note ) );
415
416
	}
417
418
	/**
419
	 * (Force) Sends overdue notices.
420
	 *
421
	 * @param WPInv_Invoice $invoice
422
	 */
423
	public function force_send_overdue_notice( $invoice ) {
424
		$email = new GetPaid_Notification_Email( 'overdue', $invoice );
425
		return $this->send_email( $invoice, $email, 'overdue', $invoice->get_email() );
426
	}
427
428
	/**
429
	 * Sends overdue notices.
430
	 *
431
	 * @TODO: Create an invoices query class.
432
	 */
433
	public function overdue() {
434
		global $wpdb;
435
436
		$email = new GetPaid_Notification_Email( __FUNCTION__ );
437
438
		// Fetch reminder days.
439
		$reminder_days = array_unique( wp_parse_id_list( $email->get_option( 'days' ) ) );
440
441
		// Abort if non is set.
442
		if ( empty( $reminder_days ) ) {
443
			return;
444
		}
445
446
		// Retrieve date query.
447
		$date_query = $this->get_date_query( $reminder_days );
448
449
		// Invoices table.
450
		$table = $wpdb->prefix . 'getpaid_invoices';
451
452
		// Fetch invoices.
453
		$invoices  = $wpdb->get_col(
454
			"SELECT posts.ID FROM $wpdb->posts as posts
455
			LEFT JOIN $table as invoices ON invoices.post_id = posts.ID
456
			WHERE posts.post_type = 'wpi_invoice' AND posts.post_status = 'wpi-pending' $date_query"
457
        );
458
459
		foreach ( $invoices as $invoice ) {
460
461
			// Only send this email for invoices created via the admin page.
462
			if ( ! $this->is_payment_form_invoice( $invoice ) ) {
463
				$invoice       = new WPInv_Invoice( $invoice );
464
				$email->object = $invoice;
465
466
				if ( $invoice->needs_payment() && ! $invoice->is_renewal() ) {
467
					$this->send_email( $invoice, $email, __FUNCTION__, $invoice->get_email() );
468
				}
469
			}
470
		}
471
472
	}
473
474
	/**
475
	 * Calculates the date query for an invoices query
476
	 *
477
	 * @param array $reminder_days
478
	 * @return string
479
	 */
480
	public function get_date_query( $reminder_days ) {
481
482
		$date_query = array(
483
			'relation' => 'OR',
484
		);
485
486
		foreach ( $reminder_days as $days ) {
487
			$date = date_parse( date( 'Y-m-d', strtotime( "-$days days", current_time( 'timestamp' ) ) ) );
488
489
			$date_query[] = array(
490
				'year'  => $date['year'],
491
				'month' => $date['month'],
492
				'day'   => $date['day'],
493
			);
494
495
		}
496
497
		$date_query = new WP_Date_Query( $date_query, 'invoices.due_date' );
498
499
		return $date_query->get_sql();
500
501
	}
502
503
}
504