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

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

1,352 lines 35.0 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) 2022, 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 donations made to this campaign.
75 *
76 * @var WP_Query|false
77 */
78 private $donations;
79
80 /**
81 * The amount donated to the campaign.
82 *
83 * @var int|false
84 */
85 private $donated_amount;
86
87 /**
88 * The form object for this campaign.
89 *
90 * @var Charitable_Donation_Form
91 */
92 private $donation_form;
93
94 /**
95 * The Charitable_Object_Fields object for this campaign.
96 *
97 * @since 1.6.0
98 *
99 * @var Charitable_Object_Fields
100 */
101 protected $fields;
102
103 /**
104 * Class constructor.
105 *
106 * @since 1.0.0
107 *
108 * @param mixed $post The post ID or WP_Post object for this this campaign.
109 */
110 public function __construct( $post ) {
111 if ( ! $post instanceof WP_Post ) {
112 $post = get_post( $post );
113 }
114
115 $this->post = $post;
116 }
117
118 /**
119 * Magic getter.
120 *
121 * @since 1.0.0
122 *
123 * @param string $key The key of the field to get.
124 * @return mixed
125 */
126 public function __get( $key ) {
127 if ( property_exists( $this->post, $key ) ) {
128 return $this->post->$key;
129 }
130
131 return $this->get( $key );
132 }
133
134 /**
135 * Limit properties to be serialized.
136 *
137 * @since 1.6.18
138 *
139 * @return array
140 */
141 public function __sleep() {
142 return array(
143 'post',
144 'end_time',
145 'goal',
146 'donations',
147 'donated_amount',
148 'donation_form',
149 );
150 }
151
152 /**
153 * Returns the campaign's post_meta values. _campaign_ is automatically prepended to the meta key.
154 *
155 * @see get_post_meta
156 *
157 * @since 1.0.0
158 *
159 * @param string $key The field key.
160 * @param boolean $single Whether to return a single value or an array.
161 * @return mixed This will return an array if single is false. If it's true, the value of the
162 * meta_value field will be returned.
163 */
164 public function get( $key, $single = true ) {
165 if ( $this->fields()->has_value_callback( $key ) ) {
166 return $this->fields()->get( $key );
167 }
168
169 /* Look for a local method. */
170 if ( method_exists( $this, 'get_' . $key ) ) {
171 $method = 'get_' . $key;
172 return $this->$method();
173 }
174
175 return $this->get_meta( '_campaign_' . $key, $single );
176 }
177
178 /**
179 * Return the campaign's post_meta values.
180 *
181 * @since since
182 *
183 * @param string $meta_name The meta key.
184 * @param boolean $single Whether to return a single value or an array.
185 * @return mixed This will return an array if single is false. If it's true, the value of the
186 * meta_value field will be returned.
187 */
188 public function get_meta( $meta_name, $single = true ) {
189 /**
190 * Filter the meta value of a particular campaign field.
191 *
192 * @since 1.0.0
193 *
194 * @param mixed $value The meta value.
195 * @param string $meta_name The name of the meta field.
196 * @param boolean $single Whether we're returning a single result.
197 * @param Charitable_Campaign $campaign This instance of `Charitable_Campaign`.
198 */
199 return apply_filters( 'charitable_campaign_get_meta_value', get_post_meta( $this->post->ID, $meta_name, $single ), $meta_name, $single, $this );
200 }
201
202 /**
203 * Return the Charitable_Object_Fields instance.
204 *
205 * @since 1.6.0
206 *
207 * @return Charitable_Object_Fields
208 */
209 public function fields() {
210 if ( ! isset( $this->fields ) ) {
211 $this->fields = new Charitable_Object_Fields( charitable()->campaign_fields(), $this );
212 }
213
214 return $this->fields;
215 }
216
217 /**
218 * Returns whether the campaign is endless (i.e. no end date has been set).
219 *
220 * @since 1.0.0
221 *
222 * @return boolean
223 */
224 public function is_endless() {
225 return 0 == $this->get_meta( '_campaign_end_date' );
226 }
227
228 /**
229 * Return whether the campaign supports custom donation amounts.
230 *
231 * @since 1.6.53
232 *
233 * @return boolean
234 */
235 public function get_allow_custom_donations() {
236 return (int) get_post_meta( $this->post->ID, '_campaign_allow_custom_donations', true );
237 }
238
239 /**
240 * Return the suggested amounts, or an empty array if there are none.
241 *
242 * @since 1.0.0
243 *
244 * @return array
245 */
246 public function get_suggested_donations() {
247 $value = get_post_meta( $this->post->ID, '_campaign_suggested_donations', true );
248
249 if ( ! is_array( $value ) ) {
250 $value = array();
251 }
252
253 return apply_filters( 'charitable_campaign_suggested_donations', $value, $this );
254 }
255
256 /**
257 * Returns the end date in your preferred format.
258 *
259 * If a format is not provided, the user-defined date_format in WordPress settings is used.
260 *
261 * @since 1.0.0
262 *
263 * @param string $date_format A date format accepted by PHP's date() function.
264 * @return string|false String if an end date is set. False if campaign has no end date.
265 */
266 public function get_end_date( $date_format = '' ) {
267 if ( $this->is_endless() ) {
268 return false;
269 }
270
271 if ( ! strlen( $date_format ) ) {
272 $date_format = get_option( 'date_format', 'd/m/Y' );
273 }
274
275 /* Filter the end date format using the charitable_campaign_end_date_format hook. */
276 $date_format = apply_filters( 'charitable_campaign_end_date_format', $date_format, $this );
277
278 /* This is how the end date is stored in the database, so just return that directly. */
279 if ( 'Y-m-d H:i:s' == $date_format ) {
280 return $this->get_meta( '_campaign_end_date' );
281 }
282
283 return date_i18n( $date_format, $this->get_end_time() );
284 }
285
286 /**
287 * Returns the timetamp of the end date.
288 *
289 * @since 1.0.0
290 *
291 * @return int|false Int if campaign has an end date. False if campaign has no end date.
292 */
293 public function get_end_time() {
294 if ( ! isset( $this->end_time ) ) {
295 $end_date = $this->get_meta( '_campaign_end_date' );
296
297 if ( ! $end_date ) {
298 return false;
299 }
300
301 /* The date is stored in the format of Y-m-d H:i:s. */
302 $date_time = explode( ' ', $end_date );
303 $date = explode( '-', $date_time[0] );
304 $time = explode( ':', $date_time[1] );
305
306 $this->end_time = mktime( $time[0], $time[1], $time[2], $date[1], $date[2], $date[0] );
307 }
308
309 return $this->end_time;
310 }
311
312 /**
313 * Returns the amount of time left in the campaign in seconds.
314 *
315 * @since 1.0.0
316 *
317 * @return int $time_left Int if campaign has an end date. False if campaign has no end date.
318 */
319 public function get_seconds_left() {
320 if ( $this->is_endless() ) {
321 return false;
322 }
323
324 $time_left = $this->get_end_time() - current_time( 'timestamp' );
325
326 return $time_left < 0 ? 0 : $time_left;
327 }
328
329 /**
330 * Returns the amount of time left in the campaign as a descriptive string.
331 *
332 * @uses charitable_campaign_ended Change the text displayed when there is no time left.
333 * @uses charitabile_campaign_minutes_left Change the text displayed when there is less than an hour left.
334 * @uses charitabile_campaign_hours_left Change the text displayed when there is less than a day left.
335 * @uses charitabile_campaign_days_left Change the text displayed when there is more than a day left.
336 * @uses charitable_campaign_time_left Change the text displayed when there is time left.
337 *
338 * @since 1.0.0
339 *
340 * @return string
341 */
342 public function get_time_left() {
343 if ( $this->is_endless() ) {
344 return '';
345 }
346
347 $hour = 3600;
348 $day = 86400;
349
350 $seconds_left = $this->get_seconds_left();
351
352 if ( 0 === $seconds_left ) {
353
354 /* Condition 1: The campaign has finished. */
355
356 $time_left = apply_filters( 'charitable_campaign_ended', __( 'Campaign has ended', 'charitable' ), $this );
357
358 } elseif ( $seconds_left <= $hour ) {
359
360 /* Condition 2: There is less than an hour left. */
361
362 $minutes_remaining = ceil( $seconds_left / 60 );
363 $time_left = apply_filters(
364 'charitabile_campaign_minutes_left',
365 sprintf( _n( '%s Minute Left', '%s Minutes Left', $minutes_remaining, 'charitable' ), '<span class="amount time-left minutes-left">' . $minutes_remaining . '</span>' ),
366 $this
367 );
368
369 } elseif ( $seconds_left <= $day ) {
370
371 /* Condition 3: There is less than a day left. */
372
373 $hours_remaining = floor( $seconds_left / 3600 );
374 $time_left = apply_filters(
375 'charitabile_campaign_hours_left',
376 sprintf( _n( '%s Hour Left', '%s Hours Left', $hours_remaining, 'charitable' ), '<span class="amount time-left hours-left">' . $hours_remaining . '</span>' ),
377 $this
378 );
379
380 } else {
381
382 /* Condition 4: There is more than a day left. */
383
384 $days_remaining = floor( $seconds_left / 86400 );
385 $time_left = apply_filters(
386 'charitabile_campaign_days_left',
387 sprintf( _n( '%s Day Left', '%s Days Left', $days_remaining, 'charitable' ), '<span class="amount time-left days-left">' . $days_remaining . '</span>' ),
388 $this
389 );
390
391 }//end if
392
393 return apply_filters( 'charitable_campaign_time_left', $time_left, $this );
394 }
395
396 /**
397 * Returns whether the campaign has ended.
398 *
399 * @since 1.0.0
400 *
401 * @return boolean
402 */
403 public function has_ended() {
404 return ! $this->is_endless() && 0 == $this->get_seconds_left();
405 }
406
407 /**
408 * Returns whether a campaign is currently able to receive donations.
409 *
410 * @since 1.5.0
411 *
412 * @return boolean
413 */
414 public function can_receive_donations() {
415 $can = 'active' == $this->get_status();
416
417 /**
418 * Filter whether the campaign can receive donations.
419 *
420 * @since 1.5.0
421 *
422 * @param boolean $can Whether the campaign can receive donations.
423 * @param Charitable_Campaign $campaign This instance of `Charitable_Campaign`.
424 */
425 return apply_filters( 'charitable_campaign_can_receive_donations', $can, $this );
426 }
427
428 /**
429 * Return a text notice to say that a campaign has finished.
430 *
431 * @since 1.0.0
432 *
433 * @return string
434 */
435 public function get_finished_notice() {
436 if ( ! $this->has_ended() ) {
437 return '';
438 }
439
440 if ( ! $this->has_goal() ) {
441 $message = __( 'This campaign ended %s ago', 'charitable' );
442 } elseif ( $this->has_achieved_goal() ) {
443 $message = __( 'This campaign successfully reached its funding goal and ended %s ago', 'charitable' );
444 } else {
445 $message = __( 'This campaign failed to reach its funding goal %s ago', 'charitable' );
446 }
447
448 return apply_filters( 'charitable_campaign_finished_notice', sprintf( $message, '<span class="time-ago">' . human_time_diff( $this->get_end_time() ) . '</span>' ), $this );
449 }
450
451 /**
452 * Return the time since the campaign finished, or zero if it's still going.
453 *
454 * @since 1.0.0
455 *
456 * @return int
457 */
458 public function get_time_since_ended() {
459 if ( 0 !== $this->get_seconds_left() ) {
460 return 0;
461 }
462
463 return current_time( 'U' ) - $this->get_end_time();
464 }
465
466 /**
467 * Returns the fundraising goal of the campaign.
468 *
469 * @since 1.0.0
470 *
471 * @return string|false Amount if goal is set. False otherwise.
472 */
473 public function get_goal() {
474 if ( ! isset( $this->goal ) ) {
475 $this->goal = $this->has_goal() ? $this->get_meta( '_campaign_goal' ) : false;
476 }
477
478 return $this->goal;
479 }
480
481 /**
482 * Returns whether a goal has been set (anything greater than $0 is a goal).
483 *
484 * @since 1.0.0
485 *
486 * @return boolean
487 */
488 public function has_goal() {
489 return 0 < (float) $this->get_meta( '_campaign_goal' );
490 }
491
492 /**
493 * Returns the fundraising goal formatted as a monetary amount.
494 *
495 * @since 1.0.0
496 *
497 * @return string
498 */
499 public function get_monetary_goal() {
500 if ( ! $this->has_goal() ) {
501 return '';
502 }
503
504 return charitable_format_money( (string) $this->get_meta( '_campaign_goal' ) );
505 }
506
507 /**
508 * Returns whether the goal has been achieved.
509 *
510 * @since 1.0.0
511 *
512 * @return boolean
513 */
514 public function has_achieved_goal() {
515 return $this->get_donated_amount( true ) >= $this->get_goal();
516 }
517
518 /**
519 * Return a message to say whether the campaign has achieved its goal.
520 *
521 * @since 1.6.0
522 *
523 * @param string $success Optional. The success message.
524 * @param string $failure Optional. The failure message.
525 * @return string
526 */
527 public function get_goal_achieved_message( $success = '', $failure = '' ) {
528 if ( $this->has_achieved_goal() ) {
529
530 if ( strlen( $success ) ) {
531 return $success;
532 }
533
534 /**
535 * Filter the default message to show when a campaign has achieved its goal.
536 *
537 * @since 1.6.0
538 *
539 * @param string $message The success message.
540 * @param Charitable_Campaign $campaign This instance of `Charitable_Campaign`.
541 */
542 return apply_filters( 'charitable_campaign_goal_achievement_success_message', __( 'The campaign achieved its fundraising goal.', 'charitable' ), $this );
543 }
544
545 if ( strlen( $failure ) ) {
546 return $failure;
547 }
548
549 /**
550 * Filter the default message to show when a campaign has not achieved its goal.
551 *
552 * @since 1.6.0
553 *
554 * @param string $message The failure message.
555 * @param Charitable_Campaign $campaign This instance of `Charitable_Campaign`.
556 */
557 return apply_filters( 'charitable_campaign_goal_achievement_failure_message', __( 'The campaign did not reach its fundraising goal.', 'charitable' ), $this );
558 }
559
560 /**
561 * Return the campaign status.
562 *
563 * If the campaign is published, this will return whether either 'active' or 'finished'.
564 *
565 * @since 1.0.0
566 *
567 * @return string
568 */
569 public function get_status() {
570 $status = $this->post->post_status;
571
572 if ( 'publish' == $status ) {
573 $status = $this->has_ended() ? 'finished' : 'active';
574 }
575
576 /**
577 * Filter the returned campaign status.
578 *
579 * @since 1.0.0
580 *
581 * @param string $status The campaign status.
582 * @param string $post_status The post_status of the campaign.
583 * @param Charitable_Campaign $campaign This instance of `Charitable_Campaign`.
584 */
585 return apply_filters( 'charitable_campaign_status', $status, $this->post->post_status, $this );
586 }
587
588 /**
589 * Return a status key for the campaign.
590 *
591 * This will return one of the following:
592 *
593 * inactive : A campaign that is not published.
594 * ended : A campaign without a goal has finished
595 * successful : A campaign with a goal has finished & achieved its goal
596 * unsucessful : A campaign with a goal has finished without achieving its goal
597 * ending : A campaign is ending soon.
598 * active : A campaign that is active and not ending soon.
599 *
600 * @since 1.3.7
601 *
602 * @return string
603 */
604 public function get_status_key() {
605 /**
606 * Set the threshold in seconds for defining whether a campaign is ending soon.
607 *
608 * @since 1.0.0
609 *
610 * @param int $seconds By default, this equals 604,800.
611 */
612 $ending_soon_threshold = apply_filters( 'charitable_campaign_ending_soon_threshold', WEEK_IN_SECONDS );
613 $ended = $this->has_ended();
614
615 if ( 'publish' != $this->post->post_status ) {
616 return 'inactive';
617 }
618
619 if ( $ended && ! $this->has_goal() ) {
620 return 'ended';
621 }
622
623 if ( $ended && $this->has_achieved_goal() ) {
624 return 'successful';
625 }
626
627 if ( $ended ) {
628 return 'unsucessful';
629 }
630
631 if ( ! $this->is_endless() && $this->get_seconds_left() < $ending_soon_threshold ) {
632 return 'ending';
633 }
634
635 return 'active';
636 }
637
638 /**
639 * Return the campaign status tag.
640 *
641 * @since 1.0.0
642 *
643 * @return string
644 */
645 public function get_status_tag( $show_achievement = null, $show_active_tag = null ) {
646 $key = $this->get_status_key();
647
648 if ( is_null( $show_achievement ) ) {
649 $show_achievement = apply_filters( 'charitable_campaign_show_achievement_status_tag', true );
650 }
651
652 if ( is_null( $show_active_tag ) ) {
653 $show_active_tag = apply_filters( 'charitable_campaign_show_active_status_tag', false );
654 }
655
656 switch ( $key ) {
657 case 'ended':
658 $tag = __( 'Ended', 'charitable' );
659 break;
660
661 case 'successful':
662 $tag = $show_achievement ? __( 'Successful', 'charitable' ) : __( 'Ended', 'charitable' );
663 break;
664
665 case 'unsucessful':
666 $tag = $show_achievement ? __( 'Unsuccessful', 'charitable' ) : __( 'Ended', 'charitable' );
667 break;
668
669 case 'ending':
670 $tag = __( 'Ending Soon', 'charitable' );
671 break;
672
673 case 'active':
674 case 'publish':
675 $tag = $show_active_tag ? __( 'Active', 'charitable' ) : '';
676 break;
677
678 case 'inactive':
679 switch ( $this->post->post_status ) {
680 case 'pending':
681 $tag = __( 'Pending', 'charitable' );
682 break;
683
684 case 'draft':
685 $tag = __( 'Draft', 'charitable' );
686 break;
687
688 case 'future':
689 $tag = __( 'Scheduled', 'charitable' );
690 break;
691
692 default:
693 $tag = '';
694 }
695 break;
696
697 default:
698 $tag = '';
699
700 }//end switch
701
702 /**
703 * Filter the campaign status tag.
704 *
705 * @since 1.0.0
706 *
707 * @param string $tag The tag for the status.
708 * @param string $key The key of the current status.
709 * @param Charitable_Campaign $this This campaign object.
710 */
711 return apply_filters( 'charitable_campaign_status_tag', $tag, $key, $this );
712 }
713
714 /**
715 * Return the permalink for this campaign.
716 *
717 * @since 1.6.0
718 *
719 * @return string|false The permalink URL or false if post does not exist.
720 */
721 public function get_permalink() {
722 return get_permalink( $this->ID );
723 }
724
725 /**
726 * Return the admin edit link for this campaign.
727 *
728 * @since 1.6.0
729 *
730 * @return string
731 */
732 public function get_admin_edit_link() {
733 $post_type_object = get_post_type_object( Charitable::CAMPAIGN_POST_TYPE );
734
735 if ( $post_type_object->_edit_link ) {
736 $link = admin_url( sprintf( $post_type_object->_edit_link . '&amp;action=edit', $this->ID ) );
737 } else {
738 $link = '';
739 }
740
741 return $link;
742 }
743
744 /**
745 * Returns the donations made to this campaign.
746 *
747 * @since 1.0.0
748 *
749 * @return WP_Query
750 */
751 public function get_donations() {
752 $this->donations = get_transient( self::get_donations_cache_key( $this->ID ) );
753
754 if ( false === $this->donations ) {
755
756 $this->donations = charitable_get_table( 'campaign_donations' )->get_donations_on_campaign( $this->ID );
757
758 set_transient( self::get_donations_cache_key( $this->ID ), $this->donations, 0 );
759 }
760
761 return $this->donations;
762 }
763
764 /**
765 * Return the current amount of donations.
766 *
767 * @since 1.0.0
768 *
769 * @param boolean $sanitize Whether to sanitize the amount. False by default.
770 * @return string String if $sanitize is false. If $sanitize is true, return a float or WP_Error if the amount is not a string.
771 */
772 public function get_donated_amount( $sanitize = false ) {
773 $this->donated_amount = get_transient( self::get_donation_amount_cache_key( $this->ID ) );
774
775 if ( false === $this->donated_amount ) {
776 $this->donated_amount = charitable_get_table( 'campaign_donations' )->get_campaign_donated_amount( $this->ID, false, false );
777
778 set_transient( self::get_donation_amount_cache_key( $this->ID ), $this->donated_amount, 0 );
779 }
780
781 $amount = $this->donated_amount;
782
783 /* If returning a string, return it in the site number format. */
784 if ( ! $sanitize ) {
785 $amount = Charitable_Currency::get_instance()->sanitize_database_amount( $amount );
786 }
787
788 /**
789 * Filter the campaign donated amount.
790 *
791 * @since 1.2.0
792 *
793 * @param string|float|WP_Error $amount The donated amount.
794 * @param Charitable_Campaign $this This campaign object.
795 * @param boolean $sanitize Whether the amount should be sanitized.
796 */
797 return apply_filters( 'charitable_campaign_donated_amount', $amount, $this, $sanitize );
798 }
799
800 /**
801 * Return the currency formatted donation amount.
802 *
803 * @since 1.6.0
804 *
805 * @return string
806 */
807 public function get_donated_amount_formatted() {
808 return charitable_format_money( $this->get_donated_amount() );
809 }
810
811 /**
812 * Return a string describing the campaign's donation summary.
813 *
814 * @since 1.0.0
815 *
816 * @return string
817 */
818 public function get_donation_summary() {
819 $currency_helper = charitable_get_currency_helper();
820 $amount = $this->get_donated_amount();
821 $goal = $this->get_meta( '_campaign_goal' );
822
823 if ( $goal ) {
824 $ret = sprintf(
825 _x( '%1$s donated of %2$s goal', 'amount donated of goal', 'charitable' ),
826 '<span class="amount">' . $currency_helper->get_monetary_amount( $amount ) . '</span>',
827 '<span class="goal-amount">' . $currency_helper->get_monetary_amount( $goal ) . '</span>'
828 );
829 } else {
830 $ret = sprintf(
831 _x( '%s donated', 'amount donated', 'charitable' ),
832 '<span class="amount">' . $currency_helper->get_monetary_amount( $amount ) . '</span>'
833 );
834 }
835
836 /**
837 * Filter the returned string if you want to display the donation summary in a different way.
838 *
839 * @since 1.0.0
840 * @since 1.5.0 Added the $amount argument.
841 * @since 1.5.0 Added the $goal argument.
842 *
843 * @param string $ret The summary.
844 * @param Charitable_Campaign $this This campaign object.
845 * @param float $amount The amount donated.
846 * @param int $goal The campaign goal.
847 */
848 return apply_filters( 'charitable_donation_summary', $ret, $this, $amount, $goal );
849 }
850
851 /**
852 * Return the percentage donated. Use this if you want a formatted string.
853 *
854 * @since 1.0.0
855 *
856 * @return string|false String if campaign has a goal. False if no goal is set.
857 */
858 public function get_percent_donated() {
859 $percent = $this->get_percent_donated_raw();
860
861 if ( false === $percent ) {
862 return $percent;
863 }
864
865 $percent = number_format( $percent, 2 );
866
867 return apply_filters( 'charitable_percent_donated', $percent . '%', $percent, $this );
868 }
869
870 /**
871 * Returns the percentage donated as a number.
872 *
873 * @since 1.0.0
874 *
875 * @return int
876 */
877 public function get_percent_donated_raw() {
878 if ( ! $this->has_goal() ) {
879 return false;
880 }
881
882 return ( $this->get_donated_amount( true ) / $this->get_goal() ) * 100;
883 }
884
885 /**
886 * Return the number of people who have donated to the campaign.
887 *
888 * @since 1.0.0
889 *
890 * @return int
891 */
892 public function get_donor_count() {
893 /**
894 * Set the number of donors who have donated to this campaign.
895 *
896 * @since 1.4.7
897 *
898 * @param int $count Number of donors.
899 * @param Charitable_Campaign $this This campaign object.
900 */
901 return apply_filters(
902 'charitable_campaign_donor_count',
903 (int) charitable_get_table( 'campaign_donations' )->count_campaign_donors( $this->ID ),
904 $this
905 );
906 }
907
908 /**
909 * Returns the donation form object.
910 *
911 * @since 1.0.0
912 *
913 * @return Charitable_Donation_Form_Interface
914 */
915 public function get_donation_form() {
916 if ( ! isset( $this->donation_form ) ) {
917 /**
918 * Return the class name of the donation form for this campaign.
919 *
920 * @since 1.0.0
921 *
922 * @param string $class The form class.
923 * @param Charitable_Campaign $this This campaign object.
924 */
925 $form_class = apply_filters( 'charitable_donation_form_class', 'Charitable_Donation_Form', $this );
926
927 $this->donation_form = new $form_class( $this );
928 }
929
930 return $this->donation_form;
931 }
932
933 /**
934 * Returns the amount to be donated to the campaign as it is currently set in the session.
935 *
936 * @since 1.0.0
937 *
938 * @return int
939 */
940 public function get_donation_amount_in_session() {
941 $donation = charitable_get_session()->get_donation_by_campaign( $this->ID );
942 $amount = is_array( $donation ) ? $donation['amount'] : 0;
943
944 /**
945 * The donation amount for this campaign stored in the session.
946 *
947 * @since 1.0.0
948 *
949 * @param float|int $amount The amount.
950 * @param Charitable_Campaign $this This campaign object.
951 */
952 return apply_filters( 'charitable_session_donation_amount', $amount, $this );
953 }
954
955 /**
956 * Returns the donation period for the amount in session.
957 *
958 * @since 1.6.25
959 *
960 * @return false|string
961 */
962 public function get_donation_period_in_session() {
963 $donation = charitable_get_session()->get_donation_by_campaign( $this->ID );
964
965 if ( ! $donation ) {
966 return false;
967 }
968
969 if ( ! is_array( $donation ) || ! array_key_exists( 'donation_period', $donation ) ) {
970 return 'once';
971 }
972
973 return $donation['donation_period'];
974 }
975
976 /**
977 * Return the initial/default donation period when
978 *
979 * @since 1.6.32
980 *
981 * @return string
982 */
983 public function get_initial_donation_period() {
984 $period = $this->get_donation_period_in_session();
985
986 if ( false !== $period ) {
987 return $period;
988 }
989
990 if ( ! class_exists( 'Charitable_Recurring' ) ) {
991 return 'once';
992 }
993
994 switch ( $this->get( 'recurring_donation_mode' ) ) {
995 case 'advanced':
996 $period = 'one-time' === $this->get( 'recurring_default_tab' ) ? 'once' : 'recurring';
997 break;
998
999 default:
1000 $period = 'once';
1001 }
1002
1003 return $period;
1004 }
1005
1006 /**
1007 * Get the default donation amount for this campaign.
1008 *
1009 * @since 1.6.32
1010 *
1011 * @return int
1012 */
1013 public function get_default_donation_amount() {
1014 /**
1015 * Filter the default donation amount.
1016 *
1017 * Note, the donation amount should be expressed in a format where
1018 * a period (.) is used as the decimal separator. i.e. 2.50 is
1019 * two dollars and fifty cents.
1020 *
1021 * @since 1.5.6
1022 *
1023 * @param float|int $amount The amount to be filtered. $0 by default.
1024 * @param Charitable_Campaign $campaign The instance of `Charitable_Campaign`.
1025 */
1026 return apply_filters( 'charitable_default_donation_amount', 0, $this );
1027 }
1028
1029 /**
1030 * Renders the donate button template.
1031 *
1032 * @since 1.0.0
1033 *
1034 * @return void
1035 */
1036 public function donate_button_template() {
1037 if ( ! $this->can_receive_donations() ) {
1038 return;
1039 }
1040
1041 $display_option = charitable_get_option( 'donation_form_display', 'separate_page' );
1042
1043 switch ( $display_option ) {
1044 case 'separate_page':
1045 $template_name = 'campaign/donate-button.php';
1046 break;
1047
1048 case 'same_page':
1049 $template_name = 'campaign/donate-link.php';
1050 break;
1051
1052 case 'modal':
1053 $template_name = 'campaign/donate-modal.php';
1054 break;
1055
1056 default:
1057 $template_name = apply_filters( 'charitable_donate_button_template', 'campaign/donate-button.php', $this );
1058 }
1059
1060 charitable_template( $template_name, array( 'campaign' => $this ) );
1061 }
1062
1063 /**
1064 * Renders the donate button template.
1065 *
1066 * @since 1.2.3
1067 *
1068 * @return void
1069 */
1070 public function donate_button_loop_template() {
1071 if ( ! $this->can_receive_donations() ) {
1072 return;
1073 }
1074
1075 $display_option = charitable_get_option( 'donation_form_display', 'separate_page' );
1076
1077 switch ( $display_option ) {
1078 case 'modal':
1079 $template_name = 'campaign-loop/donate-modal.php';
1080 break;
1081
1082 default:
1083 $template_name = apply_filters( 'charitable_donate_button_loop_template', 'campaign-loop/donate-link.php', $this );
1084 }
1085
1086 charitable_template( $template_name, array( 'campaign' => $this ) );
1087 }
1088
1089 /**
1090 * Returns the campaign creator.
1091 *
1092 * By default, this just returns the user from the post_author field, but
1093 * it can be overridden by plugins.
1094 *
1095 * @since 1.0.0
1096 *
1097 * @return int $user_id
1098 */
1099 public function get_campaign_creator() {
1100 return apply_filters( 'charitable_campaign_creator', $this->post->post_author, $this );
1101 }
1102
1103 /**
1104 * Return the campaign creator's name.
1105 *
1106 * @since 1.6.0
1107 *
1108 * @return string
1109 */
1110 public function get_campaign_creator_name() {
1111 return get_the_author_meta( 'display_name', $this->get_campaign_creator() );
1112 }
1113
1114 /**
1115 * Return the campaign creator's email address.
1116 *
1117 * @since 1.6.0
1118 *
1119 * @return string
1120 */
1121 public function get_campaign_creator_email() {
1122 return get_the_author_meta( 'user_email', $this->get_campaign_creator() );
1123 }
1124
1125 /**
1126 * Sanitize the campaign goal.
1127 *
1128 * @since 1.0.0
1129 *
1130 * @param string $value Current value of goal.
1131 * @return string|int
1132 */
1133 public static function sanitize_campaign_goal( $value ) {
1134 if ( empty( $value ) || ! $value ) {
1135 return 0;
1136 }
1137
1138 return charitable_get_currency_helper()->sanitize_monetary_amount( $value );
1139 }
1140
1141 /**
1142 * Sanitize the campaign end date.
1143 *
1144 * We use WP_Locale to parse the month that the user has set.
1145 *
1146 * @global WP_Locale $wp_locale
1147 *
1148 * @since 1.0.0
1149 *
1150 * @param string $value Current end date value.
1151 * @param array $submitted The submitted data.
1152 * @return string|int
1153 */
1154 public static function sanitize_campaign_end_date( $value, $submitted = array() ) {
1155 $end_time = array_key_exists( '_campaign_end_time', $submitted ) ? $submitted['_campaign_end_time'] : '23:59:59';
1156
1157 if ( charitable()->registry()->get( 'i18n' )->decline_months() ) {
1158 $end_date = $value . ' ' . $end_time;
1159 } else {
1160 $end_date = charitable_sanitize_date( $value, 'Y-m-d ' . $end_time );
1161 }
1162
1163 if ( ! $end_date ) {
1164 $end_date = 0;
1165 }
1166
1167 return $end_date;
1168 }
1169
1170 /**
1171 * Sanitize the campaign suggested donations.
1172 *
1173 * @since 1.0.0
1174 *
1175 * @param array $value Current suggested donations value.
1176 * @return array
1177 */
1178 public static function sanitize_campaign_suggested_donations( $value ) {
1179 if ( ! is_array( $value ) ) {
1180 return array();
1181 }
1182
1183 $value = array_filter( $value, array( 'Charitable_Campaign', 'filter_suggested_donation' ) );
1184
1185 if ( empty( $value ) ) {
1186 return $value;
1187 }
1188
1189 foreach ( $value as $key => $suggestion ) {
1190 $value[ $key ]['amount'] = charitable_sanitize_amount( (string) $suggestion['amount'] );
1191
1192 /**
1193 * Sanitize the description field.
1194 *
1195 * @since 1.6.51
1196 *
1197 * @param boolean $sanitize Whether to sanitize the suggested amount description.
1198 * Returning false will allow HTML to be used, though it will
1199 * still be filtered using wp_kses_post.
1200 */
1201 if ( apply_filters( 'charitable_sanitize_suggested_amount_description', true ) ) {
1202 $value[ $key ]['description'] = sanitize_text_field( $suggestion['description'] );
1203 } else {
1204 $value[ $key ]['description'] = wp_kses_post( $suggestion['description'] );
1205 }
1206 }
1207
1208 return $value;
1209 }
1210
1211 /**
1212 * Filter out any suggested donations that do not have an amount set.
1213 *
1214 * @since 1.0.0
1215 *
1216 * @param array|string $donation Suggested donation or an array of suggested donations.
1217 * @return boolean
1218 */
1219 public static function filter_suggested_donation( $donation ) {
1220 if ( is_array( $donation ) ) {
1221 return isset( $donation['amount'] ) && ! empty( $donation['amount'] );
1222 }
1223
1224 return ! empty( $donation['amount'] );
1225 }
1226
1227 /**
1228 * Sanitize any checkbox value.
1229 *
1230 * @since 1.0.0
1231 *
1232 * @param mixed $value Current checkbox value.
1233 * @return boolean
1234 */
1235 public static function sanitize_checkbox( $value ) {
1236 return intval( true == $value || 'on' === $value );
1237 }
1238
1239 /**
1240 * Sanitize the campaign description.
1241 *
1242 * @since 1.0.0
1243 *
1244 * @param string $value Current description value.
1245 * @return string
1246 */
1247 public static function sanitize_campaign_description( $value ) {
1248 return sanitize_text_field( $value );
1249 }
1250
1251 /**
1252 * Sanitize the value provided for custom donations.
1253 *
1254 * @since 1.3.6
1255 *
1256 * @param mixed $value Current custom donations value.
1257 * @param array $submitted All posted values.
1258 * @return boolean
1259 */
1260 public static function sanitize_custom_donations( $value, $submitted ) {
1261 $checked = self::sanitize_checkbox( $value );
1262
1263 if ( $checked ) {
1264 return $checked;
1265 }
1266
1267 /**
1268 * Filter whether a campaign can be saved with custom donations disabled
1269 * and no suggested donations.
1270 *
1271 * @since 1.6.14
1272 *
1273 * @param boolean $permitted Whether a campaign is permitted to be saved without
1274 * suggested donations or custom donations.
1275 * @param array $submitted The submitted values.
1276 */
1277 if ( apply_filters( 'charitable_campaign_permitted_without_custom_or_suggested', false, $submitted ) ) {
1278 return $checked;
1279 }
1280
1281 /* If suggested donations are not set, custom donations needs to be enabled. */
1282 if ( ! isset( $submitted['_campaign_suggested_donations'] ) ) {
1283 return 1;
1284 }
1285
1286 $suggested_donations = self::sanitize_campaign_suggested_donations( $submitted['_campaign_suggested_donations'] );
1287
1288 if ( empty( $suggested_donations ) ) {
1289 return 1;
1290 }
1291
1292 return $checked;
1293 }
1294
1295 /**
1296 * Flush donations cache.
1297 *
1298 * @since 1.0.0
1299 *
1300 * @param int $campaign_id The campaign ID.
1301 * @return void
1302 */
1303 public static function flush_donations_cache( $campaign_id ) {
1304 delete_transient( self::get_donations_cache_key( $campaign_id ) );
1305 delete_transient( self::get_donation_amount_cache_key( $campaign_id ) );
1306
1307 do_action( 'charitable_flush_campaign_cache', $campaign_id );
1308 }
1309
1310 /**
1311 * Returns the key used for caching all donations made to this campaign.
1312 *
1313 * @since 1.0.0
1314 *
1315 * @param int $campaign_id The ID of the campaign.
1316 * @return string
1317 */
1318 private static function get_donations_cache_key( $campaign_id ) {
1319 return 'charitable_campaign_' . $campaign_id . '_donations';
1320 }
1321
1322 /**
1323 * Returns the key used for caching the donation amount for this campaign.
1324 *
1325 * @since 1.0.0
1326 *
1327 * @param int $campaign_id The ID of the campaign.
1328 * @return string
1329 */
1330 private static function get_donation_amount_cache_key( $campaign_id ) {
1331 return 'charitable_campaign_' . $campaign_id . '_donation_amount';
1332 }
1333
1334 /**
1335 * Deprecated method used to sanitize meta.
1336 *
1337 * @deprecated 1.7.0
1338 *
1339 * @since 1.4.12 Deprecated.
1340 *
1341 * @param mixed $value Value of meta field.
1342 * @param string $key Key of meta field.
1343 * @param array $submitted Posted values.
1344 */
1345 public static function sanitize_meta( $value, $key, $submitted ) {
1346 charitable_get_deprecated()->deprecated_function( __METHOD__, '1.4.2' );
1347 return apply_filters( 'charitable_sanitize_campaign_meta' . $key, $value, $submitted );
1348 }
1349 }
1350
1351 endif;
1352