PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.13
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.13
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
← All changes | includes/forms/class-charitable-profile-form.php +317 -144 1.7.0 → 1.8.13 View file →
@@ -3,9 +3,9 @@
3 3 * Class that manages the display and processing of the profile form.
4 4 *
5 5 * @package Charitable/Classes/Charitable_Profile_Form
6 6 * @author David Bisset
7 - * @copyright Copyright (c) 2022, WP Charitable LLC
7 + * @copyright Copyright (c) 2023, WP Charitable LLC
8 8 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9 9 * @since 1.0.0
10 10 * @version 1.6.51
11 11 */
@@ -67,8 +67,11 @@
67 67 $this->shortcode_args = $args;
68 68
69 69 /* For backwards-compatibility */
70 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 );
71 74 }
72 75
73 76 /**
74 77 * Return the current user's Charitable_User object.
@@ -88,8 +91,9 @@
88 91 /**
89 92 * Returns the value of a particular key.
90 93 *
91 94 * @since 1.0.0
95 + * @version 1.8.9.1
92 96 *
93 97 * @param string $key The key of the data we are searching for.
94 98 * @param string $default Optional. The value that will be used if none is set.
95 99 * @return mixed
@@ -94,11 +98,13 @@
94 98 * @param string $default Optional. The value that will be used if none is set.
95 99 * @return mixed
96 100 */
97 101 public function get_user_value( $key, $default = '' ) {
102 + // phpcs:disable WordPress.Security.NonceVerification.Missing
98 103 if ( isset( $_POST[ $key ] ) ) {
99 - return $_POST[ $key ];
104 + return sanitize_text_field( wp_unslash( $_POST[ $key ] ) );
100 105 }
106 + // phpcs:enable WordPress.Security.NonceVerification.Missing
101 107
102 108 $value = $this->get_user_prop_or_default( $key, $default );
103 109
104 110 /**
@@ -134,54 +140,58 @@
134 140 *
135 141 * @return array
136 142 */
137 143 public function get_user_fields() {
138 - $user_fields = apply_filters( 'charitable_user_fields', array(
139 - 'username' => array(
140 - 'type' => 'paragraph',
141 - 'priority' => '',
142 - 'content' => sprintf(
143 - /* translators: %s: username */
144 - __( 'Username: %s.', 'charitable' ),
145 - $this->get_user()->user_login
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 + ),
146 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 + ),
147 191 ),
148 - 'first_name' => array(
149 - 'label' => __( 'First name', 'charitable' ),
150 - 'type' => 'text',
151 - 'priority' => 4,
152 - 'required' => true,
153 - 'value' => $this->get_user_value( 'first_name' ),
154 - ),
155 - 'last_name' => array(
156 - 'label' => __( 'Last name', 'charitable' ),
157 - 'type' => 'text',
158 - 'priority' => 6,
159 - 'required' => true,
160 - 'value' => $this->get_user_value( 'last_name' ),
161 - ),
162 - 'user_email' => array(
163 - 'label' => __( 'Email', 'charitable' ),
164 - 'type' => 'email',
165 - 'required' => true,
166 - 'priority' => 8,
167 - 'value' => $this->get_user_value( 'user_email' ),
168 - ),
169 - 'organisation' => array(
170 - 'label' => __( 'Organization', 'charitable' ),
171 - 'type' => 'text',
172 - 'priority' => 10,
173 - 'required' => false,
174 - 'value' => $this->get_user_value( 'organisation' ),
175 - ),
176 - 'description' => array(
177 - 'label' => __( 'Bio', 'charitable' ),
178 - 'type' => 'textarea',
179 - 'required' => false,
180 - 'priority' => 12,
181 - 'value' => $this->get_user_value( 'description' ),
182 - ),
183 - ), $this );
192 + $this
193 + );
184 194
185 195 uasort( $user_fields, 'charitable_priority_sort' );
186 196
187 197 return $user_fields;
@@ -272,31 +282,35 @@
272 282 *
273 283 * @return array
274 284 */
275 285 public function get_social_fields() {
276 - $social_fields = apply_filters( 'charitable_user_social_fields', array(
277 - 'user_url' => array(
278 - 'label' => __( 'Your Website', 'charitable' ),
279 - 'type' => 'url',
280 - 'priority' => 42,
281 - 'required' => false,
282 - 'value' => $this->get_user_value( 'user_url' ),
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 + ),
283 310 ),
284 - 'twitter' => array(
285 - 'label' => __( 'Twitter', 'charitable' ),
286 - 'type' => 'text',
287 - 'priority' => 44,
288 - 'required' => false,
289 - 'value' => $this->get_user_value( 'twitter' ),
290 - ),
291 - 'facebook' => array(
292 - 'label' => __( 'Facebook', 'charitable' ),
293 - 'type' => 'text',
294 - 'priority' => 46,
295 - 'required' => false,
296 - 'value' => $this->get_user_value( 'facebook' ),
297 - ),
298 - ), $this );
311 + $this
312 + );
299 313
300 314 uasort( $social_fields, 'charitable_priority_sort' );
301 315
302 316 return $social_fields;
@@ -316,10 +330,10 @@
316 330 }
317 331
318 332 $donor = $this->get_user()->get_donor();
319 333
320 - if ( array_key_exists( 'contact_consent', $_POST ) ) {
321 - $consent = $_POST['contact_consent'];
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
322 336 } elseif ( is_null( $donor ) ) {
323 337 $consent = false;
324 338 } else {
325 339 $consent = $donor->contact_consent;
@@ -332,17 +346,21 @@
332 346 *
333 347 * @param array $communication_fields List of fields.
334 348 * @param Charitable_Profile_Form $form Instance of `Charitable_Profile_Form`.
335 349 */
336 - $communication_fields = apply_filters( 'charitable_user_communication_preferences_fields', array(
337 - 'contact_consent' => array(
338 - 'type' => 'checkbox',
339 - 'label' => charitable_get_option( 'contact_consent_label', __( 'Yes, I am happy for you to contact me via email or phone.', 'charitable' ) ),
340 - 'priority' => 8,
341 - 'required' => false,
342 - 'checked' => $consent,
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 + ),
343 360 ),
344 - ), $this );
361 + $this
362 + );
345 363
346 364 if ( empty( $communication_fields ) ) {
347 365 return $fields;
348 366 }
@@ -348,16 +366,19 @@
348 366 }
349 367
350 368 uasort( $fields, 'charitable_priority_sort' );
351 369
352 - return array_merge( $fields, array(
353 - 'communication_fields' => array(
354 - 'legend' => __( 'Communication Preferences', 'charitable' ),
355 - 'type' => 'fieldset',
356 - 'fields' => $communication_fields,
357 - 'priority' => 60,
358 - ),
359 - ) );
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 + );
360 381 }
361 382
362 383 /**
363 384 * Profile fields to be displayed.
@@ -366,34 +387,38 @@
366 387 *
367 388 * @return array[]
368 389 */
369 390 public function get_fields() {
370 - $fields = apply_filters( 'charitable_user_profile_fields', array(
371 - 'user_fields' => array(
372 - 'legend' => __( 'Your Details', 'charitable' ),
373 - 'type' => 'fieldset',
374 - 'fields' => $this->get_user_fields(),
375 - 'priority' => 0,
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 + ),
376 418 ),
377 - 'password_fields' => array(
378 - 'legend' => __( 'Change Your Password', 'charitable' ),
379 - 'type' => 'fieldset',
380 - 'fields' => $this->get_password_fields(),
381 - 'priority' => 10,
382 - ),
383 - 'address_fields' => array(
384 - 'legend' => __( 'Your Address', 'charitable' ),
385 - 'type' => 'fieldset',
386 - 'fields' => $this->get_address_fields(),
387 - 'priority' => 20,
388 - ),
389 - 'social_fields' => array(
390 - 'legend' => __( 'Your Social Profiles', 'charitable' ),
391 - 'type' => 'fieldset',
392 - 'fields' => $this->get_social_fields(),
393 - 'priority' => 40,
394 - ),
395 - ), $this );
419 + $this
420 + );
396 421
397 422 $fields = $this->maybe_get_communication_preferences_fields( $fields );
398 423
399 424 uasort( $fields, 'charitable_priority_sort' );
@@ -408,11 +433,14 @@
408 433 *
409 434 * @return array
410 435 */
411 436 public function get_hidden_fields() {
412 - return array_merge( parent::get_hidden_fields(), array(
413 - 'current_email' => $this->get_user()->get_email(),
414 - ) );
437 + return array_merge(
438 + parent::get_hidden_fields(),
439 + array(
440 + 'current_email' => $this->get_user()->get_email(),
441 + )
442 + );
415 443 }
416 444
417 445 /**
418 446 * The fields displayed on the password form.
@@ -421,29 +449,33 @@
421 449 *
422 450 * @return array[]
423 451 */
424 452 public function get_password_fields() {
425 - $password_fields = apply_filters( 'charitable_user_profile_password_fields', array(
426 - 'current_pass' => array(
427 - 'priority' => 2,
428 - 'type' => 'password',
429 - 'label' => __( 'Current Password (leave blank to leave unchanged)', 'charitable' ),
430 - 'value' => '',
431 - 'required' => false,
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 + ),
432 475 ),
433 - 'user_pass' => array(
434 - 'priority' => 4,
435 - 'type' => 'password',
436 - 'label' => __( 'New Password (leave blank to leave unchanged)', 'charitable' ),
437 - 'required' => false,
438 - ),
439 - 'user_pass_repeat' => array(
440 - 'priority' => 6,
441 - 'type' => 'password',
442 - 'label' => __( 'New Password (again)', 'charitable' ),
443 - 'required' => false,
444 - ),
445 - ), $this );
476 + $this
477 + );
446 478
447 479 uasort( $password_fields, 'charitable_priority_sort' );
448 480
449 481 return $password_fields;
@@ -515,13 +547,13 @@
515 547 *
516 548 * @return boolean
517 549 */
518 550 public function is_changing_password() {
519 - if ( ! isset( $_POST['user_pass'] ) || empty( $_POST['user_pass'] ) ) {
551 + if ( ! isset( $_POST['user_pass'] ) || empty( $_POST['user_pass'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
520 552 return false;
521 553 }
522 554
523 - if ( ! isset( $_POST['current_pass'] ) || empty( $_POST['current_pass'] ) ) {
555 + if ( ! isset( $_POST['current_pass'] ) || empty( $_POST['current_pass'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
524 556 return false;
525 557 }
526 558
527 559 return true;
@@ -535,15 +567,15 @@
535 567 * @return boolean
536 568 */
537 569 public function validate_password_change() {
538 570 /* The current password must be correct. */
539 - if ( false == wp_check_password( $_POST['current_pass'], $this->get_user()->user_pass ) ) {
571 + if ( false == wp_check_password( $_POST['current_pass'], $this->get_user()->user_pass ) ) { // phpcs:ignore
540 572 charitable_get_notices()->add_error( __( 'Current password is incorrect.', 'charitable' ) );
541 573 return false;
542 574 }
543 575
544 576 /* The new password must match the repeat (if set). */
545 - if ( isset( $_POST['user_pass_repeat'] ) && $_POST['user_pass_repeat'] != $_POST['user_pass'] ) {
577 + if ( isset( $_POST['user_pass_repeat'] ) && $_POST['user_pass_repeat'] != $_POST['user_pass'] ) { // phpcs:ignore
546 578 charitable_get_notices()->add_error( __( 'New passwords did not match.', 'charitable' ) );
547 579 return false;
548 580 }
549 581
@@ -557,17 +589,17 @@
557 589 *
558 590 * @return boolean
559 591 */
560 592 public function is_changing_email() {
561 - if ( ! isset( $_POST['user_email'] ) || empty( $_POST['user_email'] ) ) {
593 + if ( ! isset( $_POST['user_email'] ) || empty( $_POST['user_email'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
562 594 return false;
563 595 }
564 596
565 - if ( ! isset( $_POST['current_email'] ) ) {
566 - return $_POST['user_email'] != $this->get_user()->get_email();
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
567 599 }
568 600
569 - return $_POST['user_email'] != $_POST['current_email'];
601 + return $_POST['user_email'] !== $_POST['current_email']; // phpcs:ignore WordPress.Security.NonceVerification.Missing
570 602 }
571 603
572 604 /**
573 605 * Validate the email change.
@@ -572,13 +604,22 @@
572 604 /**
573 605 * Validate the email change.
574 606 *
575 607 * @since 1.6.2
608 + * @version 1.8.9.1
576 609 *
577 610 * @return boolean
578 611 */
579 612 public function validate_email_change() {
580 - if ( 0 != charitable_get_donor_id_by_email( $_POST['user_email'] ) ) {
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 ) ) {
581 622 charitable_get_notices()->add_error(
582 623 __( 'This email address is already in use.', 'charitable' )
583 624 );
584 625 return false;
@@ -620,11 +661,143 @@
620 661 __( 'charitable_user_profile_after_fields hook has been removed. Use charitable_form_after_fields instead.', 'charitable' ),
621 662 '1.4.0'
622 663 );
623 664
624 - if ( 'Charitable_Profile_Form' == get_class( $form ) ) {
665 + if ( 'Charitable_Profile_Form' == get_class( $form ) ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
625 666 do_action( 'charitable_user_profile_after_fields', $form );
626 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 + $user_id = (int) $form->get_user()->ID;
716 + $files_avatar = isset( $_FILES['avatar'] ) ? $_FILES['avatar'] : null; // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.ValidatedSanitizedInput.MissingUnslash
717 + $has_upload = is_array( $files_avatar )
718 + && ! empty( $files_avatar['name'] )
719 + && UPLOAD_ERR_NO_FILE !== (int) ( $files_avatar['error'] ?? UPLOAD_ERR_NO_FILE );
720 +
721 + if ( $has_upload ) {
722 + $attachment_id = $form->upload_post_attachment( 'avatar', 0 );
723 +
724 + if ( is_wp_error( $attachment_id ) ) {
725 + return $submitted;
726 + }
727 +
728 + $submitted['avatar'] = (int) $attachment_id;
729 +
730 + // Delete the previously stored avatar — only if it belongs to this user.
731 + // Without this ownership check, a poisoned 'avatar' user-meta value
732 + // (planted by a no-file submission, see below) would chain into
733 + // wp_delete_attachment() on an attacker-chosen attachment ID.
734 + $old_avatar = get_user_meta( $user_id, 'avatar', true );
735 + if ( ! empty( $old_avatar )
736 + && $this->user_owns_attachment( $user_id, (int) $old_avatar ) ) {
737 + wp_delete_attachment( (int) $old_avatar );
738 + }
739 +
740 + update_user_meta( $user_id, 'avatar', (int) $attachment_id );
741 +
742 + return $submitted;
743 + }
744 +
745 + // No file in $_FILES. The avatar uploader (plupload) uploads the image
746 + // asynchronously and posts the resulting attachment ID as a hidden field
747 + // value, so the normal "change avatar" flow lands here with the new ID and
748 + // no $_FILES — we must not reject it.
749 + //
750 + // Block meta poisoning: only persist an attachment the submitting user
751 + // actually owns (a no-op re-save of their current avatar is also allowed).
752 + // An attacker-supplied ID owned by someone else is discarded so it cannot
753 + // be stored in user meta and later weaponised into wp_delete_attachment()
754 + // on a subsequent $_FILES submission.
755 + $current = (int) get_user_meta( $user_id, 'avatar', true );
756 + $posted = array_key_exists( 'avatar', $submitted ) ? $submitted['avatar'] : null;
757 +
758 + // Absent, empty, or non-scalar value means the avatar was cleared. The
759 + // non-scalar guard blocks array poisoning (e.g. avatar[]=foo would
760 + // otherwise coerce via (int) to 1 and bypass the ownership check below).
761 + if ( null === $posted || '' === $posted || ! is_scalar( $posted ) ) {
762 + /* The picture has been removed. */
763 + $submitted['avatar'] = '';
764 + return $submitted;
765 + }
766 +
767 + $posted = (int) $posted;
768 +
769 + // Discard anything that is neither the current avatar nor an attachment
770 + // this user owns.
771 + if ( $posted !== $current && ! $this->user_owns_attachment( $user_id, $posted ) ) {
772 + $submitted['avatar'] = $current ?: '';
773 + return $submitted;
774 + }
775 +
776 + $submitted['avatar'] = $posted;
777 +
778 + return $submitted;
779 + }
780 +
781 + /**
782 + * Verify the given user owns the given attachment.
783 + *
784 + * @since 1.8.11.2
785 + *
786 + * @param int $user_id The user ID.
787 + * @param int $attachment_id The attachment ID.
788 + * @return bool
789 + */
790 + private function user_owns_attachment( $user_id, $attachment_id ) {
791 + if ( $user_id <= 0 || $attachment_id <= 0 ) {
792 + return false;
793 + }
794 +
795 + $attachment = get_post( $attachment_id );
796 +
797 + return $attachment instanceof WP_Post
798 + && 'attachment' === $attachment->post_type
799 + && (int) $attachment->post_author === (int) $user_id;
627 800 }
628 801 }
629 802
630 803 endif;