PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.7.0.5
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.7.0.5
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 / forms / class-charitable-donation-form.php

class-charitable-donation-form.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.7.0.5, at includes/forms/class-charitable-donation-form.php

1,434 lines 38.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Donation form model class.
4 *
5 * @package Charitable/Classes/Charitable_Donation_Form
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.60
11 */
12
13 // Exit if accessed directly.
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 if ( ! class_exists( 'Charitable_Donation_Form' ) ) :
19
20 /**
21 * Charitable_Donation_Form
22 *
23 * @since 1.0.0
24 */
25 class Charitable_Donation_Form extends Charitable_Form implements Charitable_Donation_Form_Interface {
26
27 /**
28 * The current campaign.
29 *
30 * @since 1.0.0
31 *
32 * @var Charitable_Campaign
33 */
34 protected $campaign;
35
36 /**
37 * The current user, or false if the user is not logged in.
38 *
39 * @since 1.0.0
40 *
41 * @var Charitable_User|false
42 */
43 protected $user;
44
45 /**
46 * Form fields.
47 *
48 * @since 1.0.0
49 *
50 * @var array
51 */
52 protected $form_fields;
53
54 /**
55 * Nonce action.
56 *
57 * @since 1.0.0
58 *
59 * @var string
60 */
61 protected $nonce_action = 'charitable_donation';
62
63 /**
64 * Nonce name.
65 *
66 * @since 1.0.0
67 *
68 * @var string
69 */
70 protected $nonce_name = '_charitable_donation_nonce';
71
72 /**
73 * Action to be executed upon form submission.
74 *
75 * @since 1.0.0
76 *
77 * @var string
78 */
79 protected $form_action = 'make_donation';
80
81 /**
82 * Value to indicate whether the user has all required fields filled out.
83 *
84 * @since 1.0.0
85 *
86 * @var boolean
87 */
88 protected $user_has_required_fields;
89
90 /**
91 * Flag thrown when the form submission has been validated.
92 *
93 * @since 1.0.0
94 *
95 * @var boolean
96 */
97 protected $validated = false;
98
99 /**
100 * Whether the form submission is valid.
101 *
102 * @since 1.0.0
103 *
104 * @var boolean
105 */
106 protected $valid;
107
108 /**
109 * Create a donation form object.
110 *
111 * @since 1.0.0
112 * @since 1.5.0 $campaign argument became optional. Previously it was required.
113 *
114 * @param Charitable_Campaign|null $campaign Optional. Campaign receiving the donation, or NULL if
115 * the campaign will be selected in the form.
116 */
117 public function __construct( Charitable_Campaign $campaign = null ) {
118 $this->campaign = $campaign;
119 $this->id = uniqid();
120
121 $this->setup_payment_fields();
122 $this->check_test_mode();
123
124 /* For backwards-compatibility */
125 add_action( 'charitable_form_field', array( $this, 'render_field' ), 10, 6 );
126
127 /* added in 1.7.0.4 */
128 if ( 'below_donation_title' === charitable_get_option( 'donation_form_minimal_amount_notice_display', false ) ) {
129 add_action( 'charitable_after_donation_amount_wrapper_header', array( $this, 'minimum_donation_amount_notice' ), 10, 2 );
130 } else if ( 'after_donation_title' === charitable_get_option( 'donation_form_minimal_amount_notice_display', false ) ) {
131 add_action( 'charitable_before_donation_amount_wrapper_header', array( $this, 'minimum_donation_amount_notice' ), 10, 2 );
132 }
133
134
135 }
136
137 /**
138 * Returns the campaign associated with this donation form object.
139 *
140 * @since 1.0.0
141 * @since 1.5.0 May now return NULL when the donation form was set up without a campaign.
142 *
143 * @return Charitable_Campaign|null
144 */
145 public function get_campaign() {
146 return $this->campaign;
147 }
148
149 /**
150 * Return the current user.
151 *
152 * @since 1.0.0
153 *
154 * @return Charitable_User|false Object if the user is logged in. False otherwise.
155 */
156 public function get_user() {
157 if ( ! isset( $this->user ) ) {
158 $user = wp_get_current_user();
159 $this->user = $user->ID ? new Charitable_User( $user ) : false;
160 }
161
162 return $this->user;
163 }
164
165 /**
166 * Returns the set value for a particular user field.
167 *
168 * @since 1.0.0
169 *
170 * @param string $key The field key.
171 * @param string $default Optional. The value that will be used if none is set.
172 * @return mixed
173 */
174 public function get_user_value( $key, $default = '' ) {
175 if ( isset( $_POST[ $key ] ) ) {
176 return $_POST[ $key ];
177 }
178
179 $donation_id = $this->get_validated_donation_id();
180
181 if ( $donation_id ) {
182 $donation = charitable_get_donation( $donation_id );
183 $value = $donation->get_donor()->get_donor_meta( $key );
184
185 if ( $value ) {
186 return $value;
187 }
188 }
189
190 if ( ! $this->get_user() || ! $this->get_user()->has_prop( $key ) ) {
191 return $default;
192 }
193
194 return $this->get_user()->get( $key );
195 }
196
197 /**
198 * Returns the fields related to the person making the donation.
199 *
200 * @since 1.0.0
201 *
202 * @return array
203 */
204 public function get_user_fields() {
205 /**
206 * Filter the donor fields.
207 *
208 * @since 1.0.0
209 *
210 * @param array $fields Set of donor fields.
211 * @param Charitable_Donation_Form $form Instance of `Charitable_Donation_Form`.
212 */
213 $fields = apply_filters( 'charitable_donation_form_user_fields', $this->get_sanitized_donation_fields( 'user' ), $this );
214 $fields = $this->hide_non_required_user_fields( $fields );
215
216 uasort( $fields, 'charitable_priority_sort' );
217
218 return $fields;
219 }
220
221 /**
222 * Returns the fields related to the person making the donation.
223 *
224 * @since 1.6.0
225 *
226 * @return array
227 */
228 public function get_meta_fields() {
229 /**
230 * Filter the meta fields.
231 *
232 * @since 1.6.0
233 *
234 * @param array $fields Set of meta fields.
235 * @param Charitable_Donation_Form $form Instance of `Charitable_Donation_Form`.
236 */
237 $fields = apply_filters( 'charitable_donation_form_meta_fields', $this->get_sanitized_donation_fields( 'meta' ), $this );
238
239 uasort( $fields, 'charitable_priority_sort' );
240
241 return $fields;
242 }
243
244 /**
245 * Return donation fields for a particular section.
246 *
247 * @since 1.6.0
248 * @since 1.6.1 Changed access to public.
249 *
250 * @param string $section The section of the donation form we need fields for.
251 * @return array
252 */
253 public function get_sanitized_donation_fields( $section ) {
254 $fields = charitable()->donation_fields()->get_donation_form_fields( $section );
255
256 if ( empty( $fields ) ) {
257 return array();
258 }
259
260 $keys = array_keys( $fields );
261
262 return array_combine(
263 $keys,
264 array_map( array( $this, 'set_field_value' ), wp_list_pluck( $fields, 'donation_form' ), $keys )
265 );
266 }
267
268 /**
269 * Only show the required user fields if that option was enabled by the site admin.
270 *
271 * @since 1.2.0
272 *
273 * @param array $fields The user fields.
274 * @return array[]
275 */
276 public function hide_non_required_user_fields( $fields ) {
277 if ( ! charitable_get_option( 'donation_form_minimal_fields', false ) ) {
278 return $fields;
279 }
280
281 return array_filter( $fields, array( $this, 'filter_required_fields' ) );
282 }
283
284 /**
285 * Return fields used for account creation.
286 *
287 * By default, this just returns the password field. You can include a username
288 * field with ...
289 *
290 * @since 1.0.0
291 *
292 * @return array
293 */
294 public function get_user_account_fields() {
295 $account_fields = array(
296 'user_pass' => array(
297 'label' => __( 'Password', 'charitable' ),
298 'type' => 'password',
299 'priority' => 4,
300 'required' => true,
301 'requires_registration' => true,
302 'data_type' => 'user',
303 ),
304 );
305
306 if ( apply_filters( 'charitable_donor_usernames', false ) ) {
307 $account_fields['user_login'] = array(
308 'label' => __( 'Username', 'charitable' ),
309 'type' => 'text',
310 'priority' => 2,
311 'required' => true,
312 'requires_registration' => true,
313 'data_type' => 'user',
314 );
315 }
316
317 return $account_fields;
318 }
319
320 /**
321 * Returns the donation fields.
322 *
323 * @since 1.0.0
324 *
325 * @return array[]
326 */
327 public function get_donation_fields() {
328 $fields = array(
329 'donation_amount' => array(
330 'type' => 'donation-amount',
331 'priority' => 4,
332 'required' => false,
333 ),
334 );
335
336 /**
337 * Filter the donation amount fields.
338 *
339 * @since 1.0.0
340 *
341 * @param array $fields The list of fields.
342 * @param Charitable_Donation_Form $form Instance of `Charitable_Donation_Form`.
343 */
344 $fields = apply_filters( 'charitable_donation_form_donation_fields', $fields, $this );
345
346 uasort( $fields, 'charitable_priority_sort' );
347
348 return $fields;
349 }
350
351 /**
352 * Return the donation form fields.
353 *
354 * @since 1.0.0
355 *
356 * @return array[]
357 */
358 public function get_fields() {
359 $fields = array(
360 'donation_fields' => array(
361 'legend' => _x( 'Your Donation', 'donation form amount section header', 'charitable' ),
362 'type' => 'donation-amount-wrapper',
363 'fields' => $this->get_donation_fields(),
364 'priority' => 20,
365 ),
366 'details_fields' => array(
367 'legend' => __( 'Details', 'charitable' ),
368 'type' => 'details-fields',
369 'class' => 'fieldset',
370 'priority' => 40,
371 'fields' => array(
372 'donor_fields' => array(
373 'type' => 'donor-fields',
374 'fields' => $this->get_user_fields(),
375 'priority' => 44,
376 ),
377 'meta_fields' => array(
378 'type' => 'meta-fields',
379 'fields' => $this->get_meta_fields(),
380 'priority' => 48,
381 ),
382 ),
383 ),
384 );
385
386 $fields = $this->maybe_add_terms_conditions_fields( $fields );
387
388 $fields = $this->maybe_add_minimum_donation_amount( $fields );
389
390 /**
391 * Filter the donation form fields.
392 *
393 * @since 1.0.0
394 *
395 * @param array $fields The donation form fields.
396 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
397 */
398 $fields = apply_filters( 'charitable_donation_form_fields', $fields, $this );
399
400 uasort( $fields, 'charitable_priority_sort' );
401
402 return $fields;
403 }
404
405 /**
406 * Add payment fields to the donation form if necessary.
407 *
408 * @since 1.0.0
409 *
410 * @param array[] $fields All existing fields in the donation form.
411 * @return array[]
412 */
413 public function add_payment_fields( $fields ) {
414 $gateways_helper = charitable_get_helper( 'gateways' );
415 $default_gateway = $gateways_helper->get_default_gateway();
416 $gateways = array();
417 $has_gateway_fields = false;
418
419 foreach ( $gateways_helper->get_active_gateways() as $gateway_id => $gateway_class ) {
420 $gateway = new $gateway_class;
421 $gateway_fields = $this->add_credit_card_fields( array(), $gateway );
422
423 /**
424 * Filter the gateway fields.
425 *
426 * @since 1.0.0
427 *
428 * @param array $gateway_fields List of gateway fields.
429 * @param Charitable_Gateway $gateway Instance of `Charitable_Gateway`.
430 */
431 $gateway_fields = apply_filters( 'charitable_donation_form_gateway_fields', $gateway_fields, $gateway );
432 $has_gateway_fields = $has_gateway_fields || ! empty( $gateway_fields );
433 $gateways[ $gateway_id ] = array(
434 'label' => $gateway->get_label(),
435 'fields' => $gateway_fields,
436 );
437 }
438
439 /* Add the payment section if there are gateway fields to be filled out. */
440 if ( $has_gateway_fields || count( $gateways ) > 1 ) {
441 $fields['payment_fields'] = array(
442 'type' => 'gateway-fields',
443 'legend' => __( 'Payment', 'charitable' ),
444 'default' => $default_gateway,
445 'gateways' => $gateways,
446 'priority' => 60,
447 );
448 }
449
450 return $fields;
451 }
452
453 /**
454 * Include a paragraph showing the currently set donation
455 * amount before the amount form, if one is set.
456 *
457 * @since 1.4.14
458 * @since 1.5.0 $fields argument deprecated and return
459 * type changed to string.
460 *
461 * @return string
462 */
463 public function maybe_show_current_donation_amount() {
464 if ( ! $this->get_campaign() ) {
465 return '';
466 }
467
468 $amount = $this->get_campaign()->get_donation_amount_in_session();
469
470 if ( ! $amount ) {
471 $content = charitable_template_from_session_content(
472 'donation_form_current_amount_text',
473 array(
474 'campaign_id' => $this->get_campaign()->ID,
475 'form_id' => $this->get_form_identifier(),
476 ),
477 ''
478 );
479 } else {
480 $content = charitable_template_donation_form_current_amount_text( $amount, $this->get_form_identifier(), $this->get_campaign()->ID );
481 }
482
483 return $content;
484 }
485
486 /**
487 * Add credit card fields to the donation form if this gateway requires it.
488 *
489 * @since 1.0.0
490 *
491 * @param array[] $fields Current gateway fields. Deprecated as of 1.5.0.
492 * @param Charitable_Gateway $gateway Instance of `Charitable_Gateway`.
493 * @return array[]
494 */
495 public function add_credit_card_fields( $fields, Charitable_Gateway $gateway ) {
496 if ( $gateway->supports( 'credit-card' ) ) {
497 $fields = array_merge( $fields, $gateway->get_credit_card_fields() );
498 }
499
500 return $fields;
501 }
502
503 /**
504 * Render the donation form.
505 *
506 * @since 1.0.0
507 *
508 * @return void
509 */
510 public function render() {
511 charitable_template(
512 'donation-form/form-donation.php',
513 array(
514 'campaign' => $this->get_campaign(),
515 'form' => $this,
516 )
517 );
518 }
519
520 /**
521 * Return the validated donation ID.
522 *
523 * The donation is picked from a GET parameter or a POST. Either way,
524 * the donation ID is checked to ensure it corresponds to a donation
525 * and one that the current user should have access to.
526 *
527 * @since 1.5.14
528 *
529 * @return int
530 */
531 public function get_validated_donation_id() {
532 if ( array_key_exists( 'donation_id', $_GET ) ) {
533 $donation_id = $_GET['donation_id'];
534 } elseif ( array_key_exists( 'ID', $_POST ) ) {
535 $donation_id = $_POST['ID'];
536 } else {
537 return 0;
538 }
539
540 if ( charitable_user_can_access_donation( $donation_id ) ) {
541 return $donation_id;
542 }
543
544 return 0;
545 }
546
547 /**
548 * Adds hidden fields to the start of the donation form.
549 *
550 * @since 1.5.0
551 *
552 * @return array
553 */
554 public function get_hidden_fields() {
555 $fields = parent::get_hidden_fields();
556
557 if ( ! is_null( $this->campaign ) ) {
558 $fields['campaign_id'] = $this->campaign->ID;
559 $fields['description'] = get_the_title( $this->campaign->ID );
560 }
561
562 $fields['ID'] = $this->get_validated_donation_id();
563
564 /**
565 * Filter the hidden fields in the donation form.
566 *
567 * @since 1.0.0
568 *
569 * @param string[] $fields The hidden fields as key=>value pairs.
570 * @param Charitable_Donation_Form $form The donation form instance.
571 */
572 return apply_filters( 'charitable_donation_form_hidden_fields', $fields, $this );
573 }
574
575 /**
576 * Set the gateway as a hidden field when there is only one gateway.
577 *
578 * @since 1.0.0
579 *
580 * @param string[] $fields The hidden fields as key=>value pairs.
581 * @return string[]
582 */
583 public function add_hidden_gateway_field( $fields ) {
584 $gateways = charitable_get_helper( 'gateways' )->get_active_gateways();
585
586 if ( count( $gateways ) !== 1 ) {
587 return $fields;
588 }
589
590 $gateway_keys = array_keys( $gateways );
591
592 $fields['gateway'] = $gateway_keys[0];
593
594 return $fields;
595 }
596
597 /**
598 * Add a password field to the end of the form.
599 *
600 * @since 1.0.0
601 *
602 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
603 * @return void
604 */
605 public function add_password_field( $form ) {
606 if ( ! $form->is_current_form( $this->id ) ) {
607 return;
608 }
609
610 /* Make sure we are not logged in. */
611 if ( 0 !== wp_get_current_user()->ID ) {
612 return;
613 }
614
615 charitable_template( 'donation-form/user-login-fields.php' );
616 }
617
618 /**
619 * Validate the form submission.
620 *
621 * @since 1.0.0
622 *
623 * @return boolean
624 */
625 public function validate_submission() {
626 /* If we have already validated the submission, return the value. */
627 if ( $this->validated ) {
628 return $this->valid;
629 }
630
631 $this->validated = true;
632
633 $this->valid = $this->validate_security_check()
634 && $this->check_required_fields( $this->get_merged_fields() )
635 && $this->validate_email()
636 && $this->validate_amount()
637 && $this->validate_gateway();
638
639 /**
640 * Filter the overall validation result.
641 *
642 * @since 1.0.0
643 *
644 * @param boolean $ret The result to be returned. True or False.
645 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
646 */
647 $this->valid = apply_filters( 'charitable_validate_donation_form_submission', $this->valid, $this );
648
649 return $this->valid;
650 }
651
652 /**
653 * Checks whether the security checks (nonce and honeypot) pass.
654 *
655 * @since 1.4.6
656 *
657 * @return boolean
658 */
659 public function validate_security_check() {
660 $ret = true;
661
662 if ( ! $this->validate_nonce() || ! $this->validate_honeypot() ) {
663 charitable_get_notices()->add_error(
664 __( 'Unfortunately, we were unable to verify your form submission. Please reload the page and try again.', 'charitable' )
665 );
666
667 $ret = false;
668 }
669
670 /**
671 * Filter the security validation result.
672 *
673 * @since 1.4.7
674 *
675 * @param boolean $ret The result to be returned. True or False.
676 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
677 */
678 return apply_filters( 'charitable_validate_donation_form_submission_security_check', $ret, $this );
679 }
680
681 /**
682 * Checks whether the submitted email is valid.
683 *
684 * @since 1.4.6
685 *
686 * @return boolean
687 */
688 public function validate_email() {
689 $ret = true;
690
691 /* Don't process donations with dummy emails. */
692 $submitted = $this->get_submitted_value( 'email' );
693 if ( ! is_null( $submitted ) && ! is_email( $submitted ) ) {
694 charitable_get_notices()->add_error( sprintf(
695 /* translators: %s: email address */
696 __( '%s is not a valid email address.', 'charitable' ),
697 $submitted
698 ) );
699
700 $ret = false;
701 }
702
703 /**
704 * Filter the email validation result.
705 *
706 * @since 1.4.7
707 *
708 * @param boolean $ret The result to be returned. True or False.
709 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
710 */
711 return apply_filters( 'charitable_validate_donation_form_submission_email_check', $ret, $this );
712 }
713
714 /**
715 * Checks whether the submitted gateway is valid.
716 *
717 * @since 1.4.6
718 *
719 * @return boolean
720 */
721 public function validate_gateway() {
722 $ret = true;
723
724 /* Validate the gateway. */
725 if ( ! Charitable_Gateways::get_instance()->is_valid_gateway( $this->get_submitted_value( 'gateway' ) ) ) {
726 charitable_get_notices()->add_error( __( 'The gateway submitted is not valid.', 'charitable' ) );
727 $ret = false;
728 }
729
730 /**
731 * Filter the gateway validation result.
732 *
733 * @since 1.4.7
734 *
735 * @param boolean $ret The result to be returned. True or False.
736 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
737 */
738 return apply_filters( 'charitable_validate_donation_form_submission_gateway_check', $ret, $this );
739
740 }
741
742 /**
743 * Checks whether the set amount is valid.
744 *
745 * @since 1.4.6
746 *
747 * @return boolean
748 */
749 public function validate_amount() {
750 $ret = true;
751
752 /* Ensure that a valid amount has been submitted. */
753 $submitted = $this->get_submitted_values();
754 $minimum = charitable_get_minimum_donation_amount( $submitted['campaign_id'] );
755 $maximum = charitable_get_maximum_donation_amount( $submitted['campaign_id'] );
756 $amount = self::get_donation_amount();
757
758 if ( $minimum > 0 && $amount < $minimum ) {
759 charitable_get_notices()->add_error(
760 sprintf(
761 __( 'You must donate more than %s.', 'charitable' ),
762 charitable_format_money( $minimum, false, true )
763 )
764 );
765
766 $ret = false;
767 } elseif ( $maximum && $maximum < $amount ) {
768 charitable_get_notices()->add_error(
769 sprintf(
770 __( 'The maximum donation amount permitted is %s.', 'charitable' ),
771 charitable_format_money( $maximum, false, true )
772 )
773 );
774
775 $ret = false;
776 } elseif ( $minimum === 0 && $amount <= 0 && ! apply_filters( 'charitable_permit_0_donation', false ) ) {
777 charitable_get_notices()->add_error( sprintf(
778 __( 'You must donate more than %s.', 'charitable' ),
779 charitable_format_money( $minimum )
780 ) );
781
782 $ret = false;
783 }
784
785 /**
786 * Filter the amount validation result.
787 *
788 * @since 1.4.7
789 *
790 * @param boolean $ret The result to be returned. True or False.
791 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
792 */
793 return apply_filters( 'charitable_validate_donation_form_submission_amount_check', $ret, $this );
794
795 }
796
797 /**
798 * Return the donation values.
799 *
800 * @since 1.0.0
801 *
802 * @return array
803 */
804 public function get_donation_values() {
805 $submitted = $this->get_submitted_values();
806 $values = array(
807 'ID' => $this->get_validated_donation_id(),
808 'user_id' => get_current_user_id(),
809 'gateway' => $submitted['gateway'],
810 'campaigns' => array(
811 array(
812 'campaign_id' => $submitted['campaign_id'],
813 'amount' => self::get_donation_amount(),
814 ),
815 ),
816 );
817
818 if ( 0 !== $values['ID'] ) {
819 $values['donation_key'] = charitable_get_donation( $values['ID'] )->get_donation_key();
820 }
821
822 foreach ( $this->get_merged_fields() as $key => $field ) {
823 if ( isset( $field['data_type'] ) || 'gateways' == $key ) {
824 if ( 'gateways' == $key ) {
825 foreach ( $field as $gateway_id => $gateway_fields ) {
826 foreach ( $gateway_fields as $key => $field ) {
827 if ( ! isset( $field['type'] ) ) {
828 continue;
829 }
830
831 $field_type = $field['type'];
832 $default = 'checkbox' == $field_type ? false : '';
833 $value = isset( $submitted[ $key ] ) ? $submitted[ $key ] : $default;
834
835 /* Strip extra spaces from the credit card number. */
836 if ( 'cc_number' == $key ) {
837 $value = trim( str_replace( ' ', '', $value ) );
838 }
839
840 $values['gateways'][ $gateway_id ][ $key ] = $value;
841 }
842 }
843 } elseif ( isset( $field['type'] ) ) {
844 $data_type = $field['data_type'];
845 $field_type = $field['type'];
846 $default = 'checkbox' == $field_type ? false : '';
847
848 $values[ $data_type ][ $key ] = isset( $submitted[ $key ] ) ? $submitted[ $key ] : $default;
849 }
850 }
851 }
852
853 /**
854 * Filter the submitted donation form values, before any processing happens.
855 *
856 * @since 1.0.0
857 *
858 * @param array $values The structured submitted values.
859 * @param array $submitted The raw submitted values (i.e. $_POST).
860 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
861 */
862 return apply_filters( 'charitable_donation_form_submission_values', $values, $submitted, $this );
863 }
864
865 /**
866 * Returns all fields as a merged array.
867 *
868 * @since 1.0.0
869 *
870 * @return array[]
871 */
872 public function get_merged_fields() {
873 if ( ! isset( $this->merged_fields ) ) {
874 $this->merged_fields = array();
875
876 foreach ( $this->get_fields() as $section_id => $section ) {
877 $this->merged_fields = array_merge( $this->merged_fields, $this->get_fields_from_section( $section_id, $section ) );
878 }
879 }
880
881 return $this->merged_fields;
882 }
883
884 /**
885 * Return the fields from a particular section.
886 *
887 * This will loop recursively, so if the section has a "fields" property,
888 * it will loop over those fields and call itself again for each of those.
889 *
890 * @since 1.6.0
891 *
892 * @param string $section_id The key of the section, or field.
893 * @param array $section The section arguments.
894 * @return array
895 */
896 public function get_fields_from_section( $section_id, $section ) {
897 if ( 'payment_fields' == $section_id ) {
898 $section_fields = array();
899
900 foreach ( $section['gateways'] as $gateway_id => $gateway_section ) {
901 if ( isset( $gateway_section['fields'] ) ) {
902 $section_fields['gateways'][ $gateway_id ] = $gateway_section['fields'];
903 }
904 }
905
906 return $section_fields;
907 }
908
909 if ( array_key_exists( 'fields', $section ) ) {
910 $section_fields = array();
911
912 foreach ( $section['fields'] as $section_id => $section ) {
913 $section_fields = array_merge( $section_fields, $this->get_fields_from_section( $section_id, $section ) );
914 }
915
916 return $section_fields;
917 }
918
919 return array( $section_id => $section );
920 }
921
922 /**
923 * Checks whether the user has all required fields.
924 *
925 * @since 1.2.0
926 *
927 * @return boolean
928 */
929 public function user_has_required_fields() {
930 if ( ! isset( $this->user_has_required_fields ) ) {
931
932 foreach ( $this->get_user_fields() as $field ) {
933
934 if ( ! isset( $field['required'] ) || false == $field['required'] ) {
935 continue;
936 }
937
938 if ( empty( $field['value'] ) ) {
939 $this->user_has_required_fields = false;
940 return $this->user_has_required_fields;
941 }
942 }
943
944 $this->user_has_required_fields = true;
945 }
946
947 return $this->user_has_required_fields;
948 }
949
950 /**
951 * Maybe add terms and conditions fields to the form.
952 *
953 * @since 1.6.0
954 *
955 * @param array $fields The registered donation form fields.
956 * @return array
957 */
958 public function maybe_add_minimum_donation_amount( $fields ) {
959
960 if ( false === $this->get_campaign()->ID || false === $fields || ! isset( $fields['donation_fields']['fields'] ) ) {
961 return $fields;
962 }
963
964 if ( false !== charitable_get_option( 'donation_form_minimal_amount_notice_display', false ) || 'below_amount_selection' !== charitable_get_option( 'donation_form_minimal_amount_notice_display', false ) ) {
965 return $fields;
966 }
967
968 $terms_fields = array();
969 $minimum_amount = get_post_meta( $this->get_campaign()->ID, '_campaign_minimum_donation_amount', true );
970
971 // do not display a message in the donation form if the field is zero or empty.
972 if ( false === $minimum_amount || '' === trim( $minimum_amount ) ) {
973 return $fields;
974 }
975
976 // default location of the minimum donation amount.
977 $fields['donation_fields']['fields']['minimum_donation_amount'] = array(
978 'legend' => __( 'Mininum Donation Test', 'charitable' ),
979 'type' => 'content',
980 'content' => $this->minimum_donation_amount_notice( true, $minimum_amount ),
981 'priority' => 80,
982 );
983
984 /**
985 * Filter the minimum donation notification fields.
986 *
987 * @since 1.7.0.3
988 *
989 * @param array $terms_fields List of terms fields.
990 * @param Charitable_Donation_Form $form Instance of `Charitable_Donation_Form`.
991 */
992 $minimum_fields = apply_filters( 'charitable_donation_minimum_fields', $fields, $this );
993
994 if ( empty( $minimum_fields ) ) {
995 return $fields;
996 }
997
998 return $fields;
999 }
1000
1001 public function minimum_donation_amount_notice( $return = true, $minimum_amount = false ) {
1002
1003 if ( false === $minimum_amount ) {
1004 $minimum_amount = get_post_meta( $this->get_campaign()->ID, '_campaign_minimum_donation_amount', true );
1005 }
1006
1007 // do not display a message in the donation form if the field is zero or empty.
1008 if ( false === $minimum_amount || '' === trim( $minimum_amount ) ) {
1009 return '';
1010 } else {
1011 $message = '<p class="minimum-donation-amount-text">' . __( 'The minimum donation for this campaign is ', 'charitable' ) . charitable_format_money( $minimum_amount, false, true ) . '.</p>';
1012 }
1013
1014 if ( $return ) {
1015 return $message;
1016 } else {
1017 echo $message;
1018 }
1019
1020 }
1021
1022
1023 /**
1024 * Maybe add terms and conditions fields to the form.
1025 *
1026 * @since 1.6.0
1027 *
1028 * @param array $fields The registered donation form fields.
1029 * @return array
1030 */
1031 public function maybe_add_terms_conditions_fields( $fields ) {
1032 $terms_fields = array();
1033 $show_consent = (int) charitable_get_option( 'contact_consent', 0 ) && Charitable_Upgrade::get_instance()->upgrade_has_been_completed( 'upgrade_donor_tables' );
1034
1035 if ( charitable_is_privacy_policy_activated() ) {
1036 $terms_fields['privacy_policy_text'] = array(
1037 'type' => 'content',
1038 'content' => '<p class="charitable-privacy-policy-text">' . charitable_get_privacy_policy_field_text() . '</p>',
1039 'priority' => 4,
1040 );
1041 }
1042
1043 if ( charitable_is_contact_consent_activated() ) {
1044 $terms_fields['contact_consent'] = array(
1045 'type' => 'checkbox',
1046 'label' => charitable_get_option( 'contact_consent_label', __( 'Yes, I am happy for you to contact me via email or phone.', 'charitable' ) ),
1047 'priority' => 8,
1048 'required' => false,
1049 'data_type' => 'meta',
1050 );
1051 }
1052
1053 if ( charitable_is_terms_and_conditions_activated() ) {
1054 $terms_fields['terms_text'] = array(
1055 'type' => 'content',
1056 'content' => '<div class="charitable-terms-text">' . charitable_get_terms_and_conditions() . '</div>',
1057 'priority' => 12,
1058 );
1059
1060 $terms_fields['accept_terms'] = array(
1061 'type' => 'checkbox',
1062 'label' => charitable_get_terms_and_conditions_field_label(),
1063 'priority' => 16,
1064 'required' => true,
1065 'data_type' => 'meta',
1066 );
1067 }
1068
1069 /**
1070 * Filter the terms and conditions fields.
1071 *
1072 * @since 1.6.0
1073 *
1074 * @param array $terms_fields List of terms fields.
1075 * @param Charitable_Donation_Form $form Instance of `Charitable_Donation_Form`.
1076 */
1077 $terms_fields = apply_filters( 'charitable_donation_form_terms_fields', $terms_fields, $this );
1078
1079 if ( empty( $terms_fields ) ) {
1080 return $fields;
1081 }
1082
1083 return array_merge( $fields, array(
1084 'terms_fields' => array(
1085 'legend' => __( 'Terms and Conditions', 'charitable' ),
1086 'type' => 'fieldset',
1087 'fields' => $terms_fields,
1088 'priority' => 80,
1089 ),
1090 ) );
1091 }
1092
1093 /**
1094 * Return the terms and conditions text.
1095 *
1096 * @deprecated 2.0.0
1097 *
1098 * @since 1.6.0
1099 * @since 1.6.14 Deprecated.
1100 *
1101 * @param int $terms_page The ID of the terms page.
1102 * @return string
1103 */
1104 public function get_terms( $terms_page ) {
1105 charitable_get_deprecated()->deprecated_function(
1106 __METHOD__,
1107 '1.6.14',
1108 'charitable_get_terms_and_conditions'
1109 );
1110
1111 return charitable_get_terms_and_conditions();
1112 }
1113
1114 /**
1115 * Return the terms text, with [terms] replaced by a link to the terms and conditions.
1116 *
1117 * @deprecated 2.0.0
1118 *
1119 * @since 1.6.0
1120 * @since 1.6.14 Deprecated.
1121 *
1122 * @param int $terms_page The ID of the terms page.
1123 * @return string
1124 */
1125 public function get_parsed_terms_text( $terms_page ) {
1126 charitable_get_deprecated()->deprecated_function(
1127 __METHOD__,
1128 '1.6.14',
1129 'charitable_get_terms_and_conditions_field_label'
1130 );
1131
1132 return charitable_get_terms_and_conditions_field_label();
1133 }
1134
1135 /**
1136 * Return the privacy policy text, with [privacy_policy] replaced by a link to the privacy policy page.
1137 *
1138 * @deprecated 2.0.0
1139 *
1140 * @since 1.6.0
1141 * @since 1.6.14 Deprecated.
1142 *
1143 * @param int $privacy_page The ID of the privacy policy page.
1144 * @return string
1145 */
1146 public function get_parsed_privacy_text( $privacy_page ) {
1147 charitable_get_deprecated()->deprecated_function(
1148 __METHOD__,
1149 '1.6.14',
1150 'charitable_get_privacy_policy_field_text'
1151 );
1152
1153 return charitable_get_privacy_policy_field_text();
1154 }
1155
1156 /**
1157 * Returns whether the form should hide the user fields.
1158 *
1159 * @since 1.6.0
1160 *
1161 * @return boolean
1162 */
1163 public function should_hide_user_fields() {
1164 $hide_fields = $this->get_user() && $this->user_has_required_fields() && ! is_customize_preview();
1165
1166 /**
1167 * Filter whether the user fields should be hidden.
1168 *
1169 * @since 1.6.0
1170 *
1171 * @param boolean $hide_fields Whether the fields should be hidden.
1172 * @param Charitable_Donation_Form $form The instance of `Charitable_Donation_Form`.
1173 */
1174 return apply_filters( 'charitable_hide_fields_for_logged_in_users', $hide_fields, $this );
1175 }
1176
1177 /**
1178 * Return the donation amount.
1179 *
1180 * @since 1.0.0
1181 *
1182 * @return float
1183 */
1184 public static function get_donation_amount() {
1185 $amount = isset( $_POST['donation_amount'] ) ? $_POST['donation_amount'] : 0;
1186
1187 if ( 0 === $amount || 'custom' == $amount ) {
1188 $amount = isset( $_POST['custom_donation_amount'] ) ? $_POST['custom_donation_amount'] : 0;
1189 }
1190
1191 /**
1192 * Only sanitize the amount if it's a number (no letters).
1193 *
1194 * With Recurring Donations, it's possible that amount might be 'recurring-custom',
1195 * which should not be sanitized.
1196 */
1197 if ( ! is_string( $amount ) || ! preg_match( '/[A-Za-z]/', $amount ) ) {
1198 $amount = charitable_get_currency_helper()->sanitize_monetary_amount( (string) $amount );
1199 }
1200
1201 /**
1202 * Filter the donation amount.
1203 *
1204 * @since 1.3.0
1205 *
1206 * @param float|WP_Error $amount The donation amount.
1207 */
1208 return apply_filters( 'charitable_donation_form_amount', $amount );
1209 }
1210
1211 /**
1212 * Set up payment fields based on the gateways that are installed and which one is default.
1213 *
1214 * @since 1.0.0
1215 *
1216 * @return void
1217 */
1218 protected function setup_payment_fields() {
1219 $active_gateways = charitable_get_helper( 'gateways' )->get_active_gateways();
1220 $has_gateways = apply_filters( 'charitable_has_active_gateways', ! empty( $active_gateways ) );
1221
1222 /* If no gateways have been selected, display a notice and return the fields */
1223 if ( ! $has_gateways ) {
1224 charitable_get_notices()->add_error( $this->get_no_active_gateways_notice() );
1225 return;
1226 }
1227
1228 if ( count( $active_gateways ) == 1 ) {
1229 add_filter( 'charitable_donation_form_hidden_fields', array( $this, 'add_hidden_gateway_field' ) );
1230 }
1231
1232 add_action( 'charitable_donation_form_fields', array( $this, 'add_payment_fields' ) );
1233 }
1234
1235 /**
1236 * Set a field's initial value.
1237 *
1238 * @since 1.5.0
1239 *
1240 * @param array $field Field definition.
1241 * @param string $key The key of the field.
1242 * @return array
1243 */
1244 protected function set_field_value( $field, $key ) {
1245 $donation_id = $this->get_validated_donation_id();
1246 $submitted = isset( $_POST[ $key ] ) ? $_POST[ $key ] : null;
1247
1248 /* Mark a checkbox as checked. */
1249 if ( 'checkbox' === $field['type'] ) {
1250 $checked_value = isset( $field['value'] ) ? $field['value'] : 1;
1251
1252 if ( ! is_null( $submitted ) ) {
1253 $field['checked'] = $submitted == $checked_value;
1254 } elseif( $donation_id ) {
1255 $field['checked'] = charitable_get_donation( $donation_id )->get( $key ) == $checked_value;
1256 }
1257 } else {
1258 if ( ! is_null( $submitted ) ) {
1259 $field['value'] = $submitted;
1260 } elseif ( $donation_id ) {
1261 $donation = charitable_get_donation( $donation_id );
1262 $field['value'] = $donation ? $donation->get( $key ) : '';
1263 }
1264 }
1265
1266 if ( array_key_exists( 'value', $field ) ) {
1267 return $field;
1268 }
1269
1270 if ( 'user' == $field['data_type'] ) {
1271 $field['value'] = $this->get_user_value( $key, $field['default'] );
1272 } else {
1273 $field['value'] = $field['default'];
1274 }
1275
1276 return $field;
1277 }
1278
1279 /**
1280 * Given a notice and an additional action that requires
1281 * admin credentials, return the appropriate notice for
1282 * the current user.
1283 *
1284 * @since 1.5.0
1285 *
1286 * @return string
1287 */
1288 protected function get_credentialed_notice( $notice, $admin_action ) {
1289 if ( current_user_can( 'manage_charitable_settings' ) ) {
1290 $notice .= '&nbsp;' . $admin_action;
1291 }
1292
1293 return $notice;
1294 }
1295
1296 /**
1297 * A formatted notice to advise that there are no gateways active.
1298 *
1299 * @since 1.0.0
1300 *
1301 * @return string
1302 */
1303 protected function get_no_active_gateways_notice() {
1304 $notice = $this->get_credentialed_notice(
1305 __( 'There are no active payment gateways.', 'charitable' ),
1306 sprintf( '<a href="%s">%s</a>.', admin_url( 'admin.php?page=charitable-settings&tab=gateways' ), _x( 'Enable one now', 'enable payment gateway', 'charitable' ) )
1307 );
1308
1309 return apply_filters( 'charitable_no_active_gateways_notice', $notice, current_user_can( 'manage_charitable_settings' ) );
1310 }
1311
1312 /**
1313 * Determine the status of Test Mode and display an alert if it is active
1314 *
1315 * @since 1.4.7
1316 *
1317 * @return void
1318 */
1319 protected function check_test_mode() {
1320 $in_test_mode = charitable_get_option( 'test_mode', 0 );
1321
1322 /* If test mode is enabled, and current user is an admin, display an alert on the form. */
1323 if ( $in_test_mode && current_user_can( 'manage_charitable_settings' ) ) {
1324 charitable_get_notices()->add_error( $this->get_test_mode_active_notice() );
1325 }
1326 }
1327
1328 /**
1329 * A formatted notice to advise that Test Mode is active.
1330 *
1331 * @since 1.4.7
1332 *
1333 * @return string
1334 */
1335 protected function get_test_mode_active_notice() {
1336 $notice = $this->get_credentialed_notice(
1337 __( 'Test mode is active.', 'charitable' ),
1338 sprintf( '<a href="%s">%s</a>.',
1339 admin_url( 'admin.php?page=charitable-settings&tab=gateways' ),
1340 __( 'Disable Test Mode', 'charitable' )
1341 )
1342 );
1343
1344 return apply_filters( 'charitable_test_mode_active_notice', $notice, current_user_can( 'manage_charitable_settings' ) );
1345 }
1346
1347 /**
1348 * Return the donor value fields.
1349 *
1350 * @since 1.0.0
1351 *
1352 * @param array $submitted The submitted values.
1353 * @return string[]
1354 */
1355 protected function get_donor_value_fields( $submitted ) {
1356 $donor_fields = array();
1357
1358 if ( isset( $submitted['first_name'] ) ) {
1359 $donor_fields['first_name'] = $submitted['first_name'];
1360 }
1361
1362 if ( isset( $submitted['last_name'] ) ) {
1363 $donor_fields['last_name'] = $submitted['last_name'];
1364 }
1365
1366 if ( isset( $submitted['user_email'] ) ) {
1367 $donor_fields['email'] = $submitted['user_email'];
1368 }
1369
1370 return $donor_fields;
1371 }
1372
1373 /**
1374 * Checks whether the form submission contains profile fields.
1375 *
1376 * @since 1.0.0
1377 *
1378 * @return boolean
1379 */
1380 protected function has_profile_fields( $submitted, $user_fields ) {
1381 foreach ( $user_fields as $key => $field ) {
1382 if ( $field['requires_registration'] && isset( $submitted[ $key ] ) ) {
1383 return true;
1384 }
1385 }
1386
1387 return false;
1388 }
1389
1390 /**
1391 * Returns true if required fields are missing.
1392 *
1393 * @since 1.0.0
1394 *
1395 * @param array $required_fields
1396 * @return boolean
1397 */
1398 protected function is_missing_required_fields( $required_fields ) {
1399 if ( is_user_logged_in() ) {
1400 return false;
1401 }
1402
1403 if ( is_null( $this->get_submitted_value( 'gateway' ) ) ) {
1404 charitable_get_notices()->add_error( sprintf( '<p>%s</p>',
1405 __( 'Your donation could not be processed. No payment gateway was selected.', 'charitable' )
1406 ) );
1407
1408 return false;
1409 }
1410
1411 return ! $this->check_required_fields( $required_fields );
1412 }
1413
1414 /**
1415 * Use custom template for some form fields.
1416 *
1417 * @since 1.0.0
1418 * @since 1.5.0 Deprecated. This is handled by `Charitable_Public_Form_View` now.
1419 *
1420 * @param string|false $custom_template
1421 * @return string|false|Charitable_Template
1422 */
1423 public function use_custom_templates( $custom_template ) {
1424 charitable_get_deprecated()->deprecated_function(
1425 __METHOD__,
1426 '1.5.0'
1427 );
1428
1429 return $custom_template;
1430 }
1431 }
1432
1433 endif;
1434