PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.11.1
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.11.1
1.8.13.1 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 All 212 releases
charitable / includes / forms / class-charitable-profile-form.php

class-charitable-profile-form.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.11.1, at includes/forms/class-charitable-profile-form.php

753 lines 19.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class that manages the display and processing of the profile form.
4 *
5 * @package Charitable/Classes/Charitable_Profile_Form
6 * @author David Bisset
7 * @copyright Copyright (c) 2023, WP Charitable LLC
8 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9 * @since 1.0.0
10 * @version 1.6.51
11 */
12
13 // Exit if accessed directly.
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 if ( ! class_exists( 'Charitable_Profile_Form' ) ) :
19
20 /**
21 * Charitable_Profile_Form
22 *
23 * @since 1.0.0
24 */
25 class Charitable_Profile_Form extends Charitable_Form {
26
27 /**
28 * Shortcode parameters.
29 *
30 * @var array
31 */
32 protected $shortcode_args;
33
34 /**
35 * @var string
36 */
37 protected $nonce_action = 'charitable_user_profile';
38
39 /**
40 * @var string
41 */
42 protected $nonce_name = '_charitable_user_profile_nonce';
43
44 /**
45 * Action to be executed upon form submission.
46 *
47 * @var string
48 */
49 protected $form_action = 'update_profile';
50
51 /**
52 * The current user.
53 *
54 * @var Charitable_User
55 */
56 protected $user;
57
58 /**
59 * Create class object.
60 *
61 * @since 1.0.0
62 *
63 * @param array $args User-defined shortcode attributes.
64 */
65 public function __construct( $args = array() ) {
66 $this->id = uniqid();
67 $this->shortcode_args = $args;
68
69 /* For backwards-compatibility */
70 add_action( 'charitable_form_field', array( $this, 'render_field' ), 10, 6 );
71
72 add_filter( 'charitable_user_fields', array( $this, 'add_avatar_field' ), 10, 2 );
73 add_filter( 'charitable_profile_update_values', array( $this, 'save_avatar' ), 10, 3 );
74 }
75
76 /**
77 * Return the current user's Charitable_User object.
78 *
79 * @since 1.0.0
80 *
81 * @return Charitable_User
82 */
83 public function get_user() {
84 if ( ! isset( $this->user ) ) {
85 $this->user = new Charitable_User( wp_get_current_user() );
86 }
87
88 return $this->user;
89 }
90
91 /**
92 * Returns the value of a particular key.
93 *
94 * @since 1.0.0
95 * @version 1.8.9.1
96 *
97 * @param string $key The key of the data we are searching for.
98 * @param string $default Optional. The value that will be used if none is set.
99 * @return mixed
100 */
101 public function get_user_value( $key, $default = '' ) {
102 // phpcs:disable WordPress.Security.NonceVerification.Missing
103 if ( isset( $_POST[ $key ] ) ) {
104 return sanitize_text_field( wp_unslash( $_POST[ $key ] ) );
105 }
106 // phpcs:enable WordPress.Security.NonceVerification.Missing
107
108 $value = $this->get_user_prop_or_default( $key, $default );
109
110 /**
111 * Deprecated filter. Use the filter below instead.
112 *
113 * @deprecated 1.8.0
114 *
115 * @since 1.0.0
116 * @since 1.5.7 Deprecated.
117 *
118 * @param string $value The value.
119 * @param string $key The key of the value.
120 * @param Charitable_User $user Instance of `Charitable_User`.
121 */
122 $value = apply_filters( 'charitable_campaign_submission_user_value', $value, $key, $this->get_user() );
123
124 /**
125 * Filter the set value for a particular key.
126 *
127 * @since 1.5.7
128 *
129 * @param string $value The value.
130 * @param string $key The key of the value.
131 * @param Charitable_User $user Instance of `Charitable_User`.
132 */
133 return apply_filters( 'charitable_profile_value', $value, $key, $this->get_user() );
134 }
135
136 /**
137 * Return the core user fields.
138 *
139 * @since 1.0.0
140 *
141 * @return array
142 */
143 public function get_user_fields() {
144 $user_fields = apply_filters(
145 'charitable_user_fields',
146 array(
147 'username' => array(
148 'type' => 'paragraph',
149 'priority' => '',
150 'content' => sprintf(
151 /* translators: %s: username */
152 __( 'Username: %s.', 'charitable' ),
153 $this->get_user()->user_login
154 ),
155 ),
156 'first_name' => array(
157 'label' => __( 'First name', 'charitable' ),
158 'type' => 'text',
159 'priority' => 4,
160 'required' => true,
161 'value' => $this->get_user_value( 'first_name' ),
162 ),
163 'last_name' => array(
164 'label' => __( 'Last name', 'charitable' ),
165 'type' => 'text',
166 'priority' => 6,
167 'required' => true,
168 'value' => $this->get_user_value( 'last_name' ),
169 ),
170 'user_email' => array(
171 'label' => __( 'Email', 'charitable' ),
172 'type' => 'email',
173 'required' => true,
174 'priority' => 8,
175 'value' => $this->get_user_value( 'user_email' ),
176 ),
177 'organisation' => array(
178 'label' => __( 'Organization', 'charitable' ),
179 'type' => 'text',
180 'priority' => 10,
181 'required' => false,
182 'value' => $this->get_user_value( 'organisation' ),
183 ),
184 'description' => array(
185 'label' => __( 'Bio', 'charitable' ),
186 'type' => 'textarea',
187 'required' => false,
188 'priority' => 12,
189 'value' => $this->get_user_value( 'description' ),
190 ),
191 ),
192 $this
193 );
194
195 uasort( $user_fields, 'charitable_priority_sort' );
196
197 return $user_fields;
198 }
199
200 /**
201 * Return the user's address fields.
202 *
203 * @since 1.0.0
204 *
205 * @return array
206 */
207 public function get_address_fields() {
208 /**
209 * Filter the list of user address fields.
210 *
211 * @since 1.0.0
212 *
213 * @param array $fields The fields in the form.
214 * @param Charitable_Profile_Form $form The profile form.
215 */
216 $address_fields = apply_filters(
217 'charitable_user_address_fields',
218 array(
219 'address' => array(
220 'label' => __( 'Address', 'charitable' ),
221 'type' => 'text',
222 'priority' => 22,
223 'required' => false,
224 'value' => $this->get_user_value( 'donor_address' ),
225 ),
226 'address_2' => array(
227 'label' => __( 'Address 2', 'charitable' ),
228 'type' => 'text',
229 'priority' => 24,
230 'required' => false,
231 'value' => $this->get_user_value( 'donor_address_2' ),
232 ),
233 'city' => array(
234 'label' => __( 'City', 'charitable' ),
235 'type' => 'text',
236 'priority' => 26,
237 'required' => false,
238 'value' => $this->get_user_value( 'donor_city' ),
239 ),
240 'state' => array(
241 'label' => __( 'State', 'charitable' ),
242 'type' => 'text',
243 'priority' => 28,
244 'required' => false,
245 'value' => $this->get_user_value( 'donor_state' ),
246 ),
247 'postcode' => array(
248 'label' => __( 'Postcode / ZIP code', 'charitable' ),
249 'type' => 'text',
250 'priority' => 30,
251 'required' => false,
252 'value' => $this->get_user_value( 'donor_postcode' ),
253 ),
254 'country' => array(
255 'label' => __( 'Country', 'charitable' ),
256 'type' => 'select',
257 'options' => charitable_get_location_helper()->get_countries(),
258 'priority' => 32,
259 'required' => false,
260 'value' => $this->get_user_value( 'donor_country', charitable_get_option( 'country' ) ),
261 ),
262 'phone' => array(
263 'label' => __( 'Phone', 'charitable' ),
264 'type' => 'text',
265 'priority' => 34,
266 'required' => false,
267 'value' => $this->get_user_value( 'donor_phone' ),
268 ),
269 ),
270 $this
271 );
272
273 uasort( $address_fields, 'charitable_priority_sort' );
274
275 return $address_fields;
276 }
277
278 /**
279 * Return the social fields.
280 *
281 * @since 1.0.0
282 *
283 * @return array
284 */
285 public function get_social_fields() {
286 $social_fields = apply_filters(
287 'charitable_user_social_fields',
288 array(
289 'user_url' => array(
290 'label' => __( 'Your Website', 'charitable' ),
291 'type' => 'url',
292 'priority' => 42,
293 'required' => false,
294 'value' => $this->get_user_value( 'user_url' ),
295 ),
296 'twitter' => array(
297 'label' => __( 'Twitter / X', 'charitable' ),
298 'type' => 'text',
299 'priority' => 44,
300 'required' => false,
301 'value' => $this->get_user_value( 'twitter' ),
302 ),
303 'facebook' => array(
304 'label' => __( 'Facebook', 'charitable' ),
305 'type' => 'text',
306 'priority' => 46,
307 'required' => false,
308 'value' => $this->get_user_value( 'facebook' ),
309 ),
310 ),
311 $this
312 );
313
314 uasort( $social_fields, 'charitable_priority_sort' );
315
316 return $social_fields;
317 }
318
319 /**
320 * Maybe add communication preferences fields.
321 *
322 * @since 1.6.2
323 *
324 * @param array $fields The existing profile form fields.
325 * @return array
326 */
327 public function maybe_get_communication_preferences_fields( $fields ) {
328 if ( ! charitable_is_contact_consent_activated() ) {
329 return $fields;
330 }
331
332 $donor = $this->get_user()->get_donor();
333
334 if ( array_key_exists( 'contact_consent', $_POST ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
335 $consent = sanitize_text_field( wp_unslash( $_POST['contact_consent'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
336 } elseif ( is_null( $donor ) ) {
337 $consent = false;
338 } else {
339 $consent = $donor->contact_consent;
340 }
341
342 /**
343 * Filter the communication preferences fields.
344 *
345 * @since 1.6.2
346 *
347 * @param array $communication_fields List of fields.
348 * @param Charitable_Profile_Form $form Instance of `Charitable_Profile_Form`.
349 */
350 $communication_fields = apply_filters(
351 'charitable_user_communication_preferences_fields',
352 array(
353 'contact_consent' => array(
354 'type' => 'checkbox',
355 'label' => charitable_get_option( 'contact_consent_label', __( 'Yes, I am happy for you to contact me via email or phone.', 'charitable' ) ),
356 'priority' => 8,
357 'required' => false,
358 'checked' => $consent,
359 ),
360 ),
361 $this
362 );
363
364 if ( empty( $communication_fields ) ) {
365 return $fields;
366 }
367
368 uasort( $fields, 'charitable_priority_sort' );
369
370 return array_merge(
371 $fields,
372 array(
373 'communication_fields' => array(
374 'legend' => __( 'Communication Preferences', 'charitable' ),
375 'type' => 'fieldset',
376 'fields' => $communication_fields,
377 'priority' => 60,
378 ),
379 )
380 );
381 }
382
383 /**
384 * Profile fields to be displayed.
385 *
386 * @since 1.0.0
387 *
388 * @return array[]
389 */
390 public function get_fields() {
391 $fields = apply_filters(
392 'charitable_user_profile_fields',
393 array(
394 'user_fields' => array(
395 'legend' => __( 'Your Details', 'charitable' ),
396 'type' => 'fieldset',
397 'fields' => $this->get_user_fields(),
398 'priority' => 0,
399 ),
400 'password_fields' => array(
401 'legend' => __( 'Change Your Password', 'charitable' ),
402 'type' => 'fieldset',
403 'fields' => $this->get_password_fields(),
404 'priority' => 10,
405 ),
406 'address_fields' => array(
407 'legend' => __( 'Your Address', 'charitable' ),
408 'type' => 'fieldset',
409 'fields' => $this->get_address_fields(),
410 'priority' => 20,
411 ),
412 'social_fields' => array(
413 'legend' => __( 'Your Social Profiles', 'charitable' ),
414 'type' => 'fieldset',
415 'fields' => $this->get_social_fields(),
416 'priority' => 40,
417 ),
418 ),
419 $this
420 );
421
422 $fields = $this->maybe_get_communication_preferences_fields( $fields );
423
424 uasort( $fields, 'charitable_priority_sort' );
425
426 return $fields;
427 }
428
429 /**
430 * Retrieve hidden fields.
431 *
432 * @since 1.6.2
433 *
434 * @return array
435 */
436 public function get_hidden_fields() {
437 return array_merge(
438 parent::get_hidden_fields(),
439 array(
440 'current_email' => $this->get_user()->get_email(),
441 )
442 );
443 }
444
445 /**
446 * The fields displayed on the password form.
447 *
448 * @since 1.4.0
449 *
450 * @return array[]
451 */
452 public function get_password_fields() {
453 $password_fields = apply_filters(
454 'charitable_user_profile_password_fields',
455 array(
456 'current_pass' => array(
457 'priority' => 2,
458 'type' => 'password',
459 'label' => __( 'Current Password (leave blank to leave unchanged)', 'charitable' ),
460 'value' => '',
461 'required' => false,
462 ),
463 'user_pass' => array(
464 'priority' => 4,
465 'type' => 'password',
466 'label' => __( 'New Password (leave blank to leave unchanged)', 'charitable' ),
467 'required' => false,
468 ),
469 'user_pass_repeat' => array(
470 'priority' => 6,
471 'type' => 'password',
472 'label' => __( 'New Password (again)', 'charitable' ),
473 'required' => false,
474 ),
475 ),
476 $this
477 );
478
479 uasort( $password_fields, 'charitable_priority_sort' );
480
481 return $password_fields;
482 }
483
484 /**
485 * Update profile after form submission.
486 *
487 * @since 1.0.0
488 *
489 * @return void
490 */
491 public static function update_profile() {
492 $form = new Charitable_Profile_Form();
493
494 if ( ! $form->validate_nonce() || ! $form->validate_honeypot() ) {
495 charitable_get_notices()->add_error( __( 'Unfortunately, we were unable to verify your form submission. Please reload the page and try again.', 'charitable' ) );
496 return;
497 }
498
499 $user = $form->get_user();
500
501 /* Verify that the user is logged in. */
502 if ( 0 == $user->ID ) {
503 return;
504 }
505
506 $fields = $form->get_merged_fields();
507
508 /**
509 * Filter the profile values.
510 */
511 $submitted = apply_filters(
512 'charitable_profile_update_values',
513 $form->get_submitted_values(),
514 $fields,
515 $form
516 );
517
518 /* Remove the current_pass and user_pass_repeat fields, if set. */
519 unset(
520 $submitted['current_pass'],
521 $submitted['user_pass_repeat']
522 );
523
524 $valid = $form->check_required_fields( $fields );
525
526 if ( $valid && $form->is_changing_password() ) {
527 $valid = $form->validate_password_change();
528 }
529
530 if ( $valid && $form->is_changing_email() ) {
531 $valid = $form->validate_email_change();
532 }
533
534 if ( $valid ) {
535 $user->update_profile( $submitted, array_keys( $fields ) );
536
537 charitable_get_notices()->add_success( __( 'Your profile has been updated.', 'charitable' ) );
538
539 do_action( 'charitable_profile_updated', $submitted, $fields, $form );
540 }
541 }
542
543 /**
544 * Check whether the password is being changed.
545 *
546 * @since 1.4.0
547 *
548 * @return boolean
549 */
550 public function is_changing_password() {
551 if ( ! isset( $_POST['user_pass'] ) || empty( $_POST['user_pass'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
552 return false;
553 }
554
555 if ( ! isset( $_POST['current_pass'] ) || empty( $_POST['current_pass'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
556 return false;
557 }
558
559 return true;
560 }
561
562 /**
563 * Changes a password if the current password is correct and the repeat matches the new password.
564 *
565 * @since 1.4.0
566 *
567 * @return boolean
568 */
569 public function validate_password_change() {
570 /* The current password must be correct. */
571 if ( false == wp_check_password( $_POST['current_pass'], $this->get_user()->user_pass ) ) { // phpcs:ignore
572 charitable_get_notices()->add_error( __( 'Current password is incorrect.', 'charitable' ) );
573 return false;
574 }
575
576 /* The new password must match the repeat (if set). */
577 if ( isset( $_POST['user_pass_repeat'] ) && $_POST['user_pass_repeat'] != $_POST['user_pass'] ) { // phpcs:ignore
578 charitable_get_notices()->add_error( __( 'New passwords did not match.', 'charitable' ) );
579 return false;
580 }
581
582 return true;
583 }
584
585 /**
586 * Check whether the email address is being changed.
587 *
588 * @since 1.6.2
589 *
590 * @return boolean
591 */
592 public function is_changing_email() {
593 if ( ! isset( $_POST['user_email'] ) || empty( $_POST['user_email'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
594 return false;
595 }
596
597 if ( ! isset( $_POST['current_email'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
598 return $_POST['user_email'] !== $this->get_user()->get_email(); // phpcs:ignore WordPress.Security.NonceVerification.Missing
599 }
600
601 return $_POST['user_email'] !== $_POST['current_email']; // phpcs:ignore WordPress.Security.NonceVerification.Missing
602 }
603
604 /**
605 * Validate the email change.
606 *
607 * @since 1.6.2
608 * @version 1.8.9.1
609 *
610 * @return boolean
611 */
612 public function validate_email_change() {
613 // phpcs:disable WordPress.Security.NonceVerification.Missing
614 $user_email = isset( $_POST['user_email'] ) ? sanitize_email( wp_unslash( $_POST['user_email'] ) ) : '';
615 // phpcs:enable WordPress.Security.NonceVerification.Missing
616
617 if ( empty( $user_email ) ) {
618 return false;
619 }
620
621 if ( 0 != charitable_get_donor_id_by_email( $user_email ) ) {
622 charitable_get_notices()->add_error(
623 __( 'This email address is already in use.', 'charitable' )
624 );
625 return false;
626 }
627
628 return true;
629 }
630
631 /**
632 * Return a user's set value for a particular value, or return the default value.
633 *
634 * @since 1.5.7
635 *
636 * @param string $key The property we're getting the value for.
637 * @param string $default The fallback value.
638 * @return string
639 */
640 protected function get_user_prop_or_default( $key, $default ) {
641 $user = $this->get_user();
642
643 return $user && $user->has_prop( $key ) ? $user->get( $key ) : $default;
644 }
645
646 /**
647 * Add the charitable_user_profile_after_fields hook but fire off a deprecated notice.
648 *
649 * @deprecated 1.4.0
650 * @since 1.4.0
651 *
652 * @return void
653 */
654 public static function add_deprecated_charitable_user_profile_after_fields_hook( $form ) {
655 if ( ! has_action( 'charitable_user_profile_after_fields' ) ) {
656 return;
657 }
658
659 charitable_get_deprecated()->doing_it_wrong(
660 __METHOD__,
661 __( 'charitable_user_profile_after_fields hook has been removed. Use charitable_form_after_fields instead.', 'charitable' ),
662 '1.4.0'
663 );
664
665 if ( 'Charitable_Profile_Form' == get_class( $form ) ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
666 do_action( 'charitable_user_profile_after_fields', $form );
667 }
668 }
669
670 /**
671 * Add avatar section to user profile form.
672 *
673 * @param array[] $fields The fields in the form.
674 * @param Charitable_Profile_Form $form The profile form object.
675 * @return array[]
676 * @access public
677 * @since 1.0.0
678 */
679 public function add_avatar_field( $fields, $form ) {
680 $user = $form->get_user();
681 $user_avatar = new Charitable_User_Avatar();
682 $avatar = $user_avatar->get_user_avatar( false, $user );
683
684 if ( ! $avatar && charitable_user_has_gravatar( $user->user_email ) ) {
685 $avatar = get_avatar( $user->ID, 100 );
686 }
687
688 $fields['avatar'] = apply_filters(
689 'charitable_user_avatar_field_args',
690 array(
691 'label' => __( 'Your Profile Photo', 'charitable' ),
692 'type' => 'picture',
693 'uploader' => true,
694 'size' => 100,
695 'value' => $avatar,
696 'priority' => 14,
697 'fullwidth' => true,
698 )
699 );
700
701 return $fields;
702 }
703
704 /**
705 * Upload avatar and add file fields to the submitted fields.
706 *
707 * @param array $submitted The values submitted by the user.
708 * @param array[] $fields The fields in the form.
709 * @param Charitable_Profile_Form $form The profile form object.
710 * @return array
711 * @access public
712 * @since 1.0.0
713 */
714 public function save_avatar( $submitted, $fields, $form ) {
715 if ( isset( $_FILES ) && isset( $_FILES['avatar'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
716
717 $attachment_id = $form->upload_post_attachment( 'avatar', 0 );
718
719 if ( ! is_wp_error( $attachment_id ) ) {
720
721 $submitted['avatar'] = $attachment_id;
722
723 /* Delete the previously upload avatar. */
724 $old_avatar = get_user_meta( $form->get_user()->ID, 'avatar', true );
725
726 if ( ! empty( $old_avatar ) ) {
727
728 wp_delete_attachment( $old_avatar );
729
730 }
731
732 update_user_meta( $form->get_user()->ID, 'avatar', $attachment_id );
733
734 } else {
735 /**
736 * Handle image upload error.
737 *
738 * @todo
739 */
740 }//end if
741 } elseif ( ! array_key_exists( 'avatar', $submitted ) ) {
742
743 /* The picture has been removed. */
744 $submitted['avatar'] = '';
745
746 }//end if
747
748 return $submitted;
749 }
750 }
751
752 endif;
753