Completed
Pull Request — master (#1055)
by Rami
19:01
created

Give_Customer::get_meta()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 3
rs 10
ccs 0
cts 0
cp 0
crap 2
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     http://opensource.org/licenses/gpl-2.0.php 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
207
		// Get donor's all email including primary email.
208 53
		$this->emails   = (array) $this->get_meta( 'additional_email', false );
209 1
		$this->emails = array( 'primary' => $this->email ) + $this->emails;
210
211
		// Customer ID and email are the only things that are necessary, make sure they exist.
212 53
		if ( ! empty( $this->id ) && ! empty( $this->email ) ) {
213
			return true;
214
		}
215
216 53
		return false;
217
218 53
	}
219
220
	/**
221 53
	 * Magic __get function to dispatch a call to retrieve a private property.
222
	 *
223
	 * @since  1.0
224 53
	 * @access public
225
	 */
226
	public function __get( $key ) {
227 53
228
		if ( method_exists( $this, 'get_' . $key ) ) {
229 53
230 53
			return call_user_func( array( $this, 'get_' . $key ) );
231
232 53
		} else {
233
234 53
			/* translators: %s: property key */
235
			return new WP_Error( 'give-customer-invalid-property', sprintf( esc_html__( 'Can\'t get property %s.', 'give' ), $key ) );
236
237
		}
238
239
	}
240
241
	/**
242
	 * Creates a customer
243
	 *
244
	 * @since  1.0
245
	 * @access public
246
	 *
247 53
	 * @param  array $data Array of attributes for a customer.
248
	 *
249 53
	 * @return bool|int    False if not a valid creation, customer ID if user is found or valid creation.
250 1
	 */
251
	public function create( $data = array() ) {
252
253 53
		if ( $this->id != 0 || empty( $data ) ) {
254
			return false;
255 53
		}
256
257 53
		$defaults = array(
258
			'payment_ids' => ''
259 53
		);
260
261 53
		$args = wp_parse_args( $data, $defaults );
262 53
		$args = $this->sanitize_columns( $args );
263
264 53
		if ( empty( $args['email'] ) || ! is_email( $args['email'] ) ) {
265 53
			return false;
266
		}
267 53
268
		if ( ! empty( $args['payment_ids'] ) && is_array( $args['payment_ids'] ) ) {
269 53
			$args['payment_ids'] = implode( ',', array_unique( array_values( $args['payment_ids'] ) ) );
270
		}
271
272
		/**
273
		 * Fires before creating customers.
274
		 *
275
		 * @since 1.0
276
		 *
277
		 * @param array $args Customer attributes.
278
		 */
279
		do_action( 'give_customer_pre_create', $args );
280
281
		$created = false;
282
283 52
		// The DB class 'add' implies an update if the customer being asked to be created already exists
284
		if ( $this->db->add( $data ) ) {
285 52
286 1
			// We've successfully added/updated the customer, reset the class vars with the new data
287
			$customer = $this->db->get_customer_by( 'email', $args['email'] );
288
289 52
			// Setup the customer data with the values from DB
290
			$this->setup_customer( $customer );
291 52
292
			$created = $this->id;
293 52
		}
294
295 7
		/**
296
		 * Fires after creating customers.
297 7
		 *
298 1
		 * @since 1.0
299 1
		 *
300
		 * @param bool|int $created False if not a valid creation,
301 7
		 *                          customer ID if user is found or valid creation.
302
		 * @param array    $args    Customer attributes.
303 7
		 */
304
		do_action( 'give_customer_post_create', $created, $args );
305
306
		return $created;
307 52
308
	}
309 52
310
	/**
311 52
	 * Update a customer record
312
	 *
313 52
	 * @since  1.0
314
	 * @access public
315
	 *
316 52
	 * @param  array $data Array of data attributes for a customer (checked via whitelist).
317 1
	 *
318
	 * @return bool        If the update was successful or not.
319 1
	 */
320
	public function update( $data = array() ) {
321
322
		if ( empty( $data ) ) {
323 1
			return false;
324 1
		}
325
326 52
		$data = $this->sanitize_columns( $data );
327
328 52
		/**
329
		 * Fires before updating customers.
330 52
		 *
331
		 * @since 1.0
332
		 *
333
		 * @param int   $customer_id Customer id.
334
		 * @param array $data        Customer attributes.
335
		 */
336
		do_action( 'give_customer_pre_update', $this->id, $data );
337
338
		$updated = false;
339
340
		if ( $this->db->update( $this->id, $data ) ) {
341
342
			$customer = $this->db->get_customer_by( 'id', $this->id );
343
			$this->setup_customer( $customer );
344 3
345
			$updated = true;
346 3
		}
347
348
		/**
349
		 * Fires after updating customers.
350 3
		 *
351
		 * @since 1.0
352 3
		 *
353 3
		 * @param bool  $updated     If the update was successful or not.
354 3
		 * @param int   $customer_id Customer id.
355
		 * @param array $data        Customer attributes.
356 3
		 */
357
		do_action( 'give_customer_post_update', $updated, $this->id, $data );
358 3
359
		return $updated;
360 3
	}
361
362 3
	/**
363 3
	 * Attach Payment
364
	 *
365
	 * Attach payment to the customer then triggers increasing stats.
366
	 *
367 3
	 * @since  1.0
368 3
	 * @access public
369
	 *
370 3
	 * @param  int  $payment_id   The payment ID to attach to the customer.
371
	 * @param  bool $update_stats For backwards compatibility, if we should increase the stats or not.
372 3
	 *
373
	 * @return bool            If the attachment was successfuly.
374 3
	 */
375
	public function attach_payment( $payment_id = 0, $update_stats = true ) {
376 3
377
		if ( empty( $payment_id ) ) {
378 3
			return false;
379
		}
380 3
381
		if ( empty( $this->payment_ids ) ) {
382 3
383
			$new_payment_ids = $payment_id;
384
385
		} else {
386
387
			$payment_ids = array_map( 'absint', explode( ',', $this->payment_ids ) );
388
389
			if ( in_array( $payment_id, $payment_ids ) ) {
390
				$update_stats = false;
391
			}
392
393 3
			$payment_ids[] = $payment_id;
394
395 3
			$new_payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
396
397 3
		}
398
399
		/**
400
		 * Fires before attaching payments to customers.
401
		 *
402
		 * @since 1.0
403
		 *
404
		 * @param int $payment_id  Payment id.
405
		 * @param int $customer_id Customer id.
406
		 */
407
		do_action( 'give_customer_pre_attach_payment', $payment_id, $this->id );
408
409
		$payment_added = $this->update( array( 'payment_ids' => $new_payment_ids ) );
410 42
411
		if ( $payment_added ) {
412
413 42
			$this->payment_ids = $new_payment_ids;
414 1
415
			// We added this payment successfully, increment the stats
416
			if ( $update_stats ) {
417 42
				$payment_amount = give_get_payment_amount( $payment_id );
418
419 42
				if ( ! empty( $payment_amount ) ) {
420
					$this->increase_value( $payment_amount );
421 42
				}
422 41
423 41
				$this->increase_purchase_count();
424
			}
425 42
426
		}
427 42
428
		/**
429
		 * Fires after attaching payments to customers.
430
		 *
431
		 * @since 1.0
432
		 *
433
		 * @param bool $payment_added If the attachment was successfuly.
434
		 * @param int  $payment_id    Payment id.
435
		 * @param int  $customer_id   Customer id.
436
		 */
437
		do_action( 'give_customer_post_attach_payment', $payment_added, $payment_id, $this->id );
438
439 6
		return $payment_added;
440
	}
441
442 6
	/**
443 1
	 * Remove Payment
444
	 *
445
	 * Remove a payment from this customer, then triggers reducing stats.
446 6
	 *
447
	 * @since  1.0
448 6
	 * @access public
449 1
	 *
450 1
	 * @param  int  $payment_id   The Payment ID to remove.
451
	 * @param  bool $update_stats For backwards compatibility, if we should increase the stats or not.
452 6
	 *
453
	 * @return boolean               If the removal was successful.
454 6
	 */
455 6
	public function remove_payment( $payment_id = 0, $update_stats = true ) {
456 6
457
		if ( empty( $payment_id ) ) {
458 6
			return false;
459
		}
460 6
461
		$payment = new Give_Payment( $payment_id );
462
463
		if ( 'publish' !== $payment->status && 'revoked' !== $payment->status ) {
464
			$update_stats = false;
465
		}
466
467
		$new_payment_ids = '';
468
469
		if ( ! empty( $this->payment_ids ) ) {
470
471
			$payment_ids = array_map( 'absint', explode( ',', $this->payment_ids ) );
472 42
473
			$pos = array_search( $payment_id, $payment_ids );
474 42
			if ( false === $pos ) {
475
				return false;
476 42
			}
477
478 42
			unset( $payment_ids[ $pos ] );
479 41
			$payment_ids = array_filter( $payment_ids );
480 41
481
			$new_payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
482 42
483
		}
484 42
485
		/**
486
		 * Fires before removing payments from customers.
487
		 *
488
		 * @since 1.0
489
		 *
490
		 * @param int $payment_id  Payment id.
491
		 * @param int $customer_id Customer id.
492
		 */
493
		do_action( 'give_customer_pre_remove_payment', $payment_id, $this->id );
494
495
		$payment_removed = $this->update( array( 'payment_ids' => $new_payment_ids ) );
496 7
497
		if ( $payment_removed ) {
498 7
499
			$this->payment_ids = $new_payment_ids;
500 7
501 2
			if ( $update_stats ) {
502 2
				// We removed this payment successfully, decrement the stats
503
				$payment_amount = give_get_payment_amount( $payment_id );
504 7
505
				if ( ! empty( $payment_amount ) ) {
506 7
					$this->decrease_value( $payment_amount );
507 6
				}
508 6
509
				$this->decrease_purchase_count();
510 7
			}
511
512 7
		}
513
514
		/**
515
		 * Fires after removing payments from customers.
516
		 *
517
		 * @since 1.0
518
		 *
519
		 * @param bool $payment_removed If the removal was successfuly.
520
		 * @param int  $payment_id      Payment id.
521
		 * @param int  $customer_id     Customer id.
522
		 */
523
		do_action( 'give_customer_post_remove_payment', $payment_removed, $payment_id, $this->id );
524
525 52
		return $payment_removed;
526
527 52
	}
528 52
529
	/**
530 52
	 * Increase the donation count of a customer.
531 52
	 *
532
	 * @since  1.0
533 52
	 * @access public
534
	 *
535 52
	 * @param  int $count The number to increase by.
536
	 *
537
	 * @return int        The donation count.
538
	 */
539
	public function increase_purchase_count( $count = 1 ) {
540
541
		// Make sure it's numeric and not negative.
542
		if ( ! is_numeric( $count ) || $count != absint( $count ) ) {
543
			return false;
544
		}
545 1
546
		$new_total = (int) $this->purchase_count + (int) $count;
547 1
548 1
		/**
549
		 * Fires before increasing customer donation count.
550 1
		 *
551
		 * @since 1.0
552
		 *
553
		 * @param int $count       The number to increase by.
554
		 * @param int $customer_id Customer id.
555
		 */
556
		do_action( 'give_customer_pre_increase_purchase_count', $count, $this->id );
557
558
		if ( $this->update( array( 'purchase_count' => $new_total ) ) ) {
559
			$this->purchase_count = $new_total;
560
		}
561
562
		/**
563 1
		 * Fires after increasing customer donation count.
564
		 *
565 1
		 * @since 1.0
566 1
		 *
567
		 * @param int $purchase_count Customer donation count.
568
		 * @param int $count          The number increased by.
569
		 * @param int $customer_id    Customer id.
570 1
		 */
571
		do_action( 'give_customer_post_increase_purchase_count', $this->purchase_count, $count, $this->id );
572 1
573 1
		return $this->purchase_count;
574 1
	}
575
576 1
	/**
577 1
	 * Decrease the customer donation count.
578 1
	 *
579
	 * @since  1.0
580 1
	 * @access public
581
	 *
582 1
	 * @param  int $count The amount to decrease by.
583
	 *
584 1
	 * @return mixed      If successful, the new count, otherwise false.
585 1
	 */
586 1
	public function decrease_purchase_count( $count = 1 ) {
587
588 1
		// Make sure it's numeric and not negative
589
		if ( ! is_numeric( $count ) || $count != absint( $count ) ) {
590
			return false;
591 1
		}
592
593
		$new_total = (int) $this->purchase_count - (int) $count;
594
595
		if ( $new_total < 0 ) {
596
			$new_total = 0;
597
		}
598
599
		/**
600
		 * Fires before decreasing customer donation count.
601 52
		 *
602
		 * @since 1.0
603 52
		 *
604
		 * @param int $count       The number to decrease by.
605 52
		 * @param int $customer_id Customer id.
606
		 */
607
		do_action( 'give_customer_pre_decrease_purchase_count', $count, $this->id );
608
609
		if ( $this->update( array( 'purchase_count' => $new_total ) ) ) {
610
			$this->purchase_count = $new_total;
611
		}
612
613
		/**
614
		 * Fires after decreasing customer donation count.
615
		 *
616
		 * @since 1.0
617
		 *
618 52
		 * @param int $purchase_count Customer donation count.
619
		 * @param int $count          The number decreased by.
620 52
		 * @param int $customer_id    Customer id.
621 52
		 */
622
		do_action( 'give_customer_post_decrease_purchase_count', $this->purchase_count, $count, $this->id );
623 52
624
		return $this->purchase_count;
625
	}
626 52
627 52
	/**
628
	 * Increase the customer's lifetime value.
629
	 *
630
	 * @since  1.0
631
	 * @access public
632 52
	 *
633 52
	 * @param  float $value The value to increase by.
634 52
	 *
635 52
	 * @return mixed        If successful, the new value, otherwise false.
636 1
	 */
637 1
	public function increase_value( $value = 0.00 ) {
638 52
639
		$new_value = floatval( $this->purchase_value ) + $value;
640 52
641
		/**
642 52
		 * Fires before increasing customer lifetime value.
643 52
		 *
644
		 * @since 1.0
645
		 *
646 52
		 * @param float $value       The value to increase by.
647
		 * @param int   $customer_id Customer id.
648 52
		 */
649
		do_action( 'give_customer_pre_increase_value', $value, $this->id );
650 42
651
		if ( $this->update( array( 'purchase_value' => $new_value ) ) ) {
652 42
			$this->purchase_value = $new_value;
653
		}
654 42
655
		/**
656
		 * Fires after increasing customer lifetime value.
657 42
		 *
658
		 * @since 1.0
659 42
		 *
660
		 * @param float $purchase_value Customer lifetime value.
661
		 * @param float $value          The value increased by.
662
		 * @param int   $customer_id    Customer id.
663
		 */
664
		do_action( 'give_customer_post_increase_value', $this->purchase_value, $value, $this->id );
665
666
		return $this->purchase_value;
667 52
	}
668
669 52
	/**
670
	 * Decrease a customer's lifetime value.
671
	 *
672
	 * @since  1.0
673
	 * @access public
674
	 *
675
	 * @param  float $value The value to decrease by.
676
	 *
677
	 * @return mixed        If successful, the new value, otherwise false.
678
	 */
679
	public function decrease_value( $value = 0.00 ) {
680
681
		$new_value = floatval( $this->purchase_value ) - $value;
682
683
		if ( $new_value < 0 ) {
684
			$new_value = 0.00;
685
		}
686
687
		/**
688
		 * Fires before decreaseing customer lifetime value.
689
		 *
690
		 * @since 1.0
691
		 *
692
		 * @param float $value       The value to decrease by.
693
		 * @param int   $customer_id Customer id.
694
		 */
695
		do_action( 'give_customer_pre_decrease_value', $value, $this->id );
696
697
		if ( $this->update( array( 'purchase_value' => $new_value ) ) ) {
698
			$this->purchase_value = $new_value;
699
		}
700
701
		/**
702
		 * Fires after decreaseing customer lifetime value.
703
		 *
704
		 * @since 1.0
705
		 *
706
		 * @param float $purchase_value Customer lifetime value.
707
		 * @param float $value          The value decreased by.
708
		 * @param int   $customer_id    Customer id.
709
		 */
710
		do_action( 'give_customer_post_decrease_value', $this->purchase_value, $value, $this->id );
711
712
		return $this->purchase_value;
713
	}
714
715
	/**
716
	 * Decrease/Increase a customer's lifetime value.
717
     *
718
     * This function will update donation stat on basis of current amount and new amount donation difference.
719
     * Difference value can positive or negative. Negative value will decrease user donation stat while positive value increase donation stat.
720
     *
721
	 * @since  1.0
722
     * @access public
723
	 *
724
	 * @param  float $curr_amount Current Donation amount.
725
	 * @param  float $new_amount  New (changed) Donation amount.
726
	 *
727
	 * @return mixed              If successful, the new donation stat value, otherwise false.
728
	 */
729
	public function update_donation_value( $curr_amount, $new_amount ) {
730
        /**
731
         * Payment total difference value can be:
732
         *  zero   (in case amount not change)
733
         *  or -ve (in case amount decrease)
734
         *  or +ve (in case amount increase)
735
         */
736
        $payment_total_diff = $new_amount - $curr_amount;
737
738
        // We do not need to update donation stat if donation did not change.
739
        if( ! $payment_total_diff ) {
740
            return false;
741
        }
742
743
744
        if( $payment_total_diff > 0 ) {
745
            $this->increase_value( $payment_total_diff );
746
        } else {
747
            // Pass payment total difference as +ve value to decrease amount from user lifetime stat.
748
            $this->decrease_value( -$payment_total_diff );
749
        }
750
751
        return $this->purchase_value;
752
	}
753
754
	/**
755
	 * Get the parsed notes for a customer as an array.
756
	 *
757
	 * @since  1.0
758
     * @access public
759
	 *
760
	 * @param  int $length The number of notes to get.
761
	 * @param  int $paged  What note to start at.
762
	 *
763
	 * @return array       The notes requested.
764
	 */
765
	public function get_notes( $length = 20, $paged = 1 ) {
766
767
		$length = is_numeric( $length ) ? $length : 20;
768
		$offset = is_numeric( $paged ) && $paged != 1 ? ( ( absint( $paged ) - 1 ) * $length ) : 0;
769
770
		$all_notes   = $this->get_raw_notes();
771
		$notes_array = array_reverse( array_filter( explode( "\n\n", $all_notes ) ) );
772
773
		$desired_notes = array_slice( $notes_array, $offset, $length );
774
775
		return $desired_notes;
776
777
	}
778
779
	/**
780
	 * Get the total number of notes we have after parsing.
781
	 *
782
	 * @since  1.0
783
     * @access public
784
	 *
785
	 * @return int The number of notes for the customer.
786
	 */
787
	public function get_notes_count() {
788
789
		$all_notes   = $this->get_raw_notes();
790
		$notes_array = array_reverse( array_filter( explode( "\n\n", $all_notes ) ) );
791
792
		return count( $notes_array );
793
794
	}
795
796
	/**
797
	 * Add a note for the customer.
798
	 *
799
	 * @since  1.0
800
     * @access public
801
	 *
802
	 * @param  string $note   The note to add. Default is empty.
803
	 *
804
	 * @return string|boolean The new note if added successfully, false otherwise.
805
	 */
806
	public function add_note( $note = '' ) {
807
808
		$note = trim( $note );
809
		if ( empty( $note ) ) {
810
			return false;
811
		}
812
813
		$notes = $this->get_raw_notes();
814
815
		if ( empty( $notes ) ) {
816
			$notes = '';
817
		}
818
819
		$note_string = date_i18n( 'F j, Y H:i:s', current_time( 'timestamp' ) ) . ' - ' . $note;
820
		$new_note    = apply_filters( 'give_customer_add_note_string', $note_string );
821
		$notes .= "\n\n" . $new_note;
822
823
		/**
824
		 * Fires before customer note added.
825
		 *
826
		 * @since 1.0
827
		 *
828
		 * @param string $new_note    New note to add.
829
		 * @param int    $customer_id Customer id.
830
		 */
831
		do_action( 'give_customer_pre_add_note', $new_note, $this->id );
832
833
		$updated = $this->update( array( 'notes' => $notes ) );
834
835
		if ( $updated ) {
836
			$this->notes = $this->get_notes();
837
		}
838
839
		/**
840
		 * Fires after customer note added.
841
		 *
842
		 * @since 1.0
843
		 *
844
		 * @param array  $customer_notes Customer notes.
845
		 * @param string $new_note       New note added.
846
		 * @param int    $customer_id    Customer id.
847
		 */
848
		do_action( 'give_customer_post_add_note', $this->notes, $new_note, $this->id );
849
850
		// Return the formatted note, so we can test, as well as update any displays
851
		return $new_note;
852
853
	}
854
855
	/**
856
	 * Get the notes column for the customer
857
	 *
858
	 * @since  1.0
859
     * @access private
860
	 *
861
	 * @return string The Notes for the customer, non-parsed.
862
	 */
863
	private function get_raw_notes() {
864
865
		$all_notes = $this->db->get_column( 'notes', $this->id );
866
867
		return $all_notes;
868
869
	}
870
871
	/**
872
	 * Retrieve customer meta field for a customer.
873
	 *
874
	 * @since  1.6
875
	 * @access public
876
	 *
877
	 * @param  string $meta_key The meta key to retrieve. Default is empty.
878
	 * @param  bool   $single   Whether to return a single value. Default is true.
879
	 *
880
	 * @return mixed            Will be an array if $single is false. Will be value of meta data field if $single is true.
881
	 */
882
	public function get_meta( $meta_key = '', $single = true ) {
883
		return Give()->customer_meta->get_meta( $this->id, $meta_key, $single );
884
	}
885
886
	/**
887
	 * Add meta data field to a customer.
888
	 *
889
	 * @since  1.6
890
	 * @access public
891
	 *
892
	 * @param  string $meta_key   Metadata name. Default is empty.
893
	 * @param  mixed  $meta_value Metadata value.
894
	 * @param  bool   $unique     Optional. Whether the same key should not be added. Default is false.
895
	 *
896
	 * @return bool               False for failure. True for success.
897
	 */
898
	public function add_meta( $meta_key = '', $meta_value, $unique = false ) {
899
		return Give()->customer_meta->add_meta( $this->id, $meta_key, $meta_value, $unique );
900
	}
901
902
	/**
903
	 * Update customer meta field based on customer ID.
904
	 *
905
	 * @since  1.6
906
	 * @access public
907
	 *
908
	 * @param  string $meta_key   Metadata key. Default is empty.
909
	 * @param  mixed  $meta_value Metadata value.
910
	 * @param  mixed  $prev_value Optional. Previous value to check before removing. Default is empty.
911
	 *
912
	 * @return bool               False on failure, true if success.
913
	 */
914
	public function update_meta( $meta_key = '', $meta_value, $prev_value = '' ) {
915
		return Give()->customer_meta->update_meta( $this->id, $meta_key, $meta_value, $prev_value );
916
	}
917
918
	/**
919
	 * Remove metadata matching criteria from a customer.
920
	 *
921
	 * @since  1.6
922
	 * @access public
923
	 *
924
	 * @param  string $meta_key   Metadata name. Default is empty.
925
	 * @param  mixed  $meta_value Optional. Metadata value. Default is empty.
926
	 *
927
	 * @return bool               False for failure. True for success.
928
	 */
929
	public function delete_meta( $meta_key = '', $meta_value = '' ) {
930
		return Give()->customer_meta->delete_meta( $this->id, $meta_key, $meta_value );
931
	}
932
933
	/**
934
	 * Sanitize the data for update/create
935
	 *
936
	 * @since  1.0
937
     * @access private
938
	 *
939
	 * @param  array $data The data to sanitize.
940
	 *
941
	 * @return array       The sanitized data, based off column defaults.
942
	 */
943
	private function sanitize_columns( $data ) {
944
945
		$columns        = $this->db->get_columns();
946
		$default_values = $this->db->get_column_defaults();
947
948
		foreach ( $columns as $key => $type ) {
949
950
			// Only sanitize data that we were provided
951
			if ( ! array_key_exists( $key, $data ) ) {
952
				continue;
953
			}
954
955
			switch ( $type ) {
956
957
				case '%s':
958
					if ( 'email' == $key ) {
959
						$data[ $key ] = sanitize_email( $data[ $key ] );
960
					} elseif ( 'notes' == $key ) {
961
						$data[ $key ] = strip_tags( $data[ $key ] );
962
					} else {
963
						$data[ $key ] = sanitize_text_field( $data[ $key ] );
964
					}
965
					break;
966
967
				case '%d':
968
					if ( ! is_numeric( $data[ $key ] ) || (int) $data[ $key ] !== absint( $data[ $key ] ) ) {
969
						$data[ $key ] = $default_values[ $key ];
970
					} else {
971
						$data[ $key ] = absint( $data[ $key ] );
972
					}
973
					break;
974
975
				case '%f':
976
					// Convert what was given to a float
977
					$value = floatval( $data[ $key ] );
978
979
					if ( ! is_float( $value ) ) {
980
						$data[ $key ] = $default_values[ $key ];
981
					} else {
982
						$data[ $key ] = $value;
983
					}
984
					break;
985
986
				default:
987
					$data[ $key ] = sanitize_text_field( $data[ $key ] );
988
					break;
989
990
			}
991
992
		}
993
994
		return $data;
995
	}
996
997
	/**
998
	 * Attach an email to the donor
999
	 *
1000
	 * @since  1.7
1001
	 * @param  string $email   The email address to attach to the customer
1002
	 * @param  bool   $primary Allows setting the email added as the primary
1003
	 * @return bool            If the email was added successfully
1004
	 */
1005
	public function add_email( $email = '', $primary = false ) {
1006
		if( ! is_email( $email ) ) {
1007
			return false;
1008
		}
1009
		$existing = new Give_Customer( $email );
1010
1011
		if( $existing->id > 0 ) {
1012
			// Email address already belongs to another customer
1013
			return false;
1014
		}
1015
1016
		if ( email_exists( $email ) ) {
1017
			$user = get_user_by( 'email', $email );
1018
			if ( $user->ID != $this->user_id ) {
1019
				return false;
1020
			}
1021
		}
1022
1023
		do_action( 'give_donor_pre_add_email', $email, $this->id, $this );
1024
1025
		// Add is used to ensure duplicate emails are not added
1026
		$ret = (bool) $this->add_meta( 'additional_email', $email );
1027
1028
		do_action( 'give_donor_post_add_email', $email, $this->id, $this );
1029
1030
		if ( $ret && true === $primary ) {
1031
			$this->set_primary_email( $email );
1032
		}
1033
1034
		return $ret;
1035
	}
1036
1037
1038
	/**
1039
	 * Remove an email from the customer
1040
	 *
1041
	 * @since  1.7
1042
	 * @param  string $email The email address to remove from the customer
1043
	 * @return bool          If the email was removeed 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
	 * @param  string $new_primary_email The email address to remove from the customer
1066
	 * @return bool                      If the email was set as primary successfully
1067
	 */
1068
	public function set_primary_email( $new_primary_email = '' ) {
1069
		if( ! is_email( $new_primary_email ) ) {
1070
			return false;
1071
		}
1072
1073
		do_action( 'give_donor_pre_set_primary_email', $new_primary_email, $this->id, $this );
1074
1075
		$existing = new Give_Customer( $new_primary_email );
1076
1077
		if( $existing->id > 0 && (int) $existing->id !== (int) $this->id ) {
1078
			// This email belongs to another customer
1079
			return false;
1080
		}
1081
1082
		$old_email = $this->email;
1083
1084
		// Update customer record with new email
1085
		$update = $this->update( array( 'email' => $new_primary_email ) );
1086
1087
		// Remove new primary from list of additional emails
1088
		$remove = $this->remove_email( $new_primary_email );
1089
1090
		// Add old email to additional emails list
1091
		$add = $this->add_email( $old_email );
1092
1093
		$ret = $update && $remove && $add;
1094
1095
		if( $ret ) {
1096
			$this->email = $new_primary_email;
1097
		}
1098
1099
		do_action( 'give_donor_post_set_primary_email', $new_primary_email, $this->id, $this );
1100
1101
		return $ret;
1102
	}
1103
}
1104