PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.7.0.12
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.7.0.12
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.12, at includes/campaigns/class-charitable-campaign.php

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