← All changes
|
includes/campaigns/class-charitable-campaign.php
+568
-31
1.6.6
→
1.8.13
View file →
| @@ -1,18 +1,20 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | /** |
| 3 | - * Campaign model | |
| 3 | + * Campaign model. | |
| 4 | 4 | * |
| 5 | 5 | * @package Charitable/Classes/Charitable_Campaign |
| 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.0.0 | |
| 10 | + * @version 1.6.54 | |
| 11 | 11 | */ |
| 12 | 12 | |
| 13 | 13 | // Exit if accessed directly. |
| 14 | -if ( ! defined( 'ABSPATH' ) ) { exit; } | |
| 14 | +if ( ! defined( 'ABSPATH' ) ) { | |
| 15 | + exit; | |
| 16 | +} | |
| 15 | 17 | |
| 16 | 18 | if ( ! class_exists( 'Charitable_Campaign' ) ) : |
| 17 | 19 | |
| 18 | 20 | /** |
| @@ -68,8 +70,15 @@ | ||
| 68 | 70 | */ |
| 69 | 71 | private $goal; |
| 70 | 72 | |
| 71 | 73 | /** |
| 74 | + * The fundraising goal for the campaign. | |
| 75 | + * | |
| 76 | + * @var string|false | |
| 77 | + */ | |
| 78 | + private $min_donation_amount; | |
| 79 | + | |
| 80 | + /** | |
| 72 | 81 | * The donations made to this campaign. |
| 73 | 82 | * |
| 74 | 83 | * @var WP_Query|false |
| 75 | 84 | */ |
| @@ -109,12 +118,65 @@ | ||
| 109 | 118 | if ( ! $post instanceof WP_Post ) { |
| 110 | 119 | $post = get_post( $post ); |
| 111 | 120 | } |
| 112 | 121 | |
| 122 | + add_filter( 'charitable_campaign_description_template_content', array( $this, 'description_content' ), 10, 2 ); | |
| 123 | + | |
| 113 | 124 | $this->post = $post; |
| 114 | 125 | } |
| 115 | 126 | |
| 116 | 127 | /** |
| 128 | + * Return description content. | |
| 129 | + * | |
| 130 | + * @since 1.7.0.4 | |
| 131 | + * | |
| 132 | + * @param string $content The content. | |
| 133 | + * @param object $campaign The campaign object. | |
| 134 | + * @return mixed | |
| 135 | + */ | |
| 136 | + public function description_content( $content = false, $campaign = false ) { | |
| 137 | + | |
| 138 | + // search for YouTube and Vimeo video urls by default and attempt to convert them to oembed. | |
| 139 | + | |
| 140 | + preg_match_all( '#(?<=v=|v\/|vi=|vi\/|youtu.be\/)[a-zA-Z0-9_-]{11}#', $content, $matches ); | |
| 141 | + | |
| 142 | + if ( $matches ) : | |
| 143 | + foreach ( $matches as $video_id ) { | |
| 144 | + if ( ! empty( $video_id ) ) : | |
| 145 | + $video_embed_args = apply_filters( 'charitable_campaign_description_template_video', array(), 'youtube', $video_id, $content ); | |
| 146 | + $embed = wp_oembed_get( 'https://youtube.com/watch?v=' . $video_id[0], $video_embed_args ); | |
| 147 | + | |
| 148 | + $content = str_replace( trim( 'https://youtube.com/watch?v=' . $video_id[0] ), $embed, $content ); | |
| 149 | + $content = str_replace( trim( 'http://youtube.com/watch?v=' . $video_id[0] ), $embed, $content ); | |
| 150 | + $content = str_replace( trim( 'https://www.youtube.com/watch?v=' . $video_id[0] ), $embed, $content ); | |
| 151 | + $content = str_replace( trim( 'http://www.youtube.com/watch?v=' . $video_id[0] ), $embed, $content ); | |
| 152 | + endif; | |
| 153 | + } | |
| 154 | + endif; | |
| 155 | + | |
| 156 | + preg_match( '/https?:\/\/(?:www\.)?vimeo\.com\/\d{8}/', $content, $matches ); | |
| 157 | + | |
| 158 | + if ( $matches ) : | |
| 159 | + foreach ( $matches as $video_url ) { | |
| 160 | + if ( ! empty( $video_id ) ) : | |
| 161 | + $video_embed_args = apply_filters( 'charitable_campaign_description_template_video', array(), 'vimeo', $video_id, $content, $campaign ); | |
| 162 | + $embed = wp_oembed_get( $video_url, $video_embed_args ); | |
| 163 | + | |
| 164 | + $content = str_replace( trim( $video_url ), $embed, $content ); | |
| 165 | + endif; | |
| 166 | + } | |
| 167 | + endif; | |
| 168 | + | |
| 169 | + // allow developers and plugins to hook into content. | |
| 170 | + $content = apply_filters( 'charitable_campaign_description_content', $content, $campaign ); | |
| 171 | + | |
| 172 | + // apply WP's the_content filter, which other plugins can hook into. | |
| 173 | + $content = apply_filters( 'the_content', $content ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- 'the_content' is a WordPress core filter hook. | |
| 174 | + | |
| 175 | + return $content; | |
| 176 | + } | |
| 177 | + | |
| 178 | + /** | |
| 117 | 179 | * Magic getter. |
| 118 | 180 | * |
| 119 | 181 | * @since 1.0.0 |
| 120 | 182 | * |
| @@ -121,9 +183,9 @@ | ||
| 121 | 183 | * @param string $key The key of the field to get. |
| 122 | 184 | * @return mixed |
| 123 | 185 | */ |
| 124 | 186 | public function __get( $key ) { |
| 125 | - if ( property_exists( $this->post, $key ) ) { | |
| 187 | + if ( null !== $this->post && property_exists( $this->post, $key ) ) { | |
| 126 | 188 | return $this->post->$key; |
| 127 | 189 | } |
| 128 | 190 | |
| 129 | 191 | return $this->get( $key ); |
| @@ -129,8 +191,26 @@ | ||
| 129 | 191 | return $this->get( $key ); |
| 130 | 192 | } |
| 131 | 193 | |
| 132 | 194 | /** |
| 195 | + * Limit properties to be serialized. | |
| 196 | + * | |
| 197 | + * @since 1.6.18 | |
| 198 | + * | |
| 199 | + * @return array | |
| 200 | + */ | |
| 201 | + public function __sleep() { | |
| 202 | + return array( | |
| 203 | + 'post', | |
| 204 | + 'end_time', | |
| 205 | + 'goal', | |
| 206 | + 'donations', | |
| 207 | + 'donated_amount', | |
| 208 | + 'donation_form', | |
| 209 | + ); | |
| 210 | + } | |
| 211 | + | |
| 212 | + /** | |
| 133 | 213 | * Returns the campaign's post_meta values. _campaign_ is automatically prepended to the meta key. |
| 134 | 214 | * |
| 135 | 215 | * @see get_post_meta |
| 136 | 216 | * |
| @@ -165,8 +245,13 @@ | ||
| 165 | 245 | * @return mixed This will return an array if single is false. If it's true, the value of the |
| 166 | 246 | * meta_value field will be returned. |
| 167 | 247 | */ |
| 168 | 248 | public function get_meta( $meta_name, $single = true ) { |
| 249 | + | |
| 250 | + if ( empty( $this->post->ID ) ) { | |
| 251 | + return; | |
| 252 | + } | |
| 253 | + | |
| 169 | 254 | /** |
| 170 | 255 | * Filter the meta value of a particular campaign field. |
| 171 | 256 | * |
| 172 | 257 | * @since 1.0.0 |
| @@ -194,8 +279,20 @@ | ||
| 194 | 279 | return $this->fields; |
| 195 | 280 | } |
| 196 | 281 | |
| 197 | 282 | /** |
| 283 | + * Returns the id of the campaign (post). | |
| 284 | + * | |
| 285 | + * @since 1.7.0.9 | |
| 286 | + * @version 1.8.0 | |
| 287 | + * | |
| 288 | + * @return integer | |
| 289 | + */ | |
| 290 | + public function get_campaign_id() { | |
| 291 | + return ( isset( $this->post->ID ) ) ? intval( $this->post->ID ) : false; | |
| 292 | + } | |
| 293 | + | |
| 294 | + /** | |
| 198 | 295 | * Returns whether the campaign is endless (i.e. no end date has been set). |
| 199 | 296 | * |
| 200 | 297 | * @since 1.0.0 |
| 201 | 298 | * |
| @@ -205,8 +302,19 @@ | ||
| 205 | 302 | return 0 == $this->get_meta( '_campaign_end_date' ); |
| 206 | 303 | } |
| 207 | 304 | |
| 208 | 305 | /** |
| 306 | + * Return whether the campaign supports custom donation amounts. | |
| 307 | + * | |
| 308 | + * @since 1.6.53 | |
| 309 | + * | |
| 310 | + * @return boolean | |
| 311 | + */ | |
| 312 | + public function get_allow_custom_donations() { | |
| 313 | + return (int) get_post_meta( $this->post->ID, '_campaign_allow_custom_donations', true ); | |
| 314 | + } | |
| 315 | + | |
| 316 | + /** | |
| 209 | 317 | * Return the suggested amounts, or an empty array if there are none. |
| 210 | 318 | * |
| 211 | 319 | * @since 1.0.0 |
| 212 | 320 | * |
| @@ -222,8 +330,25 @@ | ||
| 222 | 330 | return apply_filters( 'charitable_campaign_suggested_donations', $value, $this ); |
| 223 | 331 | } |
| 224 | 332 | |
| 225 | 333 | /** |
| 334 | + * Return the suggested amounts, or an empty array if there are none. | |
| 335 | + * | |
| 336 | + * @since 1.0.0 | |
| 337 | + * | |
| 338 | + * @return array | |
| 339 | + */ | |
| 340 | + public function get_suggested_donations_default() { | |
| 341 | + $value = get_post_meta( $this->post->ID, '_campaign_suggested_donations_default', true ); | |
| 342 | + | |
| 343 | + if ( is_array( $value ) && isset( $value[0] ) ) { | |
| 344 | + $value = $value[0]; | |
| 345 | + } | |
| 346 | + | |
| 347 | + return apply_filters( 'charitable_campaign_suggested_donations_default', $value, $this ); | |
| 348 | + } | |
| 349 | + | |
| 350 | + /** | |
| 226 | 351 | * Returns the end date in your preferred format. |
| 227 | 352 | * |
| 228 | 353 | * If a format is not provided, the user-defined date_format in WordPress settings is used. |
| 229 | 354 | * |
| @@ -255,14 +380,14 @@ | ||
| 255 | 380 | /** |
| 256 | 381 | * Returns the timetamp of the end date. |
| 257 | 382 | * |
| 258 | 383 | * @since 1.0.0 |
| 384 | + * @since 1.8.1.10 | |
| 259 | 385 | * |
| 260 | 386 | * @return int|false Int if campaign has an end date. False if campaign has no end date. |
| 261 | 387 | */ |
| 262 | 388 | public function get_end_time() { |
| 263 | 389 | if ( ! isset( $this->end_time ) ) { |
| 264 | - | |
| 265 | 390 | $end_date = $this->get_meta( '_campaign_end_date' ); |
| 266 | 391 | |
| 267 | 392 | if ( ! $end_date ) { |
| 268 | 393 | return false; |
| @@ -272,8 +397,13 @@ | ||
| 272 | 397 | $date_time = explode( ' ', $end_date ); |
| 273 | 398 | $date = explode( '-', $date_time[0] ); |
| 274 | 399 | $time = explode( ':', $date_time[1] ); |
| 275 | 400 | |
| 401 | + // Check for undefined array keys. If any of these are not defined, return false. | |
| 402 | + if ( ! isset( $date[0] ) || ! isset( $date[1] ) || ! isset( $date[2] ) || ! isset( $time[0] ) || ! isset( $time[1] ) || ! isset( $time[2] ) ) { | |
| 403 | + return false; | |
| 404 | + } | |
| 405 | + | |
| 276 | 406 | $this->end_time = mktime( $time[0], $time[1], $time[2], $date[1], $date[2], $date[0] ); |
| 277 | 407 | } |
| 278 | 408 | |
| 279 | 409 | return $this->end_time; |
| @@ -329,9 +459,11 @@ | ||
| 329 | 459 | |
| 330 | 460 | /* Condition 2: There is less than an hour left. */ |
| 331 | 461 | |
| 332 | 462 | $minutes_remaining = ceil( $seconds_left / 60 ); |
| 333 | - $time_left = apply_filters( 'charitabile_campaign_minutes_left', | |
| 463 | + $time_left = apply_filters( | |
| 464 | + 'charitabile_campaign_minutes_left', // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- This is a legacy hook used by other Charitable code. Changing it would break existing functionality. | |
| 465 | + // translators: %s is the number of minutes left. | |
| 334 | 466 | sprintf( _n( '%s Minute Left', '%s Minutes Left', $minutes_remaining, 'charitable' ), '<span class="amount time-left minutes-left">' . $minutes_remaining . '</span>' ), |
| 335 | 467 | $this |
| 336 | 468 | ); |
| 337 | 469 | |
| @@ -339,9 +471,11 @@ | ||
| 339 | 471 | |
| 340 | 472 | /* Condition 3: There is less than a day left. */ |
| 341 | 473 | |
| 342 | 474 | $hours_remaining = floor( $seconds_left / 3600 ); |
| 343 | - $time_left = apply_filters( 'charitabile_campaign_hours_left', | |
| 475 | + $time_left = apply_filters( | |
| 476 | + 'charitabile_campaign_hours_left', // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- This is a legacy hook used by other Charitable code. Changing it would break existing functionality. | |
| 477 | + // translators: %s is the number of hours left. | |
| 344 | 478 | sprintf( _n( '%s Hour Left', '%s Hours Left', $hours_remaining, 'charitable' ), '<span class="amount time-left hours-left">' . $hours_remaining . '</span>' ), |
| 345 | 479 | $this |
| 346 | 480 | ); |
| 347 | 481 | |
| @@ -349,14 +483,16 @@ | ||
| 349 | 483 | |
| 350 | 484 | /* Condition 4: There is more than a day left. */ |
| 351 | 485 | |
| 352 | 486 | $days_remaining = floor( $seconds_left / 86400 ); |
| 353 | - $time_left = apply_filters( 'charitabile_campaign_days_left', | |
| 487 | + $time_left = apply_filters( | |
| 488 | + 'charitabile_campaign_days_left', // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- This is a legacy hook used by other Charitable code. Changing it would break existing functionality. | |
| 489 | + // translators: %s is the number of days left. | |
| 354 | 490 | sprintf( _n( '%s Day Left', '%s Days Left', $days_remaining, 'charitable' ), '<span class="amount time-left days-left">' . $days_remaining . '</span>' ), |
| 355 | 491 | $this |
| 356 | 492 | ); |
| 357 | 493 | |
| 358 | - }//end if | |
| 494 | + } //end if | |
| 359 | 495 | |
| 360 | 496 | return apply_filters( 'charitable_campaign_time_left', $time_left, $this ); |
| 361 | 497 | } |
| 362 | 498 | |
| @@ -404,12 +540,15 @@ | ||
| 404 | 540 | return ''; |
| 405 | 541 | } |
| 406 | 542 | |
| 407 | 543 | if ( ! $this->has_goal() ) { |
| 544 | + // translators: %s is the time since the campaign ended. | |
| 408 | 545 | $message = __( 'This campaign ended %s ago', 'charitable' ); |
| 409 | 546 | } elseif ( $this->has_achieved_goal() ) { |
| 547 | + // translators: %s is the time since the campaign ended. | |
| 410 | 548 | $message = __( 'This campaign successfully reached its funding goal and ended %s ago', 'charitable' ); |
| 411 | 549 | } else { |
| 550 | + // translators: %s is the time since the campaign ended. | |
| 412 | 551 | $message = __( 'This campaign failed to reach its funding goal %s ago', 'charitable' ); |
| 413 | 552 | } |
| 414 | 553 | |
| 415 | 554 | return apply_filters( 'charitable_campaign_finished_notice', sprintf( $message, '<span class="time-ago">' . human_time_diff( $this->get_end_time() ) . '</span>' ), $this ); |
| @@ -445,8 +584,24 @@ | ||
| 445 | 584 | return $this->goal; |
| 446 | 585 | } |
| 447 | 586 | |
| 448 | 587 | /** |
| 588 | + * Returns the minimum donation amount of the campaign. | |
| 589 | + * | |
| 590 | + * @since 1.0.0 | |
| 591 | + * | |
| 592 | + * @return string|false Amount if goal is set. False otherwise. | |
| 593 | + */ | |
| 594 | + public function get_min_donation_amount() { | |
| 595 | + if ( ! isset( $this->min_donation_amount ) ) { | |
| 596 | + $this->min_donation_amount = $this->get_meta( '_campaign_min_donation_amount' ) ? $this->get_meta( '_campaign_min_donation_amount' ) : false; | |
| 597 | + } | |
| 598 | + | |
| 599 | + return $this->min_donation_amount; | |
| 600 | + } | |
| 601 | + | |
| 602 | + | |
| 603 | + /** | |
| 449 | 604 | * Returns whether a goal has been set (anything greater than $0 is a goal). |
| 450 | 605 | * |
| 451 | 606 | * @since 1.0.0 |
| 452 | 607 | * |
| @@ -533,10 +688,11 @@ | ||
| 533 | 688 | * |
| 534 | 689 | * @return string |
| 535 | 690 | */ |
| 536 | 691 | public function get_status() { |
| 537 | - $status = $this->post->post_status; | |
| 538 | 692 | |
| 693 | + $status = $post_status = isset( $this->post ) && ! empty( $this->post->post_status ) ? $this->post->post_status : false; // phpcs:ignore | |
| 694 | + | |
| 539 | 695 | if ( 'publish' == $status ) { |
| 540 | 696 | $status = $this->has_ended() ? 'finished' : 'active'; |
| 541 | 697 | } |
| 542 | 698 | |
| @@ -548,9 +704,9 @@ | ||
| 548 | 704 | * @param string $status The campaign status. |
| 549 | 705 | * @param string $post_status The post_status of the campaign. |
| 550 | 706 | * @param Charitable_Campaign $campaign This instance of `Charitable_Campaign`. |
| 551 | 707 | */ |
| 552 | - return apply_filters( 'charitable_campaign_status', $status, $this->post->post_status, $this ); | |
| 708 | + return apply_filters( 'charitable_campaign_status', $status, $post_status, $this ); | |
| 553 | 709 | } |
| 554 | 710 | |
| 555 | 711 | /** |
| 556 | 712 | * Return a status key for the campaign. |
| @@ -606,15 +762,24 @@ | ||
| 606 | 762 | * Return the campaign status tag. |
| 607 | 763 | * |
| 608 | 764 | * @since 1.0.0 |
| 609 | 765 | * |
| 766 | + * @param boolean $show_achievement Whether to show the achievement status tag. | |
| 767 | + * @param boolean $show_active_tag Whether to show the active status tag. | |
| 768 | + * | |
| 610 | 769 | * @return string |
| 611 | 770 | */ |
| 612 | - public function get_status_tag() { | |
| 613 | - $key = $this->get_status_key(); | |
| 614 | - $show_achievement = apply_filters( 'charitable_campaign_show_achievement_status_tag', true ); | |
| 615 | - $show_active_tag = apply_filters( 'charitable_campaign_show_active_status_tag', false ); | |
| 771 | + public function get_status_tag( $show_achievement = null, $show_active_tag = null ) { | |
| 772 | + $key = $this->get_status_key(); | |
| 616 | 773 | |
| 774 | + if ( is_null( $show_achievement ) ) { | |
| 775 | + $show_achievement = apply_filters( 'charitable_campaign_show_achievement_status_tag', true ); | |
| 776 | + } | |
| 777 | + | |
| 778 | + if ( is_null( $show_active_tag ) ) { | |
| 779 | + $show_active_tag = apply_filters( 'charitable_campaign_show_active_status_tag', false ); | |
| 780 | + } | |
| 781 | + | |
| 617 | 782 | switch ( $key ) { |
| 618 | 783 | case 'ended': |
| 619 | 784 | $tag = __( 'Ended', 'charitable' ); |
| 620 | 785 | break; |
| @@ -631,11 +796,31 @@ | ||
| 631 | 796 | $tag = __( 'Ending Soon', 'charitable' ); |
| 632 | 797 | break; |
| 633 | 798 | |
| 634 | 799 | case 'active': |
| 800 | + case 'publish': | |
| 635 | 801 | $tag = $show_active_tag ? __( 'Active', 'charitable' ) : ''; |
| 636 | 802 | break; |
| 637 | 803 | |
| 804 | + case 'inactive': | |
| 805 | + switch ( $this->post->post_status ) { | |
| 806 | + case 'pending': | |
| 807 | + $tag = __( 'Pending', 'charitable' ); | |
| 808 | + break; | |
| 809 | + | |
| 810 | + case 'draft': | |
| 811 | + $tag = __( 'Draft', 'charitable' ); | |
| 812 | + break; | |
| 813 | + | |
| 814 | + case 'future': | |
| 815 | + $tag = __( 'Scheduled', 'charitable' ); | |
| 816 | + break; | |
| 817 | + | |
| 818 | + default: | |
| 819 | + $tag = ''; | |
| 820 | + } | |
| 821 | + break; | |
| 822 | + | |
| 638 | 823 | default: |
| 639 | 824 | $tag = ''; |
| 640 | 825 | |
| 641 | 826 | }//end switch |
| @@ -720,8 +905,9 @@ | ||
| 720 | 905 | } |
| 721 | 906 | |
| 722 | 907 | $amount = $this->donated_amount; |
| 723 | 908 | |
| 909 | + /* If returning a string, return it in the site number format. */ | |
| 724 | 910 | if ( ! $sanitize ) { |
| 725 | 911 | $amount = Charitable_Currency::get_instance()->sanitize_database_amount( $amount ); |
| 726 | 912 | } |
| 727 | 913 | |
| @@ -760,14 +946,18 @@ | ||
| 760 | 946 | $amount = $this->get_donated_amount(); |
| 761 | 947 | $goal = $this->get_meta( '_campaign_goal' ); |
| 762 | 948 | |
| 763 | 949 | if ( $goal ) { |
| 764 | - $ret = sprintf( _x( '%s donated of %s goal', 'amount donated of goal', 'charitable' ), | |
| 950 | + $ret = sprintf( | |
| 951 | + // translators: %1$s is the amount donated, %2$s is the goal amount. | |
| 952 | + _x( '%1$s donated of %2$s goal', 'amount donated of goal', 'charitable' ), | |
| 765 | 953 | '<span class="amount">' . $currency_helper->get_monetary_amount( $amount ) . '</span>', |
| 766 | 954 | '<span class="goal-amount">' . $currency_helper->get_monetary_amount( $goal ) . '</span>' |
| 767 | 955 | ); |
| 768 | 956 | } else { |
| 769 | - $ret = sprintf( _x( '%s donated', 'amount donated', 'charitable' ), | |
| 957 | + $ret = sprintf( | |
| 958 | + // translators: %s is the amount donated. | |
| 959 | + _x( '%s donated', 'amount donated', 'charitable' ), | |
| 770 | 960 | '<span class="amount">' . $currency_helper->get_monetary_amount( $amount ) . '</span>' |
| 771 | 961 | ); |
| 772 | 962 | } |
| 773 | 963 | |
| @@ -786,13 +976,44 @@ | ||
| 786 | 976 | return apply_filters( 'charitable_donation_summary', $ret, $this, $amount, $goal ); |
| 787 | 977 | } |
| 788 | 978 | |
| 789 | 979 | /** |
| 980 | + * Return a string especially for the campaign builder describing the campaign's donation summary. | |
| 981 | + * | |
| 982 | + * @since 1.8.0 | |
| 983 | + * | |
| 984 | + * @return string | |
| 985 | + */ | |
| 986 | + public function get_builder_donation_summary() { | |
| 987 | + $currency_helper = charitable_get_currency_helper(); | |
| 988 | + $amount = $this->get_donated_amount(); | |
| 989 | + $goal = $this->get_meta( '_campaign_goal' ); | |
| 990 | + | |
| 991 | + $ret = sprintf( | |
| 992 | + // translators: %s is the amount donated. | |
| 993 | + _x( '%s donated', 'amount donated', 'charitable' ), | |
| 994 | + '<span class="amount">' . $currency_helper->get_monetary_amount( $amount ) . '</span>' | |
| 995 | + ); | |
| 996 | + | |
| 997 | + /** | |
| 998 | + * Filter the returned string if you want to display the donation summary in a different way. | |
| 999 | + * | |
| 1000 | + * @since 1.8.0 | |
| 1001 | + * | |
| 1002 | + * @param string $ret The summary. | |
| 1003 | + * @param Charitable_Campaign $this This campaign object. | |
| 1004 | + * @param float $amount The amount donated. | |
| 1005 | + * @param int $goal The campaign goal. | |
| 1006 | + */ | |
| 1007 | + return apply_filters( 'charitable_builder_donation_summary', $ret, $this, $amount, $goal ); | |
| 1008 | + } | |
| 1009 | + | |
| 1010 | + /** | |
| 790 | 1011 | * Return the percentage donated. Use this if you want a formatted string. |
| 791 | 1012 | * |
| 792 | 1013 | * @since 1.0.0 |
| 793 | 1014 | * |
| 794 | - * @return string|false String if campaign has a goal. False if no goal is set. | |
| 1015 | + * @return string|false String if campaign has a goal. False if no goal is set. | |
| 795 | 1016 | */ |
| 796 | 1017 | public function get_percent_donated() { |
| 797 | 1018 | $percent = $this->get_percent_donated_raw(); |
| 798 | 1019 | |
| @@ -807,10 +1028,43 @@ | ||
| 807 | 1028 | |
| 808 | 1029 | /** |
| 809 | 1030 | * Returns the percentage donated as a number. |
| 810 | 1031 | * |
| 811 | - * @since 1.0.0 | |
| 1032 | + * Ported from Charitable Pro, 1.8.13, THEN deliberately changed — | |
| 1033 | + * Lite deviates from Pro here on purpose. Pro's own version (and my | |
| 1034 | + * first port of it) unconditionally strips the thousands separator | |
| 1035 | + * and swaps the decimal separator, which is correct for a | |
| 1036 | + * DISPLAY-locale-formatted goal like "1,000.00" but wrong for the | |
| 1037 | + * CANONICAL dot-decimal form sanitize_campaign_goal() actually | |
| 1038 | + * stores: that method runs every saved goal through | |
| 1039 | + * Charitable_Currency::sanitize_monetary_amount(), which returns | |
| 1040 | + * floatval() — always dot-decimal, regardless of site locale. On a | |
| 1041 | + * comma-decimal / dot-thousands site (Settings > General; both | |
| 1042 | + * separators are user-settable), the unconditional strip reads an | |
| 1043 | + * already-canonical "1000.5" as "10005" — a campaign that raised 300 | |
| 1044 | + * of a 1000.5 goal reported 3% instead of 30%. | |
| 812 | 1045 | * |
| 1046 | + * is_numeric() distinguishes the two shapes: a canonical dot-decimal | |
| 1047 | + * value passes it and is used via a plain floatval(), untouched; a | |
| 1048 | + * display-formatted value ("1,000.00", "1.000,50") does not, and | |
| 1049 | + * still gets the separator treatment below. This is what makes both | |
| 1050 | + * the locale-formatted-input case (list-campaigns/get-campaign's own | |
| 1051 | + * "1,000.00" trap) and the canonical-storage case correct at once. | |
| 1052 | + * | |
| 1053 | + * Callers reach well beyond the abilities surface: the front-end | |
| 1054 | + * progress bar (templates/campaign/progress-bar.php, both the bar | |
| 1055 | + * width and the aria-valuenow a screen reader announces), the | |
| 1056 | + * dashboard and reports classes, and get_percent_donated() below all | |
| 1057 | + * go through this method. Pro carries the same bug on the same | |
| 1058 | + * front-end path; Charitable_Campaign_Abilities::percent_raised() | |
| 1059 | + * being correct would not have been enough to fix what a donor's | |
| 1060 | + * screen reader announces. The verbatim-fidelity rule for this port | |
| 1061 | + * covers the abilities registrars; it does not require shipping a | |
| 1062 | + * known locale regression on a shared front-end template. | |
| 1063 | + * | |
| 1064 | + * @since 1.0.0 | |
| 1065 | + * @version 1.8.13 | |
| 1066 | + * | |
| 813 | 1067 | * @return int |
| 814 | 1068 | */ |
| 815 | 1069 | public function get_percent_donated_raw() { |
| 816 | 1070 | if ( ! $this->has_goal() ) { |
| @@ -816,9 +1070,30 @@ | ||
| 816 | 1070 | if ( ! $this->has_goal() ) { |
| 817 | 1071 | return false; |
| 818 | 1072 | } |
| 819 | 1073 | |
| 820 | - return ( $this->get_donated_amount( true ) / $this->get_goal() ) * 100; | |
| 1074 | + $donated = floatval( $this->get_donated_amount( true ) ); | |
| 1075 | + | |
| 1076 | + $goal_meta = $this->get_meta( '_campaign_goal' ); | |
| 1077 | + $currency = Charitable_Currency::get_instance(); | |
| 1078 | + $decimal_separator = $currency->get_decimal_separator(); | |
| 1079 | + $thousands_separator = $currency->get_thousands_separator(); | |
| 1080 | + | |
| 1081 | + /* | |
| 1082 | + * Already-canonical dot-decimal values (the shape every saved | |
| 1083 | + * goal is actually stored in) pass through untouched; anything | |
| 1084 | + * else is a display-formatted string and gets the thousands | |
| 1085 | + * strip + decimal-separator swap. | |
| 1086 | + */ | |
| 1087 | + $goal = is_numeric( $goal_meta ) | |
| 1088 | + ? floatval( $goal_meta ) | |
| 1089 | + : floatval( str_replace( $decimal_separator, '.', str_replace( $thousands_separator, '', $goal_meta ) ) ); | |
| 1090 | + | |
| 1091 | + if ( $goal <= 0 ) { | |
| 1092 | + return 0; | |
| 1093 | + } | |
| 1094 | + | |
| 1095 | + return ( $donated / $goal ) * 100; | |
| 821 | 1096 | } |
| 822 | 1097 | |
| 823 | 1098 | /** |
| 824 | 1099 | * Return the number of people who have donated to the campaign. |
| @@ -835,12 +1110,44 @@ | ||
| 835 | 1110 | * |
| 836 | 1111 | * @param int $count Number of donors. |
| 837 | 1112 | * @param Charitable_Campaign $this This campaign object. |
| 838 | 1113 | */ |
| 839 | - return apply_filters( 'charitable_campaign_donor_count', charitable_get_table( 'campaign_donations' )->count_campaign_donors( $this->ID ), $this ); | |
| 1114 | + return apply_filters( | |
| 1115 | + 'charitable_campaign_donor_count', | |
| 1116 | + (int) charitable_get_table( 'campaign_donations' )->count_campaign_donors( $this->ID ), | |
| 1117 | + $this | |
| 1118 | + ); | |
| 840 | 1119 | } |
| 841 | 1120 | |
| 842 | 1121 | /** |
| 1122 | + * Return the number of donations (transactions) for the campaign. | |
| 1123 | + * | |
| 1124 | + * Ported from Charitable Pro unchanged. Backs the | |
| 1125 | + * charitable/get-campaign-performance ability's donation_count field — | |
| 1126 | + * a campaign with two donations from the same donor has a donor_count | |
| 1127 | + * of 1 but a get_donation_count() of 2. | |
| 1128 | + * | |
| 1129 | + * @since 1.8.13 | |
| 1130 | + * | |
| 1131 | + * @return int | |
| 1132 | + */ | |
| 1133 | + public function get_donation_count() { | |
| 1134 | + /** | |
| 1135 | + * Filter the number of donations (transactions) for the campaign. | |
| 1136 | + * | |
| 1137 | + * @since 1.8.13 | |
| 1138 | + * | |
| 1139 | + * @param int $count Number of donations. | |
| 1140 | + * @param Charitable_Campaign $this This campaign object. | |
| 1141 | + */ | |
| 1142 | + return apply_filters( | |
| 1143 | + 'charitable_campaign_donation_count', | |
| 1144 | + (int) charitable_get_table( 'campaign_donations' )->count_campaign_donations( $this->ID ), | |
| 1145 | + $this | |
| 1146 | + ); | |
| 1147 | + } | |
| 1148 | + | |
| 1149 | + /** | |
| 843 | 1150 | * Returns the donation form object. |
| 844 | 1151 | * |
| 845 | 1152 | * @since 1.0.0 |
| 846 | 1153 | * |
| @@ -886,8 +1193,82 @@ | ||
| 886 | 1193 | return apply_filters( 'charitable_session_donation_amount', $amount, $this ); |
| 887 | 1194 | } |
| 888 | 1195 | |
| 889 | 1196 | /** |
| 1197 | + * Returns the donation period for the amount in session. | |
| 1198 | + * | |
| 1199 | + * @since 1.6.25 | |
| 1200 | + * | |
| 1201 | + * @return false|string | |
| 1202 | + */ | |
| 1203 | + public function get_donation_period_in_session() { | |
| 1204 | + $donation = charitable_get_session()->get_donation_by_campaign( $this->ID ); | |
| 1205 | + | |
| 1206 | + if ( ! $donation ) { | |
| 1207 | + return false; | |
| 1208 | + } | |
| 1209 | + | |
| 1210 | + if ( ! is_array( $donation ) || ! array_key_exists( 'donation_period', $donation ) ) { | |
| 1211 | + return 'once'; | |
| 1212 | + } | |
| 1213 | + | |
| 1214 | + return $donation['donation_period']; | |
| 1215 | + } | |
| 1216 | + | |
| 1217 | + /** | |
| 1218 | + * Return the initial/default donation period when | |
| 1219 | + * | |
| 1220 | + * @since 1.6.32 | |
| 1221 | + * | |
| 1222 | + * @return string | |
| 1223 | + */ | |
| 1224 | + public function get_initial_donation_period() { | |
| 1225 | + $period = $this->get_donation_period_in_session(); | |
| 1226 | + | |
| 1227 | + if ( false !== $period ) { | |
| 1228 | + return $period; | |
| 1229 | + } | |
| 1230 | + | |
| 1231 | + if ( ! class_exists( 'Charitable_Recurring' ) ) { | |
| 1232 | + return 'once'; | |
| 1233 | + } | |
| 1234 | + | |
| 1235 | + switch ( $this->get( 'recurring_donation_mode' ) ) { | |
| 1236 | + case 'advanced': | |
| 1237 | + $period = 'one-time' === $this->get( 'recurring_default_tab' ) ? 'once' : 'recurring'; | |
| 1238 | + break; | |
| 1239 | + | |
| 1240 | + default: | |
| 1241 | + $period = 'once'; | |
| 1242 | + } | |
| 1243 | + | |
| 1244 | + return $period; | |
| 1245 | + } | |
| 1246 | + | |
| 1247 | + /** | |
| 1248 | + * Get the default donation amount for this campaign. | |
| 1249 | + * | |
| 1250 | + * @since 1.6.32 | |
| 1251 | + * | |
| 1252 | + * @return int | |
| 1253 | + */ | |
| 1254 | + public function get_default_donation_amount() { | |
| 1255 | + /** | |
| 1256 | + * Filter the default donation amount. | |
| 1257 | + * | |
| 1258 | + * Note, the donation amount should be expressed in a format where | |
| 1259 | + * a period (.) is used as the decimal separator. i.e. 2.50 is | |
| 1260 | + * two dollars and fifty cents. | |
| 1261 | + * | |
| 1262 | + * @since 1.5.6 | |
| 1263 | + * | |
| 1264 | + * @param float|int $amount The amount to be filtered. $0 by default. | |
| 1265 | + * @param Charitable_Campaign $campaign The instance of `Charitable_Campaign`. | |
| 1266 | + */ | |
| 1267 | + return apply_filters( 'charitable_default_donation_amount', 0, $this ); | |
| 1268 | + } | |
| 1269 | + | |
| 1270 | + /** | |
| 890 | 1271 | * Renders the donate button template. |
| 891 | 1272 | * |
| 892 | 1273 | * @since 1.0.0 |
| 893 | 1274 | * |
| @@ -934,18 +1315,19 @@ | ||
| 934 | 1315 | |
| 935 | 1316 | $display_option = charitable_get_option( 'donation_form_display', 'separate_page' ); |
| 936 | 1317 | |
| 937 | 1318 | switch ( $display_option ) { |
| 938 | - case 'modal' : | |
| 1319 | + case 'modal': | |
| 939 | 1320 | $template_name = 'campaign-loop/donate-modal.php'; |
| 940 | 1321 | break; |
| 941 | 1322 | |
| 942 | - default : | |
| 1323 | + default: | |
| 943 | 1324 | $template_name = apply_filters( 'charitable_donate_button_loop_template', 'campaign-loop/donate-link.php', $this ); |
| 944 | 1325 | } |
| 945 | 1326 | |
| 946 | 1327 | charitable_template( $template_name, array( 'campaign' => $this ) ); |
| 947 | 1328 | } |
| 1329 | + | |
| 948 | 1330 | /** |
| 949 | 1331 | * Returns the campaign creator. |
| 950 | 1332 | * |
| 951 | 1333 | * By default, this just returns the user from the post_author field, but |
| @@ -997,13 +1379,29 @@ | ||
| 997 | 1379 | return charitable_get_currency_helper()->sanitize_monetary_amount( $value ); |
| 998 | 1380 | } |
| 999 | 1381 | |
| 1000 | 1382 | /** |
| 1383 | + * Sanitize the campaign minimum amount. | |
| 1384 | + * | |
| 1385 | + * @since 1.7.0.3 | |
| 1386 | + * | |
| 1387 | + * @param string $value Current value of min amount. | |
| 1388 | + * @return string|int | |
| 1389 | + */ | |
| 1390 | + public static function sanitize_min_donation_amount( $value ) { | |
| 1391 | + if ( empty( $value ) || ! $value ) { | |
| 1392 | + return 0; | |
| 1393 | + } | |
| 1394 | + | |
| 1395 | + return charitable_get_currency_helper()->sanitize_monetary_amount( $value ); | |
| 1396 | + } | |
| 1397 | + | |
| 1398 | + /** | |
| 1001 | 1399 | * Sanitize the campaign end date. |
| 1002 | 1400 | * |
| 1003 | 1401 | * We use WP_Locale to parse the month that the user has set. |
| 1004 | 1402 | * |
| 1005 | - * @global WP_Locale $wp_locale | |
| 1403 | + * @global WP_Locale $wp_locale | |
| 1006 | 1404 | * |
| 1007 | 1405 | * @since 1.0.0 |
| 1008 | 1406 | * |
| 1009 | 1407 | * @param string $value Current end date value. |
| @@ -1011,10 +1409,15 @@ | ||
| 1011 | 1409 | * @return string|int |
| 1012 | 1410 | */ |
| 1013 | 1411 | public static function sanitize_campaign_end_date( $value, $submitted = array() ) { |
| 1014 | 1412 | $end_time = array_key_exists( '_campaign_end_time', $submitted ) ? $submitted['_campaign_end_time'] : '23:59:59'; |
| 1015 | - $end_date = charitable_sanitize_date( $value, 'Y-m-d ' . $end_time ); | |
| 1016 | 1413 | |
| 1414 | + if ( charitable()->registry()->get( 'i18n' )->decline_months() ) { | |
| 1415 | + $end_date = $value . ' ' . $end_time; | |
| 1416 | + } else { | |
| 1417 | + $end_date = charitable_sanitize_date( $value, 'Y-m-d ' . $end_time ); | |
| 1418 | + } | |
| 1419 | + | |
| 1017 | 1420 | if ( ! $end_date ) { |
| 1018 | 1421 | $end_date = 0; |
| 1019 | 1422 | } |
| 1020 | 1423 | |
| @@ -1028,9 +1431,24 @@ | ||
| 1028 | 1431 | * |
| 1029 | 1432 | * @param array $value Current suggested donations value. |
| 1030 | 1433 | * @return array |
| 1031 | 1434 | */ |
| 1032 | - public static function sanitize_campaign_suggested_donations( $value ) { | |
| 1435 | + public static function sanitize_campaign_suggested_donations_default( $value ) { | |
| 1436 | + | |
| 1437 | + return $value; // this is likely an array. | |
| 1438 | + } | |
| 1439 | + | |
| 1440 | + /** | |
| 1441 | + * Sanitize the campaign suggested donations. | |
| 1442 | + * | |
| 1443 | + * @since 1.0.0 | |
| 1444 | + * | |
| 1445 | + * @param array $value Current suggested donations value. | |
| 1446 | + * @param bool $submitted Whether the value was submitted. | |
| 1447 | + * @param int $campaign_id The campaign ID. | |
| 1448 | + * @return array | |
| 1449 | + */ | |
| 1450 | + public static function sanitize_campaign_suggested_donations( $value, $submitted = false, $campaign_id = false ) { // phpcs:ignore | |
| 1033 | 1451 | if ( ! is_array( $value ) ) { |
| 1034 | 1452 | return array(); |
| 1035 | 1453 | } |
| 1036 | 1454 | |
| @@ -1041,8 +1459,33 @@ | ||
| 1041 | 1459 | } |
| 1042 | 1460 | |
| 1043 | 1461 | foreach ( $value as $key => $suggestion ) { |
| 1044 | 1462 | $value[ $key ]['amount'] = charitable_sanitize_amount( (string) $suggestion['amount'] ); |
| 1463 | + | |
| 1464 | + /* | |
| 1465 | + * A description is OPTIONAL. filter_suggested_donation() above keeps any entry that has | |
| 1466 | + * a non-empty 'amount', so array( 'amount' => 25 ) is a perfectly valid suggestion with | |
| 1467 | + * no 'description' key at all - which is the normal shape when a campaign offers bare | |
| 1468 | + * amounts. Reading it unguarded emitted an "Undefined array key" warning on every such | |
| 1469 | + * sanitize pass. Defaulted rather than skipped so the key is always present in the | |
| 1470 | + * returned array, which is what callers and the stored meta already assume. | |
| 1471 | + */ | |
| 1472 | + $description = isset( $suggestion['description'] ) ? $suggestion['description'] : ''; | |
| 1473 | + | |
| 1474 | + /** | |
| 1475 | + * Sanitize the description field. | |
| 1476 | + * | |
| 1477 | + * @since 1.6.51 | |
| 1478 | + * | |
| 1479 | + * @param boolean $sanitize Whether to sanitize the suggested amount description. | |
| 1480 | + * Returning false will allow HTML to be used, though it will | |
| 1481 | + * still be filtered using wp_kses_post. | |
| 1482 | + */ | |
| 1483 | + if ( apply_filters( 'charitable_sanitize_suggested_amount_description', true ) ) { | |
| 1484 | + $value[ $key ]['description'] = sanitize_text_field( $description ); | |
| 1485 | + } else { | |
| 1486 | + $value[ $key ]['description'] = wp_kses_post( $description ); | |
| 1487 | + } | |
| 1045 | 1488 | } |
| 1046 | 1489 | |
| 1047 | 1490 | return $value; |
| 1048 | 1491 | } |
| @@ -1077,21 +1520,96 @@ | ||
| 1077 | 1520 | |
| 1078 | 1521 | /** |
| 1079 | 1522 | * Sanitize the campaign description. |
| 1080 | 1523 | * |
| 1081 | - * @since 1.0.0 | |
| 1524 | + * @since 1.0.0 | |
| 1525 | + * @updated 1.7.0.4 | |
| 1082 | 1526 | * |
| 1083 | 1527 | * @param string $value Current description value. |
| 1084 | 1528 | * @return string |
| 1085 | 1529 | */ |
| 1086 | 1530 | public static function sanitize_campaign_description( $value ) { |
| 1087 | - return sanitize_text_field( $value ); | |
| 1531 | + | |
| 1532 | + /** | |
| 1533 | + * Allowed HTML in description. | |
| 1534 | + */ | |
| 1535 | + $wp_kses_allowed_html = array( | |
| 1536 | + 'a' => array( | |
| 1537 | + 'href' => array(), | |
| 1538 | + 'target' => array(), | |
| 1539 | + 'class' => array(), | |
| 1540 | + 'title' => array(), | |
| 1541 | + ), | |
| 1542 | + 'br' => array(), | |
| 1543 | + 'img' => array( | |
| 1544 | + 'src' => array(), | |
| 1545 | + 'class' => array(), | |
| 1546 | + 'alt' => array(), | |
| 1547 | + ), | |
| 1548 | + 'h1' => array( | |
| 1549 | + 'class' => array(), | |
| 1550 | + ), | |
| 1551 | + 'h2' => array( | |
| 1552 | + 'class' => array(), | |
| 1553 | + ), | |
| 1554 | + 'h3' => array( | |
| 1555 | + 'class' => array(), | |
| 1556 | + ), | |
| 1557 | + 'h4' => array( | |
| 1558 | + 'class' => array(), | |
| 1559 | + ), | |
| 1560 | + 'h5' => array( | |
| 1561 | + 'class' => array(), | |
| 1562 | + ), | |
| 1563 | + 'h6' => array( | |
| 1564 | + 'class' => array(), | |
| 1565 | + ), | |
| 1566 | + 'div' => array( | |
| 1567 | + 'class' => array(), | |
| 1568 | + ), | |
| 1569 | + 'p' => array( | |
| 1570 | + 'style' => array(), | |
| 1571 | + 'class' => array(), | |
| 1572 | + ), | |
| 1573 | + 'ul' => array( | |
| 1574 | + 'class' => array(), | |
| 1575 | + ), | |
| 1576 | + 'li' => array( | |
| 1577 | + 'id' => array(), | |
| 1578 | + 'class' => array(), | |
| 1579 | + ), | |
| 1580 | + 'em' => array(), | |
| 1581 | + 'span' => array( | |
| 1582 | + 'class' => array(), | |
| 1583 | + ), | |
| 1584 | + 'strong' => array(), | |
| 1585 | + ); | |
| 1586 | + | |
| 1587 | + if ( apply_filters( 'charitable_sanitize_campaign_description', false ) ) { | |
| 1588 | + return sanitize_text_field( $value ); | |
| 1589 | + } else { | |
| 1590 | + return wp_kses( apply_filters( 'charitable_campaign_description', $value ), apply_filters( 'charitable_sanitize_campaign_description_allowed_html', $wp_kses_allowed_html ) ); | |
| 1591 | + } | |
| 1088 | 1592 | } |
| 1089 | 1593 | |
| 1090 | 1594 | /** |
| 1595 | + * Sanitize the campaign donation button text.. | |
| 1596 | + * | |
| 1597 | + * @since 1.7.0.9 | |
| 1598 | + * | |
| 1599 | + * @param string $value Current text value. | |
| 1600 | + * @return string | |
| 1601 | + */ | |
| 1602 | + public static function sanitize_campaign_donate_button_text( $value = false ) { | |
| 1603 | + | |
| 1604 | + return wp_strip_all_tags( $value ); | |
| 1605 | + } | |
| 1606 | + | |
| 1607 | + /** | |
| 1091 | 1608 | * Sanitize the value provided for custom donations. |
| 1092 | 1609 | * |
| 1093 | - * @since 1.3.6 | |
| 1610 | + * @since 1.3.6 | |
| 1611 | + * @version 1.8.0 | |
| 1094 | 1612 | * |
| 1095 | 1613 | * @param mixed $value Current custom donations value. |
| 1096 | 1614 | * @param array $submitted All posted values. |
| 1097 | 1615 | * @return boolean |
| @@ -1100,8 +1618,27 @@ | ||
| 1100 | 1618 | $checked = self::sanitize_checkbox( $value ); |
| 1101 | 1619 | |
| 1102 | 1620 | if ( $checked ) { |
| 1103 | 1621 | return $checked; |
| 1622 | + } | |
| 1623 | + | |
| 1624 | + /** | |
| 1625 | + * Filter whether a campaign can be saved with custom donations disabled | |
| 1626 | + * and no suggested donations. | |
| 1627 | + * | |
| 1628 | + * @since 1.6.14 | |
| 1629 | + * | |
| 1630 | + * @param boolean $permitted Whether a campaign is permitted to be saved without | |
| 1631 | + * suggested donations or custom donations. | |
| 1632 | + * @param array $submitted The submitted values. | |
| 1633 | + */ | |
| 1634 | + if ( apply_filters( 'charitable_campaign_permitted_without_custom_or_suggested', false, $submitted ) ) { | |
| 1635 | + return $checked; | |
| 1636 | + } | |
| 1637 | + | |
| 1638 | + // see if literally "0" was passed. | |
| 1639 | + if ( is_string( $value ) && '0' === $value ) { | |
| 1640 | + return 0; | |
| 1104 | 1641 | } |
| 1105 | 1642 | |
| 1106 | 1643 | /* If suggested donations are not set, custom donations needs to be enabled. */ |
| 1107 | 1644 | if ( ! isset( $submitted['_campaign_suggested_donations'] ) ) { |