PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.12.3
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.12.3
1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 1.7.0.9 All 211 releases
charitable / includes / users / class-charitable-user.php

class-charitable-user.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.12.3, at includes/users/class-charitable-user.php

1,153 lines 29.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Contains the class that models users in Charitable.
4 *
5 * There are several different user roles in Charitable, and one user
6 * may be more than one. People who make donations get the "donor" role;
7 * people who create campaigns (via Charitable Ambassadors) get
8 * the "campaign_creator" role; people who create fundraisers for campaigns
9 * get the "fundraiser" role.
10 *
11 * @package Charitable/Classes/Charitable_User
12 * @author David Bisset
13 * @copyright Copyright (c) 2023, WP Charitable LLC
14 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
15 * @since 1.0.0
16 * @version 1.8.9.1
17 */
18
19 // Exit if accessed directly.
20 if ( ! defined( 'ABSPATH' ) ) {
21 exit;
22 }
23
24 if ( ! class_exists( 'Charitable_User' ) ) :
25
26 /**
27 * Charitable_User
28 *
29 * @since 1.0.0
30 *
31 * @property string $nickname
32 * @property string $description
33 * @property string $user_description
34 * @property string $first_name
35 * @property string $user_firstname
36 * @property string $last_name
37 * @property string $user_lastname
38 * @property string $user_login
39 * @property string $user_pass
40 * @property string $user_nicename
41 * @property string $user_email
42 * @property string $user_url
43 * @property string $user_registered
44 * @property string $user_activation_key
45 * @property string $user_status
46 * @property int $user_level
47 * @property string $display_name
48 * @property string $spam
49 * @property string $deleted
50 * @property string $locale
51 */
52 class Charitable_User extends WP_User {
53
54 /**
55 * The donor ID.
56 *
57 * NOTE: This is not the same as the user ID.
58 *
59 * @since 1.0.0
60 *
61 * @var int
62 */
63 protected $donor_id;
64
65 /**
66 * A mapping of user keys.
67 *
68 * @since 1.4.0
69 *
70 * @var string[]
71 */
72 protected $mapped_keys;
73
74 /**
75 * Core keys.
76 *
77 * @since 1.4.0
78 *
79 * @var string[]
80 */
81 protected $core_keys;
82
83 /**
84 * Create class object.
85 *
86 * @since 1.0.0
87 *
88 * @param int|string|stdClass|WP_User $id User's ID, a WP_User object, or a user object from the DB.
89 * @param string $name Optional. User's username.
90 * @param int $blog_id Optional Blog ID, defaults to current blog.
91 * @return void
92 */
93 public function __construct( $id = 0, $name = '', $blog_id = '' ) { // phpcs:ignore
94 parent::__construct( $id, $name, $blog_id );
95 }
96
97 /**
98 * Create object using a donor ID.
99 *
100 * @since 1.0.0
101 *
102 * @param int $donor_id The donor ID.
103 * @return Charitable_user
104 */
105 public static function init_with_donor( $donor_id ) {
106 $user_id = charitable_get_table( 'donors' )->get_user_id( $donor_id );
107 $user = charitable_get_user( $user_id );
108 $user->set_donor_id( $donor_id );
109 return $user;
110 }
111
112 /**
113 * Magic getter method. Looks for the specified key in the mapped keys before using WP_User's __get method.
114 *
115 * @since 1.0.0
116 *
117 * @param string $key The key to retrieve.
118 * @return mixed
119 */
120 public function __get( $key ) {
121 return parent::__get( $this->map_key( $key ) );
122 }
123
124 /**
125 * Display the donor name when printing the object.
126 *
127 * @since 1.0.0
128 *
129 * @return string
130 */
131 public function __toString() {
132 return $this->get_name();
133 }
134
135 /**
136 * Determine whether a property or meta key is set
137 *
138 * Consults the users and usermeta tables.
139 *
140 * @since 1.5.0
141 *
142 * @param string $key Property.
143 * @return boolean
144 */
145 public function has_prop( $key ) {
146 return parent::has_prop( $this->map_key( $key ) );
147 }
148
149 /**
150 * Returns whether the user is logged in.
151 *
152 * @since 1.0.0
153 *
154 * @return boolean
155 */
156 public function is_logged_in() {
157 return 0 !== $this->ID;
158 }
159
160 /**
161 * Set the donor ID of this user.
162 *
163 * @since 1.0.0
164 *
165 * @param int $donor_id The Donor ID.
166 * @return void
167 */
168 public function set_donor_id( $donor_id ) {
169 $this->donor_id = $donor_id;
170 }
171
172 /**
173 * Return the donor ID of this user.
174 *
175 * @since 1.0.0
176 *
177 * @return int|false $donor_id
178 */
179 public function get_donor_id() {
180 if ( isset( $this->donor_id ) || ! is_null( $this->get_donor() ) ) {
181 return $this->donor_id;
182 }
183
184 return false;
185 }
186
187 /**
188 * Return the donor record.
189 *
190 * @since 1.0.0
191 *
192 * @return Object|null Object if a donor record could be matched. null if user is logged out or no donor record found.
193 */
194 public function get_donor() {
195 if ( ! $this->is_logged_in() && ! isset( $this->donor_id ) ) {
196 return null;
197 }
198
199 if ( isset( $this->donor_id ) ) {
200
201 $donor = wp_cache_get( $this->donor_id, 'donors' );
202
203 if ( is_object( $donor ) ) {
204 return $donor;
205 }
206
207 $donor = charitable_get_table( 'donors' )->get( $this->donor_id );
208
209 } else {
210
211 $donor = charitable_get_table( 'donors' )->get_by( 'email', $this->get( 'user_email' ) );
212
213 if ( ! is_object( $donor ) ) {
214 return null;
215 }
216
217 $this->donor_id = $donor->donor_id;
218
219 }//end if
220
221 wp_cache_add( $this->donor_id, $donor, 'donors' );
222
223 return $donor;
224 }
225
226 /**
227 * Returns whether the user has ever made a donation.
228 *
229 * @since 1.0.0
230 *
231 * @return boolean
232 */
233 public function is_donor() {
234 return ! is_null( $this->get_donor() );
235 }
236
237 /**
238 * Returns whether the user has verified their email address.
239 *
240 * @since 1.5.0
241 *
242 * @return int If verified, return 1. Otherwise return 0.
243 */
244 public function is_verified() {
245 return absint( get_user_meta( $this->ID, '_charitable_user_verified', true ) );
246 }
247
248 /**
249 * Mark a user as verified.
250 *
251 * @since 1.5.0
252 *
253 * @param boolean $verified Whether the user is verified.
254 * @return int|boolean Meta ID if the key didn't exist, true on successful update, false on failure.
255 */
256 public function mark_as_verified( $verified ) {
257 $verified = update_user_meta( $this->ID, '_charitable_user_verified', $verified );
258
259 if ( ! $verified ) {
260 return $verified;
261 }
262
263 /* Delete the time that the verification email was sent. */
264 delete_user_meta( $this->ID, '_charitable_email_verification_email_send_time' );
265
266 /* Check for a notice about having sent a verification email and remove it. */
267 charitable_get_notices()->clear_notice( "/We have sent you an email to confirm your email address. Haven\'t received the email\?/" );
268
269 /* Check for an existing donor account. */
270 $donor_id = $this->get_donor_id();
271
272 if ( $donor_id ) {
273 $donor = new Charitable_Donor( $donor_id );
274 $donor->set_user_id( $this->ID );
275 }
276
277 /**
278 * Do something when a user is verified.
279 *
280 * @since 1.6.23
281 *
282 * @param Charitable_User $user The user object.
283 */
284 do_action( 'charitable_user_verified', $this );
285
286 return $verified;
287 }
288
289 /**
290 * Returns the email address of the donor.
291 *
292 * @since 1.0.0
293 *
294 * @return string
295 */
296 public function get_email() {
297 if ( $this->get_donor() ) {
298 $email = $this->get_donor()->email;
299 } else {
300 $email = $this->get( 'user_email' );
301 }
302
303 /**
304 * Filter the user email address.
305 *
306 * @since 1.0.0
307 *
308 * @param string $email The email address.
309 * @param Charitable_User $user This instance of `Charitable_User`.
310 */
311 return apply_filters( 'charitable_user_email', $email, $this );
312 }
313
314 /**
315 * Returns the display name of the user.
316 *
317 * @since 1.0.0
318 *
319 * @return string
320 */
321 public function get_name() {
322 if ( $this->is_donor() ) {
323 $name = rtrim( sprintf( '%s %s', $this->get_donor()->first_name, $this->get_donor()->last_name ) );
324 } else {
325 $name = $this->display_name;
326 }
327
328 if ( ! $name ) {
329 $name = '';
330 }
331
332 return apply_filters( 'charitable_user_name', $name, $this );
333 }
334
335 /**
336 * Returns the first name of the user.
337 *
338 * @since 1.1.0
339 *
340 * @return string
341 */
342 public function get_first_name() {
343 if ( $this->is_donor() && $this->get_donor()->first_name ) {
344 $name = $this->get_donor()->first_name;
345 } else {
346 $name = $this->first_name;
347 }
348
349 return apply_filters( 'charitable_user_first_name', $name, $this );
350 }
351
352 /**
353 * Returns the last name of the user.
354 *
355 * @since 1.1.0
356 *
357 * @return string
358 */
359 public function get_last_name() {
360 if ( $this->is_donor() && $this->get_donor()->last_name ) {
361 $name = $this->get_donor()->last_name;
362 } else {
363 $name = $this->last_name;
364 }
365 return apply_filters( 'charitable_user_last_name', $name, $this );
366 }
367
368 /**
369 * Returns the user's location.
370 *
371 * @since 1.0.0
372 *
373 * @return string
374 */
375 public function get_location() {
376 $city = $this->get( 'donor_city' );
377 $state = $this->get( 'donor_state' );
378 $country = $this->get( 'donor_country' );
379 $location = '';
380
381 if ( strlen( $city ) || strlen( $state ) ) {
382 $region = strlen( $city ) ? $city : $state;
383
384 if ( strlen( $country ) ) {
385 $location = sprintf( '%s, %s', $region, $country );
386 } else {
387 $location = $region;
388 }
389 } elseif ( strlen( $country ) ) {
390 $location = $country;
391 }
392
393 return apply_filters( 'charitable_user_location', $location, $this );
394 }
395
396 /**
397 * Returns printable address of donor.
398 *
399 * @since 1.0.0
400 *
401 * @param int $donation_id Optional. If set, will return the address provided for the specific donation. Otherwise, returns the current address for the user.
402 * @return string
403 */
404 public function get_address( $donation_id = '' ) {
405 $address_details = false;
406
407 if ( $donation_id ) {
408 $address_details = get_post_meta( $donation_id, 'donor', true );
409 }
410
411 /* If the address fields were not set by the check above, get them from the user meta. */
412 if ( ! is_array( $address_details ) ) {
413
414 $address_details = array(
415 'first_name' => $this->get( 'first_name' ),
416 'last_name' => $this->get( 'last_name' ),
417 'company' => $this->get( 'donor_company' ),
418 'address' => $this->get( 'donor_address' ),
419 'address_2' => $this->get( 'donor_address_2' ),
420 'city' => $this->get( 'donor_city' ),
421 'state' => $this->get( 'donor_state' ),
422 'postcode' => $this->get( 'donor_postcode' ),
423 'country' => $this->get( 'donor_country' ),
424 );
425
426 }
427
428 /**
429 * Filter the address details for the current user.
430 *
431 * Please note that prior to 1.6.44, the hook used here was `charitable_user_address_fields`,
432 * which is used elsewhere in a different context and thus resulted in unexpected results.
433 *
434 * @since 1.6.44
435 *
436 * @param array $address_details The address details.
437 * @param Charitable_User $user The user object.
438 * @param int $donation_id The donation ID.
439 */
440 $address_details = apply_filters( 'charitable_user_address_details', $address_details, $this, $donation_id );
441
442 return charitable_get_location_helper()->get_formatted_address( $address_details );
443 }
444
445 /**
446 * Return all donations made by donor.
447 *
448 * @since 1.0.0
449 *
450 * @param boolean $distinct_donations If true, will only count unique donations.
451 * @return object[]
452 */
453 public function get_donations( $distinct_donations = false ) {
454 return charitable_get_table( 'campaign_donations' )->get_donations_by_donor( $this->get_donor_id(), $distinct_donations );
455 }
456
457 /**
458 * Return the number of donations made by the donor.
459 *
460 * @since 1.0.0
461 *
462 * @param boolean $distinct_donations If true, will only count unique donations.
463 * @return int
464 */
465 public function count_donations( $distinct_donations = false ) {
466 return charitable_get_table( 'campaign_donations' )->count_donations_by_donor( $this->get_donor_id(), $distinct_donations );
467 }
468
469 /**
470 * Return the number of campaigns that the donor has supported.
471 *
472 * @since 1.0.0
473 *
474 * @return int
475 */
476 public function count_campaigns_supported() {
477 return charitable_get_table( 'campaign_donations' )->count_campaigns_supported_by_donor( $this->get_donor_id() );
478 }
479
480 /**
481 * Return the total amount donated by the donor.
482 *
483 * @since 1.0.0
484 * @version 1.8.1
485 *
486 * @param int $campaign_id Optional. If set, returns total donated to this particular campaign.
487 * @return float
488 */
489 public function get_total_donated( $campaign_id = false ) {
490 $amount = wp_cache_get( $this->get_donor_id(), 'charitable_donor_total_donation_amount_' . $campaign_id );
491
492 if ( false === $amount ) {
493
494 $args = apply_filters(
495 'charitable_user_total_donated_query_args',
496 array(
497 'output' => 'raw',
498 'donor_id' => $this->get_donor_id(),
499 'distinct_donors' => true,
500 'fields' => 'amount',
501 'campaign' => (int) $campaign_id,
502 ),
503 $this
504 );
505
506 $query = new Charitable_Donor_Query( $args );
507
508 if ( is_object( $query->current() ) ) { // test to see if it's an object.
509
510 $amount = $query->current()->amount;
511
512 wp_cache_set( $this->get_donor_id(), $amount, 'charitable_donor_total_donation_amount_' . $campaign_id );
513
514 }
515 }
516
517 return (float) $amount;
518 }
519
520 /**
521 * Returns the user's avatar as a fully formatted <img> tag.
522 *
523 * By default, this will return the gravatar, but it can
524 * be extended to add support for locally hosted avatars.
525 *
526 * @since 1.0.0
527 *
528 * @param int $size The length and width of the avatar.
529 * @param int $donor_id Optional. The donor ID.
530 * @return string
531 */
532 public function get_avatar( $size = 100, $donor_id = false ) {
533 /**If you use this filter, return an attachment ID. */
534
535 $avatar_attachment_id = get_user_meta( $this->ID, 'avatar', true );
536
537 $avatar_attachment_id = apply_filters( 'charitable_user_avatar', $avatar_attachment_id, $this );
538
539 /* If we don't have an attachment ID, display the gravatar. */
540 if ( ! $avatar_attachment_id ) {
541 return get_avatar( $this->ID, $size );
542 }
543
544 $img_size = apply_filters( 'charitable_user_avatar_media_size', array( $size, $size ), $size );
545
546 $attachment_src = wp_get_attachment_image_src( $avatar_attachment_id, $img_size );
547
548 /* No image for the given attachment ID? Fall back to the gravatar. */
549 if ( ! $attachment_src ) {
550 return get_avatar( $this->ID, $size );
551 }
552
553 return apply_filters(
554 'charitable_user_avatar_custom',
555 sprintf(
556 '<img src="%s" alt="%s" class="avatar photo" width="%s" height="%s" />',
557 $attachment_src[0],
558 esc_attr( $this->display_name ),
559 $attachment_src[1],
560 $attachment_src[2]
561 ),
562 $avatar_attachment_id,
563 $size,
564 $this
565 );
566 }
567
568 /**
569 * Return the src of the avatar.
570 *
571 * @since 1.0.0
572 *
573 * @param int $size The length and the width of the avatar.
574 * @return string
575 */
576 public function get_avatar_src( $size = 100 ) {
577 /* If this returns something, we don't need to deal with the gravatar. */
578 $avatar = apply_filters( 'charitable_user_avatar_src', false, $this, $size );
579
580 if ( false === $avatar ) {
581
582 /* The gravatars are returned as fully formatted img tags, so we need to pull out the src. */
583 $gravatar = get_avatar( $this->ID, $size );
584
585 preg_match( "@src='([^']+)'@", $gravatar, $matches );
586
587 $avatar = array_pop( $matches );
588 }
589
590 return $avatar;
591 }
592
593 /**
594 * Return the campaigns created by the user.
595 *
596 * @since 1.0.0
597 *
598 * @param array $args Optional. Any arguments accepted by WP_Query.
599 * @return WP_Query
600 */
601 public function get_campaigns( $args = array() ) {
602 $defaults = array(
603 'author' => $this->ID,
604 );
605
606 $args = wp_parse_args( $args, $defaults );
607
608 return Charitable_Campaigns::query( $args );
609 }
610
611 /**
612 * Checks whether the user has any current campaigns (i.e. non-expired).
613 *
614 * @since 1.0.0
615 * @version 1.8.9.1
616 *
617 * @param array $args Query arguments.
618 * @return WP_Query
619 */
620 public function get_current_campaigns( $args = array() ) {
621 $defaults = array(
622 'author' => $this->ID,
623 'meta_query' => array(
624 'relation' => 'OR',
625 array(
626 'key' => '_campaign_end_date',
627 'value' => date( 'Y-m-d H:i:s' ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
628 'compare' => '>=',
629 'type' => 'datetime',
630 ),
631 array(
632 'key' => '_campaign_end_date',
633 'value' => '0',
634 ),
635 ),
636 );
637
638 $args = wp_parse_args( $args, $defaults );
639
640 return Charitable_Campaigns::query( $args );
641 }
642
643 /**
644 * Returns all current campaigns by the user.
645 *
646 * @since 1.0.0
647 *
648 * @return WP_Query
649 */
650 public function has_current_campaigns() {
651 return $this->get_current_campaigns()->found_posts;
652 }
653
654 /**
655 * Returns the user's donation and campaign creation activity.
656 *
657 * @see WP_Query
658 * @since 1.0.0
659 *
660 * @param array $args Optional. Any arguments accepted by WP_Query.
661 * @return WP_Query
662 */
663 public function get_activity( $args = array() ) {
664 $defaults = array(
665 'author' => $this->ID,
666 'post_status' => array( 'charitable-completed', 'charitable-preapproved', 'publish' ),
667 'post_type' => array( 'donation', 'campaign' ),
668 'order' => 'DESC',
669 'orderby' => 'date',
670 );
671
672 $args = wp_parse_args( $args, $defaults );
673
674 $args = apply_filters( 'charitable_user_activity_args', $args, $this );
675
676 return new WP_Query( $args );
677 }
678
679 /**
680 * Add a new donor. This may also create a user account for them.
681 *
682 * @since 1.0.0
683 *
684 * @param array $submitted Values submitted by the user.
685 * @return int $donor_id Donor ID.
686 */
687 public function add_donor( $submitted = array() ) {
688 $email = false;
689
690 if ( isset( $submitted['email'] ) ) {
691 $email = $submitted['email'];
692 }
693
694 if ( ! $email && $this->is_logged_in() ) {
695 $email = $this->user_email;
696 }
697
698 /**
699 * Filter whether donors can be added without an email address.
700 *
701 * Prior to Charitable 1.6, this was never permitted. As of Charitable 1.6, donors
702 * without an email address can be added by default, primarily to support manual
703 * donations without an email address. Use this filter to disable that ability.
704 *
705 * NOTE: By default, the public donation form still requires an email address, so this
706 * primarily affects programmatically created donors, or donors created via manual
707 * donations in the admin.
708 *
709 * @see https://github.com/Charitable/Charitable/issues/535
710 *
711 * @since 1.6.0
712 *
713 * @param boolean $permitted Whether donors can be added without an email address.
714 */
715 if ( ! $email && ! charitable_permit_donor_without_email() ) {
716 charitable_get_deprecated()->doing_it_wrong(
717 __METHOD__,
718 __( 'Unable to add donor. Email not set for logged out user.', 'charitable' ),
719 '1.0.0'
720 );
721
722 return 0;
723 }
724
725 /**
726 * Filter the donor values.
727 *
728 * @since 1.0.0
729 *
730 * @param array $donor_values The donor values.
731 * @param Charitable_User $user This instance of `Charitable_User`.
732 * @param array $submitted Submitted values.
733 */
734 $donor_values = apply_filters(
735 'charitable_donor_values',
736 array(
737 'user_id' => $this->ID,
738 'email' => $email,
739 'first_name' => array_key_exists( 'first_name', $submitted ) ? $submitted['first_name'] : $this->first_name,
740 'last_name' => array_key_exists( 'last_name', $submitted ) ? $submitted['last_name'] : $this->last_name,
741 ),
742 $this,
743 $submitted
744 );
745
746 $donor_id = charitable_get_table( 'donors' )->insert( $donor_values );
747
748 /**
749 * If the user is logged in, add the "Donor" role to their account.
750 */
751 if ( $this->is_logged_in() ) {
752 $this->add_role( 'donor' );
753 }
754
755 do_action( 'charitable_after_insert_donor', $donor_id, $this );
756
757 return $donor_id;
758 }
759
760 /**
761 * Insert a new donor with submitted values.
762 *
763 * @since 1.4.0
764 * @version 1.8.1.15
765 *
766 * @param array $submitted The submitted values.
767 * @param array $keys The keys of fields that are to be updated.
768 * @return int
769 */
770 public static function create_profile( $submitted = array(), $keys = array() ) {
771 $user = new Charitable_User();
772
773 // Ensure that an ID isn't being submitted.
774 // Adding here but this function might be deprecated or not in use (outside of tests).
775 if ( array_key_exists( 'ID', $submitted ) ) {
776 unset( $submitted['ID'] );
777 }
778
779 $user_id = $user->update_profile( $submitted, $keys );
780 return new Charitable_User( $user_id );
781 }
782
783 /**
784 * Update the user's details with submitted values.
785 *
786 * @since 1.0.0
787 *
788 * @param array $submitted The submitted values.
789 * @param array $keys The keys of fields that are to be updated.
790 * @return int
791 */
792 public function update_profile( $submitted = array(), $keys = array() ) {
793 if ( empty( $submitted ) ) {
794 $submitted = $_POST; // phpcs:ignore
795 }
796
797 if ( empty( $keys ) ) {
798 $keys = array_keys( $submitted );
799 }
800
801 $user_id = $this->update_core_user( $submitted );
802
803 /* If there were problems with creating the user, stop here. */
804 if ( ! $user_id ) {
805 return $user_id;
806 }
807
808 /* Create or update the user's donor record. */
809 $this->update_donor_record( $submitted, $keys );
810
811 $this->update_user_meta( $submitted, $keys );
812
813 return $user_id;
814 }
815
816 /**
817 * Create or update a donor record for the current user.
818 *
819 * @since 1.6.2
820 *
821 * @param array $submitted The submitted values.
822 * @param array $keys The keys of fields that are to be updated.
823 * @return int The donor ID, or 0 if none was created or edited.
824 */
825 public function update_donor_record( $submitted, $keys ) {
826 $donor_fields = array_intersect( $keys, charitable_get_donor_keys() );
827
828 if ( array_key_exists( 'user_email', $submitted ) ) {
829 $donor_fields[] = 'email';
830 $submitted['email'] = $submitted['user_email'];
831 }
832
833 if ( empty( $donor_fields ) ) {
834 return 0;
835 }
836
837 $donor_data = array(
838 'user_id' => $this->ID,
839 );
840
841 foreach ( $donor_fields as $field ) {
842 if ( array_key_exists( $field, $submitted ) ) {
843 $value = $submitted[ $field ];
844 } elseif ( 'contact_consent' === $field ) {
845 $value = 0;
846 } else {
847 $value = '';
848 }
849
850 $donor_data[ $field ] = $value;
851 }
852
853 if ( array_key_exists( 'donor_id', $donor_data ) ) {
854 $donor_id = $donor_data['donor_id'];
855 } else {
856 $donor_id = $this->get_donor_id();
857 }
858
859 /*
860 * Clear out the cache.
861 *
862 * Only when there is actually an ID. get_donor_id() returns false for a donor who does not
863 * exist yet, which is the normal path into the insert() below - and the `if ( $donor_id )`
864 * immediately after this already accounts for that. Passing false as a cache key trips
865 * WP 6.1+'s "Cache key must be an integer or a non-empty string" _doing_it_wrong notice on
866 * every new-donor save. Deleting the key false was a no-op regardless, so guarding it
867 * changes no behaviour.
868 */
869 if ( $donor_id ) {
870 wp_cache_delete( $donor_id, 'donors' );
871 }
872
873 if ( $donor_id ) {
874 charitable_get_table( 'donors' )->update( $donor_id, $donor_data );
875
876 return $donor_id;
877 }
878
879 return charitable_get_table( 'donors' )->insert( $donor_data );
880 }
881
882 /**
883 * Save core fields of the user (i.e. the wp_users data)
884 *
885 * @uses wp_insert_user
886 *
887 * @since 1.0.0
888 *
889 * @param array $submitted Values submitted by the user.
890 * @return int User ID
891 */
892 public function update_core_user( $submitted ) {
893 $core_fields = array_intersect( array_keys( $submitted ), $this->get_core_keys() );
894
895 if ( empty( $core_fields ) ) {
896 return 0;
897 }
898
899 $values = array();
900
901 /* If we're updating an active user, set the ID */
902 if ( 0 !== $this->ID ) {
903 $values['ID'] = $this->ID;
904 }
905
906 foreach ( $core_fields as $field ) {
907 if ( $field === 'role' ) {
908 continue; }
909 $values[ $field ] = $submitted[ $field ];
910 }
911
912 /* Set the user's display name based on their name. */
913 $display_name = $this->sanitize_display_name( $values );
914
915 if ( $display_name ) {
916 $values['display_name'] = $display_name;
917 }
918
919 /* Insert the user */
920 if ( 0 === $this->ID ) {
921
922 if ( ! isset( $values['user_pass'] ) || strlen( $values['user_pass'] ) == 0 ) {
923 charitable_get_notices()->add_error( '<strong>ERROR:</strong> Password field is required.' );
924 return false;
925 }
926
927 if ( ! isset( $values['user_login'] ) ) {
928 $values['user_login'] = $values['user_email'];
929 }
930
931 /**
932 * `wp_insert_user` calls `sanitize_user` internally - make the
933 * same call here so `$values['user_login']` matches what is
934 * eventually saved to the database
935 */
936 $values['user_login'] = sanitize_user( $values['user_login'], true );
937
938 $user_id = wp_insert_user( $values );
939
940 if ( is_wp_error( $user_id ) ) {
941 charitable_get_notices()->add_errors_from_wp_error( $user_id );
942 return false;
943 }
944
945 $this->init( self::get_data_by( 'id', $user_id ) );
946
947 $signon = self::signon( $values['user_login'], $values['user_pass'] );
948
949 if ( is_wp_error( $signon ) ) {
950 charitable_get_notices()->add_errors_from_wp_error( $signon );
951 return false;
952 }
953
954 /**
955 * Do something after a user has been registered.
956 *
957 * @since 1.0.0
958 *
959 * @param int $user_id The new user's ID.
960 * @param array $values Values submitted to register user.
961 */
962 do_action( 'charitable_after_insert_user', $user_id, $values );
963
964 } else {
965 $values['ID'] = $this->ID;
966 $user_id = wp_update_user( $values );
967 }//end if
968
969 /* If there was an error when inserting or updating the user, lodge the error. */
970 if ( is_wp_error( $user_id ) ) {
971 charitable_get_notices()->add_errors_from_wp_error( $user_id );
972 return false;
973 }
974
975 /**
976 * Do something after a user's account has been updated or created.
977 *
978 * @since 1.0.0
979 *
980 * @param int $user_id The user's ID.
981 * @param array $values Values submitted to save user.
982 */
983 do_action( 'charitable_after_save_user', $user_id, $values );
984
985 return $user_id;
986 }
987
988 /**
989 * Save the user's meta fields.
990 *
991 * @since 1.0.0
992 *
993 * @param array $submitted The submitted values.
994 * @param array $keys The keys of fields that are to be updated.
995 * @return int Number of fields updated.
996 */
997 public function update_user_meta( $submitted, $keys ) {
998 /* Exclude the core keys */
999 $meta_fields = array_diff( $keys, $this->get_core_keys() );
1000 $updated = 0;
1001
1002 foreach ( $meta_fields as $field ) {
1003
1004 if ( isset( $submitted[ $field ] ) ) {
1005 $meta_key = $this->map_key( $field );
1006 $meta_value = sanitize_meta( $meta_key, $submitted[ $field ], 'user' );
1007
1008 update_user_meta( $this->ID, $meta_key, $meta_value );
1009
1010 ++$updated;
1011 }
1012 }
1013
1014 return $updated;
1015 }
1016
1017 /**
1018 * Log a user is with their username and password.
1019 *
1020 * @since 1.0.0
1021 *
1022 * @param string $username The user's username.
1023 * @param string $password User password.
1024 * @return WP_User|WP_Error|false WP_User on login, WP_Error on failure. False if feature is disabled.
1025 */
1026 public static function signon( $username, $password ) {
1027 if ( ! apply_filters( 'charitable_auto_login_after_registration', true, $username ) ) {
1028 return false;
1029 }
1030
1031 if ( is_user_logged_in() ) {
1032 return false;
1033 }
1034
1035 $creds = array(
1036 'user_login' => $username,
1037 'user_password' => $password,
1038 'remember' => true,
1039 );
1040
1041 return wp_signon( $creds, false );
1042 }
1043
1044 /**
1045 * Return a display name for the user.
1046 *
1047 * @since 1.5.2
1048 *
1049 * @param array $values Submitted values.
1050 * @return string|false String if we received a display_name
1051 * or had a first name or last name.
1052 */
1053 public function sanitize_display_name( $values ) {
1054 if ( array_key_exists( 'display_name', $values ) ) {
1055 return $values['display_name'];
1056 }
1057
1058 $first_name = array_key_exists( 'first_name', $values ) ? $values['first_name'] : $this->first_name;
1059 $last_name = array_key_exists( 'last_name', $values ) ? $values['last_name'] : $this->last_name;
1060 $display = trim( sprintf( '%s %s', $first_name, $last_name ) );
1061
1062 return strlen( $display ) ? $display : false;
1063 }
1064
1065 /**
1066 * Return the array of mapped keys, where the key is mapped to a meta_key in the user meta table.
1067 *
1068 * @since 1.0.0
1069 *
1070 * @return array
1071 */
1072 public function get_mapped_keys() {
1073 if ( ! isset( $this->mapped_keys ) ) {
1074 $this->mapped_keys = charitable_get_user_mapped_keys();
1075 }
1076
1077 return $this->mapped_keys;
1078 }
1079
1080 /**
1081 * Given a key, returns the mapped key or itself if it is not mapped.
1082 *
1083 * @since 1.5.0
1084 *
1085 * @param string $key The key to map.
1086 * @return string
1087 */
1088 public function map_key( $key ) {
1089 $mapped_keys = $this->get_mapped_keys();
1090
1091 return array_key_exists( $key, $mapped_keys ) ? $mapped_keys[ $key ] : $key;
1092 }
1093
1094 /**
1095 * Return the array of core keys.
1096 *
1097 * @since 1.0.0
1098 *
1099 * @return array
1100 */
1101 public function get_core_keys() {
1102 if ( ! isset( $this->core_keys ) ) {
1103 $this->core_keys = charitable_get_user_core_keys();
1104 }
1105
1106 return $this->core_keys;
1107 }
1108
1109 /**
1110 * Deprecated function.
1111 *
1112 * Return an array of fields used for the address.
1113 *
1114 * @deprecated 2.2.0
1115 *
1116 * @since 1.0.0
1117 * @since 1.6.44 Deprecated.
1118 *
1119 * @return array
1120 */
1121 public function get_address_fields() {
1122 charitable_get_deprecated()->deprecated_function(
1123 __METHOD__,
1124 '1.6.44',
1125 ''
1126 );
1127
1128 /**
1129 * Deprecated filter in deprecated method.
1130 *
1131 * Please note that prior to 1.6.44, the hook used here was `charitable_user_address_fields`,
1132 * which is used elsewhere in a different context and thus resulted in unexpected results.
1133 *
1134 * @since 1.6.44
1135 *
1136 * @param array $address_fields The address fields.
1137 */
1138 return apply_filters(
1139 'charitable_user_address_fields_deprecated',
1140 array(
1141 'donor_address',
1142 'donor_address_2',
1143 'donor_city',
1144 'donor_state',
1145 'donor_postcode',
1146 'donor_country',
1147 )
1148 );
1149 }
1150 }
1151
1152 endif;
1153