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

class-charitable-campaign.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.13, at includes/campaigns/class-charitable-campaign.php

1,714 lines 47.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Campaign model.
4 *
5 * @package Charitable/Classes/Charitable_Campaign
6 * @author David Bisset
7 * @copyright Copyright (c) 2023, WP Charitable LLC
8 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9 * @since 1.0.0
10 * @version 1.6.54
11 */
12
13 // Exit if accessed directly.
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 if ( ! class_exists( 'Charitable_Campaign' ) ) :
19
20 /**
21 * Campaign Model
22 *
23 * @since 1.0.0
24 *
25 * @property int $ID
26 * @property int $post_author
27 * @property string $post_date
28 * @property string $post_date_gmt
29 * @property string $post_content
30 * @property string $post_title
31 * @property string $post_excerpt
32 * @property string $post_status
33 * @property string $comment_status
34 * @property string $ping_status
35 * @property string $post_password
36 * @property string $post_name
37 * @property string $to_ping
38 * @property string $pinged
39 * @property string $post_modified
40 * @property string $post_modified_gmt
41 * @property string $post_content_filtered
42 * @property int $post_parent
43 * @property string $guid
44 * @property int $menu_order
45 * @property string $post_type
46 * @property string $post_mime_type
47 * @property int $comment_count
48 * @property string $filter
49 */
50 class Charitable_Campaign {
51
52 /**
53 * The WP_Post object associated with this campaign.
54 *
55 * @var WP_Post
56 */
57 private $post;
58
59 /**
60 * The timestamp for the expiry for this campaign.
61 *
62 * @var int
63 */
64 private $end_time;
65
66 /**
67 * The fundraising goal for the campaign.
68 *
69 * @var string|false
70 */
71 private $goal;
72
73 /**
74 * The fundraising goal for the campaign.
75 *
76 * @var string|false
77 */
78 private $min_donation_amount;
79
80 /**
81 * The donations made to this campaign.
82 *
83 * @var WP_Query|false
84 */
85 private $donations;
86
87 /**
88 * The amount donated to the campaign.
89 *
90 * @var int|false
91 */
92 private $donated_amount;
93
94 /**
95 * The form object for this campaign.
96 *
97 * @var Charitable_Donation_Form
98 */
99 private $donation_form;
100
101 /**
102 * The Charitable_Object_Fields object for this campaign.
103 *
104 * @since 1.6.0
105 *
106 * @var Charitable_Object_Fields
107 */
108 protected $fields;
109
110 /**
111 * Class constructor.
112 *
113 * @since 1.0.0
114 *
115 * @param mixed $post The post ID or WP_Post object for this this campaign.
116 */
117 public function __construct( $post ) {
118 if ( ! $post instanceof WP_Post ) {
119 $post = get_post( $post );
120 }
121
122 add_filter( 'charitable_campaign_description_template_content', array( $this, 'description_content' ), 10, 2 );
123
124 $this->post = $post;
125 }
126
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 /**
179 * Magic getter.
180 *
181 * @since 1.0.0
182 *
183 * @param string $key The key of the field to get.
184 * @return mixed
185 */
186 public function __get( $key ) {
187 if ( null !== $this->post && property_exists( $this->post, $key ) ) {
188 return $this->post->$key;
189 }
190
191 return $this->get( $key );
192 }
193
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 /**
213 * Returns the campaign's post_meta values. _campaign_ is automatically prepended to the meta key.
214 *
215 * @see get_post_meta
216 *
217 * @since 1.0.0
218 *
219 * @param string $key The field key.
220 * @param boolean $single Whether to return a single value or an array.
221 * @return mixed This will return an array if single is false. If it's true, the value of the
222 * meta_value field will be returned.
223 */
224 public function get( $key, $single = true ) {
225 if ( $this->fields()->has_value_callback( $key ) ) {
226 return $this->fields()->get( $key );
227 }
228
229 /* Look for a local method. */
230 if ( method_exists( $this, 'get_' . $key ) ) {
231 $method = 'get_' . $key;
232 return $this->$method();
233 }
234
235 return $this->get_meta( '_campaign_' . $key, $single );
236 }
237
238 /**
239 * Return the campaign's post_meta values.
240 *
241 * @since since
242 *
243 * @param string $meta_name The meta key.
244 * @param boolean $single Whether to return a single value or an array.
245 * @return mixed This will return an array if single is false. If it's true, the value of the
246 * meta_value field will be returned.
247 */
248 public function get_meta( $meta_name, $single = true ) {
249
250 if ( empty( $this->post->ID ) ) {
251 return;
252 }
253
254 /**
255 * Filter the meta value of a particular campaign field.
256 *
257 * @since 1.0.0
258 *
259 * @param mixed $value The meta value.
260 * @param string $meta_name The name of the meta field.
261 * @param boolean $single Whether we're returning a single result.
262 * @param Charitable_Campaign $campaign This instance of `Charitable_Campaign`.
263 */
264 return apply_filters( 'charitable_campaign_get_meta_value', get_post_meta( $this->post->ID, $meta_name, $single ), $meta_name, $single, $this );
265 }
266
267 /**
268 * Return the Charitable_Object_Fields instance.
269 *
270 * @since 1.6.0
271 *
272 * @return Charitable_Object_Fields
273 */
274 public function fields() {
275 if ( ! isset( $this->fields ) ) {
276 $this->fields = new Charitable_Object_Fields( charitable()->campaign_fields(), $this );
277 }
278
279 return $this->fields;
280 }
281
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 /**
295 * Returns whether the campaign is endless (i.e. no end date has been set).
296 *
297 * @since 1.0.0
298 *
299 * @return boolean
300 */
301 public function is_endless() {
302 return 0 == $this->get_meta( '_campaign_end_date' );
303 }
304
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 /**
317 * Return the suggested amounts, or an empty array if there are none.
318 *
319 * @since 1.0.0
320 *
321 * @return array
322 */
323 public function get_suggested_donations() {
324 $value = get_post_meta( $this->post->ID, '_campaign_suggested_donations', true );
325
326 if ( ! is_array( $value ) ) {
327 $value = array();
328 }
329
330 return apply_filters( 'charitable_campaign_suggested_donations', $value, $this );
331 }
332
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 /**
351 * Returns the end date in your preferred format.
352 *
353 * If a format is not provided, the user-defined date_format in WordPress settings is used.
354 *
355 * @since 1.0.0
356 *
357 * @param string $date_format A date format accepted by PHP's date() function.
358 * @return string|false String if an end date is set. False if campaign has no end date.
359 */
360 public function get_end_date( $date_format = '' ) {
361 if ( $this->is_endless() ) {
362 return false;
363 }
364
365 if ( ! strlen( $date_format ) ) {
366 $date_format = get_option( 'date_format', 'd/m/Y' );
367 }
368
369 /* Filter the end date format using the charitable_campaign_end_date_format hook. */
370 $date_format = apply_filters( 'charitable_campaign_end_date_format', $date_format, $this );
371
372 /* This is how the end date is stored in the database, so just return that directly. */
373 if ( 'Y-m-d H:i:s' == $date_format ) {
374 return $this->get_meta( '_campaign_end_date' );
375 }
376
377 return date_i18n( $date_format, $this->get_end_time() );
378 }
379
380 /**
381 * Returns the timetamp of the end date.
382 *
383 * @since 1.0.0
384 * @since 1.8.1.10
385 *
386 * @return int|false Int if campaign has an end date. False if campaign has no end date.
387 */
388 public function get_end_time() {
389 if ( ! isset( $this->end_time ) ) {
390 $end_date = $this->get_meta( '_campaign_end_date' );
391
392 if ( ! $end_date ) {
393 return false;
394 }
395
396 /* The date is stored in the format of Y-m-d H:i:s. */
397 $date_time = explode( ' ', $end_date );
398 $date = explode( '-', $date_time[0] );
399 $time = explode( ':', $date_time[1] );
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
406 $this->end_time = mktime( $time[0], $time[1], $time[2], $date[1], $date[2], $date[0] );
407 }
408
409 return $this->end_time;
410 }
411
412 /**
413 * Returns the amount of time left in the campaign in seconds.
414 *
415 * @since 1.0.0
416 *
417 * @return int $time_left Int if campaign has an end date. False if campaign has no end date.
418 */
419 public function get_seconds_left() {
420 if ( $this->is_endless() ) {
421 return false;
422 }
423
424 $time_left = $this->get_end_time() - current_time( 'timestamp' );
425
426 return $time_left < 0 ? 0 : $time_left;
427 }
428
429 /**
430 * Returns the amount of time left in the campaign as a descriptive string.
431 *
432 * @uses charitable_campaign_ended Change the text displayed when there is no time left.
433 * @uses charitabile_campaign_minutes_left Change the text displayed when there is less than an hour left.
434 * @uses charitabile_campaign_hours_left Change the text displayed when there is less than a day left.
435 * @uses charitabile_campaign_days_left Change the text displayed when there is more than a day left.
436 * @uses charitable_campaign_time_left Change the text displayed when there is time left.
437 *
438 * @since 1.0.0
439 *
440 * @return string
441 */
442 public function get_time_left() {
443 if ( $this->is_endless() ) {
444 return '';
445 }
446
447 $hour = 3600;
448 $day = 86400;
449
450 $seconds_left = $this->get_seconds_left();
451
452 if ( 0 === $seconds_left ) {
453
454 /* Condition 1: The campaign has finished. */
455
456 $time_left = apply_filters( 'charitable_campaign_ended', __( 'Campaign has ended', 'charitable' ), $this );
457
458 } elseif ( $seconds_left <= $hour ) {
459
460 /* Condition 2: There is less than an hour left. */
461
462 $minutes_remaining = ceil( $seconds_left / 60 );
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.
466 sprintf( _n( '%s Minute Left', '%s Minutes Left', $minutes_remaining, 'charitable' ), '<span class="amount time-left minutes-left">' . $minutes_remaining . '</span>' ),
467 $this
468 );
469
470 } elseif ( $seconds_left <= $day ) {
471
472 /* Condition 3: There is less than a day left. */
473
474 $hours_remaining = floor( $seconds_left / 3600 );
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.
478 sprintf( _n( '%s Hour Left', '%s Hours Left', $hours_remaining, 'charitable' ), '<span class="amount time-left hours-left">' . $hours_remaining . '</span>' ),
479 $this
480 );
481
482 } else {
483
484 /* Condition 4: There is more than a day left. */
485
486 $days_remaining = floor( $seconds_left / 86400 );
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.
490 sprintf( _n( '%s Day Left', '%s Days Left', $days_remaining, 'charitable' ), '<span class="amount time-left days-left">' . $days_remaining . '</span>' ),
491 $this
492 );
493
494 } //end if
495
496 return apply_filters( 'charitable_campaign_time_left', $time_left, $this );
497 }
498
499 /**
500 * Returns whether the campaign has ended.
501 *
502 * @since 1.0.0
503 *
504 * @return boolean
505 */
506 public function has_ended() {
507 return ! $this->is_endless() && 0 == $this->get_seconds_left();
508 }
509
510 /**
511 * Returns whether a campaign is currently able to receive donations.
512 *
513 * @since 1.5.0
514 *
515 * @return boolean
516 */
517 public function can_receive_donations() {
518 $can = 'active' == $this->get_status();
519
520 /**
521 * Filter whether the campaign can receive donations.
522 *
523 * @since 1.5.0
524 *
525 * @param boolean $can Whether the campaign can receive donations.
526 * @param Charitable_Campaign $campaign This instance of `Charitable_Campaign`.
527 */
528 return apply_filters( 'charitable_campaign_can_receive_donations', $can, $this );
529 }
530
531 /**
532 * Return a text notice to say that a campaign has finished.
533 *
534 * @since 1.0.0
535 *
536 * @return string
537 */
538 public function get_finished_notice() {
539 if ( ! $this->has_ended() ) {
540 return '';
541 }
542
543 if ( ! $this->has_goal() ) {
544 // translators: %s is the time since the campaign ended.
545 $message = __( 'This campaign ended %s ago', 'charitable' );
546 } elseif ( $this->has_achieved_goal() ) {
547 // translators: %s is the time since the campaign ended.
548 $message = __( 'This campaign successfully reached its funding goal and ended %s ago', 'charitable' );
549 } else {
550 // translators: %s is the time since the campaign ended.
551 $message = __( 'This campaign failed to reach its funding goal %s ago', 'charitable' );
552 }
553
554 return apply_filters( 'charitable_campaign_finished_notice', sprintf( $message, '<span class="time-ago">' . human_time_diff( $this->get_end_time() ) . '</span>' ), $this );
555 }
556
557 /**
558 * Return the time since the campaign finished, or zero if it's still going.
559 *
560 * @since 1.0.0
561 *
562 * @return int
563 */
564 public function get_time_since_ended() {
565 if ( 0 !== $this->get_seconds_left() ) {
566 return 0;
567 }
568
569 return current_time( 'U' ) - $this->get_end_time();
570 }
571
572 /**
573 * Returns the fundraising goal of the campaign.
574 *
575 * @since 1.0.0
576 *
577 * @return string|false Amount if goal is set. False otherwise.
578 */
579 public function get_goal() {
580 if ( ! isset( $this->goal ) ) {
581 $this->goal = $this->has_goal() ? $this->get_meta( '_campaign_goal' ) : false;
582 }
583
584 return $this->goal;
585 }
586
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 /**
604 * Returns whether a goal has been set (anything greater than $0 is a goal).
605 *
606 * @since 1.0.0
607 *
608 * @return boolean
609 */
610 public function has_goal() {
611 return 0 < (float) $this->get_meta( '_campaign_goal' );
612 }
613
614 /**
615 * Returns the fundraising goal formatted as a monetary amount.
616 *
617 * @since 1.0.0
618 *
619 * @return string
620 */
621 public function get_monetary_goal() {
622 if ( ! $this->has_goal() ) {
623 return '';
624 }
625
626 return charitable_format_money( (string) $this->get_meta( '_campaign_goal' ) );
627 }
628
629 /**
630 * Returns whether the goal has been achieved.
631 *
632 * @since 1.0.0
633 *
634 * @return boolean
635 */
636 public function has_achieved_goal() {
637 return $this->get_donated_amount( true ) >= $this->get_goal();
638 }
639
640 /**
641 * Return a message to say whether the campaign has achieved its goal.
642 *
643 * @since 1.6.0
644 *
645 * @param string $success Optional. The success message.
646 * @param string $failure Optional. The failure message.
647 * @return string
648 */
649 public function get_goal_achieved_message( $success = '', $failure = '' ) {
650 if ( $this->has_achieved_goal() ) {
651
652 if ( strlen( $success ) ) {
653 return $success;
654 }
655
656 /**
657 * Filter the default message to show when a campaign has achieved its goal.
658 *
659 * @since 1.6.0
660 *
661 * @param string $message The success message.
662 * @param Charitable_Campaign $campaign This instance of `Charitable_Campaign`.
663 */
664 return apply_filters( 'charitable_campaign_goal_achievement_success_message', __( 'The campaign achieved its fundraising goal.', 'charitable' ), $this );
665 }
666
667 if ( strlen( $failure ) ) {
668 return $failure;
669 }
670
671 /**
672 * Filter the default message to show when a campaign has not achieved its goal.
673 *
674 * @since 1.6.0
675 *
676 * @param string $message The failure message.
677 * @param Charitable_Campaign $campaign This instance of `Charitable_Campaign`.
678 */
679 return apply_filters( 'charitable_campaign_goal_achievement_failure_message', __( 'The campaign did not reach its fundraising goal.', 'charitable' ), $this );
680 }
681
682 /**
683 * Return the campaign status.
684 *
685 * If the campaign is published, this will return whether either 'active' or 'finished'.
686 *
687 * @since 1.0.0
688 *
689 * @return string
690 */
691 public function get_status() {
692
693 $status = $post_status = isset( $this->post ) && ! empty( $this->post->post_status ) ? $this->post->post_status : false; // phpcs:ignore
694
695 if ( 'publish' == $status ) {
696 $status = $this->has_ended() ? 'finished' : 'active';
697 }
698
699 /**
700 * Filter the returned campaign status.
701 *
702 * @since 1.0.0
703 *
704 * @param string $status The campaign status.
705 * @param string $post_status The post_status of the campaign.
706 * @param Charitable_Campaign $campaign This instance of `Charitable_Campaign`.
707 */
708 return apply_filters( 'charitable_campaign_status', $status, $post_status, $this );
709 }
710
711 /**
712 * Return a status key for the campaign.
713 *
714 * This will return one of the following:
715 *
716 * inactive : A campaign that is not published.
717 * ended : A campaign without a goal has finished
718 * successful : A campaign with a goal has finished & achieved its goal
719 * unsucessful : A campaign with a goal has finished without achieving its goal
720 * ending : A campaign is ending soon.
721 * active : A campaign that is active and not ending soon.
722 *
723 * @since 1.3.7
724 *
725 * @return string
726 */
727 public function get_status_key() {
728 /**
729 * Set the threshold in seconds for defining whether a campaign is ending soon.
730 *
731 * @since 1.0.0
732 *
733 * @param int $seconds By default, this equals 604,800.
734 */
735 $ending_soon_threshold = apply_filters( 'charitable_campaign_ending_soon_threshold', WEEK_IN_SECONDS );
736 $ended = $this->has_ended();
737
738 if ( 'publish' != $this->post->post_status ) {
739 return 'inactive';
740 }
741
742 if ( $ended && ! $this->has_goal() ) {
743 return 'ended';
744 }
745
746 if ( $ended && $this->has_achieved_goal() ) {
747 return 'successful';
748 }
749
750 if ( $ended ) {
751 return 'unsucessful';
752 }
753
754 if ( ! $this->is_endless() && $this->get_seconds_left() < $ending_soon_threshold ) {
755 return 'ending';
756 }
757
758 return 'active';
759 }
760
761 /**
762 * Return the campaign status tag.
763 *
764 * @since 1.0.0
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 *
769 * @return string
770 */
771 public function get_status_tag( $show_achievement = null, $show_active_tag = null ) {
772 $key = $this->get_status_key();
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
782 switch ( $key ) {
783 case 'ended':
784 $tag = __( 'Ended', 'charitable' );
785 break;
786
787 case 'successful':
788 $tag = $show_achievement ? __( 'Successful', 'charitable' ) : __( 'Ended', 'charitable' );
789 break;
790
791 case 'unsucessful':
792 $tag = $show_achievement ? __( 'Unsuccessful', 'charitable' ) : __( 'Ended', 'charitable' );
793 break;
794
795 case 'ending':
796 $tag = __( 'Ending Soon', 'charitable' );
797 break;
798
799 case 'active':
800 case 'publish':
801 $tag = $show_active_tag ? __( 'Active', 'charitable' ) : '';
802 break;
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
823 default:
824 $tag = '';
825
826 }//end switch
827
828 /**
829 * Filter the campaign status tag.
830 *
831 * @since 1.0.0
832 *
833 * @param string $tag The tag for the status.
834 * @param string $key The key of the current status.
835 * @param Charitable_Campaign $this This campaign object.
836 */
837 return apply_filters( 'charitable_campaign_status_tag', $tag, $key, $this );
838 }
839
840 /**
841 * Return the permalink for this campaign.
842 *
843 * @since 1.6.0
844 *
845 * @return string|false The permalink URL or false if post does not exist.
846 */
847 public function get_permalink() {
848 return get_permalink( $this->ID );
849 }
850
851 /**
852 * Return the admin edit link for this campaign.
853 *
854 * @since 1.6.0
855 *
856 * @return string
857 */
858 public function get_admin_edit_link() {
859 $post_type_object = get_post_type_object( Charitable::CAMPAIGN_POST_TYPE );
860
861 if ( $post_type_object->_edit_link ) {
862 $link = admin_url( sprintf( $post_type_object->_edit_link . '&amp;action=edit', $this->ID ) );
863 } else {
864 $link = '';
865 }
866
867 return $link;
868 }
869
870 /**
871 * Returns the donations made to this campaign.
872 *
873 * @since 1.0.0
874 *
875 * @return WP_Query
876 */
877 public function get_donations() {
878 $this->donations = get_transient( self::get_donations_cache_key( $this->ID ) );
879
880 if ( false === $this->donations ) {
881
882 $this->donations = charitable_get_table( 'campaign_donations' )->get_donations_on_campaign( $this->ID );
883
884 set_transient( self::get_donations_cache_key( $this->ID ), $this->donations, 0 );
885 }
886
887 return $this->donations;
888 }
889
890 /**
891 * Return the current amount of donations.
892 *
893 * @since 1.0.0
894 *
895 * @param boolean $sanitize Whether to sanitize the amount. False by default.
896 * @return string String if $sanitize is false. If $sanitize is true, return a float or WP_Error if the amount is not a string.
897 */
898 public function get_donated_amount( $sanitize = false ) {
899 $this->donated_amount = get_transient( self::get_donation_amount_cache_key( $this->ID ) );
900
901 if ( false === $this->donated_amount ) {
902 $this->donated_amount = charitable_get_table( 'campaign_donations' )->get_campaign_donated_amount( $this->ID, false, false );
903
904 set_transient( self::get_donation_amount_cache_key( $this->ID ), $this->donated_amount, 0 );
905 }
906
907 $amount = $this->donated_amount;
908
909 /* If returning a string, return it in the site number format. */
910 if ( ! $sanitize ) {
911 $amount = Charitable_Currency::get_instance()->sanitize_database_amount( $amount );
912 }
913
914 /**
915 * Filter the campaign donated amount.
916 *
917 * @since 1.2.0
918 *
919 * @param string|float|WP_Error $amount The donated amount.
920 * @param Charitable_Campaign $this This campaign object.
921 * @param boolean $sanitize Whether the amount should be sanitized.
922 */
923 return apply_filters( 'charitable_campaign_donated_amount', $amount, $this, $sanitize );
924 }
925
926 /**
927 * Return the currency formatted donation amount.
928 *
929 * @since 1.6.0
930 *
931 * @return string
932 */
933 public function get_donated_amount_formatted() {
934 return charitable_format_money( $this->get_donated_amount() );
935 }
936
937 /**
938 * Return a string describing the campaign's donation summary.
939 *
940 * @since 1.0.0
941 *
942 * @return string
943 */
944 public function get_donation_summary() {
945 $currency_helper = charitable_get_currency_helper();
946 $amount = $this->get_donated_amount();
947 $goal = $this->get_meta( '_campaign_goal' );
948
949 if ( $goal ) {
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' ),
953 '<span class="amount">' . $currency_helper->get_monetary_amount( $amount ) . '</span>',
954 '<span class="goal-amount">' . $currency_helper->get_monetary_amount( $goal ) . '</span>'
955 );
956 } else {
957 $ret = sprintf(
958 // translators: %s is the amount donated.
959 _x( '%s donated', 'amount donated', 'charitable' ),
960 '<span class="amount">' . $currency_helper->get_monetary_amount( $amount ) . '</span>'
961 );
962 }
963
964 /**
965 * Filter the returned string if you want to display the donation summary in a different way.
966 *
967 * @since 1.0.0
968 * @since 1.5.0 Added the $amount argument.
969 * @since 1.5.0 Added the $goal argument.
970 *
971 * @param string $ret The summary.
972 * @param Charitable_Campaign $this This campaign object.
973 * @param float $amount The amount donated.
974 * @param int $goal The campaign goal.
975 */
976 return apply_filters( 'charitable_donation_summary', $ret, $this, $amount, $goal );
977 }
978
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 /**
1011 * Return the percentage donated. Use this if you want a formatted string.
1012 *
1013 * @since 1.0.0
1014 *
1015 * @return string|false String if campaign has a goal. False if no goal is set.
1016 */
1017 public function get_percent_donated() {
1018 $percent = $this->get_percent_donated_raw();
1019
1020 if ( false === $percent ) {
1021 return $percent;
1022 }
1023
1024 $percent = number_format( $percent, 2 );
1025
1026 return apply_filters( 'charitable_percent_donated', $percent . '%', $percent, $this );
1027 }
1028
1029 /**
1030 * Returns the percentage donated as a number.
1031 *
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%.
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 *
1067 * @return int
1068 */
1069 public function get_percent_donated_raw() {
1070 if ( ! $this->has_goal() ) {
1071 return false;
1072 }
1073
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;
1096 }
1097
1098 /**
1099 * Return the number of people who have donated to the campaign.
1100 *
1101 * @since 1.0.0
1102 *
1103 * @return int
1104 */
1105 public function get_donor_count() {
1106 /**
1107 * Set the number of donors who have donated to this campaign.
1108 *
1109 * @since 1.4.7
1110 *
1111 * @param int $count Number of donors.
1112 * @param Charitable_Campaign $this This campaign object.
1113 */
1114 return apply_filters(
1115 'charitable_campaign_donor_count',
1116 (int) charitable_get_table( 'campaign_donations' )->count_campaign_donors( $this->ID ),
1117 $this
1118 );
1119 }
1120
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 /**
1150 * Returns the donation form object.
1151 *
1152 * @since 1.0.0
1153 *
1154 * @return Charitable_Donation_Form_Interface
1155 */
1156 public function get_donation_form() {
1157 if ( ! isset( $this->donation_form ) ) {
1158 /**
1159 * Return the class name of the donation form for this campaign.
1160 *
1161 * @since 1.0.0
1162 *
1163 * @param string $class The form class.
1164 * @param Charitable_Campaign $this This campaign object.
1165 */
1166 $form_class = apply_filters( 'charitable_donation_form_class', 'Charitable_Donation_Form', $this );
1167
1168 $this->donation_form = new $form_class( $this );
1169 }
1170
1171 return $this->donation_form;
1172 }
1173
1174 /**
1175 * Returns the amount to be donated to the campaign as it is currently set in the session.
1176 *
1177 * @since 1.0.0
1178 *
1179 * @return int
1180 */
1181 public function get_donation_amount_in_session() {
1182 $donation = charitable_get_session()->get_donation_by_campaign( $this->ID );
1183 $amount = is_array( $donation ) ? $donation['amount'] : 0;
1184
1185 /**
1186 * The donation amount for this campaign stored in the session.
1187 *
1188 * @since 1.0.0
1189 *
1190 * @param float|int $amount The amount.
1191 * @param Charitable_Campaign $this This campaign object.
1192 */
1193 return apply_filters( 'charitable_session_donation_amount', $amount, $this );
1194 }
1195
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 /**
1271 * Renders the donate button template.
1272 *
1273 * @since 1.0.0
1274 *
1275 * @return void
1276 */
1277 public function donate_button_template() {
1278 if ( ! $this->can_receive_donations() ) {
1279 return;
1280 }
1281
1282 $display_option = charitable_get_option( 'donation_form_display', 'separate_page' );
1283
1284 switch ( $display_option ) {
1285 case 'separate_page':
1286 $template_name = 'campaign/donate-button.php';
1287 break;
1288
1289 case 'same_page':
1290 $template_name = 'campaign/donate-link.php';
1291 break;
1292
1293 case 'modal':
1294 $template_name = 'campaign/donate-modal.php';
1295 break;
1296
1297 default:
1298 $template_name = apply_filters( 'charitable_donate_button_template', 'campaign/donate-button.php', $this );
1299 }
1300
1301 charitable_template( $template_name, array( 'campaign' => $this ) );
1302 }
1303
1304 /**
1305 * Renders the donate button template.
1306 *
1307 * @since 1.2.3
1308 *
1309 * @return void
1310 */
1311 public function donate_button_loop_template() {
1312 if ( ! $this->can_receive_donations() ) {
1313 return;
1314 }
1315
1316 $display_option = charitable_get_option( 'donation_form_display', 'separate_page' );
1317
1318 switch ( $display_option ) {
1319 case 'modal':
1320 $template_name = 'campaign-loop/donate-modal.php';
1321 break;
1322
1323 default:
1324 $template_name = apply_filters( 'charitable_donate_button_loop_template', 'campaign-loop/donate-link.php', $this );
1325 }
1326
1327 charitable_template( $template_name, array( 'campaign' => $this ) );
1328 }
1329
1330 /**
1331 * Returns the campaign creator.
1332 *
1333 * By default, this just returns the user from the post_author field, but
1334 * it can be overridden by plugins.
1335 *
1336 * @since 1.0.0
1337 *
1338 * @return int $user_id
1339 */
1340 public function get_campaign_creator() {
1341 return apply_filters( 'charitable_campaign_creator', $this->post->post_author, $this );
1342 }
1343
1344 /**
1345 * Return the campaign creator's name.
1346 *
1347 * @since 1.6.0
1348 *
1349 * @return string
1350 */
1351 public function get_campaign_creator_name() {
1352 return get_the_author_meta( 'display_name', $this->get_campaign_creator() );
1353 }
1354
1355 /**
1356 * Return the campaign creator's email address.
1357 *
1358 * @since 1.6.0
1359 *
1360 * @return string
1361 */
1362 public function get_campaign_creator_email() {
1363 return get_the_author_meta( 'user_email', $this->get_campaign_creator() );
1364 }
1365
1366 /**
1367 * Sanitize the campaign goal.
1368 *
1369 * @since 1.0.0
1370 *
1371 * @param string $value Current value of goal.
1372 * @return string|int
1373 */
1374 public static function sanitize_campaign_goal( $value ) {
1375 if ( empty( $value ) || ! $value ) {
1376 return 0;
1377 }
1378
1379 return charitable_get_currency_helper()->sanitize_monetary_amount( $value );
1380 }
1381
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 /**
1399 * Sanitize the campaign end date.
1400 *
1401 * We use WP_Locale to parse the month that the user has set.
1402 *
1403 * @global WP_Locale $wp_locale
1404 *
1405 * @since 1.0.0
1406 *
1407 * @param string $value Current end date value.
1408 * @param array $submitted The submitted data.
1409 * @return string|int
1410 */
1411 public static function sanitize_campaign_end_date( $value, $submitted = array() ) {
1412 $end_time = array_key_exists( '_campaign_end_time', $submitted ) ? $submitted['_campaign_end_time'] : '23:59:59';
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
1420 if ( ! $end_date ) {
1421 $end_date = 0;
1422 }
1423
1424 return $end_date;
1425 }
1426
1427 /**
1428 * Sanitize the campaign suggested donations.
1429 *
1430 * @since 1.0.0
1431 *
1432 * @param array $value Current suggested donations value.
1433 * @return array
1434 */
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
1451 if ( ! is_array( $value ) ) {
1452 return array();
1453 }
1454
1455 $value = array_filter( $value, array( 'Charitable_Campaign', 'filter_suggested_donation' ) );
1456
1457 if ( empty( $value ) ) {
1458 return $value;
1459 }
1460
1461 foreach ( $value as $key => $suggestion ) {
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 }
1488 }
1489
1490 return $value;
1491 }
1492
1493 /**
1494 * Filter out any suggested donations that do not have an amount set.
1495 *
1496 * @since 1.0.0
1497 *
1498 * @param array|string $donation Suggested donation or an array of suggested donations.
1499 * @return boolean
1500 */
1501 public static function filter_suggested_donation( $donation ) {
1502 if ( is_array( $donation ) ) {
1503 return isset( $donation['amount'] ) && ! empty( $donation['amount'] );
1504 }
1505
1506 return ! empty( $donation['amount'] );
1507 }
1508
1509 /**
1510 * Sanitize any checkbox value.
1511 *
1512 * @since 1.0.0
1513 *
1514 * @param mixed $value Current checkbox value.
1515 * @return boolean
1516 */
1517 public static function sanitize_checkbox( $value ) {
1518 return intval( true == $value || 'on' === $value );
1519 }
1520
1521 /**
1522 * Sanitize the campaign description.
1523 *
1524 * @since 1.0.0
1525 * @updated 1.7.0.4
1526 *
1527 * @param string $value Current description value.
1528 * @return string
1529 */
1530 public static function sanitize_campaign_description( $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 }
1592 }
1593
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 /**
1608 * Sanitize the value provided for custom donations.
1609 *
1610 * @since 1.3.6
1611 * @version 1.8.0
1612 *
1613 * @param mixed $value Current custom donations value.
1614 * @param array $submitted All posted values.
1615 * @return boolean
1616 */
1617 public static function sanitize_custom_donations( $value, $submitted ) {
1618 $checked = self::sanitize_checkbox( $value );
1619
1620 if ( $checked ) {
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;
1641 }
1642
1643 /* If suggested donations are not set, custom donations needs to be enabled. */
1644 if ( ! isset( $submitted['_campaign_suggested_donations'] ) ) {
1645 return 1;
1646 }
1647
1648 $suggested_donations = self::sanitize_campaign_suggested_donations( $submitted['_campaign_suggested_donations'] );
1649
1650 if ( empty( $suggested_donations ) ) {
1651 return 1;
1652 }
1653
1654 return $checked;
1655 }
1656
1657 /**
1658 * Flush donations cache.
1659 *
1660 * @since 1.0.0
1661 *
1662 * @param int $campaign_id The campaign ID.
1663 * @return void
1664 */
1665 public static function flush_donations_cache( $campaign_id ) {
1666 delete_transient( self::get_donations_cache_key( $campaign_id ) );
1667 delete_transient( self::get_donation_amount_cache_key( $campaign_id ) );
1668
1669 do_action( 'charitable_flush_campaign_cache', $campaign_id );
1670 }
1671
1672 /**
1673 * Returns the key used for caching all donations made to this campaign.
1674 *
1675 * @since 1.0.0
1676 *
1677 * @param int $campaign_id The ID of the campaign.
1678 * @return string
1679 */
1680 private static function get_donations_cache_key( $campaign_id ) {
1681 return 'charitable_campaign_' . $campaign_id . '_donations';
1682 }
1683
1684 /**
1685 * Returns the key used for caching the donation amount for this campaign.
1686 *
1687 * @since 1.0.0
1688 *
1689 * @param int $campaign_id The ID of the campaign.
1690 * @return string
1691 */
1692 private static function get_donation_amount_cache_key( $campaign_id ) {
1693 return 'charitable_campaign_' . $campaign_id . '_donation_amount';
1694 }
1695
1696 /**
1697 * Deprecated method used to sanitize meta.
1698 *
1699 * @deprecated 1.7.0
1700 *
1701 * @since 1.4.12 Deprecated.
1702 *
1703 * @param mixed $value Value of meta field.
1704 * @param string $key Key of meta field.
1705 * @param array $submitted Posted values.
1706 */
1707 public static function sanitize_meta( $value, $key, $submitted ) {
1708 charitable_get_deprecated()->deprecated_function( __METHOD__, '1.4.2' );
1709 return apply_filters( 'charitable_sanitize_campaign_meta' . $key, $value, $submitted );
1710 }
1711 }
1712
1713 endif;
1714