PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 2.24.0
GiveWP – Donation Plugin and Fundraising Platform v2.24.0
4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / includes / class-give-donor.php
give / includes Last commit date
admin 3 years ago api 3 years ago database 5 years ago deprecated 3 years ago donors 3 years ago emails 3 years ago forms 3 years ago frontend 6 years ago gateways 3 years ago libraries 4 years ago payments 4 years ago actions.php 5 years ago ajax-functions.php 4 years ago class-give-async-process.php 6 years ago class-give-background-updater.php 6 years ago class-give-cache-setting.php 4 years ago class-give-cache.php 3 years ago class-give-cli-commands.php 3 years ago class-give-comment.php 6 years ago class-give-cron.php 6 years ago class-give-donate-form.php 4 years ago class-give-donor.php 3 years ago class-give-email-access.php 5 years ago class-give-license-handler.php 4 years ago class-give-logging.php 5 years ago class-give-readme-parser.php 4 years ago class-give-roles.php 6 years ago class-give-scripts.php 4 years ago class-give-session.php 5 years ago class-give-stats.php 6 years ago class-give-template-loader.php 6 years ago class-give-tooltips.php 6 years ago class-give-translation.php 4 years ago class-notices.php 4 years ago country-functions.php 5 years ago currencies-list.php 4 years ago currency-functions.php 3 years ago error-tracking.php 6 years ago filters.php 3 years ago formatting.php 3 years ago install.php 4 years ago login-register.php 4 years ago misc-functions.php 4 years ago plugin-compatibility.php 6 years ago post-types.php 5 years ago price-functions.php 6 years ago process-donation.php 4 years ago setting-functions.php 6 years ago shortcodes.php 3 years ago template-functions.php 4 years ago user-functions.php 3 years ago
class-give-donor.php
1756 lines
1 <?php
2 /**
3 * Donor
4 *
5 * @package Give
6 * @subpackage Classes/Give_Donor
7 * @copyright Copyright (c) 2016, GiveWP
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 array
123 */
124 protected $notes = null;
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 int|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 = Give()->donors;
155
156 if ( false === $_id_or_email || ( is_numeric( $_id_or_email ) && (int) $_id_or_email !== absint( $_id_or_email ) ) ) {
157 return false;
158 }
159
160 $by_user_id = is_bool( $by_user_id ) ? $by_user_id : false;
161
162 if ( is_numeric( $_id_or_email ) ) {
163 $field = $by_user_id ? 'user_id' : 'id';
164 } else {
165 $field = 'email';
166 }
167
168 $donor = $this->db->get_donor_by( $field, $_id_or_email );
169
170 if ( empty( $donor ) || ! is_object( $donor ) ) {
171 return false;
172 }
173
174 $this->setup_donor( $donor );
175
176 }
177
178 /**
179 * Setup Donor
180 *
181 * Set donor variables.
182 *
183 * @since 1.0
184 * @access private
185 *
186 * @param object $donor The Donor Object.
187 *
188 * @return bool If the setup was successful or not.
189 */
190 private function setup_donor( $donor ) {
191
192 if ( ! is_object( $donor ) ) {
193 return false;
194 }
195
196 // Get cached donors.
197 $donor_vars = Give_Cache::get_group( $donor->id, 'give-donors' );
198
199 if ( is_null( $donor_vars ) ) {
200 foreach ( $donor as $key => $value ) {
201
202 switch ( $key ) {
203
204 // @todo We will remove this statement when we will remove notes column from donor table
205 // https://github.com/impress-org/give/issues/3632
206 case 'notes':
207 break;
208
209 default:
210 $this->$key = $value;
211 break;
212
213 }
214 }
215
216 // Get donor's all email including primary email.
217 $this->emails = (array) $this->get_meta( 'additional_email', false );
218 $this->emails = [ 'primary' => $this->email ] + $this->emails;
219
220 $this->setup_address();
221
222 Give_Cache::set_group( $donor->id, get_object_vars( $this ), 'give-donors' );
223 } else {
224 foreach ( $donor_vars as $donor_var => $value ) {
225 $this->$donor_var = $value;
226 }
227 }
228
229 // Donor ID and email are the only things that are necessary, make sure they exist.
230 if ( ! empty( $this->id ) && ! empty( $this->email ) ) {
231 return true;
232 }
233
234 return false;
235
236 }
237
238
239 /**
240 * Setup donor address.
241 *
242 * @since 2.0
243 * @access public
244 */
245 public function setup_address() {
246 global $wpdb;
247 $meta_type = Give()->donor_meta->meta_type;
248
249 $addresses = $this->get_addresses_from_meta_cache();
250
251 $addresses = ! empty( $addresses )
252 ? $addresses
253 : $wpdb->get_results(
254 $wpdb->prepare(
255 "
256 SELECT meta_key, meta_value FROM {$wpdb->donormeta}
257 WHERE meta_key
258 LIKE '%s'
259 AND {$meta_type}_id=%d
260 ",
261 '%give_donor_address%',
262 $this->id
263 ),
264 ARRAY_N
265 );
266
267 if ( empty( $addresses ) ) {
268 return $this->address;
269 }
270
271 foreach ( $addresses as $address ) {
272 $address[0] = str_replace( '_give_donor_address_', '', $address[0] );
273 $address[0] = explode( '_', $address[0] );
274
275 if ( 3 === count( $address[0] ) ) {
276 $this->address[ $address[0][0] ][ $address[0][2] ][ $address[0][1] ] = $address[1];
277 } else {
278 $this->address[ $address[0][0] ][ $address[0][1] ] = $address[1];
279 }
280 }
281 }
282
283
284 /**
285 * Get addresses from meta cache
286 *
287 * @since 2.5.0
288 * @return array
289 */
290 private function get_addresses_from_meta_cache() {
291 $meta = wp_cache_get( $this->id, 'donor_meta' );
292 $addresses = [];
293
294 if ( ! empty( $meta ) ) {
295 foreach ( $meta as $meta_key => $meta_value ) {
296 if ( false === strpos( $meta_key, 'give_donor_address' ) ) {
297 continue;
298 }
299
300 $addresses[] = [ $meta_key, current( $meta_value ) ];
301 }
302 }
303
304 return $addresses;
305 }
306
307 /**
308 * Returns the saved address for a donor
309 *
310 * @access public
311 *
312 * @since 2.1.3
313 *
314 * @param array $args donor address.
315 *
316 * @return array The donor's address, if any
317 */
318 public function get_donor_address( $args = [] ) {
319 $args = wp_parse_args(
320 $args,
321 [
322 'address_type' => 'billing',
323 ]
324 );
325
326 $default_address = [
327 'line1' => '',
328 'line2' => '',
329 'city' => '',
330 'state' => '',
331 'country' => '',
332 'zip' => '',
333 ];
334
335 // Backward compatibility.
336 if ( ! give_has_upgrade_completed( 'v20_upgrades_user_address' ) ) {
337
338 // Backward compatibility for user id param.
339 return wp_parse_args( (array) get_user_meta( $this->user_id, '_give_user_address', true ), $default_address );
340
341 }
342
343 if ( ! $this->id || empty( $this->address ) || ! array_key_exists( $args['address_type'], $this->address ) ) {
344 return $default_address;
345 }
346
347 switch ( true ) {
348 case is_string( end( $this->address[ $args['address_type'] ] ) ):
349 $address = wp_parse_args( $this->address[ $args['address_type'] ], $default_address );
350 break;
351
352 case is_array( end( $this->address[ $args['address_type'] ] ) ):
353 $address = wp_parse_args( array_shift( $this->address[ $args['address_type'] ] ), $default_address );
354 break;
355 }
356
357 return $address;
358 }
359
360 /**
361 * Magic __get function to dispatch a call to retrieve a private property.
362 *
363 * @since 1.0
364 * @access public
365 *
366 * @param $key
367 *
368 * @return mixed|\WP_Error
369 */
370 public function __get( $key ) {
371
372 if ( method_exists( $this, 'get_' . $key ) ) {
373
374 return call_user_func( [ $this, 'get_' . $key ] );
375
376 } else {
377
378 /* translators: %s: property key */
379 return new WP_Error( 'give-donor-invalid-property', sprintf( esc_html__( 'Can\'t get property %s.', 'give' ), $key ) );
380
381 }
382
383 }
384
385 /**
386 * Creates a donor.
387 *
388 * @since 1.0
389 * @access public
390 *
391 * @param array $data Array of attributes for a donor.
392 *
393 * @return bool|int False if not a valid creation, donor ID if user is found or valid creation.
394 */
395 public function create( $data = [] ) {
396
397 if ( $this->id != 0 || empty( $data ) ) {
398 return false;
399 }
400
401 $defaults = [
402 'payment_ids' => '',
403 ];
404
405 $args = wp_parse_args( $data, $defaults );
406 $args = $this->sanitize_columns( $args );
407
408 if ( empty( $args['email'] ) || ! is_email( $args['email'] ) ) {
409 return false;
410 }
411
412 if ( ! empty( $args['payment_ids'] ) && is_array( $args['payment_ids'] ) ) {
413 $args['payment_ids'] = implode( ',', array_unique( array_values( $args['payment_ids'] ) ) );
414 }
415
416 /**
417 * Fires before creating donors.
418 *
419 * @since 1.0
420 *
421 * @param array $args Donor attributes.
422 */
423 do_action( 'give_donor_pre_create', $args );
424
425 $created = false;
426
427 // The DB class 'add' implies an update if the donor being asked to be created already exists
428 if ( $this->db->add( $data ) ) {
429
430 // We've successfully added/updated the donor, reset the class vars with the new data
431 $donor = $this->db->get_donor_by( 'email', $args['email'] );
432
433 // Setup the donor data with the values from DB
434 $this->setup_donor( $donor );
435
436 $created = $this->id;
437 }
438
439 /**
440 * Fires after creating donors.
441 *
442 * @since 1.0
443 *
444 * @param bool|int $created False if not a valid creation, donor ID if user is found or valid creation.
445 * @param array $args Customer attributes.
446 */
447 do_action( 'give_donor_post_create', $created, $args );
448
449 return $created;
450
451 }
452
453 /**
454 * Updates a donor record.
455 *
456 * @since 1.0
457 * @access public
458 *
459 * @param array $data Array of data attributes for a donor (checked via whitelist).
460 *
461 * @return bool If the update was successful or not.
462 */
463 public function update( $data = [] ) {
464
465 if ( empty( $data ) ) {
466 return false;
467 }
468
469 $data = $this->sanitize_columns( $data );
470
471 /**
472 * Fires before updating donors.
473 *
474 * @since 1.0
475 *
476 * @param int $donor_id Donor id.
477 * @param array $data Donor attributes.
478 */
479 do_action( 'give_donor_pre_update', $this->id, $data );
480
481 $updated = false;
482
483 if ( $this->db->update( $this->id, $data ) ) {
484
485 $donor = $this->db->get_donor_by( 'id', $this->id );
486
487 $this->setup_donor( $donor );
488
489 $updated = true;
490 }
491
492 /**
493 * Fires after updating donors.
494 *
495 * @since 1.0
496 *
497 * @param bool $updated If the update was successful or not.
498 * @param int $donor_id Donor id.
499 * @param array $data Donor attributes.
500 */
501 do_action( 'give_donor_post_update', $updated, $this->id, $data );
502
503 return $updated;
504 }
505
506 /**
507 * Attach Payment
508 *
509 * Attach payment to the donor then triggers increasing stats.
510 *
511 * @since 1.0
512 * @access public
513 *
514 * @param int $payment_id The payment ID to attach to the donor.
515 * @param bool $update_stats For backwards compatibility, if we should increase the stats or not.
516 *
517 * @return bool If the attachment was successfully.
518 */
519 public function attach_payment( $payment_id = 0, $update_stats = true ) {
520
521 if ( empty( $payment_id ) ) {
522 return false;
523 }
524
525 if ( empty( $this->payment_ids ) ) {
526
527 $new_payment_ids = $payment_id;
528
529 } else {
530
531 $payment_ids = array_map( 'absint', explode( ',', $this->payment_ids ) );
532
533 if ( in_array( $payment_id, $payment_ids ) ) {
534 $update_stats = false;
535 }
536
537 $payment_ids[] = $payment_id;
538
539 $new_payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
540
541 }
542
543 /**
544 * Fires before attaching payments to donors.
545 *
546 * @since 1.0
547 *
548 * @param int $payment_id Payment id.
549 * @param int $donor_id Donor id.
550 */
551 do_action( 'give_donor_pre_attach_payment', $payment_id, $this->id );
552
553 $payment_added = $this->update( [ 'payment_ids' => $new_payment_ids ] );
554
555 if ( $payment_added ) {
556
557 $this->payment_ids = $new_payment_ids;
558
559 // We added this payment successfully, increment the stats
560 if ( $update_stats ) {
561 $payment_amount = give_donation_amount( $payment_id, [ 'type' => 'stats' ] );
562
563 if ( ! empty( $payment_amount ) ) {
564 $this->increase_value( $payment_amount );
565 }
566
567 $this->increase_purchase_count();
568 }
569 }
570
571 /**
572 * Fires after attaching payments to the donor.
573 *
574 * @since 1.0
575 *
576 * @param bool $payment_added If the attachment was successfully.
577 * @param int $payment_id Payment id.
578 * @param int $donor_id Donor id.
579 */
580 do_action( 'give_donor_post_attach_payment', $payment_added, $payment_id, $this->id );
581
582 return $payment_added;
583 }
584
585 /**
586 * Remove Payment
587 *
588 * Remove a payment from this donor, then triggers reducing stats.
589 *
590 * @since 1.0
591 * @access public
592 *
593 * @param int $payment_id The Payment ID to remove.
594 * @param bool $update_stats For backwards compatibility, if we should increase the stats or not.
595 *
596 * @return boolean If the removal was successful.
597 */
598 public function remove_payment( $payment_id = 0, $update_stats = true ) {
599
600 if ( empty( $payment_id ) ) {
601 return false;
602 }
603
604 $payment = new Give_Payment( $payment_id );
605
606 if ( 'publish' !== $payment->status && 'revoked' !== $payment->status ) {
607 $update_stats = false;
608 }
609
610 $new_payment_ids = '';
611
612 if ( ! empty( $this->payment_ids ) ) {
613
614 $payment_ids = array_map( 'absint', explode( ',', $this->payment_ids ) );
615
616 $pos = array_search( $payment_id, $payment_ids );
617 if ( false === $pos ) {
618 return false;
619 }
620
621 unset( $payment_ids[ $pos ] );
622 $payment_ids = array_filter( $payment_ids );
623
624 $new_payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
625
626 }
627
628 /**
629 * Fires before removing payments from customers.
630 *
631 * @since 1.0
632 *
633 * @param int $payment_id Payment id.
634 * @param int $donor_id Customer id.
635 */
636 do_action( 'give_donor_pre_remove_payment', $payment_id, $this->id );
637
638 $payment_removed = $this->update( [ 'payment_ids' => $new_payment_ids ] );
639
640 if ( $payment_removed ) {
641
642 $this->payment_ids = $new_payment_ids;
643
644 if ( $update_stats ) {
645 // We removed this payment successfully, decrement the stats
646 $payment_amount = give_donation_amount( $payment_id );
647
648 if ( ! empty( $payment_amount ) ) {
649 $this->decrease_value( $payment_amount );
650 }
651
652 $this->decrease_donation_count();
653 }
654 }
655
656 /**
657 * Fires after removing payments from donors.
658 *
659 * @since 1.0
660 *
661 * @param bool $payment_removed If the removal was successfully.
662 * @param int $payment_id Payment id.
663 * @param int $donor_id Donor id.
664 */
665 do_action( 'give_donor_post_remove_payment', $payment_removed, $payment_id, $this->id );
666
667 return $payment_removed;
668
669 }
670
671 /**
672 * Increase the donation count of a donor.
673 *
674 * @since 1.0
675 * @access public
676 *
677 * @param int $count The number to increase by.
678 *
679 * @return int The donation count.
680 */
681 public function increase_purchase_count( $count = 1 ) {
682
683 // Make sure it's numeric and not negative.
684 if ( ! is_numeric( $count ) || $count != absint( $count ) ) {
685 return false;
686 }
687
688 $new_total = (int) $this->purchase_count + (int) $count;
689
690 /**
691 * Fires before increasing the donor's donation count.
692 *
693 * @since 1.0
694 *
695 * @param int $count The number to increase by.
696 * @param int $donor_id Donor id.
697 */
698 do_action( 'give_donor_pre_increase_donation_count', $count, $this->id );
699
700 if ( $this->update( [ 'purchase_count' => $new_total ] ) ) {
701 $this->purchase_count = $new_total;
702 }
703
704 /**
705 * Fires after increasing the donor's donation count.
706 *
707 * @since 1.0
708 *
709 * @param int $purchase_count Donor donation count.
710 * @param int $count The number increased by.
711 * @param int $donor_id Donor id.
712 */
713 do_action( 'give_donor_post_increase_donation_count', $this->purchase_count, $count, $this->id );
714
715 return $this->purchase_count;
716 }
717
718 /**
719 * Decrease the donor donation count.
720 *
721 * @since 1.0
722 * @access public
723 *
724 * @param int $count The amount to decrease by.
725 *
726 * @return mixed If successful, the new count, otherwise false.
727 */
728 public function decrease_donation_count( $count = 1 ) {
729
730 // Make sure it's numeric and not negative
731 if ( ! is_numeric( $count ) || $count != absint( $count ) ) {
732 return false;
733 }
734
735 $new_total = (int) $this->purchase_count - (int) $count;
736
737 if ( $new_total < 0 ) {
738 $new_total = 0;
739 }
740
741 /**
742 * Fires before decreasing the donor's donation count.
743 *
744 * @since 1.0
745 *
746 * @param int $count The number to decrease by.
747 * @param int $donor_id Customer id.
748 */
749 do_action( 'give_donor_pre_decrease_donation_count', $count, $this->id );
750
751 if ( $this->update( [ 'purchase_count' => $new_total ] ) ) {
752 $this->purchase_count = $new_total;
753 }
754
755 /**
756 * Fires after decreasing the donor's donation count.
757 *
758 * @since 1.0
759 *
760 * @param int $purchase_count Donor's donation count.
761 * @param int $count The number decreased by.
762 * @param int $donor_id Donor id.
763 */
764 do_action( 'give_donor_post_decrease_donation_count', $this->purchase_count, $count, $this->id );
765
766 return $this->purchase_count;
767 }
768
769 /**
770 * Increase the donor's lifetime value.
771 *
772 * @since 1.0
773 * @access public
774 *
775 * @param float $value The value to increase by.
776 *
777 * @return mixed If successful, the new value, otherwise false.
778 */
779 public function increase_value( $value = 0.00 ) {
780
781 $new_value = floatval( $this->purchase_value ) + $value;
782
783 /**
784 * Fires before increasing donor lifetime value.
785 *
786 * @since 1.0
787 *
788 * @param float $value The value to increase by.
789 * @param int $donor_id Customer id.
790 */
791 do_action( 'give_donor_pre_increase_value', $value, $this->id );
792
793 if ( $this->update( [ 'purchase_value' => $new_value ] ) ) {
794 $this->purchase_value = $new_value;
795 }
796
797 /**
798 * Fires after increasing donor lifetime value.
799 *
800 * @since 1.0
801 *
802 * @param float $purchase_value Donor's lifetime value.
803 * @param float $value The value increased by.
804 * @param int $donor_id Donor id.
805 */
806 do_action( 'give_donor_post_increase_value', $this->purchase_value, $value, $this->id );
807
808 return $this->purchase_value;
809 }
810
811 /**
812 * Decrease a donor's lifetime value.
813 *
814 * @since 1.0
815 * @access public
816 *
817 * @param float $value The value to decrease by.
818 *
819 * @return mixed If successful, the new value, otherwise false.
820 */
821 public function decrease_value( $value = 0.00 ) {
822
823 $new_value = floatval( $this->purchase_value ) - $value;
824
825 if ( $new_value < 0 ) {
826 $new_value = 0.00;
827 }
828
829 /**
830 * Fires before decreasing donor lifetime value.
831 *
832 * @since 1.0
833 *
834 * @param float $value The value to decrease by.
835 * @param int $donor_id Donor id.
836 */
837 do_action( 'give_donor_pre_decrease_value', $value, $this->id );
838
839 if ( $this->update( [ 'purchase_value' => $new_value ] ) ) {
840 $this->purchase_value = $new_value;
841 }
842
843 /**
844 * Fires after decreasing donor lifetime value.
845 *
846 * @since 1.0
847 *
848 * @param float $purchase_value Donor lifetime value.
849 * @param float $value The value decreased by.
850 * @param int $donor_id Donor id.
851 */
852 do_action( 'give_donor_post_decrease_value', $this->purchase_value, $value, $this->id );
853
854 return $this->purchase_value;
855 }
856
857 /**
858 * Decrease/Increase a donor's lifetime value.
859 *
860 * This function will update donation stat on basis of current amount and new amount donation difference.
861 * Difference value can positive or negative. Negative value will decrease user donation stat while positive value
862 * increase donation stat.
863 *
864 * @since 1.0
865 * @access public
866 *
867 * @param float $curr_amount Current Donation amount.
868 * @param float $new_amount New (changed) Donation amount.
869 *
870 * @return mixed If successful, the new donation stat value, otherwise false.
871 */
872 public function update_donation_value( $curr_amount, $new_amount ) {
873 /**
874 * Payment total difference value can be:
875 * zero (in case amount not change)
876 * or -ve (in case amount decrease)
877 * or +ve (in case amount increase)
878 */
879 $payment_total_diff = $new_amount - $curr_amount;
880
881 // We do not need to update donation stat if donation did not change.
882 if ( ! $payment_total_diff ) {
883 return false;
884 }
885
886 if ( $payment_total_diff > 0 ) {
887 $this->increase_value( $payment_total_diff );
888 } else {
889 // Pass payment total difference as +ve value to decrease amount from user lifetime stat.
890 $this->decrease_value( - $payment_total_diff );
891 }
892
893 return $this->purchase_value;
894 }
895
896 /**
897 * Get the parsed notes for a donor as an array.
898 *
899 * @since 1.0
900 * @access public
901 *
902 * @param int $length The number of notes to get.
903 * @param int $paged What note to start at.
904 *
905 * @return array The notes requested.
906 */
907 public function get_notes( $length = 20, $paged = 1 ) {
908
909 $length = is_numeric( $length ) ? $length : 20;
910 $offset = is_numeric( $paged ) && $paged != 1 ? ( ( absint( $paged ) - 1 ) * $length ) : 0;
911
912 $all_notes = $this->get_raw_notes();
913 $notes_array = array_reverse( array_filter( explode( "\n\n", $all_notes ) ) );
914
915 $desired_notes = array_slice( $notes_array, $offset, $length );
916
917 return $desired_notes;
918
919 }
920
921 /**
922 * Get the total number of notes we have after parsing.
923 *
924 * @since 1.0
925 * @access public
926 *
927 * @return int The number of notes for the donor.
928 */
929 public function get_notes_count() {
930
931 $all_notes = $this->get_raw_notes();
932 $notes_array = array_reverse( array_filter( explode( "\n\n", $all_notes ) ) );
933
934 return count( $notes_array );
935
936 }
937
938 /**
939 * Get the total donation amount.
940 *
941 * @since 1.8.17
942 *
943 * @param array $args Pass any additional data.
944 *
945 * @return string|float
946 */
947 public function get_total_donation_amount( $args = [] ) {
948
949 /**
950 * Filter total donation amount.
951 *
952 * @since 1.8.17
953 *
954 * @param string|float $purchase_value Donor Purchase value.
955 * @param integer $donor_id Donor ID.
956 * @param array $args Pass additional data.
957 */
958 return apply_filters( 'give_get_total_donation_amount', $this->purchase_value, $this->id, $args );
959 }
960
961 /**
962 * Add a note for the donor.
963 *
964 * @since 1.0
965 * @access public
966 *
967 * @param string $note The note to add. Default is empty.
968 *
969 * @return string|boolean The new note if added successfully, false otherwise.
970 */
971 public function add_note( $note = '' ) {
972
973 $note = trim( $note );
974 if ( empty( $note ) ) {
975 return false;
976 }
977
978 $notes = $this->get_raw_notes();
979
980 if ( empty( $notes ) ) {
981 $notes = '';
982 }
983
984 // Backward compatibility.
985 $note_string = date_i18n( 'F j, Y H:i:s', current_time( 'timestamp' ) ) . ' - ' . $note;
986 $formatted_new_note = apply_filters( 'give_customer_add_note_string', $note_string );
987 $notes .= "\n\n" . $formatted_new_note;
988
989 /**
990 * Fires before donor note is added.
991 *
992 * @since 1.0
993 *
994 * @param string $formatted_new_note Formatted new note to add.
995 * @param int $donor_id Donor id.
996 */
997 do_action( 'give_donor_pre_add_note', $formatted_new_note, $this->id );
998
999 if ( ! give_has_upgrade_completed( 'v230_move_donor_note' ) ) {
1000 // Backward compatibility.
1001 $updated = $this->update( [ 'notes' => $notes ] );
1002 } else {
1003 $updated = Give()->comment->db->add(
1004 [
1005 'comment_content' => $note,
1006 'user_id' => get_current_user_id(),
1007 'comment_parent' => $this->id,
1008 'comment_type' => 'donor',
1009 ]
1010 );
1011 }
1012
1013 if ( $updated ) {
1014 $this->notes = $this->get_notes();
1015 }
1016
1017 /**
1018 * Fires after donor note added.
1019 *
1020 * @since 1.0
1021 *
1022 * @param array $donor_notes Donor notes.
1023 * @param string $formatted_new_note Formatted new note added.
1024 * @param int $donor_id Donor id.
1025 */
1026 do_action( 'give_donor_post_add_note', $this->notes, $formatted_new_note, $this->id );
1027
1028 // Return the formatted note, so we can test, as well as update any displays
1029 return $formatted_new_note;
1030 }
1031
1032 /**
1033 * Get the notes column for the donor
1034 *
1035 * @since 1.0
1036 * @access private
1037 *
1038 * @return string The Notes for the donor, non-parsed.
1039 */
1040 private function get_raw_notes() {
1041 $all_notes = '';
1042 $comments = Give()->comment->db->get_results_by( [ 'comment_parent' => $this->id ] );
1043
1044 // Generate notes output as we are doing before 2.3.0.
1045 if ( ! empty( $comments ) ) {
1046 /* @var stdClass $comment */
1047 foreach ( $comments as $comment ) {
1048 $all_notes .= date_i18n( 'F j, Y H:i:s', strtotime( $comment->comment_date ) ) . " - {$comment->comment_content}\n\n";
1049 }
1050 }
1051
1052 // Backward compatibility.
1053 if ( ! give_has_upgrade_completed( 'v230_move_donor_note' ) ) {
1054 $all_notes = $this->db->get_column( 'notes', $this->id );
1055 }
1056
1057 return $all_notes;
1058
1059 }
1060
1061 /**
1062 * Retrieve a meta field for a donor.
1063 *
1064 * @since 1.6
1065 * @access public
1066 *
1067 * @param string $meta_key The meta key to retrieve. Default is empty.
1068 * @param bool $single Whether to return a single value. Default is true.
1069 *
1070 * @return mixed Will be an array if $single is false. Will be value of meta data field if $single is
1071 * true.
1072 */
1073 public function get_meta( $meta_key = '', $single = true ) {
1074 return Give()->donor_meta->get_meta( $this->id, $meta_key, $single );
1075 }
1076
1077 /**
1078 * Add a meta data field to a donor.
1079 *
1080 * @since 1.6
1081 * @access public
1082 *
1083 * @param string $meta_key Metadata name. Default is empty.
1084 * @param mixed $meta_value Metadata value.
1085 * @param bool $unique Optional. Whether the same key should not be added. Default is false.
1086 *
1087 * @return bool False for failure. True for success.
1088 */
1089 public function add_meta( $meta_key, $meta_value, $unique = false ) {
1090 return Give()->donor_meta->add_meta( $this->id, $meta_key, $meta_value, $unique );
1091 }
1092
1093 /**
1094 * Update a meta field based on donor ID.
1095 *
1096 * @since 1.6
1097 * @access public
1098 *
1099 * @param string $meta_key Metadata key. Default is empty.
1100 * @param mixed $meta_value Metadata value.
1101 * @param mixed $prev_value Optional. Previous value to check before removing. Default is empty.
1102 *
1103 * @return bool False on failure, true if success.
1104 */
1105 public function update_meta( $meta_key, $meta_value, $prev_value = '' ) {
1106 return Give()->donor_meta->update_meta( $this->id, $meta_key, $meta_value, $prev_value );
1107 }
1108
1109 /**
1110 * Remove metadata matching criteria from a donor.
1111 *
1112 * @since 1.6
1113 * @access public
1114 *
1115 * @param string $meta_key Metadata name. Default is empty.
1116 * @param mixed $meta_value Optional. Metadata value. Default is empty.
1117 *
1118 * @return bool False for failure. True for success.
1119 */
1120 public function delete_meta( $meta_key = '', $meta_value = '' ) {
1121 return Give()->donor_meta->delete_meta( $this->id, $meta_key, $meta_value );
1122 }
1123
1124 /**
1125 * Sanitize the data for update/create
1126 *
1127 * @since 1.0
1128 * @access private
1129 *
1130 * @param array $data The data to sanitize.
1131 *
1132 * @return array The sanitized data, based off column defaults.
1133 */
1134 private function sanitize_columns( $data ) {
1135
1136 $columns = $this->db->get_columns();
1137 $default_values = $this->db->get_column_defaults();
1138
1139 foreach ( $columns as $key => $type ) {
1140
1141 // Only sanitize data that we were provided
1142 if ( ! array_key_exists( $key, $data ) ) {
1143 continue;
1144 }
1145
1146 switch ( $type ) {
1147
1148 case '%s':
1149 if ( 'email' == $key ) {
1150 $data[ $key ] = sanitize_email( $data[ $key ] );
1151 } elseif ( 'notes' == $key ) {
1152 $data[ $key ] = strip_tags( $data[ $key ] );
1153 } else {
1154 $data[ $key ] = sanitize_text_field( $data[ $key ] );
1155 }
1156 break;
1157
1158 case '%d':
1159 if ( ! is_numeric( $data[ $key ] ) || (int) $data[ $key ] !== absint( $data[ $key ] ) ) {
1160 $data[ $key ] = $default_values[ $key ];
1161 } else {
1162 $data[ $key ] = absint( $data[ $key ] );
1163 }
1164 break;
1165
1166 case '%f':
1167 // Convert what was given to a float
1168 $value = floatval( $data[ $key ] );
1169
1170 if ( ! is_float( $value ) ) {
1171 $data[ $key ] = $default_values[ $key ];
1172 } else {
1173 $data[ $key ] = $value;
1174 }
1175 break;
1176
1177 default:
1178 $data[ $key ] = sanitize_text_field( $data[ $key ] );
1179 break;
1180
1181 }
1182 }
1183
1184 return $data;
1185 }
1186
1187 /**
1188 * Attach an email to the donor
1189 *
1190 * @since 1.7
1191 * @access public
1192 *
1193 * @param string $email The email address to attach to the donor
1194 * @param bool $primary Allows setting the email added as the primary
1195 *
1196 * @return bool If the email was added successfully
1197 */
1198 public function add_email( $email = '', $primary = false ) {
1199 if ( ! is_email( $email ) ) {
1200 return false;
1201 }
1202 $existing = new Give_Donor( $email );
1203
1204 if ( $existing->id > 0 ) {
1205 // Email address already belongs to another donor
1206 return false;
1207 }
1208
1209 if ( email_exists( $email ) ) {
1210 $user = get_user_by( 'email', $email );
1211 if ( $user->ID != $this->user_id ) {
1212 return false;
1213 }
1214 }
1215
1216 do_action( 'give_donor_pre_add_email', $email, $this->id, $this );
1217
1218 // Add is used to ensure duplicate emails are not added
1219 $ret = (bool) $this->add_meta( 'additional_email', $email );
1220
1221 do_action( 'give_donor_post_add_email', $email, $this->id, $this );
1222
1223 if ( $ret && true === $primary ) {
1224 $this->set_primary_email( $email );
1225 }
1226
1227 return $ret;
1228 }
1229
1230 /**
1231 * Remove an email from the donor.
1232 *
1233 * @since 1.7
1234 * @access public
1235 *
1236 * @param string $email The email address to remove from the donor.
1237 *
1238 * @return bool If the email was removed successfully.
1239 */
1240 public function remove_email( $email = '' ) {
1241
1242 if ( ! is_email( $email ) ) {
1243 return false;
1244 }
1245
1246 do_action( 'give_donor_pre_remove_email', $email, $this->id, $this );
1247
1248 $ret = (bool) $this->delete_meta( 'additional_email', $email );
1249
1250 do_action( 'give_donor_post_remove_email', $email, $this->id, $this );
1251
1252 return $ret;
1253 }
1254
1255 /**
1256 * Set an email address as the donor's primary email.
1257 *
1258 * This will move the donor's previous primary email to an additional email.
1259 *
1260 * @since 1.7
1261 * @access public
1262 *
1263 * @param string $new_primary_email The email address to remove from the donor.
1264 *
1265 * @return bool If the email was set as primary successfully.
1266 */
1267 public function set_primary_email( $new_primary_email = '' ) {
1268 if ( ! is_email( $new_primary_email ) ) {
1269 return false;
1270 }
1271
1272 do_action( 'give_donor_pre_set_primary_email', $new_primary_email, $this->id, $this );
1273
1274 $existing = new Give_Donor( $new_primary_email );
1275
1276 if ( $existing->id > 0 && (int) $existing->id !== (int) $this->id ) {
1277 // This email belongs to another donor.
1278 return false;
1279 }
1280
1281 $old_email = $this->email;
1282
1283 // Update donor record with new email.
1284 $update = $this->update( [ 'email' => $new_primary_email ] );
1285
1286 // Remove new primary from list of additional emails.
1287 $remove = $this->remove_email( $new_primary_email );
1288
1289 // Add old email to additional emails list.
1290 $add = $this->add_email( $old_email );
1291
1292 $ret = $update && $remove && $add;
1293
1294 if ( $ret ) {
1295 $this->email = $new_primary_email;
1296 }
1297
1298 do_action( 'give_donor_post_set_primary_email', $new_primary_email, $this->id, $this );
1299
1300 return $ret;
1301 }
1302
1303 /**
1304 * Check if address valid or not.
1305 *
1306 * @since 2.0
1307 * @access private
1308 *
1309 * @param $address
1310 *
1311 * @return bool
1312 */
1313 private function is_valid_address( $address ) {
1314 $is_valid_address = true;
1315
1316 // Address ready to process even if only one value set.
1317 foreach ( $address as $address_type => $value ) {
1318 // @todo: Handle state field validation on basis of country.
1319 if ( in_array( $address_type, [ 'line2', 'state' ] ) ) {
1320 continue;
1321 }
1322
1323 if ( empty( $value ) ) {
1324 $is_valid_address = false;
1325 break;
1326 }
1327 }
1328
1329 return $is_valid_address;
1330 }
1331
1332 /**
1333 * Add donor address
1334 *
1335 * @since 2.0
1336 * @access public
1337 *
1338 * @param string $address_type
1339 * @param array $address {
1340 *
1341 * @type string $address2
1342 * @type string city
1343 * @type string zip
1344 * @type string state
1345 * @type string country
1346 * }
1347 *
1348 * @return bool
1349 */
1350 public function add_address( $address_type, $address ) {
1351 // Bailout.
1352 if ( empty( $address_type ) || ! $this->is_valid_address( $address ) || ! $this->id ) {
1353 return false;
1354 }
1355
1356 // Check if multiple address exist or not and set params.
1357 $multi_address_id = null;
1358 if ( $is_multi_address = ( false !== strpos( $address_type, '[]' ) ) ) {
1359 $address_type = $is_multi_address ? str_replace( '[]', '', $address_type ) : $address_type;
1360 } elseif ( $is_multi_address = ( false !== strpos( $address_type, '_' ) ) ) {
1361 $exploded_address_type = explode( '_', $address_type );
1362 $multi_address_id = $is_multi_address ? array_pop( $exploded_address_type ) : $address_type;
1363
1364 $address_type = $is_multi_address ? array_shift( $exploded_address_type ) : $address_type;
1365 }
1366
1367 // Bailout: do not save duplicate orders
1368 if ( $this->does_address_exist( $address_type, $address ) && $multi_address_id === null ) {
1369 return false;
1370 }
1371
1372 // Set default address.
1373 $address = wp_parse_args(
1374 $address,
1375 [
1376 'line1' => '',
1377 'line2' => '',
1378 'city' => '',
1379 'state' => '',
1380 'country' => '',
1381 'zip' => '',
1382 ]
1383 );
1384
1385 // Set meta key prefix.
1386 global $wpdb;
1387 $meta_key_prefix = "_give_donor_address_{$address_type}_{address_name}";
1388 $meta_type = Give()->donor_meta->meta_type;
1389
1390 if ( $is_multi_address ) {
1391 if ( is_null( $multi_address_id ) ) {
1392 // Get latest address key to set multi address id.
1393 $multi_address_id = $wpdb->get_var(
1394 $wpdb->prepare(
1395 "
1396 SELECT meta_key FROM {$wpdb->donormeta}
1397 WHERE meta_key
1398 LIKE '%s'
1399 AND {$meta_type}_id=%d
1400 ORDER BY meta_id DESC
1401 LIMIT 1
1402 ",
1403 "%_give_donor_address_{$address_type}_line1%",
1404 $this->id
1405 )
1406 );
1407
1408 if ( ! empty( $multi_address_id ) ) {
1409 $multi_address_id = absint( substr( strrchr( $multi_address_id, '_' ), 1 ) );
1410 $multi_address_id ++;
1411 } else {
1412 $multi_address_id = 0;
1413 }
1414 }
1415
1416 $meta_key_prefix = "_give_donor_address_{$address_type}_{address_name}_{$multi_address_id}";
1417 }
1418
1419 // Save donor address.
1420 foreach ( $address as $type => $value ) {
1421 $meta_key = str_replace( '{address_name}', $type, $meta_key_prefix );
1422 Give()->donor_meta->update_meta( $this->id, $meta_key, $value );
1423 }
1424
1425 $this->setup_address();
1426
1427 return true;
1428 }
1429
1430 /**
1431 * Remove donor address
1432 *
1433 * @since 2.0
1434 * @access public
1435 * @global wpdb $wpdb
1436 *
1437 * @param string $address_id
1438 *
1439 * @return bool
1440 */
1441 public function remove_address( $address_id ) {
1442 global $wpdb;
1443
1444 // Get address type.
1445 $is_multi_address = false !== strpos( $address_id, '_' ) ? true : false;
1446
1447 $address_key_arr = explode( '_', $address_id );
1448
1449 $address_type = false !== strpos( $address_id, '_' ) ? array_shift( $address_key_arr ) : $address_id;
1450 $address_count = false !== strpos( $address_id, '_' ) ? array_pop( $address_key_arr ) : null;
1451
1452 // Set meta key prefix.
1453 $meta_key_prefix = "_give_donor_address_{$address_type}_%";
1454 if ( $is_multi_address && is_numeric( $address_count ) ) {
1455 $meta_key_prefix .= "_{$address_count}";
1456 }
1457
1458 $meta_type = Give()->donor_meta->meta_type;
1459
1460 // Process query.
1461 $row_affected = $wpdb->query(
1462 $wpdb->prepare(
1463 "
1464 DELETE FROM {$wpdb->donormeta}
1465 WHERE meta_key
1466 LIKE '%s'
1467 AND {$meta_type}_id=%d
1468 ",
1469 $meta_key_prefix,
1470 $this->id
1471 )
1472 );
1473
1474 // Delete cache.
1475 Give_Cache::delete_group( $this->id, 'give-donors' );
1476 wp_cache_delete( $this->id, "{$meta_type}_meta" );
1477
1478 $this->setup_address();
1479
1480 return (bool) $row_affected;
1481 }
1482
1483 /**
1484 * Update donor address
1485 *
1486 * @since 2.0
1487 * @access public
1488 * @global wpdb $wpdb
1489 *
1490 * @param string $address_id
1491 * @param array $address
1492 *
1493 * @return bool
1494 */
1495 public function update_address( $address_id, $address ) {
1496 global $wpdb;
1497
1498 // Get address type.
1499 $is_multi_address = false !== strpos( $address_id, '_' ) ? true : false;
1500 $exploded_address_id = explode( '_', $address_id );
1501
1502 $address_type = false !== strpos( $address_id, '_' ) ? array_shift( $exploded_address_id ) : $address_id;
1503
1504 $address_count = false !== strpos( $address_id, '_' ) ? array_pop( $exploded_address_id ) : null;
1505
1506 // Set meta key prefix.
1507 $meta_key_prefix = "_give_donor_address_{$address_type}_%";
1508 if ( $is_multi_address && is_numeric( $address_count ) ) {
1509 $meta_key_prefix .= "_{$address_count}";
1510 }
1511
1512 $meta_type = Give()->donor_meta->meta_type;
1513
1514 // Process query.
1515 $row_affected = $wpdb->get_results(
1516 $wpdb->prepare(
1517 "
1518 SELECT meta_key FROM {$wpdb->donormeta}
1519 WHERE meta_key
1520 LIKE '%s'
1521 AND {$meta_type}_id=%d
1522 ",
1523 $meta_key_prefix,
1524 $this->id
1525 )
1526 );
1527
1528 // Return result.
1529 if ( ! count( $row_affected ) ) {
1530 return false;
1531 }
1532
1533 // Update address.
1534 if ( ! $this->add_address( $address_id, $address ) ) {
1535 return false;
1536 }
1537
1538 return true;
1539 }
1540
1541
1542 /**
1543 * Check if donor already has current address
1544 *
1545 * @since 2.0
1546 * @access public
1547 *
1548 * @param string $current_address_type
1549 * @param array $current_address
1550 *
1551 * @return bool|null
1552 */
1553 public function does_address_exist( $current_address_type, $current_address ) {
1554 $status = false;
1555
1556 // Bailout.
1557 if ( empty( $current_address_type ) || empty( $current_address ) ) {
1558 return null;
1559 }
1560
1561 // Bailout.
1562 if ( empty( $this->address ) || empty( $this->address[ $current_address_type ] ) ) {
1563 return $status;
1564 }
1565
1566 // Get address.
1567 $address = $this->address[ $current_address_type ];
1568
1569 switch ( true ) {
1570
1571 // Single address.
1572 case is_string( end( $address ) ):
1573 $status = $this->is_address_match( $current_address, $address );
1574 break;
1575
1576 // Multi address.
1577 case is_array( end( $address ) ):
1578 // Compare address.
1579 foreach ( $address as $saved_address ) {
1580 if ( empty( $saved_address ) ) {
1581 continue;
1582 }
1583
1584 // Exit loop immediately if address exist.
1585 if ( $status = $this->is_address_match( $current_address, $saved_address ) ) {
1586 break;
1587 }
1588 }
1589 break;
1590 }
1591
1592 return $status;
1593 }
1594
1595 /**
1596 * Compare address.
1597 *
1598 * @since 2.0
1599 * @access private
1600 *
1601 * @param array $address_1
1602 * @param array $address_2
1603 *
1604 * @return bool
1605 */
1606 private function is_address_match( $address_1, $address_2 ) {
1607 $result = array_diff_assoc( $address_1, $address_2 );
1608
1609 return empty( $result );
1610 }
1611
1612 /**
1613 * Split donor name into first name and last name
1614 *
1615 * @param int $id Donor ID
1616 *
1617 * @since 2.0
1618 * @return object
1619 */
1620 public function split_donor_name( $id ) {
1621 $first_name = $last_name = '';
1622 $donor = new Give_Donor( $id );
1623
1624 $split_donor_name = explode( ' ', $donor->name, 2 );
1625
1626 // Check for existence of first name after split of donor name.
1627 if ( is_array( $split_donor_name ) && ! empty( $split_donor_name[0] ) ) {
1628 $first_name = $split_donor_name[0];
1629 }
1630
1631 // Check for existence of last name after split of donor name.
1632 if ( is_array( $split_donor_name ) && ! empty( $split_donor_name[1] ) ) {
1633 $last_name = $split_donor_name[1];
1634 }
1635
1636 return (object) [
1637 'first_name' => $first_name,
1638 'last_name' => $last_name,
1639 ];
1640 }
1641
1642 /**
1643 * Retrieves first name of donor with backward compatibility
1644 *
1645 * @since 2.0
1646 * @return string
1647 */
1648 public function get_first_name() {
1649 $first_name = $this->get_meta( '_give_donor_first_name' );
1650 if ( ! $first_name ) {
1651 $first_name = $this->split_donor_name( $this->id )->first_name;
1652 }
1653
1654 return $first_name;
1655 }
1656
1657 /**
1658 * Retrieves last name of donor with backward compatibility
1659 *
1660 * @since 2.0
1661 * @return string
1662 */
1663 public function get_last_name() {
1664 $first_name = $this->get_meta( '_give_donor_first_name' );
1665 $last_name = $this->get_meta( '_give_donor_last_name' );
1666
1667 // This condition will prevent unnecessary splitting of donor name to fetch last name.
1668 if ( ! $first_name && ! $last_name ) {
1669 $last_name = $this->split_donor_name( $this->id )->last_name;
1670 }
1671
1672 return ( $last_name ) ? $last_name : '';
1673 }
1674
1675 /**
1676 * Retrieves company name of donor
1677 *
1678 * @since 2.1
1679 *
1680 * @return string $company_name Donor Company Name
1681 */
1682 public function get_company_name() {
1683 $company_name = $this->get_meta( '_give_donor_company' );
1684
1685 return $company_name;
1686 }
1687
1688 /**
1689 * Retrieves last donation for the donor.
1690 *
1691 * @since 2.1
1692 *
1693 * @return string $company_name Donor Company Name
1694 */
1695 public function get_last_donation() {
1696
1697 $payments = array_unique( array_values( explode( ',', $this->payment_ids ) ) );
1698
1699 return end( $payments );
1700
1701 }
1702
1703 /**
1704 * Retrieves last donation for the donor.
1705 *
1706 * @since 2.1
1707 *
1708 * @param bool $formatted Whether to return with the date format or not.
1709 *
1710 * @return string The date of the last donation.
1711 */
1712 public function get_last_donation_date( $formatted = false ) {
1713 $completed_data = '';
1714
1715 // Return if donation id is invalid.
1716 if ( ! ( $last_donation = absint( $this->get_last_donation() ) ) ) {
1717 return $completed_data;
1718 }
1719
1720 $completed_data = give_get_payment_completed_date( $last_donation );
1721
1722 if ( $formatted ) {
1723 return date_i18n( give_date_format(), strtotime( $completed_data ) );
1724 }
1725
1726 return $completed_data;
1727
1728 }
1729
1730 /**
1731 * Retrieves a donor's initials (first name and last name).
1732 *
1733 * @since 2.1
1734 *
1735 * @return string The donor's two initials (no middle).
1736 */
1737 public function get_donor_initals() {
1738 /**
1739 * Filter the donor name initials
1740 *
1741 * @since 2.1.0
1742 */
1743 return apply_filters(
1744 'get_donor_initals',
1745 give_get_name_initial(
1746 [
1747 'firstname' => $this->get_first_name(),
1748 'lastname' => $this->get_last_name(),
1749 ]
1750 )
1751 );
1752
1753 }
1754
1755 }
1756