← All changes
|
includes/forms/class-charitable-donation-form.php
+627
-125
1.6.7
→
1.8.13
View file →
| @@ -2,13 +2,13 @@ | ||
| 2 | 2 | /** |
| 3 | 3 | * Donation form model class. |
| 4 | 4 | * |
| 5 | 5 | * @package Charitable/Classes/Charitable_Donation_Form |
| 6 | - * @author Eric Daams | |
| 7 | - * @copyright Copyright (c) 2018, Studio 164a | |
| 6 | + * @author David Bisset | |
| 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 | - * @version 1.6.0 | |
| 10 | + * @version 1.6.60 | |
| 11 | 11 | */ |
| 12 | 12 | |
| 13 | 13 | // Exit if accessed directly. |
| 14 | 14 | if ( ! defined( 'ABSPATH' ) ) { |
| @@ -105,12 +105,22 @@ | ||
| 105 | 105 | */ |
| 106 | 106 | protected $valid; |
| 107 | 107 | |
| 108 | 108 | /** |
| 109 | + * Cached merged fields array. | |
| 110 | + * | |
| 111 | + * @since 1.0.0 | |
| 112 | + * | |
| 113 | + * @var array | |
| 114 | + */ | |
| 115 | + protected $merged_fields; | |
| 116 | + | |
| 117 | + /** | |
| 109 | 118 | * Create a donation form object. |
| 110 | 119 | * |
| 111 | 120 | * @since 1.0.0 |
| 112 | 121 | * @since 1.5.0 $campaign argument became optional. Previously it was required. |
| 122 | + * @since 1.8.2 Added minimum donation amount notice display option. | |
| 113 | 123 | * |
| 114 | 124 | * @param Charitable_Campaign|null $campaign Optional. Campaign receiving the donation, or NULL if |
| 115 | 125 | * the campaign will be selected in the form. |
| 116 | 126 | */ |
| @@ -117,13 +127,30 @@ | ||
| 117 | 127 | public function __construct( Charitable_Campaign $campaign = null ) { |
| 118 | 128 | $this->campaign = $campaign; |
| 119 | 129 | $this->id = uniqid(); |
| 120 | 130 | |
| 131 | + // paypal_commerce_connection_check() registers the filters that hide | |
| 132 | + // PayPal Commerce from the donor-facing gateway list when no seller | |
| 133 | + // is connected, and that swap the no-active-gateways notice for a | |
| 134 | + // donor-friendly fallback. Both filters must register BEFORE | |
| 135 | + // setup_payment_fields() evaluates the active-gateway count — that | |
| 136 | + // branch only fires (and thus only triggers the friendly fallback) | |
| 137 | + // when the gateway count drops to zero, which depends on the filter | |
| 138 | + // already being wired. | |
| 139 | + $this->paypal_commerce_connection_check(); | |
| 121 | 140 | $this->setup_payment_fields(); |
| 122 | 141 | $this->check_test_mode(); |
| 142 | + $this->stripe_key_check(); | |
| 143 | + $this->square_connection_check(); | |
| 123 | 144 | |
| 124 | 145 | /* For backwards-compatibility */ |
| 125 | 146 | add_action( 'charitable_form_field', array( $this, 'render_field' ), 10, 6 ); |
| 147 | + /* added in 1.7.0.4 */ | |
| 148 | + if ( 'below_donation_title' === charitable_get_option( 'donation_form_minimal_amount_notice_display', false ) ) { | |
| 149 | + add_action( 'charitable_after_donation_amount_wrapper_header', array( $this, 'minimum_max_donation_amount_notice' ), 10, 2 ); | |
| 150 | + } elseif ( 'above_donation_title' === charitable_get_option( 'donation_form_minimal_amount_notice_display', false ) || 'after_donation_title' === charitable_get_option( 'donation_form_minimal_amount_notice_display', false ) ) { | |
| 151 | + add_action( 'charitable_before_donation_amount_wrapper_header', array( $this, 'minimum_max_donation_amount_notice' ), 10, 2 ); | |
| 152 | + } | |
| 126 | 153 | } |
| 127 | 154 | |
| 128 | 155 | /** |
| 129 | 156 | * Returns the campaign associated with this donation form object. |
| @@ -137,8 +164,19 @@ | ||
| 137 | 164 | return $this->campaign; |
| 138 | 165 | } |
| 139 | 166 | |
| 140 | 167 | /** |
| 168 | + * Return the form template, which is the look. Right now, the default is false with "minimal as the beta. | |
| 169 | + * | |
| 170 | + * @since 1.8.3.5 | |
| 171 | + * | |
| 172 | + * @return string | |
| 173 | + */ | |
| 174 | + public function get_form_template() { | |
| 175 | + return charitable_get_option( 'donation_form_template', false ); | |
| 176 | + } | |
| 177 | + | |
| 178 | + /** | |
| 141 | 179 | * Return the current user. |
| 142 | 180 | * |
| 143 | 181 | * @since 1.0.0 |
| 144 | 182 | * |
| @@ -156,8 +194,9 @@ | ||
| 156 | 194 | /** |
| 157 | 195 | * Returns the set value for a particular user field. |
| 158 | 196 | * |
| 159 | 197 | * @since 1.0.0 |
| 198 | + * @version 1.8.9.1 | |
| 160 | 199 | * |
| 161 | 200 | * @param string $key The field key. |
| 162 | 201 | * @param string $default Optional. The value that will be used if none is set. |
| 163 | 202 | * @return mixed |
| @@ -162,11 +201,13 @@ | ||
| 162 | 201 | * @param string $default Optional. The value that will be used if none is set. |
| 163 | 202 | * @return mixed |
| 164 | 203 | */ |
| 165 | 204 | public function get_user_value( $key, $default = '' ) { |
| 205 | + // phpcs:disable WordPress.Security.NonceVerification.Missing | |
| 166 | 206 | if ( isset( $_POST[ $key ] ) ) { |
| 167 | - return $_POST[ $key ]; | |
| 207 | + return sanitize_text_field( wp_unslash( $_POST[ $key ] ) ); | |
| 168 | 208 | } |
| 209 | + // phpcs:enable WordPress.Security.NonceVerification.Missing | |
| 169 | 210 | |
| 170 | 211 | $donation_id = $this->get_validated_donation_id(); |
| 171 | 212 | |
| 172 | 213 | if ( $donation_id ) { |
| @@ -284,25 +325,25 @@ | ||
| 284 | 325 | */ |
| 285 | 326 | public function get_user_account_fields() { |
| 286 | 327 | $account_fields = array( |
| 287 | 328 | 'user_pass' => array( |
| 288 | - 'label' => __( 'Password', 'charitable' ), | |
| 289 | - 'type' => 'password', | |
| 290 | - 'priority' => 4, | |
| 291 | - 'required' => true, | |
| 329 | + 'label' => __( 'Password', 'charitable' ), | |
| 330 | + 'type' => 'password', | |
| 331 | + 'priority' => 4, | |
| 332 | + 'required' => true, | |
| 292 | 333 | 'requires_registration' => true, |
| 293 | - 'data_type' => 'user', | |
| 334 | + 'data_type' => 'user', | |
| 294 | 335 | ), |
| 295 | 336 | ); |
| 296 | 337 | |
| 297 | 338 | if ( apply_filters( 'charitable_donor_usernames', false ) ) { |
| 298 | 339 | $account_fields['user_login'] = array( |
| 299 | - 'label' => __( 'Username', 'charitable' ), | |
| 300 | - 'type' => 'text', | |
| 301 | - 'priority' => 2, | |
| 302 | - 'required' => true, | |
| 340 | + 'label' => __( 'Username', 'charitable' ), | |
| 341 | + 'type' => 'text', | |
| 342 | + 'priority' => 2, | |
| 343 | + 'required' => true, | |
| 303 | 344 | 'requires_registration' => true, |
| 304 | - 'data_type' => 'user', | |
| 345 | + 'data_type' => 'user', | |
| 305 | 346 | ); |
| 306 | 347 | } |
| 307 | 348 | |
| 308 | 349 | return $account_fields; |
| @@ -342,8 +383,9 @@ | ||
| 342 | 383 | /** |
| 343 | 384 | * Return the donation form fields. |
| 344 | 385 | * |
| 345 | 386 | * @since 1.0.0 |
| 387 | + * @version 1.8.3.5 | |
| 346 | 388 | * |
| 347 | 389 | * @return array[] |
| 348 | 390 | */ |
| 349 | 391 | public function get_fields() { |
| @@ -348,12 +390,13 @@ | ||
| 348 | 390 | */ |
| 349 | 391 | public function get_fields() { |
| 350 | 392 | $fields = array( |
| 351 | 393 | 'donation_fields' => array( |
| 352 | - 'legend' => __( 'Your Donation', 'charitable' ), | |
| 353 | - 'type' => 'donation-amount-wrapper', | |
| 354 | - 'fields' => $this->get_donation_fields(), | |
| 355 | - 'priority' => 20, | |
| 394 | + 'legend' => _x( 'Your Donation', 'donation form amount section header', 'charitable' ), | |
| 395 | + 'type' => 'donation-amount-wrapper', | |
| 396 | + 'fields' => $this->get_donation_fields(), | |
| 397 | + 'priority' => 20, | |
| 398 | + 'multi_currency' => true, | |
| 356 | 399 | ), |
| 357 | 400 | 'details_fields' => array( |
| 358 | 401 | 'legend' => __( 'Details', 'charitable' ), |
| 359 | 402 | 'type' => 'details-fields', |
| @@ -375,8 +418,10 @@ | ||
| 375 | 418 | ); |
| 376 | 419 | |
| 377 | 420 | $fields = $this->maybe_add_terms_conditions_fields( $fields ); |
| 378 | 421 | |
| 422 | + $fields = $this->maybe_add_minimum_donation_amount( $fields ); | |
| 423 | + | |
| 379 | 424 | /** |
| 380 | 425 | * Filter the donation form fields. |
| 381 | 426 | * |
| 382 | 427 | * @since 1.0.0 |
| @@ -405,9 +450,9 @@ | ||
| 405 | 450 | $gateways = array(); |
| 406 | 451 | $has_gateway_fields = false; |
| 407 | 452 | |
| 408 | 453 | foreach ( $gateways_helper->get_active_gateways() as $gateway_id => $gateway_class ) { |
| 409 | - $gateway = new $gateway_class; | |
| 454 | + $gateway = new $gateway_class(); | |
| 410 | 455 | $gateway_fields = $this->add_credit_card_fields( array(), $gateway ); |
| 411 | 456 | |
| 412 | 457 | /** |
| 413 | 458 | * Filter the gateway fields. |
| @@ -456,12 +501,25 @@ | ||
| 456 | 501 | |
| 457 | 502 | $amount = $this->get_campaign()->get_donation_amount_in_session(); |
| 458 | 503 | |
| 459 | 504 | if ( ! $amount ) { |
| 460 | - $content = charitable_template_from_session_content( 'donation_form_current_amount_text', array( | |
| 461 | - 'campaign_id' => $this->get_campaign()->ID, | |
| 462 | - 'form_id' => $this->get_form_identifier(), | |
| 463 | - ), '' ); | |
| 505 | + $content = charitable_template_from_session_content( | |
| 506 | + 'donation_form_current_amount_text', | |
| 507 | + array( | |
| 508 | + 'campaign_id' => $this->get_campaign()->ID, | |
| 509 | + 'form_id' => $this->get_form_identifier(), | |
| 510 | + ), | |
| 511 | + '' | |
| 512 | + ); | |
| 513 | + } elseif ( defined( 'CHARITABLE_DISABLE_SHOW_CURRENT_DONATION_AMOUNT' ) && CHARITABLE_DISABLE_SHOW_CURRENT_DONATION_AMOUNT ) { | |
| 514 | + $content = charitable_template_from_session_content( | |
| 515 | + 'donation_form_current_amount_text', | |
| 516 | + array( | |
| 517 | + 'campaign_id' => $this->get_campaign()->ID, | |
| 518 | + 'form_id' => $this->get_form_identifier(), | |
| 519 | + ), | |
| 520 | + '' | |
| 521 | + ); | |
| 464 | 522 | } else { |
| 465 | 523 | $content = charitable_template_donation_form_current_amount_text( $amount, $this->get_form_identifier(), $this->get_campaign()->ID ); |
| 466 | 524 | } |
| 467 | 525 | |
| @@ -487,17 +545,22 @@ | ||
| 487 | 545 | |
| 488 | 546 | /** |
| 489 | 547 | * Render the donation form. |
| 490 | 548 | * |
| 491 | - * @since 1.0.0 | |
| 549 | + * @since 1.0.0 | |
| 550 | + * @version 1.8.3.6 added get_form_template() | |
| 492 | 551 | * |
| 493 | 552 | * @return void |
| 494 | 553 | */ |
| 495 | 554 | public function render() { |
| 496 | - charitable_template( 'donation-form/form-donation.php', array( | |
| 497 | - 'campaign' => $this->get_campaign(), | |
| 498 | - 'form' => $this, | |
| 499 | - ) ); | |
| 555 | + charitable_template( | |
| 556 | + 'donation-form/form-donation.php', | |
| 557 | + array( | |
| 558 | + 'campaign' => $this->get_campaign(), | |
| 559 | + 'form_template' => $this->get_form_template(), | |
| 560 | + 'form' => $this, | |
| 561 | + ) | |
| 562 | + ); | |
| 500 | 563 | } |
| 501 | 564 | |
| 502 | 565 | /** |
| 503 | 566 | * Return the validated donation ID. |
| @@ -506,19 +569,23 @@ | ||
| 506 | 569 | * the donation ID is checked to ensure it corresponds to a donation |
| 507 | 570 | * and one that the current user should have access to. |
| 508 | 571 | * |
| 509 | 572 | * @since 1.5.14 |
| 573 | + * @version 1.8.9.1 | |
| 510 | 574 | * |
| 511 | 575 | * @return int |
| 512 | 576 | */ |
| 513 | 577 | public function get_validated_donation_id() { |
| 578 | + // phpcs:disable WordPress.Security.NonceVerification.Recommended,WordPress.Security.NonceVerification.Missing | |
| 514 | 579 | if ( array_key_exists( 'donation_id', $_GET ) ) { |
| 515 | - $donation_id = $_GET['donation_id']; | |
| 580 | + $donation_id = intval( wp_unslash( $_GET['donation_id'] ) ); | |
| 516 | 581 | } elseif ( array_key_exists( 'ID', $_POST ) ) { |
| 517 | - $donation_id = $_POST['ID']; | |
| 582 | + $donation_id = intval( wp_unslash( $_POST['ID'] ) ); | |
| 518 | 583 | } else { |
| 584 | + // phpcs:enable WordPress.Security.NonceVerification.Recommended,WordPress.Security.NonceVerification.Missing | |
| 519 | 585 | return 0; |
| 520 | 586 | } |
| 587 | + // phpcs:enable WordPress.Security.NonceVerification.Recommended,WordPress.Security.NonceVerification.Missing | |
| 521 | 588 | |
| 522 | 589 | if ( charitable_user_can_access_donation( $donation_id ) ) { |
| 523 | 590 | return $donation_id; |
| 524 | 591 | } |
| @@ -641,13 +708,22 @@ | ||
| 641 | 708 | public function validate_security_check() { |
| 642 | 709 | $ret = true; |
| 643 | 710 | |
| 644 | 711 | if ( ! $this->validate_nonce() || ! $this->validate_honeypot() ) { |
| 712 | + charitable_get_notices()->add_error( | |
| 713 | + __( 'Unfortunately, we were unable to verify your form submission. Please reload the page and try again.', 'charitable' ) | |
| 714 | + ); | |
| 645 | 715 | |
| 646 | - charitable_get_notices()->add_error( __( 'There was an error with processing your form submission. Please reload the page and try again.', 'charitable' ) ); | |
| 716 | + // Log the security validation failure | |
| 717 | + $error_details = ! $this->validate_nonce() ? 'nonce_failed' : 'honeypot_failed'; | |
| 718 | + $context = array( | |
| 719 | + 'campaign_id' => $this->campaign ? $this->campaign->ID : 0, | |
| 720 | + 'amount' => $this->get_submitted_value( 'donation_amount' ), | |
| 721 | + 'gateway' => $this->get_submitted_value( 'gateway' ), | |
| 722 | + ); | |
| 723 | + charitable_log_form_error( 'security_failure', $error_details, $context ); | |
| 647 | 724 | |
| 648 | 725 | $ret = false; |
| 649 | - | |
| 650 | 726 | } |
| 651 | 727 | |
| 652 | 728 | /** |
| 653 | 729 | * Filter the security validation result. |
| @@ -670,15 +746,26 @@ | ||
| 670 | 746 | public function validate_email() { |
| 671 | 747 | $ret = true; |
| 672 | 748 | |
| 673 | 749 | /* Don't process donations with dummy emails. */ |
| 674 | - if ( array_key_exists( 'email', $_POST ) && ! is_email( $_POST['email'] ) ) { | |
| 675 | - charitable_get_notices()->add_error( sprintf( | |
| 750 | + $submitted = $this->get_submitted_value( 'email' ); | |
| 751 | + if ( ! is_null( $submitted ) && ! is_email( $submitted ) ) { | |
| 752 | + charitable_get_notices()->add_error( | |
| 753 | + sprintf( | |
| 676 | 754 | /* translators: %s: email address */ |
| 677 | - __( '%s is not a valid email address.', 'charitable' ), | |
| 678 | - $_POST['email'] | |
| 679 | - ) ); | |
| 755 | + __( '%s is not a valid email address.', 'charitable' ), | |
| 756 | + $submitted | |
| 757 | + ) | |
| 758 | + ); | |
| 680 | 759 | |
| 760 | + // Log the email validation failure | |
| 761 | + $context = array( | |
| 762 | + 'campaign_id' => $this->campaign ? $this->campaign->ID : 0, | |
| 763 | + 'amount' => $this->get_submitted_value( 'donation_amount' ), | |
| 764 | + 'gateway' => $this->get_submitted_value( 'gateway' ), | |
| 765 | + ); | |
| 766 | + charitable_log_form_error( 'validation_failure', 'invalid_email', $context ); | |
| 767 | + | |
| 681 | 768 | $ret = false; |
| 682 | 769 | } |
| 683 | 770 | |
| 684 | 771 | /** |
| @@ -699,18 +786,14 @@ | ||
| 699 | 786 | * |
| 700 | 787 | * @return boolean |
| 701 | 788 | */ |
| 702 | 789 | public function validate_gateway() { |
| 703 | - | |
| 704 | 790 | $ret = true; |
| 705 | 791 | |
| 706 | 792 | /* Validate the gateway. */ |
| 707 | - if ( ! Charitable_Gateways::get_instance()->is_valid_gateway( $_POST['gateway'] ) ) { | |
| 708 | - | |
| 793 | + if ( ! Charitable_Gateways::get_instance()->is_valid_gateway( $this->get_submitted_value( 'gateway' ) ) ) { | |
| 709 | 794 | charitable_get_notices()->add_error( __( 'The gateway submitted is not valid.', 'charitable' ) ); |
| 710 | - | |
| 711 | 795 | $ret = false; |
| 712 | - | |
| 713 | 796 | } |
| 714 | 797 | |
| 715 | 798 | /** |
| 716 | 799 | * Filter the gateway validation result. |
| @@ -720,9 +803,8 @@ | ||
| 720 | 803 | * @param boolean $ret The result to be returned. True or False. |
| 721 | 804 | * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`. |
| 722 | 805 | */ |
| 723 | 806 | return apply_filters( 'charitable_validate_donation_form_submission_gateway_check', $ret, $this ); |
| 724 | - | |
| 725 | 807 | } |
| 726 | 808 | |
| 727 | 809 | /** |
| 728 | 810 | * Checks whether the set amount is valid. |
| @@ -734,25 +816,67 @@ | ||
| 734 | 816 | public function validate_amount() { |
| 735 | 817 | $ret = true; |
| 736 | 818 | |
| 737 | 819 | /* Ensure that a valid amount has been submitted. */ |
| 738 | - $minimum = charitable_get_minimum_donation_amount(); | |
| 739 | - $amount = self::get_donation_amount(); | |
| 820 | + $submitted = $this->get_submitted_values(); | |
| 821 | + $minimum = charitable_get_minimum_donation_amount( $submitted['campaign_id'] ); | |
| 822 | + $maximum = charitable_get_maximum_donation_amount( $submitted['campaign_id'] ); | |
| 823 | + $amount = self::get_donation_amount(); | |
| 740 | 824 | |
| 741 | 825 | if ( $minimum > 0 && $amount < $minimum ) { |
| 742 | - charitable_get_notices()->add_error( sprintf( | |
| 743 | - __( 'You must donate more than %s.', 'charitable' ), | |
| 744 | - charitable_format_money( $minimum ) | |
| 745 | - ) ); | |
| 826 | + charitable_get_notices()->add_error( | |
| 827 | + sprintf( | |
| 828 | + /* translators: %s: minimum donation amount */ | |
| 829 | + __( 'You must donate more than %s.', 'charitable' ), | |
| 830 | + charitable_format_money( $minimum, false, true ) | |
| 831 | + ) | |
| 832 | + ); | |
| 746 | 833 | |
| 834 | + // Log the minimum amount validation failure | |
| 835 | + $context = array( | |
| 836 | + 'campaign_id' => $submitted['campaign_id'], | |
| 837 | + 'amount' => $amount, | |
| 838 | + 'gateway' => $this->get_submitted_value( 'gateway' ), | |
| 839 | + ); | |
| 840 | + charitable_log_form_error( 'validation_failure', 'amount_below_minimum', $context ); | |
| 841 | + | |
| 747 | 842 | $ret = false; |
| 748 | - } elseif ( $minimum == 0 && $amount <= 0 && ! apply_filters( 'charitable_permit_0_donation', false ) ) { | |
| 749 | - charitable_get_notices()->add_error( sprintf( | |
| 750 | - __( 'You must donate more than %s.', 'charitable' ), | |
| 751 | - charitable_format_money( $minimum ) | |
| 752 | - ) ); | |
| 843 | + } elseif ( $maximum && $maximum < $amount ) { | |
| 844 | + charitable_get_notices()->add_error( | |
| 845 | + sprintf( | |
| 846 | + /* translators: %s: maximum donation amount */ | |
| 847 | + __( 'The maximum donation amount permitted is %s.', 'charitable' ), | |
| 848 | + charitable_format_money( $maximum, false, true ) | |
| 849 | + ) | |
| 850 | + ); | |
| 753 | 851 | |
| 852 | + // Log the maximum amount validation failure | |
| 853 | + $context = array( | |
| 854 | + 'campaign_id' => $submitted['campaign_id'], | |
| 855 | + 'amount' => $amount, | |
| 856 | + 'gateway' => $this->get_submitted_value( 'gateway' ), | |
| 857 | + ); | |
| 858 | + charitable_log_form_error( 'validation_failure', 'amount_above_maximum', $context ); | |
| 859 | + | |
| 754 | 860 | $ret = false; |
| 861 | + } elseif ( $minimum === 0 && $amount <= 0 && ! apply_filters( 'charitable_permit_0_donation', false ) ) { | |
| 862 | + charitable_get_notices()->add_error( | |
| 863 | + sprintf( | |
| 864 | + /* translators: %s: minimum donation amount */ | |
| 865 | + __( 'You must donate more than %s.', 'charitable' ), | |
| 866 | + charitable_format_money( $minimum ) | |
| 867 | + ) | |
| 868 | + ); | |
| 869 | + | |
| 870 | + // Log the zero amount validation failure | |
| 871 | + $context = array( | |
| 872 | + 'campaign_id' => $submitted['campaign_id'], | |
| 873 | + 'amount' => $amount, | |
| 874 | + 'gateway' => $this->get_submitted_value( 'gateway' ), | |
| 875 | + ); | |
| 876 | + charitable_log_form_error( 'validation_failure', 'zero_amount_not_permitted', $context ); | |
| 877 | + | |
| 878 | + $ret = false; | |
| 755 | 879 | } |
| 756 | 880 | |
| 757 | 881 | /** |
| 758 | 882 | * Filter the amount validation result. |
| @@ -762,9 +886,8 @@ | ||
| 762 | 886 | * @param boolean $ret The result to be returned. True or False. |
| 763 | 887 | * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`. |
| 764 | 888 | */ |
| 765 | 889 | return apply_filters( 'charitable_validate_donation_form_submission_amount_check', $ret, $this ); |
| 766 | - | |
| 767 | 890 | } |
| 768 | 891 | |
| 769 | 892 | /** |
| 770 | 893 | * Return the donation values. |
| @@ -791,17 +914,12 @@ | ||
| 791 | 914 | $values['donation_key'] = charitable_get_donation( $values['ID'] )->get_donation_key(); |
| 792 | 915 | } |
| 793 | 916 | |
| 794 | 917 | foreach ( $this->get_merged_fields() as $key => $field ) { |
| 795 | - | |
| 796 | 918 | if ( isset( $field['data_type'] ) || 'gateways' == $key ) { |
| 797 | - | |
| 798 | 919 | if ( 'gateways' == $key ) { |
| 799 | - | |
| 800 | 920 | foreach ( $field as $gateway_id => $gateway_fields ) { |
| 801 | - | |
| 802 | 921 | foreach ( $gateway_fields as $key => $field ) { |
| 803 | - | |
| 804 | 922 | if ( ! isset( $field['type'] ) ) { |
| 805 | 923 | continue; |
| 806 | 924 | } |
| 807 | 925 | |
| @@ -817,15 +935,13 @@ | ||
| 817 | 935 | $values['gateways'][ $gateway_id ][ $key ] = $value; |
| 818 | 936 | } |
| 819 | 937 | } |
| 820 | 938 | } elseif ( isset( $field['type'] ) ) { |
| 821 | - | |
| 822 | 939 | $data_type = $field['data_type']; |
| 823 | 940 | $field_type = $field['type']; |
| 824 | 941 | $default = 'checkbox' == $field_type ? false : ''; |
| 825 | 942 | |
| 826 | 943 | $values[ $data_type ][ $key ] = isset( $submitted[ $key ] ) ? $submitted[ $key ] : $default; |
| 827 | - | |
| 828 | 944 | } |
| 829 | 945 | } |
| 830 | 946 | } |
| 831 | 947 | |
| @@ -848,15 +964,17 @@ | ||
| 848 | 964 | * |
| 849 | 965 | * @return array[] |
| 850 | 966 | */ |
| 851 | 967 | public function get_merged_fields() { |
| 852 | - $fields = array(); | |
| 968 | + if ( ! isset( $this->merged_fields ) ) { | |
| 969 | + $this->merged_fields = array(); | |
| 853 | 970 | |
| 854 | - foreach ( $this->get_fields() as $section_id => $section ) { | |
| 855 | - $fields = array_merge( $fields, $this->get_fields_from_section( $section_id, $section ) ); | |
| 971 | + foreach ( $this->get_fields() as $section_id => $section ) { | |
| 972 | + $this->merged_fields = array_merge( $this->merged_fields, $this->get_fields_from_section( $section_id, $section ) ); | |
| 973 | + } | |
| 856 | 974 | } |
| 857 | 975 | |
| 858 | - return $fields; | |
| 976 | + return $this->merged_fields; | |
| 859 | 977 | } |
| 860 | 978 | |
| 861 | 979 | /** |
| 862 | 980 | * Return the fields from a particular section. |
| @@ -924,8 +1042,90 @@ | ||
| 924 | 1042 | return $this->user_has_required_fields; |
| 925 | 1043 | } |
| 926 | 1044 | |
| 927 | 1045 | /** |
| 1046 | + * Maybe add minimum donation message to form. | |
| 1047 | + * | |
| 1048 | + * @since 1.7.0.4 | |
| 1049 | + * | |
| 1050 | + * @param array $fields The registered donation form fields. | |
| 1051 | + * @return array | |
| 1052 | + */ | |
| 1053 | + public function maybe_add_minimum_donation_amount( $fields ) { | |
| 1054 | + | |
| 1055 | + if ( false === $this->get_campaign()->ID || false === $fields || ! isset( $fields['donation_fields']['fields'] ) ) { | |
| 1056 | + return $fields; | |
| 1057 | + } | |
| 1058 | + | |
| 1059 | + if ( false === charitable_get_option( 'donation_form_minimal_amount_notice_display', false ) || 'below_amount_selection' !== charitable_get_option( 'donation_form_minimal_amount_notice_display', false ) ) { | |
| 1060 | + return $fields; | |
| 1061 | + } | |
| 1062 | + | |
| 1063 | + $terms_fields = array(); | |
| 1064 | + $minimum_amount = get_post_meta( $this->get_campaign()->ID, '_campaign_minimum_donation_amount', true ); | |
| 1065 | + | |
| 1066 | + // do not display a message in the donation form if the field is zero or empty. | |
| 1067 | + if ( false === $minimum_amount || '' === trim( $minimum_amount ) ) { | |
| 1068 | + return $fields; | |
| 1069 | + } | |
| 1070 | + | |
| 1071 | + // default location of the minimum donation amount. | |
| 1072 | + $fields['donation_fields']['fields']['minimum_donation_amount'] = array( | |
| 1073 | + 'legend' => __( 'Mininum Donation', 'charitable' ), | |
| 1074 | + 'type' => 'content', | |
| 1075 | + 'content' => $this->minimum_max_donation_amount_notice( true, $minimum_amount ), | |
| 1076 | + 'priority' => 80, | |
| 1077 | + ); | |
| 1078 | + | |
| 1079 | + /** | |
| 1080 | + * Filter the minimum donation notification fields. | |
| 1081 | + * | |
| 1082 | + * @since 1.7.0.3 | |
| 1083 | + * | |
| 1084 | + * @param array $terms_fields List of terms fields. | |
| 1085 | + * @param Charitable_Donation_Form $form Instance of `Charitable_Donation_Form`. | |
| 1086 | + */ | |
| 1087 | + $minimum_fields = apply_filters( 'charitable_donation_minimum_fields', $fields, $this ); | |
| 1088 | + | |
| 1089 | + if ( empty( $minimum_fields ) ) { | |
| 1090 | + return $fields; | |
| 1091 | + } | |
| 1092 | + | |
| 1093 | + return $fields; | |
| 1094 | + } | |
| 1095 | + | |
| 1096 | + | |
| 1097 | + /** | |
| 1098 | + * Generate minimum donation notice (with filter for changing text more easily). | |
| 1099 | + * | |
| 1100 | + * @since 1.8.0 | |
| 1101 | + * | |
| 1102 | + * @param string $return False if echo, true if return. | |
| 1103 | + * @param string $minimum_amount Minimum amount likely set by user in admin settings. | |
| 1104 | + * @return array | |
| 1105 | + */ | |
| 1106 | + public function minimum_max_donation_amount_notice( $return = true, $minimum_amount = false ) { | |
| 1107 | + | |
| 1108 | + if ( false === $minimum_amount ) { | |
| 1109 | + $minimum_amount = get_post_meta( $this->get_campaign()->ID, '_campaign_minimum_donation_amount', true ); | |
| 1110 | + } | |
| 1111 | + | |
| 1112 | + // do not display a message in the donation form if the field is zero or empty. | |
| 1113 | + if ( false === $minimum_amount || '' === trim( $minimum_amount ) || 0 === intval( $minimum_amount ) ) { | |
| 1114 | + return ''; | |
| 1115 | + } else { | |
| 1116 | + $message = apply_filters( 'charitable_donationa_amount_notice', '<p class="minimum-donation-amount-text">' . __( 'The minimum donation for this campaign is ', 'charitable' ) . charitable_format_money( $minimum_amount, false, true ) . '.</p>', $minimum_amount ); | |
| 1117 | + } | |
| 1118 | + | |
| 1119 | + if ( $return ) { | |
| 1120 | + return $message; | |
| 1121 | + } else { | |
| 1122 | + echo $message; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped | |
| 1123 | + } | |
| 1124 | + } | |
| 1125 | + | |
| 1126 | + | |
| 1127 | + /** | |
| 928 | 1128 | * Maybe add terms and conditions fields to the form. |
| 929 | 1129 | * |
| 930 | 1130 | * @since 1.6.0 |
| 931 | 1131 | * |
| @@ -933,40 +1133,38 @@ | ||
| 933 | 1133 | * @return array |
| 934 | 1134 | */ |
| 935 | 1135 | public function maybe_add_terms_conditions_fields( $fields ) { |
| 936 | 1136 | $terms_fields = array(); |
| 937 | - $privacy_page = (int) charitable_get_option( 'privacy_policy_page', 0 ); | |
| 938 | - $terms_page = (int) charitable_get_option( 'terms_conditions_page', 0 ); | |
| 939 | 1137 | $show_consent = (int) charitable_get_option( 'contact_consent', 0 ) && Charitable_Upgrade::get_instance()->upgrade_has_been_completed( 'upgrade_donor_tables' ); |
| 940 | 1138 | |
| 941 | - if ( $privacy_page ) { | |
| 1139 | + if ( charitable_is_privacy_policy_activated() ) { | |
| 942 | 1140 | $terms_fields['privacy_policy_text'] = array( |
| 943 | 1141 | 'type' => 'content', |
| 944 | - 'content' => '<p class="charitable-privacy-policy-text">' . $this->get_parsed_privacy_text( $privacy_page ) . '</p>', | |
| 1142 | + 'content' => '<p class="charitable-privacy-policy-text">' . charitable_get_privacy_policy_field_text() . '</p>', | |
| 945 | 1143 | 'priority' => 4, |
| 946 | 1144 | ); |
| 947 | 1145 | } |
| 948 | 1146 | |
| 949 | - if ( $show_consent ) { | |
| 1147 | + if ( charitable_is_contact_consent_activated() ) { | |
| 950 | 1148 | $terms_fields['contact_consent'] = array( |
| 951 | 1149 | 'type' => 'checkbox', |
| 952 | 1150 | 'label' => charitable_get_option( 'contact_consent_label', __( 'Yes, I am happy for you to contact me via email or phone.', 'charitable' ) ), |
| 953 | 1151 | 'priority' => 8, |
| 954 | - 'required' => false, | |
| 1152 | + 'required' => charitable_get_option( 'contact_consent_required', false ), | |
| 955 | 1153 | 'data_type' => 'meta', |
| 956 | 1154 | ); |
| 957 | 1155 | } |
| 958 | 1156 | |
| 959 | - if ( $terms_page ) { | |
| 1157 | + if ( charitable_is_terms_and_conditions_activated() ) { | |
| 960 | 1158 | $terms_fields['terms_text'] = array( |
| 961 | 1159 | 'type' => 'content', |
| 962 | - 'content' => '<div class="charitable-terms-text">' . $this->get_terms( $terms_page ) . '</div>', | |
| 1160 | + 'content' => '<div class="charitable-terms-text">' . charitable_get_terms_and_conditions() . '</div>', | |
| 963 | 1161 | 'priority' => 12, |
| 964 | 1162 | ); |
| 965 | 1163 | |
| 966 | 1164 | $terms_fields['accept_terms'] = array( |
| 967 | 1165 | 'type' => 'checkbox', |
| 968 | - 'label' => $this->get_parsed_terms_text( $terms_page ), | |
| 1166 | + 'label' => charitable_get_terms_and_conditions_field_label(), | |
| 969 | 1167 | 'priority' => 16, |
| 970 | 1168 | 'required' => true, |
| 971 | 1169 | 'data_type' => 'meta', |
| 972 | 1170 | ); |
| @@ -985,80 +1183,86 @@ | ||
| 985 | 1183 | if ( empty( $terms_fields ) ) { |
| 986 | 1184 | return $fields; |
| 987 | 1185 | } |
| 988 | 1186 | |
| 989 | - return array_merge( $fields, array( | |
| 990 | - 'terms_fields' => array( | |
| 991 | - 'legend' => __( 'Terms and Conditions', 'charitable' ), | |
| 992 | - 'type' => 'fieldset', | |
| 993 | - 'fields' => $terms_fields, | |
| 994 | - 'priority' => 80, | |
| 995 | - ), | |
| 996 | - ) ); | |
| 1187 | + return array_merge( | |
| 1188 | + $fields, | |
| 1189 | + array( | |
| 1190 | + 'terms_fields' => array( | |
| 1191 | + 'legend' => __( 'Terms and Conditions', 'charitable' ), | |
| 1192 | + 'type' => 'fieldset', | |
| 1193 | + 'fields' => $terms_fields, | |
| 1194 | + // Terms must be accepted before the donor commits to | |
| 1195 | + // a payment method, so order this fieldset just above | |
| 1196 | + // the Payment fieldset (priority 60). Donors see | |
| 1197 | + // consent → payment, not payment → consent. | |
| 1198 | + 'priority' => 56, | |
| 1199 | + ), | |
| 1200 | + ) | |
| 1201 | + ); | |
| 997 | 1202 | } |
| 998 | 1203 | |
| 999 | 1204 | /** |
| 1000 | 1205 | * Return the terms and conditions text. |
| 1001 | 1206 | * |
| 1207 | + * @deprecated 2.0.0 | |
| 1208 | + * | |
| 1002 | 1209 | * @since 1.6.0 |
| 1210 | + * @since 1.6.14 Deprecated. | |
| 1003 | 1211 | * |
| 1004 | 1212 | * @param int $terms_page The ID of the terms page. |
| 1005 | 1213 | * @return string |
| 1006 | 1214 | */ |
| 1007 | 1215 | public function get_terms( $terms_page ) { |
| 1008 | - remove_filter( 'the_content', array( charitable()->endpoints(), 'get_content' ) ); | |
| 1216 | + charitable_get_deprecated()->deprecated_function( | |
| 1217 | + __METHOD__, | |
| 1218 | + '1.6.14', | |
| 1219 | + 'charitable_get_terms_and_conditions' | |
| 1220 | + ); | |
| 1009 | 1221 | |
| 1010 | - $content = apply_filters( 'the_content', get_post_field( 'post_content', $terms_page, 'display' ) ); | |
| 1011 | - | |
| 1012 | - add_filter( 'the_content', array( charitable()->endpoints(), 'get_content' ) ); | |
| 1013 | - | |
| 1014 | - return $content; | |
| 1222 | + return charitable_get_terms_and_conditions(); | |
| 1015 | 1223 | } |
| 1016 | 1224 | |
| 1017 | 1225 | /** |
| 1018 | 1226 | * Return the terms text, with [terms] replaced by a link to the terms and conditions. |
| 1019 | 1227 | * |
| 1228 | + * @deprecated 2.0.0 | |
| 1229 | + * | |
| 1020 | 1230 | * @since 1.6.0 |
| 1231 | + * @since 1.6.14 Deprecated. | |
| 1021 | 1232 | * |
| 1022 | 1233 | * @param int $terms_page The ID of the terms page. |
| 1023 | 1234 | * @return string |
| 1024 | 1235 | */ |
| 1025 | 1236 | public function get_parsed_terms_text( $terms_page ) { |
| 1026 | - $url = get_the_permalink( $terms_page ); | |
| 1237 | + charitable_get_deprecated()->deprecated_function( | |
| 1238 | + __METHOD__, | |
| 1239 | + '1.6.14', | |
| 1240 | + 'charitable_get_terms_and_conditions_field_label' | |
| 1241 | + ); | |
| 1027 | 1242 | |
| 1028 | - if ( ! $url ) { | |
| 1029 | - return ''; | |
| 1030 | - } | |
| 1031 | - | |
| 1032 | - $text = charitable_get_option( 'terms_conditions', __( 'I have read and agree to the website [terms].', 'charitable' ) ); | |
| 1033 | - $replace = sprintf( '<a href="%s" target="_blank" class="charitable-terms-link">%s</a>', | |
| 1034 | - $url, | |
| 1035 | - __( 'terms and conditions', 'charitable' ) | |
| 1036 | - ); | |
| 1037 | - return str_replace( '[terms]', $replace, $text ); | |
| 1243 | + return charitable_get_terms_and_conditions_field_label(); | |
| 1038 | 1244 | } |
| 1039 | 1245 | |
| 1040 | 1246 | /** |
| 1041 | 1247 | * Return the privacy policy text, with [privacy_policy] replaced by a link to the privacy policy page. |
| 1042 | 1248 | * |
| 1249 | + * @deprecated 2.0.0 | |
| 1250 | + * | |
| 1043 | 1251 | * @since 1.6.0 |
| 1252 | + * @since 1.6.14 Deprecated. | |
| 1044 | 1253 | * |
| 1045 | 1254 | * @param int $privacy_page The ID of the privacy policy page. |
| 1046 | 1255 | * @return string |
| 1047 | 1256 | */ |
| 1048 | 1257 | public function get_parsed_privacy_text( $privacy_page ) { |
| 1049 | - $url = get_the_permalink( $privacy_page ); | |
| 1258 | + charitable_get_deprecated()->deprecated_function( | |
| 1259 | + __METHOD__, | |
| 1260 | + '1.6.14', | |
| 1261 | + 'charitable_get_privacy_policy_field_text' | |
| 1262 | + ); | |
| 1050 | 1263 | |
| 1051 | - if ( ! $url ) { | |
| 1052 | - return ''; | |
| 1053 | - } | |
| 1054 | - | |
| 1055 | - $text = charitable_get_option( 'privacy_policy', __( 'Your personal data will be used to process your donation, support your experience throughout this website, and for other purposes described in our [privacy_policy].', 'charitable' ) ); | |
| 1056 | - $replace = sprintf( '<a href="%s" target="_blank" class="charitable-privacy-policy-link">%s</a>', | |
| 1057 | - $url, | |
| 1058 | - __( 'privacy policy', 'charitable' ) | |
| 1059 | - ); | |
| 1060 | - return str_replace( '[privacy_policy]', $replace, $text ); | |
| 1264 | + return charitable_get_privacy_policy_field_text(); | |
| 1061 | 1265 | } |
| 1062 | 1266 | |
| 1063 | 1267 | /** |
| 1064 | 1268 | * Returns whether the form should hide the user fields. |
| @@ -1084,20 +1288,38 @@ | ||
| 1084 | 1288 | /** |
| 1085 | 1289 | * Return the donation amount. |
| 1086 | 1290 | * |
| 1087 | 1291 | * @since 1.0.0 |
| 1292 | + * @version 1.8.9.1 | |
| 1088 | 1293 | * |
| 1089 | 1294 | * @return float |
| 1090 | 1295 | */ |
| 1091 | 1296 | public static function get_donation_amount() { |
| 1092 | - $amount = isset( $_POST['donation_amount'] ) ? $_POST['donation_amount'] : 0; | |
| 1297 | + // phpcs:disable WordPress.Security.NonceVerification.Missing | |
| 1298 | + $amount = isset( $_POST['donation_amount'] ) ? sanitize_text_field( wp_unslash( $_POST['donation_amount'] ) ) : 0; | |
| 1093 | 1299 | |
| 1094 | 1300 | if ( 0 === $amount || 'custom' == $amount ) { |
| 1095 | - $amount = isset( $_POST['custom_donation_amount'] ) ? $_POST['custom_donation_amount'] : 0; | |
| 1301 | + $amount = isset( $_POST['custom_donation_amount'] ) ? sanitize_text_field( wp_unslash( $_POST['custom_donation_amount'] ) ) : 0; | |
| 1096 | 1302 | } |
| 1303 | + // phpcs:enable WordPress.Security.NonceVerification.Missing | |
| 1097 | 1304 | |
| 1098 | - $amount = charitable_get_currency_helper()->sanitize_monetary_amount( (string) $amount ); | |
| 1305 | + /** | |
| 1306 | + * Only sanitize the amount if it's a number (no letters). | |
| 1307 | + * | |
| 1308 | + * With Recurring Donations, it's possible that amount might be 'recurring-custom', | |
| 1309 | + * which should not be sanitized. | |
| 1310 | + */ | |
| 1311 | + if ( ! is_string( $amount ) || ! preg_match( '/[A-Za-z]/', $amount ) ) { | |
| 1312 | + $amount = charitable_get_currency_helper()->sanitize_monetary_amount( (string) $amount ); | |
| 1313 | + } | |
| 1099 | 1314 | |
| 1315 | + /** | |
| 1316 | + * Filter the donation amount. | |
| 1317 | + * | |
| 1318 | + * @since 1.3.0 | |
| 1319 | + * | |
| 1320 | + * @param float|WP_Error $amount The donation amount. | |
| 1321 | + */ | |
| 1100 | 1322 | return apply_filters( 'charitable_donation_form_amount', $amount ); |
| 1101 | 1323 | } |
| 1102 | 1324 | |
| 1103 | 1325 | /** |
| @@ -1127,8 +1349,9 @@ | ||
| 1127 | 1349 | /** |
| 1128 | 1350 | * Set a field's initial value. |
| 1129 | 1351 | * |
| 1130 | 1352 | * @since 1.5.0 |
| 1353 | + * @version 1.8.9.1 | |
| 1131 | 1354 | * |
| 1132 | 1355 | * @param array $field Field definition. |
| 1133 | 1356 | * @param string $key The key of the field. |
| 1134 | 1357 | * @return array |
| @@ -1134,11 +1357,23 @@ | ||
| 1134 | 1357 | * @return array |
| 1135 | 1358 | */ |
| 1136 | 1359 | protected function set_field_value( $field, $key ) { |
| 1137 | 1360 | $donation_id = $this->get_validated_donation_id(); |
| 1361 | + // phpcs:disable WordPress.Security.NonceVerification.Missing | |
| 1362 | + $submitted = isset( $_POST[ $key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $key ] ) ) : null; | |
| 1363 | + // phpcs:enable WordPress.Security.NonceVerification.Missing | |
| 1138 | 1364 | |
| 1139 | - if ( array_key_exists( $key, $_POST ) ) { | |
| 1140 | - $field['value'] = $_POST[ $key ]; | |
| 1365 | + /* Mark a checkbox as checked. */ | |
| 1366 | + if ( 'checkbox' === $field['type'] ) { | |
| 1367 | + $checked_value = isset( $field['value'] ) ? $field['value'] : 1; | |
| 1368 | + | |
| 1369 | + if ( ! is_null( $submitted ) ) { | |
| 1370 | + $field['checked'] = $submitted == $checked_value; | |
| 1371 | + } elseif ( $donation_id ) { | |
| 1372 | + $field['checked'] = charitable_get_donation( $donation_id )->get( $key ) == $checked_value; | |
| 1373 | + } | |
| 1374 | + } elseif ( ! is_null( $submitted ) ) { | |
| 1375 | + $field['value'] = $submitted; | |
| 1141 | 1376 | } elseif ( $donation_id ) { |
| 1142 | 1377 | $donation = charitable_get_donation( $donation_id ); |
| 1143 | 1378 | $field['value'] = $donation ? $donation->get( $key ) : ''; |
| 1144 | 1379 | } |
| @@ -1213,10 +1448,11 @@ | ||
| 1213 | 1448 | * @return string |
| 1214 | 1449 | */ |
| 1215 | 1450 | protected function get_test_mode_active_notice() { |
| 1216 | 1451 | $notice = $this->get_credentialed_notice( |
| 1217 | - __( 'Test mode is active.', 'charitable' ), | |
| 1218 | - sprintf( '<a href="%s">%s</a>.', | |
| 1452 | + __( 'Test mode is enabled.', 'charitable' ), | |
| 1453 | + sprintf( | |
| 1454 | + '<a href="%s">%s</a>.', | |
| 1219 | 1455 | admin_url( 'admin.php?page=charitable-settings&tab=gateways' ), |
| 1220 | 1456 | __( 'Disable Test Mode', 'charitable' ) |
| 1221 | 1457 | ) |
| 1222 | 1458 | ); |
| @@ -1224,8 +1460,272 @@ | ||
| 1224 | 1460 | return apply_filters( 'charitable_test_mode_active_notice', $notice, current_user_can( 'manage_charitable_settings' ) ); |
| 1225 | 1461 | } |
| 1226 | 1462 | |
| 1227 | 1463 | /** |
| 1464 | + * Determine if there is a Stripe key set if Stripe is a payment gateway | |
| 1465 | + * | |
| 1466 | + * @since 1.8.2 | |
| 1467 | + * | |
| 1468 | + * @return void | |
| 1469 | + */ | |
| 1470 | + protected function stripe_key_check() { | |
| 1471 | + | |
| 1472 | + if ( defined( 'CHARITABLE_DISABLE_STRIPE_KEY_CHECK' ) && CHARITABLE_DISABLE_STRIPE_KEY_CHECK ) { | |
| 1473 | + return; | |
| 1474 | + } | |
| 1475 | + | |
| 1476 | + if ( ! class_exists( 'Charitable_Gateway_Stripe_AM' ) ) { | |
| 1477 | + return; | |
| 1478 | + } | |
| 1479 | + | |
| 1480 | + $active_gateways = charitable_get_helper( 'gateways' )->get_active_gateways(); | |
| 1481 | + $has_gateways = apply_filters( 'charitable_has_active_gateways', ! empty( $active_gateways ) ); | |
| 1482 | + | |
| 1483 | + if ( ! $has_gateways ) { | |
| 1484 | + return; | |
| 1485 | + } | |
| 1486 | + | |
| 1487 | + if ( ! is_array( $active_gateways ) || ! in_array( 'Charitable_Gateway_Stripe_AM', $active_gateways ) ) { | |
| 1488 | + return; | |
| 1489 | + } | |
| 1490 | + | |
| 1491 | + // Stripe is enabled, check if the keys are set. | |
| 1492 | + $gateway = new Charitable_Gateway_Stripe_AM(); | |
| 1493 | + $keys = $gateway->get_keys(); | |
| 1494 | + | |
| 1495 | + // If there is no public key, and current user is an admin, display an alert on the form. */ | |
| 1496 | + if ( ( ! isset( $keys['public_key'] ) || empty( $keys['public_key'] ) ) && current_user_can( 'manage_charitable_settings' ) ) { | |
| 1497 | + charitable_get_notices()->add_error( $this->get_stripe_key_check_notice() ); | |
| 1498 | + } | |
| 1499 | + } | |
| 1500 | + | |
| 1501 | + /** | |
| 1502 | + * A formatted notice to advise that Test Mode is active. | |
| 1503 | + * | |
| 1504 | + * @since 1.8.2 | |
| 1505 | + * | |
| 1506 | + * @return string | |
| 1507 | + */ | |
| 1508 | + protected function get_stripe_key_check_notice() { | |
| 1509 | + $notice = $this->get_credentialed_notice( | |
| 1510 | + __( 'Stripe is enabled but some keys are missing.', 'charitable' ), | |
| 1511 | + sprintf( | |
| 1512 | + '<a href="%s">%s</a>.', | |
| 1513 | + admin_url( 'admin.php?page=charitable-settings&tab=gateways&group=gateways_stripe' ), | |
| 1514 | + __( 'View Settings', 'charitable' ) | |
| 1515 | + ) | |
| 1516 | + ); | |
| 1517 | + | |
| 1518 | + return apply_filters( 'charitable_stripe_key_check_notice', $notice, current_user_can( 'manage_charitable_settings' ) ); | |
| 1519 | + } | |
| 1520 | + | |
| 1521 | + /** | |
| 1522 | + * Check if Square connection is available for the current mode. | |
| 1523 | + * | |
| 1524 | + * @since 1.8.5 | |
| 1525 | + * | |
| 1526 | + * @return void | |
| 1527 | + */ | |
| 1528 | + protected function square_connection_check() { | |
| 1529 | + // Check if there's a Square mode connection warning | |
| 1530 | + $warning = get_option( 'charitable_square_mode_connection_warning' ); | |
| 1531 | + | |
| 1532 | + if ( ! $warning ) { | |
| 1533 | + return; | |
| 1534 | + } | |
| 1535 | + | |
| 1536 | + // Check if Square gateway is active | |
| 1537 | + $active_gateways = charitable_get_helper( 'gateways' )->get_active_gateways(); | |
| 1538 | + | |
| 1539 | + if ( ! is_array( $active_gateways ) || ! in_array( 'Charitable_Gateway_Square', $active_gateways ) ) { | |
| 1540 | + return; | |
| 1541 | + } | |
| 1542 | + | |
| 1543 | + // Only show warning to admins | |
| 1544 | + if ( ! current_user_can( 'manage_charitable_settings' ) ) { | |
| 1545 | + return; | |
| 1546 | + } | |
| 1547 | + | |
| 1548 | + // Display the warning | |
| 1549 | + charitable_get_notices()->add_error( $this->get_square_connection_warning_notice( $warning['mode'] ) ); | |
| 1550 | + } | |
| 1551 | + | |
| 1552 | + /** | |
| 1553 | + * A formatted notice to advise that Square connection is missing for the current mode. | |
| 1554 | + * | |
| 1555 | + * @since 1.8.5 | |
| 1556 | + * | |
| 1557 | + * @param string $mode The mode that's missing connection. | |
| 1558 | + * @return string | |
| 1559 | + */ | |
| 1560 | + protected function get_square_connection_warning_notice( $mode ) { | |
| 1561 | + $mode_label = 'test' === $mode ? __( 'test', 'charitable' ) : __( 'live', 'charitable' ); | |
| 1562 | + | |
| 1563 | + $notice = $this->get_credentialed_notice( | |
| 1564 | + sprintf( | |
| 1565 | + /* translators: %s: mode (test or live) */ | |
| 1566 | + __( 'Square is enabled but not connected in %s mode.', 'charitable' ), | |
| 1567 | + $mode_label | |
| 1568 | + ), | |
| 1569 | + sprintf( | |
| 1570 | + '<a href="%s">%s</a>.', | |
| 1571 | + admin_url( 'admin.php?page=charitable-settings&tab=gateways&group=gateways_square_core' ), | |
| 1572 | + __( 'Connect Square Account', 'charitable' ) | |
| 1573 | + ) | |
| 1574 | + ); | |
| 1575 | + | |
| 1576 | + return apply_filters( 'charitable_square_connection_warning_notice', $notice, current_user_can( 'manage_charitable_settings' ) ); | |
| 1577 | + } | |
| 1578 | + | |
| 1579 | + /** | |
| 1580 | + * Tracks whether the donor-facing payment list was stripped of | |
| 1581 | + * PayPal Commerce because the seller account wasn't connected. | |
| 1582 | + * Drives the donor-facing "donations temporarily unavailable" | |
| 1583 | + * message when this is the only reason there are no gateways. | |
| 1584 | + * | |
| 1585 | + * @since 1.8.11 | |
| 1586 | + * @var bool | |
| 1587 | + */ | |
| 1588 | + protected static $paypal_commerce_hidden_for_disconnect = false; | |
| 1589 | + | |
| 1590 | + /** | |
| 1591 | + * Determine PayPal Commerce's connection status and warn admins when | |
| 1592 | + * the gateway is enabled but no PayPal account is connected. | |
| 1593 | + * | |
| 1594 | + * Also wires the runtime filters that: | |
| 1595 | + * - hide PayPal Commerce from the donor-facing payment list when | |
| 1596 | + * it's not connected, so donors never see a broken option, and | |
| 1597 | + * - swap the generic "no active payment gateways" notice for a | |
| 1598 | + * donor-friendly "temporarily unavailable" message when PayPal | |
| 1599 | + * was the only enabled gateway. | |
| 1600 | + * | |
| 1601 | + * Donors never see the admin notice — capability-gated through | |
| 1602 | + * get_credentialed_notice() — but they do see the donor-side | |
| 1603 | + * graceful message. | |
| 1604 | + * | |
| 1605 | + * @since 1.8.11 | |
| 1606 | + * | |
| 1607 | + * @return void | |
| 1608 | + */ | |
| 1609 | + protected function paypal_commerce_connection_check() { | |
| 1610 | + if ( defined( 'CHARITABLE_DISABLE_PAYPAL_COMMERCE_CHECK' ) && CHARITABLE_DISABLE_PAYPAL_COMMERCE_CHECK ) { | |
| 1611 | + return; | |
| 1612 | + } | |
| 1613 | + | |
| 1614 | + if ( ! class_exists( 'Charitable_Gateway_Paypal_Commerce' ) ) { | |
| 1615 | + return; | |
| 1616 | + } | |
| 1617 | + | |
| 1618 | + $active_gateways = charitable_get_helper( 'gateways' )->get_active_gateways(); | |
| 1619 | + $has_gateways = apply_filters( 'charitable_has_active_gateways', ! empty( $active_gateways ) ); | |
| 1620 | + | |
| 1621 | + if ( ! $has_gateways ) { | |
| 1622 | + return; | |
| 1623 | + } | |
| 1624 | + | |
| 1625 | + if ( ! is_array( $active_gateways ) || ! array_key_exists( 'paypal_commerce', $active_gateways ) ) { | |
| 1626 | + return; | |
| 1627 | + } | |
| 1628 | + | |
| 1629 | + $gateway = new Charitable_Gateway_Paypal_Commerce(); | |
| 1630 | + | |
| 1631 | + if ( $gateway->is_seller_connected() ) { | |
| 1632 | + return; | |
| 1633 | + } | |
| 1634 | + | |
| 1635 | + // Wire the runtime filters once per request — gateway list cleanup | |
| 1636 | + // and the donor-friendly fallback notice. | |
| 1637 | + add_filter( 'charitable_active_gateways', array( $this, 'filter_active_gateways_hide_disconnected_paypal_commerce' ) ); | |
| 1638 | + add_filter( 'charitable_no_active_gateways_notice', array( $this, 'filter_no_active_gateways_notice_when_paypal_disconnected' ), 10, 2 ); | |
| 1639 | + | |
| 1640 | + // Admin-only notice in the form-level error list (matches Stripe pattern). | |
| 1641 | + if ( current_user_can( 'manage_charitable_settings' ) ) { // phpcs:ignore | |
| 1642 | + charitable_get_notices()->add_error( $this->get_paypal_commerce_connection_check_notice() ); | |
| 1643 | + } | |
| 1644 | + } | |
| 1645 | + | |
| 1646 | + /** | |
| 1647 | + * Build the admin-only notice text for the PayPal Commerce | |
| 1648 | + * "enabled but not connected" state. Donors never see this — the | |
| 1649 | + * admin-action link is only appended when the current user can | |
| 1650 | + * manage Charitable settings. | |
| 1651 | + * | |
| 1652 | + * @since 1.8.11 | |
| 1653 | + * | |
| 1654 | + * @return string | |
| 1655 | + */ | |
| 1656 | + protected function get_paypal_commerce_connection_check_notice() { | |
| 1657 | + $notice = $this->get_credentialed_notice( | |
| 1658 | + __( 'PayPal Commerce is enabled but no PayPal account is connected.', 'charitable' ), | |
| 1659 | + sprintf( | |
| 1660 | + '<a href="%s">%s</a>.', | |
| 1661 | + admin_url( 'admin.php?page=charitable-settings&tab=gateways&group=gateways_paypal_commerce' ), | |
| 1662 | + __( 'Connect with PayPal', 'charitable' ) | |
| 1663 | + ) | |
| 1664 | + ); | |
| 1665 | + | |
| 1666 | + return apply_filters( 'charitable_paypal_commerce_connection_check_notice', $notice, current_user_can( 'manage_charitable_settings' ) ); // phpcs:ignore | |
| 1667 | + } | |
| 1668 | + | |
| 1669 | + /** | |
| 1670 | + * Remove PayPal Commerce from the active gateway list when its seller | |
| 1671 | + * isn't connected. Donors then never see the option on the donation | |
| 1672 | + * form, and the gateway-selector form field doesn't render a broken | |
| 1673 | + * radio that would have failed at PayPal-side anyway. | |
| 1674 | + * | |
| 1675 | + * Flips $paypal_commerce_hidden_for_disconnect so the companion | |
| 1676 | + * `charitable_no_active_gateways_notice` filter can swap the | |
| 1677 | + * standard message for the donor-friendly variant when PayPal was | |
| 1678 | + * the only enabled gateway. | |
| 1679 | + * | |
| 1680 | + * @since 1.8.11 | |
| 1681 | + * | |
| 1682 | + * @param array $gateways Active gateway map (gateway_id => class name). | |
| 1683 | + * @return array | |
| 1684 | + */ | |
| 1685 | + public function filter_active_gateways_hide_disconnected_paypal_commerce( $gateways ) { | |
| 1686 | + if ( ! is_array( $gateways ) || ! array_key_exists( 'paypal_commerce', $gateways ) ) { | |
| 1687 | + return $gateways; | |
| 1688 | + } | |
| 1689 | + | |
| 1690 | + unset( $gateways['paypal_commerce'] ); | |
| 1691 | + self::$paypal_commerce_hidden_for_disconnect = true; | |
| 1692 | + | |
| 1693 | + return $gateways; | |
| 1694 | + } | |
| 1695 | + | |
| 1696 | + /** | |
| 1697 | + * Replace the "no active payment gateways" message with a | |
| 1698 | + * donor-friendly fallback when PayPal Commerce is the only | |
| 1699 | + * enabled gateway and we just stripped it for being disconnected. | |
| 1700 | + * Admins still get an actionable link; donors see graceful copy. | |
| 1701 | + * | |
| 1702 | + * @since 1.8.11 | |
| 1703 | + * | |
| 1704 | + * @param string $notice Default notice text. | |
| 1705 | + * @param bool $is_admin Whether the current user can manage Charitable settings. | |
| 1706 | + * @return string | |
| 1707 | + */ | |
| 1708 | + public function filter_no_active_gateways_notice_when_paypal_disconnected( $notice, $is_admin = false ) { | |
| 1709 | + if ( ! self::$paypal_commerce_hidden_for_disconnect ) { | |
| 1710 | + return $notice; | |
| 1711 | + } | |
| 1712 | + | |
| 1713 | + if ( $is_admin ) { | |
| 1714 | + return $this->get_credentialed_notice( | |
| 1715 | + __( 'PayPal Commerce is enabled but no PayPal account is connected.', 'charitable' ), | |
| 1716 | + sprintf( | |
| 1717 | + '<a href="%s">%s</a>.', | |
| 1718 | + admin_url( 'admin.php?page=charitable-settings&tab=gateways&group=gateways_paypal_commerce' ), | |
| 1719 | + __( 'Connect with PayPal', 'charitable' ) | |
| 1720 | + ) | |
| 1721 | + ); | |
| 1722 | + } | |
| 1723 | + | |
| 1724 | + return __( 'Online donations are temporarily unavailable. Please check back soon.', 'charitable' ); | |
| 1725 | + } | |
| 1726 | + | |
| 1727 | + /** | |
| 1228 | 1728 | * Return the donor value fields. |
| 1229 | 1729 | * |
| 1230 | 1730 | * @since 1.0.0 |
| 1231 | 1731 | * |
| @@ -1271,9 +1771,9 @@ | ||
| 1271 | 1771 | * Returns true if required fields are missing. |
| 1272 | 1772 | * |
| 1273 | 1773 | * @since 1.0.0 |
| 1274 | 1774 | * |
| 1275 | - * @param array $required_fields | |
| 1775 | + * @param array $required_fields | |
| 1276 | 1776 | * @return boolean |
| 1277 | 1777 | */ |
| 1278 | 1778 | protected function is_missing_required_fields( $required_fields ) { |
| 1279 | 1779 | if ( is_user_logged_in() ) { |
| @@ -1280,13 +1780,15 @@ | ||
| 1280 | 1780 | return false; |
| 1281 | 1781 | } |
| 1282 | 1782 | |
| 1283 | 1783 | if ( is_null( $this->get_submitted_value( 'gateway' ) ) ) { |
| 1784 | + charitable_get_notices()->add_error( | |
| 1785 | + sprintf( | |
| 1786 | + '<p>%s</p>', | |
| 1787 | + __( 'Your donation could not be processed. No payment gateway was selected.', 'charitable' ) | |
| 1788 | + ) | |
| 1789 | + ); | |
| 1284 | 1790 | |
| 1285 | - charitable_get_notices()->add_error( sprintf( '<p>%s</p>', | |
| 1286 | - __( 'Your donation could not be processed. No payment gateway was selected.', 'charitable' ) | |
| 1287 | - ) ); | |
| 1288 | - | |
| 1289 | 1791 | return false; |
| 1290 | 1792 | } |
| 1291 | 1793 | |
| 1292 | 1794 | return ! $this->check_required_fields( $required_fields ); |
| @@ -1297,9 +1799,9 @@ | ||
| 1297 | 1799 | * |
| 1298 | 1800 | * @since 1.0.0 |
| 1299 | 1801 | * @since 1.5.0 Deprecated. This is handled by `Charitable_Public_Form_View` now. |
| 1300 | 1802 | * |
| 1301 | - * @param string|false $custom_template | |
| 1803 | + * @param string|false $custom_template Custom template. | |
| 1302 | 1804 | * @return string|false|Charitable_Template |
| 1303 | 1805 | */ |
| 1304 | 1806 | public function use_custom_templates( $custom_template ) { |
| 1305 | 1807 | charitable_get_deprecated()->deprecated_function( |
| @@ -1306,9 +1808,9 @@ | ||
| 1306 | 1808 | __METHOD__, |
| 1307 | 1809 | '1.5.0' |
| 1308 | 1810 | ); |
| 1309 | 1811 | |
| 1310 | - return $custom_template; | |
| 1812 | + return $custom_template; | |
| 1311 | 1813 | } |
| 1312 | 1814 | } |
| 1313 | 1815 | |
| 1314 | 1816 | endif; |