Completed
Push — issues/1586 ( 1477c1 )
by Ravinder
18:21
created

Give_Customer::create()   B

Complexity

Conditions 8
Paths 6

Size

Total Lines 58
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 19
nc 6
nop 1
dl 0
loc 58
rs 7.1977
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
	 * @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;
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
		}
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;
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
	}
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
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
	 */
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