Completed
Pull Request — master (#1619)
by Ravinder
17:48
created

Give_Customer::update_donation_value()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 2
dl 0
loc 23
ccs 0
cts 0
cp 0
crap 12
rs 9.0856
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 24 and the first side effect is on line 14.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * Customer (Donor)
4
 *
5
 * @package     Give
6
 * @subpackage  Classes/Give_Customer
7
 * @copyright   Copyright (c) 2016, WordImpress
8
 * @license     https://opensource.org/licenses/gpl-license GNU Public License
9
 * @since       1.0
10
 */
11
12
// Exit if accessed directly.
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
/**
18
 * Give_Customer Class
19
 *
20
 * This class handles customers.
21
 *
22
 * @since 1.0
23
 */
24
class Give_Customer {
25
26
	/**
27
	 * The customer ID
28
	 *
29
	 * @since  1.0
30
	 * @access public
31
	 *
32
	 * @var    int
33
	 */
34
	public $id = 0;
35
36
	/**
37
	 * The customer's donation count
38
	 *
39
	 * @since  1.0
40
	 * @access public
41
	 *
42
	 * @var    int
43
	 */
44
	public $purchase_count = 0;
45
46
	/**
47
	 * The customer's lifetime value
48
	 *
49
	 * @since  1.0
50
	 * @access public
51
	 *
52
	 * @var    int
53
	 */
54
	public $purchase_value = 0;
55
56
	/**
57
	 * The customer's email
58
	 *
59
	 * @since  1.0
60
	 * @access public
61
	 *
62
	 * @var    string
63
	 */
64
	public $email;
65
66
	/**
67
	 * The customer's emails
68
	 *
69
	 * @since  1.7
70
	 * @access public
71
	 *
72
	 * @var    array
73
	 */
74
	public $emails;
75
76
	/**
77
	 * The customer's name
78
	 *
79
	 * @since  1.0
80
	 * @access public
81
	 *
82
	 * @var    string
83
	 */
84
	public $name;
85
86
	/**
87
	 * The customer's creation date
88
	 *
89
	 * @since  1.0
90
	 * @access public
91
	 *
92
	 * @var    string
93
	 */
94
	public $date_created;
95
96
	/**
97
	 * The payment IDs associated with the customer
98
	 *
99
	 * @since  1.0
100 52
	 * @access public
101
	 *
102 52
	 * @var    array
103
	 */
104 52
	public $payment_ids;
105
106
	/**
107
	 * The user ID associated with the customer
108 52
	 *
109
	 * @since  1.0
110 52
	 * @access public
111 52
	 *
112 52
	 * @var    int
113 52
	 */
114
	public $user_id;
115
116 52
	/**
117
	 * Customer Notes
118 52
	 *
119 52
	 * @since  1.0
120
	 * @access public
121
	 *
122 52
	 * @var    string
123
	 */
124 52
	public $notes;
125
126
	/**
127
	 * The Database Abstraction
128
	 *
129
	 * @since  1.0
130
	 * @access protected
131
	 *
132
	 * @var    Give_DB_Customers
133
	 */
134
	protected $db;
135 52
136
	/**
137 52
	 * Class Constructor
138
	 *
139
	 * Set up the Give Customer Class.
140
	 *
141 52
	 * @since  1.0
142
	 * @access public
143
	 *
144
	 * @param  bool $_id_or_email
145 52
	 * @param  bool $by_user_id
146 52
	 */
147 52
	public function __construct( $_id_or_email = false, $by_user_id = false ) {
148
149 52
		$this->db = new Give_DB_Customers;
150 52
151 52
		if ( false === $_id_or_email || ( is_numeric( $_id_or_email ) && (int) $_id_or_email !== absint( $_id_or_email ) ) ) {
152
			return false;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
153 52
		}
154
155 52
		$by_user_id = is_bool( $by_user_id ) ? $by_user_id : false;
156
157
		if ( is_numeric( $_id_or_email ) ) {
158 52
			$field = $by_user_id ? 'user_id' : 'id';
159 52
		} else {
160
			$field = 'email';
161
		}
162
163
		$customer = $this->db->get_customer_by( $field, $_id_or_email );
164
165
		if ( empty( $customer ) || ! is_object( $customer ) ) {
166
			return false;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
167
		}
168
169
		$this->setup_customer( $customer );
170
171 1
	}
172
173 1
	/**
174
	 * Setup Customer
175
	 *
176
	 * Given the customer data, let's set the variables.
177
	 *
178
	 * @since  1.0
179
	 * @access private
180 1
	 *
181
	 * @param  object $customer The Customer Object.
182
	 *
183
	 * @return bool             If the setup was successful or not.
184
	 */
185
	private function setup_customer( $customer ) {
186
187
		if ( ! is_object( $customer ) ) {
188
			return false;
189
		}
190
191
		foreach ( $customer as $key => $value ) {
192
193
			switch ( $key ) {
194
195 53
				case 'notes':
196
					$this->$key = $this->get_notes();
197 53
					break;
198
199
				default:
200
					$this->$key = $value;
201
					break;
202
203 53
			}
204
		}
205 53
206 53
		// Get donor's all email including primary email.
207
		$this->emails   = (array) $this->get_meta( 'additional_email', false );
208 53
		$this->emails = array( 'primary' => $this->email ) + $this->emails;
209 1
210
		// Customer ID and email are the only things that are necessary, make sure they exist.
211
		if ( ! empty( $this->id ) && ! empty( $this->email ) ) {
212 53
			return true;
213
		}
214
215
		return false;
216 53
217
	}
218 53
219
	/**
220
	 * Magic __get function to dispatch a call to retrieve a private property.
221 53
	 *
222
	 * @since  1.0
223
	 * @access public
224 53
	 */
225
	public function __get( $key ) {
226
227 53
		if ( method_exists( $this, 'get_' . $key ) ) {
228
229 53
			return call_user_func( array( $this, 'get_' . $key ) );
230 53
231
		} else {
232 53
233
			/* translators: %s: property key */
234 53
			return new WP_Error( 'give-customer-invalid-property', sprintf( esc_html__( 'Can\'t get property %s.', 'give' ), $key ) );
235
236
		}
237
238
	}
239
240
	/**
241
	 * Creates a customer
242
	 *
243
	 * @since  1.0
244
	 * @access public
245
	 *
246
	 * @param  array $data Array of attributes for a customer.
247 53
	 *
248
	 * @return bool|int    False if not a valid creation, customer ID if user is found or valid creation.
249 53
	 */
250 1
	public function create( $data = array() ) {
251
252
		if ( $this->id != 0 || empty( $data ) ) {
253 53
			return false;
254
		}
255 53
256
		$defaults = array(
257 53
			'payment_ids' => '',
258
		);
259 53
260
		$args = wp_parse_args( $data, $defaults );
261 53
		$args = $this->sanitize_columns( $args );
262 53
263
		if ( empty( $args['email'] ) || ! is_email( $args['email'] ) ) {
264 53
			return false;
265 53
		}
266
267 53
		if ( ! empty( $args['payment_ids'] ) && is_array( $args['payment_ids'] ) ) {
268
			$args['payment_ids'] = implode( ',', array_unique( array_values( $args['payment_ids'] ) ) );
269 53
		}
270
271
		/**
272
		 * Fires before creating customers.
273
		 *
274
		 * @since 1.0
275
		 *
276
		 * @param array $args Customer attributes.
277
		 */
278
		do_action( 'give_customer_pre_create', $args );
279
280
		$created = false;
281
282
		// The DB class 'add' implies an update if the customer being asked to be created already exists
283 52
		if ( $this->db->add( $data ) ) {
284
285 52
			// We've successfully added/updated the customer, reset the class vars with the new data
286 1
			$customer = $this->db->get_customer_by( 'email', $args['email'] );
287
288
			// Setup the customer data with the values from DB
289 52
			$this->setup_customer( $customer );
290
291 52
			$created = $this->id;
292
		}
293 52
294
		/**
295 7
		 * Fires after creating customers.
296
		 *
297 7
		 * @since 1.0
298 1
		 *
299 1
		 * @param bool|int $created False if not a valid creation,
300
		 *                          customer ID if user is found or valid creation.
301 7
		 * @param array    $args    Customer attributes.
302
		 */
303 7
		do_action( 'give_customer_post_create', $created, $args );
304
305
		return $created;
306
307 52
	}
308
309 52
	/**
310
	 * Update a customer record
311 52
	 *
312
	 * @since  1.0
313 52
	 * @access public
314
	 *
315
	 * @param  array $data Array of data attributes for a customer (checked via whitelist).
316 52
	 *
317 1
	 * @return bool        If the update was successful or not.
318
	 */
319 1
	public function update( $data = array() ) {
320
321
		if ( empty( $data ) ) {
322
			return false;
323 1
		}
324 1
325
		$data = $this->sanitize_columns( $data );
326 52
327
		/**
328 52
		 * Fires before updating customers.
329
		 *
330 52
		 * @since 1.0
331
		 *
332
		 * @param int   $customer_id Customer id.
333
		 * @param array $data        Customer attributes.
334
		 */
335
		do_action( 'give_customer_pre_update', $this->id, $data );
336
337
		$updated = false;
338
339
		if ( $this->db->update( $this->id, $data ) ) {
340
341
			$customer = $this->db->get_customer_by( 'id', $this->id );
342
			$this->setup_customer( $customer );
343
344 3
			$updated = true;
345
		}
346 3
347
		/**
348
		 * Fires after updating customers.
349
		 *
350 3
		 * @since 1.0
351
		 *
352 3
		 * @param bool  $updated     If the update was successful or not.
353 3
		 * @param int   $customer_id Customer id.
354 3
		 * @param array $data        Customer attributes.
355
		 */
356 3
		do_action( 'give_customer_post_update', $updated, $this->id, $data );
357
358 3
		return $updated;
359
	}
360 3
361
	/**
362 3
	 * Attach Payment
363 3
	 *
364
	 * Attach payment to the customer then triggers increasing stats.
365
	 *
366
	 * @since  1.0
367 3
	 * @access public
368 3
	 *
369
	 * @param  int  $payment_id   The payment ID to attach to the customer.
370 3
	 * @param  bool $update_stats For backwards compatibility, if we should increase the stats or not.
371
	 *
372 3
	 * @return bool            If the attachment was successfuly.
373
	 */
374 3
	public function attach_payment( $payment_id = 0, $update_stats = true ) {
375
376 3
		if ( empty( $payment_id ) ) {
377
			return false;
378 3
		}
379
380 3
		if ( empty( $this->payment_ids ) ) {
381
382 3
			$new_payment_ids = $payment_id;
383
384
		} else {
385
386
			$payment_ids = array_map( 'absint', explode( ',', $this->payment_ids ) );
387
388
			if ( in_array( $payment_id, $payment_ids ) ) {
389
				$update_stats = false;
390
			}
391
392
			$payment_ids[] = $payment_id;
393 3
394
			$new_payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
395 3
396
		}
397 3
398
		/**
399
		 * Fires before attaching payments to customers.
400
		 *
401
		 * @since 1.0
402
		 *
403
		 * @param int $payment_id  Payment id.
404
		 * @param int $customer_id Customer id.
405
		 */
406
		do_action( 'give_customer_pre_attach_payment', $payment_id, $this->id );
407
408
		$payment_added = $this->update( array( 'payment_ids' => $new_payment_ids ) );
409
410 42
		if ( $payment_added ) {
411
412
			$this->payment_ids = $new_payment_ids;
413 42
414 1
			// We added this payment successfully, increment the stats
415
			if ( $update_stats ) {
416
				$payment_amount = give_get_payment_amount( $payment_id );
417 42
418
				if ( ! empty( $payment_amount ) ) {
419 42
					$this->increase_value( $payment_amount );
420
				}
421 42
422 41
				$this->increase_purchase_count();
423 41
			}
424
		}
425 42
426
		/**
427 42
		 * Fires after attaching payments to customers.
428
		 *
429
		 * @since 1.0
430
		 *
431
		 * @param bool $payment_added If the attachment was successfuly.
432
		 * @param int  $payment_id    Payment id.
433
		 * @param int  $customer_id   Customer id.
434
		 */
435
		do_action( 'give_customer_post_attach_payment', $payment_added, $payment_id, $this->id );
436
437
		return $payment_added;
438
	}
439 6
440
	/**
441
	 * Remove Payment
442 6
	 *
443 1
	 * Remove a payment from this customer, then triggers reducing stats.
444
	 *
445
	 * @since  1.0
446 6
	 * @access public
447
	 *
448 6
	 * @param  int  $payment_id   The Payment ID to remove.
449 1
	 * @param  bool $update_stats For backwards compatibility, if we should increase the stats or not.
450 1
	 *
451
	 * @return boolean               If the removal was successful.
452 6
	 */
453
	public function remove_payment( $payment_id = 0, $update_stats = true ) {
454 6
455 6
		if ( empty( $payment_id ) ) {
456 6
			return false;
457
		}
458 6
459
		$payment = new Give_Payment( $payment_id );
460 6
461
		if ( 'publish' !== $payment->status && 'revoked' !== $payment->status ) {
462
			$update_stats = false;
463
		}
464
465
		$new_payment_ids = '';
466
467
		if ( ! empty( $this->payment_ids ) ) {
468
469
			$payment_ids = array_map( 'absint', explode( ',', $this->payment_ids ) );
470
471
			$pos = array_search( $payment_id, $payment_ids );
472 42
			if ( false === $pos ) {
473
				return false;
474 42
			}
475
476 42
			unset( $payment_ids[ $pos ] );
477
			$payment_ids = array_filter( $payment_ids );
478 42
479 41
			$new_payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
480 41
481
		}
482 42
483
		/**
484 42
		 * Fires before removing payments from customers.
485
		 *
486
		 * @since 1.0
487
		 *
488
		 * @param int $payment_id  Payment id.
489
		 * @param int $customer_id Customer id.
490
		 */
491
		do_action( 'give_customer_pre_remove_payment', $payment_id, $this->id );
492
493
		$payment_removed = $this->update( array( 'payment_ids' => $new_payment_ids ) );
494
495
		if ( $payment_removed ) {
496 7
497
			$this->payment_ids = $new_payment_ids;
498 7
499
			if ( $update_stats ) {
500 7
				// We removed this payment successfully, decrement the stats
501 2
				$payment_amount = give_get_payment_amount( $payment_id );
502 2
503
				if ( ! empty( $payment_amount ) ) {
504 7
					$this->decrease_value( $payment_amount );
505
				}
506 7
507 6
				$this->decrease_purchase_count();
508 6
			}
509
		}
510 7
511
		/**
512 7
		 * Fires after removing payments from customers.
513
		 *
514
		 * @since 1.0
515
		 *
516
		 * @param bool $payment_removed If the removal was successfuly.
517
		 * @param int  $payment_id      Payment id.
518
		 * @param int  $customer_id     Customer id.
519
		 */
520
		do_action( 'give_customer_post_remove_payment', $payment_removed, $payment_id, $this->id );
521
522
		return $payment_removed;
523
524
	}
525 52
526
	/**
527 52
	 * Increase the donation count of a customer.
528 52
	 *
529
	 * @since  1.0
530 52
	 * @access public
531 52
	 *
532
	 * @param  int $count The number to increase by.
533 52
	 *
534
	 * @return int        The donation count.
0 ignored issues
show
Documentation introduced by
Should the return type not be false|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
535 52
	 */
536
	public function increase_purchase_count( $count = 1 ) {
537
538
		// Make sure it's numeric and not negative.
539
		if ( ! is_numeric( $count ) || $count != absint( $count ) ) {
540
			return false;
541
		}
542
543
		$new_total = (int) $this->purchase_count + (int) $count;
544
545 1
		/**
546
		 * Fires before increasing customer donation count.
547 1
		 *
548 1
		 * @since 1.0
549
		 *
550 1
		 * @param int $count       The number to increase by.
551
		 * @param int $customer_id Customer id.
552
		 */
553
		do_action( 'give_customer_pre_increase_purchase_count', $count, $this->id );
554
555
		if ( $this->update( array( 'purchase_count' => $new_total ) ) ) {
556
			$this->purchase_count = $new_total;
557
		}
558
559
		/**
560
		 * Fires after increasing customer donation count.
561
		 *
562
		 * @since 1.0
563 1
		 *
564
		 * @param int $purchase_count Customer donation count.
565 1
		 * @param int $count          The number increased by.
566 1
		 * @param int $customer_id    Customer id.
567
		 */
568
		do_action( 'give_customer_post_increase_purchase_count', $this->purchase_count, $count, $this->id );
569
570 1
		return $this->purchase_count;
571
	}
572 1
573 1
	/**
574 1
	 * Decrease the customer donation count.
575
	 *
576 1
	 * @since  1.0
577 1
	 * @access public
578 1
	 *
579
	 * @param  int $count The amount to decrease by.
580 1
	 *
581
	 * @return mixed      If successful, the new count, otherwise false.
582 1
	 */
583
	public function decrease_purchase_count( $count = 1 ) {
584 1
585 1
		// Make sure it's numeric and not negative
586 1
		if ( ! is_numeric( $count ) || $count != absint( $count ) ) {
587
			return false;
588 1
		}
589
590
		$new_total = (int) $this->purchase_count - (int) $count;
591 1
592
		if ( $new_total < 0 ) {
593
			$new_total = 0;
594
		}
595
596
		/**
597
		 * Fires before decreasing customer donation count.
598
		 *
599
		 * @since 1.0
600
		 *
601 52
		 * @param int $count       The number to decrease by.
602
		 * @param int $customer_id Customer id.
603 52
		 */
604
		do_action( 'give_customer_pre_decrease_purchase_count', $count, $this->id );
605 52
606
		if ( $this->update( array( 'purchase_count' => $new_total ) ) ) {
607
			$this->purchase_count = $new_total;
608
		}
609
610
		/**
611
		 * Fires after decreasing customer donation count.
612
		 *
613
		 * @since 1.0
614
		 *
615
		 * @param int $purchase_count Customer donation count.
616
		 * @param int $count          The number decreased by.
617
		 * @param int $customer_id    Customer id.
618 52
		 */
619
		do_action( 'give_customer_post_decrease_purchase_count', $this->purchase_count, $count, $this->id );
620 52
621 52
		return $this->purchase_count;
622
	}
623 52
624
	/**
625
	 * Increase the customer's lifetime value.
626 52
	 *
627 52
	 * @since  1.0
628
	 * @access public
629
	 *
630
	 * @param  float $value The value to increase by.
631
	 *
632 52
	 * @return mixed        If successful, the new value, otherwise false.
633 52
	 */
634 52
	public function increase_value( $value = 0.00 ) {
635 52
636 1
		$new_value = floatval( $this->purchase_value ) + $value;
637 1
638 52
		/**
639
		 * Fires before increasing customer lifetime value.
640 52
		 *
641
		 * @since 1.0
642 52
		 *
643 52
		 * @param float $value       The value to increase by.
644
		 * @param int   $customer_id Customer id.
645
		 */
646 52
		do_action( 'give_customer_pre_increase_value', $value, $this->id );
647
648 52
		if ( $this->update( array( 'purchase_value' => $new_value ) ) ) {
649
			$this->purchase_value = $new_value;
650 42
		}
651
652 42
		/**
653
		 * Fires after increasing customer lifetime value.
654 42
		 *
655
		 * @since 1.0
656
		 *
657 42
		 * @param float $purchase_value Customer lifetime value.
658
		 * @param float $value          The value increased by.
659 42
		 * @param int   $customer_id    Customer id.
660
		 */
661
		do_action( 'give_customer_post_increase_value', $this->purchase_value, $value, $this->id );
662
663
		return $this->purchase_value;
664
	}
665
666
	/**
667 52
	 * Decrease a customer's lifetime value.
668
	 *
669 52
	 * @since  1.0
670
	 * @access public
671
	 *
672
	 * @param  float $value The value to decrease by.
673
	 *
674
	 * @return mixed        If successful, the new value, otherwise false.
675
	 */
676
	public function decrease_value( $value = 0.00 ) {
677
678
		$new_value = floatval( $this->purchase_value ) - $value;
679
680
		if ( $new_value < 0 ) {
681
			$new_value = 0.00;
682
		}
683
684
		/**
685
		 * Fires before decreaseing customer lifetime value.
686
		 *
687
		 * @since 1.0
688
		 *
689
		 * @param float $value       The value to decrease by.
690
		 * @param int   $customer_id Customer id.
691
		 */
692
		do_action( 'give_customer_pre_decrease_value', $value, $this->id );
693
694
		if ( $this->update( array( 'purchase_value' => $new_value ) ) ) {
695
			$this->purchase_value = $new_value;
696
		}
697
698
		/**
699
		 * Fires after decreaseing customer lifetime value.
700
		 *
701
		 * @since 1.0
702
		 *
703
		 * @param float $purchase_value Customer lifetime value.
704
		 * @param float $value          The value decreased by.
705
		 * @param int   $customer_id    Customer id.
706
		 */
707
		do_action( 'give_customer_post_decrease_value', $this->purchase_value, $value, $this->id );
708
709
		return $this->purchase_value;
710
	}
711
712
	/**
713
	 * Decrease/Increase a customer's lifetime value.
714
	 *
715
	 * This function will update donation stat on basis of current amount and new amount donation difference.
716
	 * Difference value can positive or negative. Negative value will decrease user donation stat while positive value increase donation stat.
717
	 *
718
	 * @since  1.0
719
	 * @access public
720
	 *
721
	 * @param  float $curr_amount Current Donation amount.
722
	 * @param  float $new_amount  New (changed) Donation amount.
723
	 *
724
	 * @return mixed              If successful, the new donation stat value, otherwise false.
725
	 */
726
	public function update_donation_value( $curr_amount, $new_amount ) {
727
		/**
728
		 * Payment total difference value can be:
729
		 *  zero   (in case amount not change)
730
		 *  or -ve (in case amount decrease)
731
		 *  or +ve (in case amount increase)
732
		 */
733
		$payment_total_diff = $new_amount - $curr_amount;
734
735
		// We do not need to update donation stat if donation did not change.
736
		if ( ! $payment_total_diff ) {
737
			return false;
738
		}
739
740
		if ( $payment_total_diff > 0 ) {
741
			$this->increase_value( $payment_total_diff );
742
		} else {
743
			// Pass payment total difference as +ve value to decrease amount from user lifetime stat.
744
			$this->decrease_value( -$payment_total_diff );
745
		}
746
747
		return $this->purchase_value;
748
	}
749
750
	/**
751
	 * Get the parsed notes for a customer as an array.
752
	 *
753
	 * @since  1.0
754
	 * @access public
755
	 *
756
	 * @param  int $length The number of notes to get.
757
	 * @param  int $paged  What note to start at.
758
	 *
759
	 * @return array       The notes requested.
760
	 */
761
	public function get_notes( $length = 20, $paged = 1 ) {
762
763
		$length = is_numeric( $length ) ? $length : 20;
764
		$offset = is_numeric( $paged ) && $paged != 1 ? ( ( absint( $paged ) - 1 ) * $length ) : 0;
765
766
		$all_notes   = $this->get_raw_notes();
767
		$notes_array = array_reverse( array_filter( explode( "\n\n", $all_notes ) ) );
768
769
		$desired_notes = array_slice( $notes_array, $offset, $length );
770
771
		return $desired_notes;
772
773
	}
774
775
	/**
776
	 * Get the total number of notes we have after parsing.
777
	 *
778
	 * @since  1.0
779
	 * @access public
780
	 *
781
	 * @return int The number of notes for the customer.
782
	 */
783
	public function get_notes_count() {
784
785
		$all_notes   = $this->get_raw_notes();
786
		$notes_array = array_reverse( array_filter( explode( "\n\n", $all_notes ) ) );
787
788
		return count( $notes_array );
789
790
	}
791
792
	/**
793
	 * Add a note for the customer.
794
	 *
795
	 * @since  1.0
796
	 * @access public
797
	 *
798
	 * @param  string $note   The note to add. Default is empty.
799
	 *
800
	 * @return string|boolean The new note if added successfully, false otherwise.
801
	 */
802
	public function add_note( $note = '' ) {
803
804
		$note = trim( $note );
805
		if ( empty( $note ) ) {
806
			return false;
807
		}
808
809
		$notes = $this->get_raw_notes();
810
811
		if ( empty( $notes ) ) {
812
			$notes = '';
813
		}
814
815
		$note_string = date_i18n( 'F j, Y H:i:s', current_time( 'timestamp' ) ) . ' - ' . $note;
816
		$new_note    = apply_filters( 'give_customer_add_note_string', $note_string );
817
		$notes .= "\n\n" . $new_note;
818
819
		/**
820
		 * Fires before customer note added.
821
		 *
822
		 * @since 1.0
823
		 *
824
		 * @param string $new_note    New note to add.
825
		 * @param int    $customer_id Customer id.
826
		 */
827
		do_action( 'give_customer_pre_add_note', $new_note, $this->id );
828
829
		$updated = $this->update( array( 'notes' => $notes ) );
830
831
		if ( $updated ) {
832
			$this->notes = $this->get_notes();
833
		}
834
835
		/**
836
		 * Fires after customer note added.
837
		 *
838
		 * @since 1.0
839
		 *
840
		 * @param array  $customer_notes Customer notes.
841
		 * @param string $new_note       New note added.
842
		 * @param int    $customer_id    Customer id.
843
		 */
844
		do_action( 'give_customer_post_add_note', $this->notes, $new_note, $this->id );
845
846
		// Return the formatted note, so we can test, as well as update any displays
847
		return $new_note;
848
849
	}
850
851
	/**
852
	 * Get the notes column for the customer
853
	 *
854
	 * @since  1.0
855
	 * @access private
856
	 *
857
	 * @return string The Notes for the customer, non-parsed.
858
	 */
859
	private function get_raw_notes() {
860
861
		$all_notes = $this->db->get_column( 'notes', $this->id );
862
863
		return $all_notes;
864
865
	}
866
867
	/**
868
	 * Retrieve customer meta field for a customer.
869
	 *
870
	 * @since  1.6
871
	 * @access public
872
	 *
873
	 * @param  string $meta_key The meta key to retrieve. Default is empty.
874
	 * @param  bool   $single   Whether to return a single value. Default is true.
875
	 *
876
	 * @return mixed            Will be an array if $single is false. Will be value of meta data field if $single is true.
877
	 */
878
	public function get_meta( $meta_key = '', $single = true ) {
879
		return Give()->customer_meta->get_meta( $this->id, $meta_key, $single );
880
	}
881
882
	/**
883
	 * Add meta data field to a customer.
884
	 *
885
	 * @since  1.6
886
	 * @access public
887
	 *
888
	 * @param  string $meta_key   Metadata name. Default is empty.
889
	 * @param  mixed  $meta_value Metadata value.
890
	 * @param  bool   $unique     Optional. Whether the same key should not be added. Default is false.
891
	 *
892
	 * @return bool               False for failure. True for success.
893
	 */
894
	public function add_meta( $meta_key = '', $meta_value, $unique = false ) {
895
		return Give()->customer_meta->add_meta( $this->id, $meta_key, $meta_value, $unique );
896
	}
897
898
	/**
899
	 * Update customer meta field based on customer ID.
900
	 *
901
	 * @since  1.6
902
	 * @access public
903
	 *
904
	 * @param  string $meta_key   Metadata key. Default is empty.
905
	 * @param  mixed  $meta_value Metadata value.
906
	 * @param  mixed  $prev_value Optional. Previous value to check before removing. Default is empty.
907
	 *
908
	 * @return bool               False on failure, true if success.
909
	 */
910
	public function update_meta( $meta_key = '', $meta_value, $prev_value = '' ) {
911
		return Give()->customer_meta->update_meta( $this->id, $meta_key, $meta_value, $prev_value );
912
	}
913
914
	/**
915
	 * Remove metadata matching criteria from a customer.
916
	 *
917
	 * @since  1.6
918
	 * @access public
919
	 *
920
	 * @param  string $meta_key   Metadata name. Default is empty.
921
	 * @param  mixed  $meta_value Optional. Metadata value. Default is empty.
922
	 *
923
	 * @return bool               False for failure. True for success.
924
	 */
925
	public function delete_meta( $meta_key = '', $meta_value = '' ) {
926
		return Give()->customer_meta->delete_meta( $this->id, $meta_key, $meta_value );
927
	}
928
929
	/**
930
	 * Sanitize the data for update/create
931
	 *
932
	 * @since  1.0
933
	 * @access private
934
	 *
935
	 * @param  array $data The data to sanitize.
936
	 *
937
	 * @return array       The sanitized data, based off column defaults.
938
	 */
939
	private function sanitize_columns( $data ) {
940
941
		$columns        = $this->db->get_columns();
942
		$default_values = $this->db->get_column_defaults();
943
944
		foreach ( $columns as $key => $type ) {
945
946
			// Only sanitize data that we were provided
947
			if ( ! array_key_exists( $key, $data ) ) {
948
				continue;
949
			}
950
951
			switch ( $type ) {
952
953
				case '%s':
954
					if ( 'email' == $key ) {
955
						$data[ $key ] = sanitize_email( $data[ $key ] );
956
					} elseif ( 'notes' == $key ) {
957
						$data[ $key ] = strip_tags( $data[ $key ] );
958
					} else {
959
						$data[ $key ] = sanitize_text_field( $data[ $key ] );
960
					}
961
					break;
962
963
				case '%d':
964
					if ( ! is_numeric( $data[ $key ] ) || (int) $data[ $key ] !== absint( $data[ $key ] ) ) {
965
						$data[ $key ] = $default_values[ $key ];
966
					} else {
967
						$data[ $key ] = absint( $data[ $key ] );
968
					}
969
					break;
970
971
				case '%f':
972
					// Convert what was given to a float
973
					$value = floatval( $data[ $key ] );
974
975
					if ( ! is_float( $value ) ) {
976
						$data[ $key ] = $default_values[ $key ];
977
					} else {
978
						$data[ $key ] = $value;
979
					}
980
					break;
981
982
				default:
983
					$data[ $key ] = sanitize_text_field( $data[ $key ] );
984
					break;
985
986
			}
987
		}
988
989
		return $data;
990
	}
991
992
	/**
993
	 * Attach an email to the donor
994
	 *
995
	 * @since  1.7
996
	 * @access public
997
	 *
998
	 * @param  string $email   The email address to attach to the customer
999
	 * @param  bool   $primary Allows setting the email added as the primary
1000
	 *
1001
	 * @return bool            If the email was added successfully
1002
	 */
1003
	public function add_email( $email = '', $primary = false ) {
1004
		if ( ! is_email( $email ) ) {
1005
			return false;
1006
		}
1007
		$existing = new Give_Customer( $email );
1008
1009
		if ( $existing->id > 0 ) {
1010
			// Email address already belongs to another customer
1011
			return false;
1012
		}
1013
1014
		if ( email_exists( $email ) ) {
1015
			$user = get_user_by( 'email', $email );
1016
			if ( $user->ID != $this->user_id ) {
1017
				return false;
1018
			}
1019
		}
1020
1021
		do_action( 'give_donor_pre_add_email', $email, $this->id, $this );
1022
1023
		// Add is used to ensure duplicate emails are not added
1024
		$ret = (bool) $this->add_meta( 'additional_email', $email );
1025
1026
		do_action( 'give_donor_post_add_email', $email, $this->id, $this );
1027
1028
		if ( $ret && true === $primary ) {
1029
			$this->set_primary_email( $email );
1030
		}
1031
1032
		return $ret;
1033
	}
1034
1035
	/**
1036
	 * Remove an email from the customer
1037
	 *
1038
	 * @since  1.7
1039
	 * @access public
1040
	 *
1041
	 * @param  string $email The email address to remove from the customer
1042
	 *
1043
	 * @return bool          If the email was removed successfully
1044
	 */
1045
	public function remove_email( $email = '' ) {
1046
		if ( ! is_email( $email ) ) {
1047
			return false;
1048
		}
1049
1050
		do_action( 'give_donor_pre_remove_email', $email, $this->id, $this );
1051
1052
		$ret = (bool) $this->delete_meta( 'additional_email', $email );
1053
1054
		do_action( 'give_donor_post_remove_email', $email, $this->id, $this );
1055
1056
		return $ret;
1057
	}
1058
1059
	/**
1060
	 * Set an email address as the customer's primary email
1061
	 *
1062
	 * This will move the customer's previous primary email to an additional email
1063
	 *
1064
	 * @since  1.7
1065
	 * @access public
1066
	 *
1067
	 * @param  string $new_primary_email The email address to remove from the customer
1068
	 *
1069
	 * @return bool                      If the email was set as primary successfully
1070
	 */
1071
	public function set_primary_email( $new_primary_email = '' ) {
1072
		if ( ! is_email( $new_primary_email ) ) {
1073
			return false;
1074
		}
1075
1076
		do_action( 'give_donor_pre_set_primary_email', $new_primary_email, $this->id, $this );
1077
1078
		$existing = new Give_Customer( $new_primary_email );
1079
1080
		if ( $existing->id > 0 && (int) $existing->id !== (int) $this->id ) {
1081
			// This email belongs to another customer
1082
			return false;
1083
		}
1084
1085
		$old_email = $this->email;
1086
1087
		// Update customer record with new email
1088
		$update = $this->update( array( 'email' => $new_primary_email ) );
1089
1090
		// Remove new primary from list of additional emails
1091
		$remove = $this->remove_email( $new_primary_email );
1092
1093
		// Add old email to additional emails list
1094
		$add = $this->add_email( $old_email );
1095
1096
		$ret = $update && $remove && $add;
1097
1098
		if ( $ret ) {
1099
			$this->email = $new_primary_email;
1100
		}
1101
1102
		do_action( 'give_donor_post_set_primary_email', $new_primary_email, $this->id, $this );
1103
1104
		return $ret;
1105
	}
1106
}
1107