Completed
Push — issues/1132 ( f70d74...bc164b )
by Ravinder
31:16 queued 11:18
created

Give_Donor::is_address_exist()   C

Complexity

Conditions 11
Paths 12

Size

Total Lines 44
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 21
nc 12
nop 2
dl 0
loc 44
rs 5.2653
c 0
b 0
f 0

How to fix   Complexity   

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
 * Donor
4
 *
5
 * @package     Give
6
 * @subpackage  Classes/Give_Donor
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_Donor Class
19
 *
20
 * This class handles customers.
21
 *
22
 * @since 1.0
23
 */
24
class Give_Donor {
25
26
	/**
27
	 * The donor ID
28
	 *
29
	 * @since  1.0
30
	 * @access public
31
	 *
32
	 * @var    int
33
	 */
34
	public $id = 0;
35
36
	/**
37
	 * The donor'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 donor'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 donor's email.
58
	 *
59
	 * @since  1.0
60
	 * @access public
61
	 *
62
	 * @var    string
63
	 */
64
	public $email;
65
66
	/**
67
	 * The donor's emails.
68
	 *
69
	 * @since  1.7
70
	 * @access public
71
	 *
72
	 * @var    array
73
	 */
74
	public $emails;
75
76
	/**
77
	 * The donor's name.
78
	 *
79
	 * @since  1.0
80
	 * @access public
81
	 *
82
	 * @var    string
83
	 */
84
	public $name;
85
86
	/**
87
	 * The donor 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 donor.
98
	 *
99
	 * @since  1.0
100
	 * @access public
101
	 *
102
	 * @var    string
103
	 */
104
	public $payment_ids;
105
106
	/**
107
	 * The user ID associated with the donor.
108
	 *
109
	 * @since  1.0
110
	 * @access public
111
	 *
112
	 * @var    int
113
	 */
114
	public $user_id;
115
116
	/**
117
	 * Donor notes saved by admins.
118
	 *
119
	 * @since  1.0
120
	 * @access public
121
	 *
122
	 * @var    string
123
	 */
124
	public $notes;
125
126
	/**
127
	 * Donor address.
128
	 *
129
	 * @since  1.0
130
	 * @access public
131
	 *
132
	 * @var    array
133
	 */
134
	public $address;
135
136
	/**
137
	 * The Database Abstraction
138
	 *
139
	 * @since  1.0
140
	 * @access protected
141
	 *
142
	 * @var    Give_DB_Donors
143
	 */
144
	protected $db;
145
146
	/**
147
	 * Give_Donor constructor.
148
	 *
149
	 * @param bool $_id_or_email
150
	 * @param bool $by_user_id
151
	 */
152
	public function __construct( $_id_or_email = false, $by_user_id = false ) {
153
154
		$this->db = new Give_DB_Donors();
155
156
		if (
157
			false === $_id_or_email
158
			|| ( is_numeric( $_id_or_email ) && (int) $_id_or_email !== absint( $_id_or_email ) )
159
		) {
160
			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...
161
		}
162
163
		$by_user_id = is_bool( $by_user_id ) ? $by_user_id : false;
164
165
		if ( is_numeric( $_id_or_email ) ) {
166
			$field = $by_user_id ? 'user_id' : 'id';
167
		} else {
168
			$field = 'email';
169
		}
170
171
		$donor = $this->db->get_donor_by( $field, $_id_or_email );
172
173
		if ( empty( $donor ) || ! is_object( $donor ) ) {
174
			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...
175
		}
176
177
		$this->setup_donor( $donor );
178
179
	}
180
181
	/**
182
	 * Setup Donor
183
	 *
184
	 * Set donor variables.
185
	 *
186
	 * @since  1.0
187
	 * @access private
188
	 *
189
	 * @param  object $donor The Donor Object.
190
	 *
191
	 * @return bool             If the setup was successful or not.
192
	 */
193
	private function setup_donor( $donor ) {
194
195
		if ( ! is_object( $donor ) ) {
196
			return false;
197
		}
198
199
		foreach ( $donor as $key => $value ) {
200
201
			switch ( $key ) {
202
203
				case 'notes':
204
					$this->$key = $this->get_notes();
205
					break;
206
207
				default:
208
					$this->$key = $value;
209
					break;
210
211
			}
212
		}
213
214
		// Get donor's all email including primary email.
215
		$this->emails = (array) $this->get_meta( 'additional_email', false );
216
		$this->emails = array( 'primary' => $this->email ) + $this->emails;
217
218
		$this->setup_address();
219
220
		// Donor ID and email are the only things that are necessary, make sure they exist.
221
		if ( ! empty( $this->id ) && ! empty( $this->email ) ) {
222
			return true;
223
		}
224
225
		return false;
226
227
	}
228
229
230
	/**
231
	 * Setup donor address.
232
	 * 
233
	 * @since 2.0
234
	 * @access public
235
	 */
236
	public function setup_address() {
237
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
238
		$addresses = $wpdb->get_results(
239
			$wpdb->prepare(
240
				"
241
				SELECT meta_key, meta_value FROM {$wpdb->prefix}give_customermeta
242
				WHERE meta_key
243
				LIKE '%%%s%%'
244
				",
245
				'give_donor_address'
246
			),
247
			ARRAY_N
248
		);
249
250
		if ( empty( $addresses ) ) {
251
			return array();
252
		}
253
254
		foreach ( $addresses as $address ) {
255
			$address[0] = str_replace( '_give_donor_address_', '', $address[0] );
256
			$address[0] = explode( '_', $address[0] );
257
258
			if ( 'address1' === $address[0][1] ) {
259
				$address[0][1] = 'line1';
260
			} elseif ( 'address2' === $address[0][1] ) {
261
				$address[0][1] = 'line2';
262
			}
263
264
			if ( 3 === count( $address[0] ) ) {
265
				$this->address[ $address[0][0] ][ $address[0][2] ][ $address[0][1] ] = $address[1];
266
			} else {
267
				$this->address[ $address[0][0] ][ $address[0][1] ] = $address[1];
268
			}
269
		}
270
	}
271
272
	/**
273
	 * Magic __get function to dispatch a call to retrieve a private property.
274
	 *
275
	 * @since  1.0
276
	 * @access public
277
	 * @param $key
278
	 *
279
	 * @return mixed|\WP_Error
280
	 */
281
	public function __get( $key ) {
282
283
		if ( method_exists( $this, 'get_' . $key ) ) {
284
285
			return call_user_func( array( $this, 'get_' . $key ) );
286
287
		} else {
288
289
			/* translators: %s: property key */
290
			return new WP_Error( 'give-donor-invalid-property', sprintf( esc_html__( 'Can\'t get property %s.', 'give' ), $key ) );
291
292
		}
293
294
	}
295
296
	/**
297
	 * Creates a donor.
298
	 *
299
	 * @since  1.0
300
	 * @access public
301
	 *
302
	 * @param  array $data Array of attributes for a donor.
303
	 *
304
	 * @return bool|int    False if not a valid creation, donor ID if user is found or valid creation.
305
	 */
306
	public function create( $data = array() ) {
307
308
		if ( $this->id != 0 || empty( $data ) ) {
309
			return false;
310
		}
311
312
		$defaults = array(
313
			'payment_ids' => '',
314
		);
315
316
		$args = wp_parse_args( $data, $defaults );
317
		$args = $this->sanitize_columns( $args );
318
319
		if ( empty( $args['email'] ) || ! is_email( $args['email'] ) ) {
320
			return false;
321
		}
322
323
		if ( ! empty( $args['payment_ids'] ) && is_array( $args['payment_ids'] ) ) {
324
			$args['payment_ids'] = implode( ',', array_unique( array_values( $args['payment_ids'] ) ) );
325
		}
326
327
		/**
328
		 * Fires before creating donors.
329
		 *
330
		 * @since 1.0
331
		 *
332
		 * @param array $args Donor attributes.
333
		 */
334
		do_action( 'give_donor_pre_create', $args );
335
336
		$created = false;
337
338
		// The DB class 'add' implies an update if the donor being asked to be created already exists
339
		if ( $this->db->add( $data ) ) {
340
341
			// We've successfully added/updated the donor, reset the class vars with the new data
342
			$donor = $this->db->get_donor_by( 'email', $args['email'] );
343
344
			// Setup the donor data with the values from DB
345
			$this->setup_donor( $donor );
346
347
			$created = $this->id;
348
		}
349
350
		/**
351
		 * Fires after creating donors.
352
		 *
353
		 * @since 1.0
354
		 *
355
		 * @param bool|int $created False if not a valid creation, donor ID if user is found or valid creation.
356
		 * @param array $args Customer attributes.
357
		 */
358
		do_action( 'give_donor_post_create', $created, $args );
359
360
		return $created;
361
362
	}
363
364
	/**
365
	 * Updates a donor record.
366
	 *
367
	 * @since  1.0
368
	 * @access public
369
	 *
370
	 * @param  array $data Array of data attributes for a donor (checked via whitelist).
371
	 *
372
	 * @return bool        If the update was successful or not.
373
	 */
374
	public function update( $data = array() ) {
375
376
		if ( empty( $data ) ) {
377
			return false;
378
		}
379
380
		$data = $this->sanitize_columns( $data );
381
382
		/**
383
		 * Fires before updating donors.
384
		 *
385
		 * @since 1.0
386
		 *
387
		 * @param int $donor_id Donor id.
388
		 * @param array $data Donor attributes.
389
		 */
390
		do_action( 'give_donor_pre_update', $this->id, $data );
391
392
		$updated = false;
393
394
		if ( $this->db->update( $this->id, $data ) ) {
395
396
			$donor = $this->db->get_donor_by( 'id', $this->id );
397
			$this->setup_donor( $donor );
398
399
			$updated = true;
400
		}
401
402
		/**
403
		 * Fires after updating donors.
404
		 *
405
		 * @since 1.0
406
		 *
407
		 * @param bool $updated If the update was successful or not.
408
		 * @param int $donor_id Donor id.
409
		 * @param array $data Donor attributes.
410
		 */
411
		do_action( 'give_donor_post_update', $updated, $this->id, $data );
412
413
		return $updated;
414
	}
415
416
	/**
417
	 * Attach Payment
418
	 *
419
	 * Attach payment to the donor then triggers increasing stats.
420
	 *
421
	 * @since  1.0
422
	 * @access public
423
	 *
424
	 * @param  int $payment_id The payment ID to attach to the donor.
425
	 * @param  bool $update_stats For backwards compatibility, if we should increase the stats or not.
426
	 *
427
	 * @return bool            If the attachment was successfully.
428
	 */
429
	public function attach_payment( $payment_id = 0, $update_stats = true ) {
430
431
		if ( empty( $payment_id ) ) {
432
			return false;
433
		}
434
435
		if ( empty( $this->payment_ids ) ) {
436
437
			$new_payment_ids = $payment_id;
438
439
		} else {
440
441
			$payment_ids = array_map( 'absint', explode( ',', $this->payment_ids ) );
442
443
			if ( in_array( $payment_id, $payment_ids ) ) {
444
				$update_stats = false;
445
			}
446
447
			$payment_ids[] = $payment_id;
448
449
			$new_payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
450
451
		}
452
453
		/**
454
		 * Fires before attaching payments to customers.
455
		 *
456
		 * @since 1.0
457
		 *
458
		 * @param int $payment_id Payment id.
459
		 * @param int $donor_id Customer id.
460
		 */
461
		do_action( 'give_donor_pre_attach_payment', $payment_id, $this->id );
462
463
		$payment_added = $this->update( array( 'payment_ids' => $new_payment_ids ) );
464
465
		if ( $payment_added ) {
466
467
			$this->payment_ids = $new_payment_ids;
468
469
			// We added this payment successfully, increment the stats
470
			if ( $update_stats ) {
471
				$payment_amount = give_get_payment_amount( $payment_id );
472
473
				if ( ! empty( $payment_amount ) ) {
474
					$this->increase_value( $payment_amount );
475
				}
476
477
				$this->increase_purchase_count();
478
			}
479
		}
480
481
		/**
482
		 * Fires after attaching payments to the donor.
483
		 *
484
		 * @since 1.0
485
		 *
486
		 * @param bool $payment_added If the attachment was successfully.
487
		 * @param int $payment_id Payment id.
488
		 * @param int $donor_id Donor id.
489
		 */
490
		do_action( 'give_donor_post_attach_payment', $payment_added, $payment_id, $this->id );
491
492
		return $payment_added;
493
	}
494
495
	/**
496
	 * Remove Payment
497
	 *
498
	 * Remove a payment from this donor, then triggers reducing stats.
499
	 *
500
	 * @since  1.0
501
	 * @access public
502
	 *
503
	 * @param  int $payment_id The Payment ID to remove.
504
	 * @param  bool $update_stats For backwards compatibility, if we should increase the stats or not.
505
	 *
506
	 * @return boolean               If the removal was successful.
507
	 */
508
	public function remove_payment( $payment_id = 0, $update_stats = true ) {
509
510
		if ( empty( $payment_id ) ) {
511
			return false;
512
		}
513
514
		$payment = new Give_Payment( $payment_id );
515
516
		if ( 'publish' !== $payment->status && 'revoked' !== $payment->status ) {
517
			$update_stats = false;
518
		}
519
520
		$new_payment_ids = '';
521
522
		if ( ! empty( $this->payment_ids ) ) {
523
524
			$payment_ids = array_map( 'absint', explode( ',', $this->payment_ids ) );
525
526
			$pos = array_search( $payment_id, $payment_ids );
527
			if ( false === $pos ) {
528
				return false;
529
			}
530
531
			unset( $payment_ids[ $pos ] );
532
			$payment_ids = array_filter( $payment_ids );
533
534
			$new_payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
535
536
		}
537
538
		/**
539
		 * Fires before removing payments from customers.
540
		 *
541
		 * @since 1.0
542
		 *
543
		 * @param int $payment_id Payment id.
544
		 * @param int $donor_id Customer id.
545
		 */
546
		do_action( 'give_donor_pre_remove_payment', $payment_id, $this->id );
547
548
		$payment_removed = $this->update( array( 'payment_ids' => $new_payment_ids ) );
549
550
		if ( $payment_removed ) {
551
552
			$this->payment_ids = $new_payment_ids;
553
554
			if ( $update_stats ) {
555
				// We removed this payment successfully, decrement the stats
556
				$payment_amount = give_get_payment_amount( $payment_id );
557
558
				if ( ! empty( $payment_amount ) ) {
559
					$this->decrease_value( $payment_amount );
560
				}
561
562
				$this->decrease_donation_count();
563
			}
564
		}
565
566
		/**
567
		 * Fires after removing payments from donors.
568
		 *
569
		 * @since 1.0
570
		 *
571
		 * @param bool $payment_removed If the removal was successfully.
572
		 * @param int $payment_id Payment id.
573
		 * @param int $donor_id Donor id.
574
		 */
575
		do_action( 'give_donor_post_remove_payment', $payment_removed, $payment_id, $this->id );
576
577
		return $payment_removed;
578
579
	}
580
581
	/**
582
	 * Increase the donation count of a donor.
583
	 *
584
	 * @since  1.0
585
	 * @access public
586
	 *
587
	 * @param  int $count The number to increase by.
588
	 *
589
	 * @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...
590
	 */
591
	public function increase_purchase_count( $count = 1 ) {
592
593
		// Make sure it's numeric and not negative.
594
		if ( ! is_numeric( $count ) || $count != absint( $count ) ) {
595
			return false;
596
		}
597
598
		$new_total = (int) $this->purchase_count + (int) $count;
599
600
		/**
601
		 * Fires before increasing the donor's donation count.
602
		 *
603
		 * @since 1.0
604
		 *
605
		 * @param int $count The number to increase by.
606
		 * @param int $donor_id Donor id.
607
		 */
608
		do_action( 'give_donor_pre_increase_donation_count', $count, $this->id );
609
610
		if ( $this->update( array( 'purchase_count' => $new_total ) ) ) {
611
			$this->purchase_count = $new_total;
612
		}
613
614
		/**
615
		 * Fires after increasing the donor's donation count.
616
		 *
617
		 * @since 1.0
618
		 *
619
		 * @param int $purchase_count Donor donation count.
620
		 * @param int $count The number increased by.
621
		 * @param int $donor_id Donor id.
622
		 */
623
		do_action( 'give_donor_post_increase_donation_count', $this->purchase_count, $count, $this->id );
624
625
		return $this->purchase_count;
626
	}
627
628
	/**
629
	 * Decrease the donor donation count.
630
	 *
631
	 * @since  1.0
632
	 * @access public
633
	 *
634
	 * @param  int $count The amount to decrease by.
635
	 *
636
	 * @return mixed      If successful, the new count, otherwise false.
637
	 */
638
	public function decrease_donation_count( $count = 1 ) {
639
640
		// Make sure it's numeric and not negative
641
		if ( ! is_numeric( $count ) || $count != absint( $count ) ) {
642
			return false;
643
		}
644
645
		$new_total = (int) $this->purchase_count - (int) $count;
646
647
		if ( $new_total < 0 ) {
648
			$new_total = 0;
649
		}
650
651
		/**
652
		 * Fires before decreasing the donor's donation count.
653
		 *
654
		 * @since 1.0
655
		 *
656
		 * @param int $count The number to decrease by.
657
		 * @param int $donor_id Customer id.
658
		 */
659
		do_action( 'give_donor_pre_decrease_donation_count', $count, $this->id );
660
661
		if ( $this->update( array( 'purchase_count' => $new_total ) ) ) {
662
			$this->purchase_count = $new_total;
663
		}
664
665
		/**
666
		 * Fires after decreasing the donor's donation count.
667
		 *
668
		 * @since 1.0
669
		 *
670
		 * @param int $purchase_count Donor's donation count.
671
		 * @param int $count The number decreased by.
672
		 * @param int $donor_id Donor id.
673
		 */
674
		do_action( 'give_donor_post_decrease_donation_count', $this->purchase_count, $count, $this->id );
675
676
		return $this->purchase_count;
677
	}
678
679
	/**
680
	 * Increase the donor's lifetime value.
681
	 *
682
	 * @since  1.0
683
	 * @access public
684
	 *
685
	 * @param  float $value The value to increase by.
686
	 *
687
	 * @return mixed        If successful, the new value, otherwise false.
688
	 */
689
	public function increase_value( $value = 0.00 ) {
690
691
		$new_value = floatval( $this->purchase_value ) + $value;
692
693
		/**
694
		 * Fires before increasing donor lifetime value.
695
		 *
696
		 * @since 1.0
697
		 *
698
		 * @param float $value The value to increase by.
699
		 * @param int $donor_id Customer id.
700
		 */
701
		do_action( 'give_donor_pre_increase_value', $value, $this->id );
702
703
		if ( $this->update( array( 'purchase_value' => $new_value ) ) ) {
704
			$this->purchase_value = $new_value;
705
		}
706
707
		/**
708
		 * Fires after increasing donor lifetime value.
709
		 *
710
		 * @since 1.0
711
		 *
712
		 * @param float $purchase_value Donor's lifetime value.
713
		 * @param float $value The value increased by.
714
		 * @param int $donor_id Donor id.
715
		 */
716
		do_action( 'give_donor_post_increase_value', $this->purchase_value, $value, $this->id );
717
718
		return $this->purchase_value;
719
	}
720
721
	/**
722
	 * Decrease a donor's lifetime value.
723
	 *
724
	 * @since  1.0
725
	 * @access public
726
	 *
727
	 * @param  float $value The value to decrease by.
728
	 *
729
	 * @return mixed        If successful, the new value, otherwise false.
730
	 */
731
	public function decrease_value( $value = 0.00 ) {
732
733
		$new_value = floatval( $this->purchase_value ) - $value;
734
735
		if ( $new_value < 0 ) {
736
			$new_value = 0.00;
737
		}
738
739
		/**
740
		 * Fires before decreasing donor lifetime value.
741
		 *
742
		 * @since 1.0
743
		 *
744
		 * @param float $value The value to decrease by.
745
		 * @param int $donor_id Donor id.
746
		 */
747
		do_action( 'give_donor_pre_decrease_value', $value, $this->id );
748
749
		if ( $this->update( array( 'purchase_value' => $new_value ) ) ) {
750
			$this->purchase_value = $new_value;
751
		}
752
753
		/**
754
		 * Fires after decreasing donor lifetime value.
755
		 *
756
		 * @since 1.0
757
		 *
758
		 * @param float $purchase_value Donor lifetime value.
759
		 * @param float $value The value decreased by.
760
		 * @param int $donor_id Donor id.
761
		 */
762
		do_action( 'give_donor_post_decrease_value', $this->purchase_value, $value, $this->id );
763
764
		return $this->purchase_value;
765
	}
766
767
	/**
768
	 * Decrease/Increase a donor's lifetime value.
769
	 *
770
	 * This function will update donation stat on basis of current amount and new amount donation difference.
771
	 * Difference value can positive or negative. Negative value will decrease user donation stat while positive value increase donation stat.
772
	 *
773
	 * @since  1.0
774
	 * @access public
775
	 *
776
	 * @param  float $curr_amount Current Donation amount.
777
	 * @param  float $new_amount New (changed) Donation amount.
778
	 *
779
	 * @return mixed              If successful, the new donation stat value, otherwise false.
780
	 */
781
	public function update_donation_value( $curr_amount, $new_amount ) {
782
		/**
783
		 * Payment total difference value can be:
784
		 *  zero   (in case amount not change)
785
		 *  or -ve (in case amount decrease)
786
		 *  or +ve (in case amount increase)
787
		 */
788
		$payment_total_diff = $new_amount - $curr_amount;
789
790
		// We do not need to update donation stat if donation did not change.
791
		if ( ! $payment_total_diff ) {
792
			return false;
793
		}
794
795
		if ( $payment_total_diff > 0 ) {
796
			$this->increase_value( $payment_total_diff );
797
		} else {
798
			// Pass payment total difference as +ve value to decrease amount from user lifetime stat.
799
			$this->decrease_value( - $payment_total_diff );
800
		}
801
802
		return $this->purchase_value;
803
	}
804
805
	/**
806
	 * Get the parsed notes for a donor as an array.
807
	 *
808
	 * @since  1.0
809
	 * @access public
810
	 *
811
	 * @param  int $length The number of notes to get.
812
	 * @param  int $paged What note to start at.
813
	 *
814
	 * @return array       The notes requested.
815
	 */
816
	public function get_notes( $length = 20, $paged = 1 ) {
817
818
		$length = is_numeric( $length ) ? $length : 20;
819
		$offset = is_numeric( $paged ) && $paged != 1 ? ( ( absint( $paged ) - 1 ) * $length ) : 0;
820
821
		$all_notes   = $this->get_raw_notes();
822
		$notes_array = array_reverse( array_filter( explode( "\n\n", $all_notes ) ) );
823
824
		$desired_notes = array_slice( $notes_array, $offset, $length );
825
826
		return $desired_notes;
827
828
	}
829
830
	/**
831
	 * Get the total number of notes we have after parsing.
832
	 *
833
	 * @since  1.0
834
	 * @access public
835
	 *
836
	 * @return int The number of notes for the donor.
837
	 */
838
	public function get_notes_count() {
839
840
		$all_notes   = $this->get_raw_notes();
841
		$notes_array = array_reverse( array_filter( explode( "\n\n", $all_notes ) ) );
842
843
		return count( $notes_array );
844
845
	}
846
847
	/**
848
	 * Add a note for the donor.
849
	 *
850
	 * @since  1.0
851
	 * @access public
852
	 *
853
	 * @param  string $note The note to add. Default is empty.
854
	 *
855
	 * @return string|boolean The new note if added successfully, false otherwise.
856
	 */
857
	public function add_note( $note = '' ) {
858
859
		$note = trim( $note );
860
		if ( empty( $note ) ) {
861
			return false;
862
		}
863
864
		$notes = $this->get_raw_notes();
865
866
		if ( empty( $notes ) ) {
867
			$notes = '';
868
		}
869
870
		$note_string = date_i18n( 'F j, Y H:i:s', current_time( 'timestamp' ) ) . ' - ' . $note;
871
		$new_note    = apply_filters( 'give_customer_add_note_string', $note_string );
872
		$notes       .= "\n\n" . $new_note;
873
874
		/**
875
		 * Fires before donor note is added.
876
		 *
877
		 * @since 1.0
878
		 *
879
		 * @param string $new_note New note to add.
880
		 * @param int $donor_id Donor id.
881
		 */
882
		do_action( 'give_donor_pre_add_note', $new_note, $this->id );
883
884
		$updated = $this->update( array( 'notes' => $notes ) );
885
886
		if ( $updated ) {
887
			$this->notes = $this->get_notes();
888
		}
889
890
		/**
891
		 * Fires after donor note added.
892
		 *
893
		 * @since 1.0
894
		 *
895
		 * @param array $donor_notes Donor notes.
896
		 * @param string $new_note New note added.
897
		 * @param int $donor_id Donor id.
898
		 */
899
		do_action( 'give_donor_post_add_note', $this->notes, $new_note, $this->id );
900
901
		// Return the formatted note, so we can test, as well as update any displays
902
		return $new_note;
903
904
	}
905
906
	/**
907
	 * Get the notes column for the donor
908
	 *
909
	 * @since  1.0
910
	 * @access private
911
	 *
912
	 * @return string The Notes for the donor, non-parsed.
913
	 */
914
	private function get_raw_notes() {
915
916
		$all_notes = $this->db->get_column( 'notes', $this->id );
917
918
		return $all_notes;
919
920
	}
921
922
	/**
923
	 * Retrieve a meta field for a donor.
924
	 *
925
	 * @since  1.6
926
	 * @access public
927
	 *
928
	 * @param  string $meta_key The meta key to retrieve. Default is empty.
929
	 * @param  bool $single Whether to return a single value. Default is true.
930
	 *
931
	 * @return mixed            Will be an array if $single is false. Will be value of meta data field if $single is true.
932
	 */
933
	public function get_meta( $meta_key = '', $single = true ) {
934
		return Give()->donor_meta->get_meta( $this->id, $meta_key, $single );
935
	}
936
937
	/**
938
	 * Add a meta data field to a donor.
939
	 *
940
	 * @since  1.6
941
	 * @access public
942
	 *
943
	 * @param  string $meta_key Metadata name. Default is empty.
944
	 * @param  mixed $meta_value Metadata value.
945
	 * @param  bool $unique Optional. Whether the same key should not be added. Default is false.
946
	 *
947
	 * @return bool               False for failure. True for success.
948
	 */
949
	public function add_meta( $meta_key = '', $meta_value, $unique = false ) {
950
		return Give()->donor_meta->add_meta( $this->id, $meta_key, $meta_value, $unique );
951
	}
952
953
	/**
954
	 * Update a meta field based on donor ID.
955
	 *
956
	 * @since  1.6
957
	 * @access public
958
	 *
959
	 * @param  string $meta_key Metadata key. Default is empty.
960
	 * @param  mixed $meta_value Metadata value.
961
	 * @param  mixed $prev_value Optional. Previous value to check before removing. Default is empty.
962
	 *
963
	 * @return bool               False on failure, true if success.
964
	 */
965
	public function update_meta( $meta_key = '', $meta_value, $prev_value = '' ) {
966
		return Give()->donor_meta->update_meta( $this->id, $meta_key, $meta_value, $prev_value );
967
	}
968
969
	/**
970
	 * Remove metadata matching criteria from a donor.
971
	 *
972
	 * @since  1.6
973
	 * @access public
974
	 *
975
	 * @param  string $meta_key Metadata name. Default is empty.
976
	 * @param  mixed $meta_value Optional. Metadata value. Default is empty.
977
	 *
978
	 * @return bool               False for failure. True for success.
979
	 */
980
	public function delete_meta( $meta_key = '', $meta_value = '' ) {
981
		return Give()->donor_meta->delete_meta( $this->id, $meta_key, $meta_value );
982
	}
983
984
	/**
985
	 * Sanitize the data for update/create
986
	 *
987
	 * @since  1.0
988
	 * @access private
989
	 *
990
	 * @param  array $data The data to sanitize.
991
	 *
992
	 * @return array       The sanitized data, based off column defaults.
993
	 */
994
	private function sanitize_columns( $data ) {
995
996
		$columns        = $this->db->get_columns();
997
		$default_values = $this->db->get_column_defaults();
998
999
		foreach ( $columns as $key => $type ) {
1000
1001
			// Only sanitize data that we were provided
1002
			if ( ! array_key_exists( $key, $data ) ) {
1003
				continue;
1004
			}
1005
1006
			switch ( $type ) {
1007
1008
				case '%s':
1009
					if ( 'email' == $key ) {
1010
						$data[ $key ] = sanitize_email( $data[ $key ] );
1011
					} elseif ( 'notes' == $key ) {
1012
						$data[ $key ] = strip_tags( $data[ $key ] );
1013
					} else {
1014
						$data[ $key ] = sanitize_text_field( $data[ $key ] );
1015
					}
1016
					break;
1017
1018
				case '%d':
1019
					if ( ! is_numeric( $data[ $key ] ) || (int) $data[ $key ] !== absint( $data[ $key ] ) ) {
1020
						$data[ $key ] = $default_values[ $key ];
1021
					} else {
1022
						$data[ $key ] = absint( $data[ $key ] );
1023
					}
1024
					break;
1025
1026
				case '%f':
1027
					// Convert what was given to a float
1028
					$value = floatval( $data[ $key ] );
1029
1030
					if ( ! is_float( $value ) ) {
1031
						$data[ $key ] = $default_values[ $key ];
1032
					} else {
1033
						$data[ $key ] = $value;
1034
					}
1035
					break;
1036
1037
				default:
1038
					$data[ $key ] = sanitize_text_field( $data[ $key ] );
1039
					break;
1040
1041
			}
1042
		}
1043
1044
		return $data;
1045
	}
1046
1047
	/**
1048
	 * Attach an email to the donor
1049
	 *
1050
	 * @since  1.7
1051
	 * @access public
1052
	 *
1053
	 * @param  string $email The email address to attach to the donor
1054
	 * @param  bool $primary Allows setting the email added as the primary
1055
	 *
1056
	 * @return bool            If the email was added successfully
1057
	 */
1058
	public function add_email( $email = '', $primary = false ) {
1059
		if ( ! is_email( $email ) ) {
1060
			return false;
1061
		}
1062
		$existing = new Give_Donor( $email );
1063
1064
		if ( $existing->id > 0 ) {
1065
			// Email address already belongs to another donor
1066
			return false;
1067
		}
1068
1069
		if ( email_exists( $email ) ) {
1070
			$user = get_user_by( 'email', $email );
1071
			if ( $user->ID != $this->user_id ) {
1072
				return false;
1073
			}
1074
		}
1075
1076
		do_action( 'give_donor_pre_add_email', $email, $this->id, $this );
1077
1078
		// Add is used to ensure duplicate emails are not added
1079
		$ret = (bool) $this->add_meta( 'additional_email', $email );
1080
1081
		do_action( 'give_donor_post_add_email', $email, $this->id, $this );
1082
1083
		if ( $ret && true === $primary ) {
1084
			$this->set_primary_email( $email );
1085
		}
1086
1087
		return $ret;
1088
	}
1089
1090
	/**
1091
	 * Remove an email from the donor.
1092
	 *
1093
	 * @since  1.7
1094
	 * @access public
1095
	 *
1096
	 * @param  string $email The email address to remove from the donor.
1097
	 *
1098
	 * @return bool          If the email was removed successfully.
1099
	 */
1100
	public function remove_email( $email = '' ) {
1101
		if ( ! is_email( $email ) ) {
1102
			return false;
1103
		}
1104
1105
		do_action( 'give_donor_pre_remove_email', $email, $this->id, $this );
1106
1107
		$ret = (bool) $this->delete_meta( 'additional_email', $email );
1108
1109
		do_action( 'give_donor_post_remove_email', $email, $this->id, $this );
1110
1111
		return $ret;
1112
	}
1113
1114
	/**
1115
	 * Set an email address as the donor's primary email.
1116
	 *
1117
	 * This will move the donor's previous primary email to an additional email.
1118
	 *
1119
	 * @since  1.7
1120
	 * @access public
1121
	 *
1122
	 * @param  string $new_primary_email The email address to remove from the donor.
1123
	 *
1124
	 * @return bool                      If the email was set as primary successfully.
1125
	 */
1126
	public function set_primary_email( $new_primary_email = '' ) {
1127
		if ( ! is_email( $new_primary_email ) ) {
1128
			return false;
1129
		}
1130
1131
		do_action( 'give_donor_pre_set_primary_email', $new_primary_email, $this->id, $this );
1132
1133
		$existing = new Give_Donor( $new_primary_email );
1134
1135
		if ( $existing->id > 0 && (int) $existing->id !== (int) $this->id ) {
1136
			// This email belongs to another donor.
1137
			return false;
1138
		}
1139
1140
		$old_email = $this->email;
1141
1142
		// Update donor record with new email.
1143
		$update = $this->update( array( 'email' => $new_primary_email ) );
1144
1145
		// Remove new primary from list of additional emails.
1146
		$remove = $this->remove_email( $new_primary_email );
1147
1148
		// Add old email to additional emails list.
1149
		$add = $this->add_email( $old_email );
1150
1151
		$ret = $update && $remove && $add;
1152
1153
		if ( $ret ) {
1154
			$this->email = $new_primary_email;
1155
		}
1156
1157
		do_action( 'give_donor_post_set_primary_email', $new_primary_email, $this->id, $this );
1158
1159
		return $ret;
1160
	}
1161
1162
	/**
1163
	 * Add donor address
1164
	 *
1165
	 * @since  2.0
1166
	 * @access public
1167
	 *
1168
	 * @param string $address_type
1169
	 * @param array  $address {
1170
	 *
1171
	 * @type string  $address2
1172
	 * @type string city
1173
	 * @type string zip
1174
	 * @type string state
1175
	 * @type string country
1176
	 * }
1177
	 *
1178
	 * @return void
1179
	 */
1180
	public function add_address( $address_type, $address ) {
1181
		$is_address_empty = true;
1182
1183
		// Address ready to process even if only one value set.
1184
		foreach ( $address as $value ) {
1185
			if( ! empty( $value ) ) {
1186
				$is_address_empty = false;
1187
				break;
1188
			}
1189
		}
1190
1191
		// Bailout.
1192
		if ( empty( $address_type ) || $is_address_empty || ! $this->id ) {
1193
			return;
1194
		}
1195
1196
		$is_multi_address = ( false !== strpos( $address_type, '[]' ) );
1197
		$address_type  = $is_multi_address ?
1198
			str_replace( '[]', '', $address_type ) :
1199
			$address_type;
1200
1201
		// Bailout: do not save duplicate orders
1202
		if( $this->is_address_exist( $address_type, $address ) ) {
1203
			return;
1204
		}
1205
1206
		// Set default address.
1207
		$address = wp_parse_args(
1208
			$address,
1209
			array(
1210
				'line1' => '',
1211
				'line2' => '',
1212
				'city'     => '',
1213
				'state'    => '',
1214
				'country'  => '',
1215
				'zip'      => '',
1216
			)
1217
		);
1218
1219
		// Set meta key prefix.
1220
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1221
		$meta_key_prefix = "_give_donor_address_{$address_type}_{address_name}";
1222
1223
		if ( $is_multi_address ) {
1224
			$address_count = $wpdb->get_var(
1225
				$wpdb->prepare(
1226
					"
1227
					SELECT COUNT(*) FROM {$wpdb->prefix}give_customermeta
1228
					WHERE meta_key
1229
					LIKE '%%%s%%'
1230
					",
1231
					"_give_donor_address_{$address_type}_address1"
1232
				)
1233
			);
1234
1235
			$address_count   = $address_count ? $address_count : 0;
1236
			$meta_key_prefix = "_give_donor_address_{$address_type}_{address_name}_{$address_count}";
1237
		}
1238
1239
		// Save donor address.
1240
		foreach ( $address as $type => $value ) {
1241
			$meta_key = str_replace( '{address_name}', $type, $meta_key_prefix );
1242
			switch ( $type ) {
1243
				case 'line1':
1244
					$meta_key = str_replace( '{address_name}', 'address1', $meta_key_prefix );
1245
					Give()->donor_meta->update_meta( $this->id, $meta_key, $value );
1246
					break;
1247
1248
				case 'line2':
1249
					$meta_key = str_replace( '{address_name}', 'address2', $meta_key_prefix );
1250
					Give()->donor_meta->update_meta( $this->id, $meta_key, $value );
1251
					break;
1252
1253
				default:
1254
					Give()->donor_meta->update_meta( $this->id, $meta_key, $value );
1255
			}
1256
		}
1257
1258
		$this->setup_address();
1259
	}
1260
1261
1262
	/**
1263
	 * Check if donor already has current address
1264
	 *
1265
	 * @since 2.0
1266
	 * @access public
1267
	 *
1268
	 * @param string $current_address_type
1269
	 * @param array $current_address
1270
	 *
1271
	 * @return bool|null
1272
	 */
1273
	public function is_address_exist( $current_address_type, $current_address ) {
1274
		$status = false;
1275
1276
1277
		// Bailout.
1278
		if( empty( $current_address_type ) || empty( $current_address ) ) {
1279
			return null;
1280
		}
1281
1282
		// Bailout.
1283
		if( empty( $this->address ) ) {
1284
			return $status;
1285
		}
1286
1287
		// Compare address.
1288
		foreach ( $this->address as $address_type => $saved_address ) {
1289
			if( $current_address_type !== $address_type ) {
1290
				continue;
1291
			} elseif( ! is_array( $saved_address[0] ) ) {
1292
				$status = ( $current_address == $saved_address );
1293
1294
			} else{
1295
				foreach ( $saved_address as $address ) {
1296
					if( empty( $saved_address ) ) {
1297
						continue;
1298
					}
1299
1300
					$status = ( $current_address == $address );
1301
1302
					// Exit loop immediately if address exist.
1303
					if( $status ) {
1304
						break;
1305
					}
1306
				}
1307
			}
1308
1309
			// Exit loop immediately if address exist.
1310
			if( $status ) {
1311
				break;
1312
			}
1313
		}
1314
1315
		return $status;
1316
	}
1317
}
1318