Completed
Push — issues/611 ( 661115...758b1c )
by Ravinder
21:11
created

includes/class-give-customer.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
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
	 * @access public
101
	 *
102
	 * @var    array
103
	 */
104
	public $payment_ids;
105
106
	/**
107
	 * The user ID associated with the customer
108
	 *
109
	 * @since  1.0
110
	 * @access public
111
	 *
112
	 * @var    int
113
	 */
114
	public $user_id;
115
116
	/**
117
	 * Customer Notes
118
	 *
119
	 * @since  1.0
120
	 * @access public
121
	 *
122
	 * @var    string
123
	 */
124
	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
136
	/**
137
	 * Class Constructor
138
	 *
139
	 * Set up the Give Customer Class.
140
	 *
141
	 * @since  1.0
142
	 * @access public
143
	 *
144
	 * @param  bool $_id_or_email
145
	 * @param  bool $by_user_id
146
	 */
147
	public function __construct( $_id_or_email = false, $by_user_id = false ) {
148
149
		$this->db = new Give_DB_Customers;
150
151
		if ( false === $_id_or_email || ( is_numeric( $_id_or_email ) && (int) $_id_or_email !== absint( $_id_or_email ) ) ) {
152
			return false;
153
		}
154
155
		$by_user_id = is_bool( $by_user_id ) ? $by_user_id : false;
156
157
		if ( is_numeric( $_id_or_email ) ) {
158
			$field = $by_user_id ? 'user_id' : 'id';
159
		} 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;
167
		}
168
169
		$this->setup_customer( $customer );
170
171
	}
172
173
	/**
174
	 * Setup Customer
175
	 *
176
	 * Given the customer data, let's set the variables.
177
	 *
178
	 * @since  1.0
179
	 * @access private
180
	 *
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
				case 'notes':
196
					$this->$key = $this->get_notes();
197
					break;
198
199
				default:
200
					$this->$key = $value;
201
					break;
202
203
			}
204
		}
205
206
		// Get donor's all email including primary email.
207
		$this->emails   = (array) $this->get_meta( 'additional_email', false );
208
		$this->emails = array( 'primary' => $this->email ) + $this->emails;
209
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
			return true;
213
		}
214
215
		return false;
216
217
	}
218
219
	/**
220
	 * Magic __get function to dispatch a call to retrieve a private property.
221
	 *
222
	 * @since  1.0
223
	 * @access public
224
	 */
225
	public function __get( $key ) {
226
227
		if ( method_exists( $this, 'get_' . $key ) ) {
228
229
			return call_user_func( array( $this, 'get_' . $key ) );
230
231
		} else {
232
233
			/* translators: %s: property key */
234
			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
	 *
248
	 * @return bool|int    False if not a valid creation, customer ID if user is found or valid creation.
249
	 */
250
	public function create( $data = array() ) {
251
252
		if ( $this->id != 0 || empty( $data ) ) {
253
			return false;
254
		}
255
256
		$defaults = array(
257
			'payment_ids' => '',
258
		);
259
260
		$args = wp_parse_args( $data, $defaults );
261
		$args = $this->sanitize_columns( $args );
262
263
		if ( empty( $args['email'] ) || ! is_email( $args['email'] ) ) {
264
			return false;
265
		}
266
267
		if ( ! empty( $args['payment_ids'] ) && is_array( $args['payment_ids'] ) ) {
268
			$args['payment_ids'] = implode( ',', array_unique( array_values( $args['payment_ids'] ) ) );
269
		}
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
		if ( $this->db->add( $data ) ) {
284
285
			// We've successfully added/updated the customer, reset the class vars with the new data
286
			$customer = $this->db->get_customer_by( 'email', $args['email'] );
287
288
			// Setup the customer data with the values from DB
289
			$this->setup_customer( $customer );
290
291
			$created = $this->id;
292
		}
293
294
		/**
295
		 * Fires after creating customers.
296
		 *
297
		 * @since 1.0
298
		 *
299
		 * @param bool|int $created False if not a valid creation,
300
		 *                          customer ID if user is found or valid creation.
301
		 * @param array    $args    Customer attributes.
302
		 */
303
		do_action( 'give_customer_post_create', $created, $args );
304
305
		return $created;
306
307
	}
308
309
	/**
310
	 * Update a customer record
311
	 *
312
	 * @since  1.0
313
	 * @access public
314
	 *
315
	 * @param  array $data Array of data attributes for a customer (checked via whitelist).
316
	 *
317
	 * @return bool        If the update was successful or not.
318
	 */
319
	public function update( $data = array() ) {
320
321
		if ( empty( $data ) ) {
322
			return false;
323
		}
324
325
		$data = $this->sanitize_columns( $data );
326
327
		/**
328
		 * Fires before updating customers.
329
		 *
330
		 * @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
			$updated = true;
345
		}
346
347
		/**
348
		 * Fires after updating customers.
349
		 *
350
		 * @since 1.0
351
		 *
352
		 * @param bool  $updated     If the update was successful or not.
353
		 * @param int   $customer_id Customer id.
354
		 * @param array $data        Customer attributes.
355
		 */
356
		do_action( 'give_customer_post_update', $updated, $this->id, $data );
357
358
		return $updated;
359
	}
360
361
	/**
362
	 * Attach Payment
363
	 *
364
	 * Attach payment to the customer then triggers increasing stats.
365
	 *
366
	 * @since  1.0
367
	 * @access public
368
	 *
369
	 * @param  int  $payment_id   The payment ID to attach to the customer.
370
	 * @param  bool $update_stats For backwards compatibility, if we should increase the stats or not.
371
	 *
372
	 * @return bool            If the attachment was successfuly.
373
	 */
374
	public function attach_payment( $payment_id = 0, $update_stats = true ) {
375
376
		if ( empty( $payment_id ) ) {
377
			return false;
378
		}
379
380
		if ( empty( $this->payment_ids ) ) {
381
382
			$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
394
			$new_payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
395
396
		}
397
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
		if ( $payment_added ) {
411
412
			$this->payment_ids = $new_payment_ids;
413
414
			// We added this payment successfully, increment the stats
415
			if ( $update_stats ) {
416
				$payment_amount = give_get_payment_amount( $payment_id );
417
418
				if ( ! empty( $payment_amount ) ) {
419
					$this->increase_value( $payment_amount );
420
				}
421
422
				$this->increase_purchase_count();
423
			}
424
		}
425
426
		/**
427
		 * 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
440
	/**
441
	 * Remove Payment
442
	 *
443
	 * Remove a payment from this customer, then triggers reducing stats.
444
	 *
445
	 * @since  1.0
446
	 * @access public
447
	 *
448
	 * @param  int  $payment_id   The Payment ID to remove.
449
	 * @param  bool $update_stats For backwards compatibility, if we should increase the stats or not.
450
	 *
451
	 * @return boolean               If the removal was successful.
452
	 */
453
	public function remove_payment( $payment_id = 0, $update_stats = true ) {
454
455
		if ( empty( $payment_id ) ) {
456
			return false;
457
		}
458
459
		$payment = new Give_Payment( $payment_id );
460
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
			if ( false === $pos ) {
473
				return false;
474
			}
475
476
			unset( $payment_ids[ $pos ] );
477
			$payment_ids = array_filter( $payment_ids );
478
479
			$new_payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
480
481
		}
482
483
		/**
484
		 * 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
497
			$this->payment_ids = $new_payment_ids;
498
499
			if ( $update_stats ) {
500
				// We removed this payment successfully, decrement the stats
501
				$payment_amount = give_get_payment_amount( $payment_id );
502
503
				if ( ! empty( $payment_amount ) ) {
504
					$this->decrease_value( $payment_amount );
505
				}
506
507
				$this->decrease_purchase_count();
508
			}
509
		}
510
511
		/**
512
		 * 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
526
	/**
527
	 * Increase the donation count of a customer.
528
	 *
529
	 * @since  1.0
530
	 * @access public
531
	 *
532
	 * @param  int $count The number to increase by.
533
	 *
534
	 * @return int        The donation count.
0 ignored issues
show
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
	 */
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
		/**
546
		 * Fires before increasing customer donation count.
547
		 *
548
		 * @since 1.0
549
		 *
550
		 * @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
		 *
564
		 * @param int $purchase_count Customer donation count.
565
		 * @param int $count          The number increased by.
566
		 * @param int $customer_id    Customer id.
567
		 */
568
		do_action( 'give_customer_post_increase_purchase_count', $this->purchase_count, $count, $this->id );
569
570
		return $this->purchase_count;
571
	}
572
573
	/**
574
	 * Decrease the customer donation count.
575
	 *
576
	 * @since  1.0
577
	 * @access public
578
	 *
579
	 * @param  int $count The amount to decrease by.
580
	 *
581
	 * @return mixed      If successful, the new count, otherwise false.
582
	 */
583
	public function decrease_purchase_count( $count = 1 ) {
584
585
		// Make sure it's numeric and not negative
586
		if ( ! is_numeric( $count ) || $count != absint( $count ) ) {
587
			return false;
588
		}
589
590
		$new_total = (int) $this->purchase_count - (int) $count;
591
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
		 * @param int $count       The number to decrease by.
602
		 * @param int $customer_id Customer id.
603
		 */
604
		do_action( 'give_customer_pre_decrease_purchase_count', $count, $this->id );
605
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
		 */
619
		do_action( 'give_customer_post_decrease_purchase_count', $this->purchase_count, $count, $this->id );
620
621
		return $this->purchase_count;
622
	}
623
624
	/**
625
	 * Increase the customer's lifetime value.
626
	 *
627
	 * @since  1.0
628
	 * @access public
629
	 *
630
	 * @param  float $value The value to increase by.
631
	 *
632
	 * @return mixed        If successful, the new value, otherwise false.
633
	 */
634
	public function increase_value( $value = 0.00 ) {
635
636
		$new_value = floatval( $this->purchase_value ) + $value;
637
638
		/**
639
		 * Fires before increasing customer lifetime value.
640
		 *
641
		 * @since 1.0
642
		 *
643
		 * @param float $value       The value to increase by.
644
		 * @param int   $customer_id Customer id.
645
		 */
646
		do_action( 'give_customer_pre_increase_value', $value, $this->id );
647
648
		if ( $this->update( array( 'purchase_value' => $new_value ) ) ) {
649
			$this->purchase_value = $new_value;
650
		}
651
652
		/**
653
		 * Fires after increasing customer lifetime value.
654
		 *
655
		 * @since 1.0
656
		 *
657
		 * @param float $purchase_value Customer lifetime value.
658
		 * @param float $value          The value increased by.
659
		 * @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
	 * Decrease a customer's lifetime value.
668
	 *
669
	 * @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