Passed
Push — master ( 6e4659...757676 )
by Brian
05:20
created

cancelled_invoice()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 6
rs 10
cc 1
nc 1
nop 1
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
		$this->init_hooks();
45
46
	}
47
48
	/**
49
	 * Registers email hooks.
50
	 */
51
	public function init_hooks() {
52
53
		add_filter( 'getpaid_get_email_merge_tags', array( $this, 'invoice_merge_tags' ), 10, 2 );
54
		add_filter( 'getpaid_invoice_email_recipients', array( $this, 'filter_email_recipients' ), 10, 2 );
55
56
		foreach ( $this->invoice_actions as $hook => $email_type ) {
57
			$this->init_email_type_hook( $hook, $email_type );
58
		}
59
	}
60
61
	/**
62
	 * Registers an email hook for an invoice action.
63
	 * 
64
	 * @param string $hook
65
	 * @param string|array $email_type
66
	 */
67
	public function init_email_type_hook( $hook, $email_type ) {
68
69
		$email_type = wpinv_parse_list( $email_type );
70
71
		foreach ( $email_type as $type ) {
72
73
			$email = new GetPaid_Notification_Email( $type );
74
75
			// Abort if it is not active.
76
			if ( ! $email->is_active() ) {
77
				continue;
78
			}
79
80
			if ( method_exists( $this, $type ) ) {
81
				add_action( $hook, array( $this, $type ), 100, 2 );
82
				continue;
83
			}
84
85
			do_action( 'getpaid_invoice_init_email_type_hook', $type, $hook );
86
		}
87
88
	}
89
90
	/**
91
	 * Filters invoice merge tags.
92
	 *
93
	 * @param array $merge_tags
94
	 * @param mixed|WPInv_Invoice|WPInv_Subscription $object
95
	 */
96
	public function invoice_merge_tags( $merge_tags, $object ) {
97
98
		if ( is_a( $object, 'WPInv_Invoice' ) ) {
99
			return array_merge(
100
				$merge_tags,
101
				$this->get_invoice_merge_tags( $object )
102
			);
103
		}
104
105
		if ( is_a( $object, 'WPInv_Subscription' ) ) {
106
			return array_merge(
107
				$merge_tags,
108
				$this->get_invoice_merge_tags( $object->get_parent_payment() )
109
			);
110
		}
111
112
		return $merge_tags;
113
114
	}
115
116
	/**
117
	 * Generates invoice merge tags.
118
	 *
119
	 * @param WPInv_Invoice $invoice
120
	 * @return array
121
	 */
122
	public function get_invoice_merge_tags( $invoice ) {
123
124
		// Abort if it does not exist.
125
		if ( ! $invoice->get_id() ) {
126
			return array();
127
		}
128
129
		$merge_tags = array(
130
			'{name}'                => sanitize_text_field( $invoice->get_user_full_name() ),
131
			'{full_name}'           => sanitize_text_field( $invoice->get_user_full_name() ),
132
			'{first_name}'          => sanitize_text_field( $invoice->get_first_name() ),
133
			'{last_name}'           => sanitize_text_field( $invoice->get_last_name() ),
134
			'{email}'               => sanitize_email( $invoice->get_email() ),
135
			'{invoice_number}'      => sanitize_text_field( $invoice->get_number() ),
136
			'{invoice_currency}'    => sanitize_text_field( $invoice->get_currency() ),
137
			'{invoice_total}'       => sanitize_text_field( wpinv_price( $invoice->get_total(), $invoice->get_currency() ) ),
138
			'{invoice_link}'        => esc_url( $invoice->get_view_url() ),
139
			'{invoice_pay_link}'    => esc_url( $invoice->get_checkout_payment_url() ),
140
			'{invoice_receipt_link}'=> esc_url( $invoice->get_receipt_url() ),
141
			'{invoice_date}'        => getpaid_format_date_value( $invoice->get_date_created() ),
142
			'{invoice_due_date}'    => getpaid_format_date_value( $invoice->get_due_date(), __( 'on receipt', 'invoicing' ) ),
143
			'{invoice_quote}'       => sanitize_text_field( strtolower( $invoice->get_label() ) ),
144
			'{invoice_label}'       => sanitize_text_field( ucfirst( $invoice->get_label() ) ),
145
			'{invoice_description}' => wp_kses_post( $invoice->get_description() ),
146
			'{subscription_name}'   => wp_kses_post( $invoice->get_subscription_name() ),
147
			'{is_was}'              => strtotime( $invoice->get_due_date() ) < current_time( 'timestamp' ) ? __( 'was', 'invoicing' ) : __( 'is', 'invoicing' ),
148
		);
149
150
		$payment_form_data = $invoice->get_meta( 'payment_form_data', true );
151
152
		if ( is_array( $payment_form_data ) ) {
153
154
			foreach ( $payment_form_data as $label => $value ) {
155
156
				$label = preg_replace( '/[^a-z0-9]+/', '_', strtolower( $label ) );
157
				$value = is_array( $value ) ? implode( ', ', $value ) : $value;
158
159
				if ( is_scalar ( $value ) ) {
160
					$merge_tags[ "{{$label}}" ] = wp_kses_post( $value );
161
				}
162
163
			}
164
165
		}
166
167
		return apply_filters( 'getpaid_invoice_email_merge_tags', $merge_tags, $invoice );
168
	}
169
170
	/**
171
	 * Helper function to send an email.
172
	 *
173
	 * @param WPInv_Invoice $invoice
174
	 * @param GetPaid_Notification_Email $email
175
	 * @param string $type
176
	 * @param string|array $recipients
177
	 * @param array $extra_args Extra template args.
178
	 */
179
	public function send_email( $invoice, $email, $type, $recipients, $extra_args = array() ) {
180
181
		do_action( 'getpaid_before_send_invoice_notification', $type, $invoice, $email );
182
183
		$skip = $invoice->is_free() && wpinv_get_option( 'skip_email_free_invoice' );
184
		if ( apply_filters( 'getpaid_skip_invoice_email', $skip, $type, $invoice ) ) {
185
			return;
186
		}
187
188
		$mailer     = new GetPaid_Notification_Email_Sender();
189
		$merge_tags = $email->get_merge_tags();
190
191
		$result = $mailer->send(
192
			apply_filters( 'getpaid_invoice_email_recipients', wpinv_parse_list( $recipients ), $email ),
193
			$email->add_merge_tags( $email->get_subject(), $merge_tags ),
194
			$email->get_content( $merge_tags, $extra_args ),
195
			$email->get_attachments()
196
		);
197
198
		// Maybe send a copy to the admin.
199
		if ( $email->include_admin_bcc() ) {
200
			$mailer->send(
201
				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

201
				/** @scrutinizer ignore-type */ wpinv_get_admin_email(),
Loading history...
202
				$email->add_merge_tags( $email->get_subject() . __( ' - ADMIN BCC COPY', 'invoicing' ), $merge_tags ),
203
				$email->get_content( $merge_tags ),
204
				$email->get_attachments()
205
			);
206
		}
207
208
		if ( $result ) {
209
			$invoice->add_note( sprintf( __( 'Successfully sent %s notification email.', 'invoicing' ), sanitize_key( $type ) ), false, false, true );
210
		} else {
211
			$invoice->add_note( sprintf( __( 'Failed sending %s notification email.', 'invoicing' ), sanitize_key( $type ) ), false, false, true );	
212
		}
213
214
		do_action( 'getpaid_after_send_invoice_notification', $type, $invoice, $email );
215
216
		return $result;
217
	}
218
219
	/**
220
	 * Also send emails to any cc users.
221
	 *
222
	 * @param array $recipients
223
	 * @param GetPaid_Notification_Email $email
224
	 */
225
	public function filter_email_recipients( $recipients, $email ) {
226
227
		if ( ! $email->is_admin_email() ) {
228
			$cc = $email->object->get_email_cc();
0 ignored issues
show
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

228
			/** @scrutinizer ignore-call */ 
229
   $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_Subscription. ( Ignorable by Annotation )

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

228
			/** @scrutinizer ignore-call */ 
229
   $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...
229
230
			if ( ! empty( $cc ) ) {
231
				$cc = array_map( 'sanitize_email', wpinv_parse_list( $cc ) );
232
				$recipients = array_filter( array_unique( array_merge( $recipients, $cc ) ) );
233
			}
234
235
		}
236
237
		return $recipients;
238
239
	}
240
241
	/**
242
	 * Sends a new invoice notification.
243
	 *
244
	 * @param WPInv_Invoice $invoice
245
	 */
246
	public function new_invoice( $invoice ) {
247
248
		// Only send this email for invoices created via the admin page.
249
		if ( ! $invoice->is_type( 'invoice' ) || $this->is_payment_form_invoice( $invoice->get_id() ) ) {
250
			return;
251
		}
252
253
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
254
		$recipient = wpinv_get_admin_email();
255
256
		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

256
		return $this->send_email( $invoice, $email, __FUNCTION__, /** @scrutinizer ignore-type */ $recipient );
Loading history...
257
258
	}
259
260
	/**
261
	 * Sends a cancelled invoice notification.
262
	 *
263
	 * @param WPInv_Invoice $invoice
264
	 */
265
	public function cancelled_invoice( $invoice ) {
266
267
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
268
		$recipient = wpinv_get_admin_email();
269
270
		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

270
		return $this->send_email( $invoice, $email, __FUNCTION__, /** @scrutinizer ignore-type */ $recipient );
Loading history...
271
272
	}
273
274
	/**
275
	 * Sends a failed invoice notification.
276
	 *
277
	 * @param WPInv_Invoice $invoice
278
	 */
279
	public function failed_invoice( $invoice ) {
280
281
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
282
		$recipient = wpinv_get_admin_email();
283
284
		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

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