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

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