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

1,350 lines 35.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
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 /**
380 * Filter the donation form fields.
381 *
382 * @since 1.0.0
383 *
384 * @param array $fields The donation form fields.
385 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
386 */
387 $fields = apply_filters( 'charitable_donation_form_fields', $fields, $this );
388
389 uasort( $fields, 'charitable_priority_sort' );
390
391 return $fields;
392 }
393
394 /**
395 * Add payment fields to the donation form if necessary.
396 *
397 * @since 1.0.0
398 *
399 * @param array[] $fields All existing fields in the donation form.
400 * @return array[]
401 */
402 public function add_payment_fields( $fields ) {
403 $gateways_helper = charitable_get_helper( 'gateways' );
404 $default_gateway = $gateways_helper->get_default_gateway();
405 $gateways = array();
406 $has_gateway_fields = false;
407
408 foreach ( $gateways_helper->get_active_gateways() as $gateway_id => $gateway_class ) {
409 $gateway = new $gateway_class;
410 $gateway_fields = $this->add_credit_card_fields( array(), $gateway );
411
412 /**
413 * Filter the gateway fields.
414 *
415 * @since 1.0.0
416 *
417 * @param array $gateway_fields List of gateway fields.
418 * @param Charitable_Gateway $gateway Instance of `Charitable_Gateway`.
419 */
420 $gateway_fields = apply_filters( 'charitable_donation_form_gateway_fields', $gateway_fields, $gateway );
421 $has_gateway_fields = $has_gateway_fields || ! empty( $gateway_fields );
422 $gateways[ $gateway_id ] = array(
423 'label' => $gateway->get_label(),
424 'fields' => $gateway_fields,
425 );
426 }
427
428 /* Add the payment section if there are gateway fields to be filled out. */
429 if ( $has_gateway_fields || count( $gateways ) > 1 ) {
430 $fields['payment_fields'] = array(
431 'type' => 'gateway-fields',
432 'legend' => __( 'Payment', 'charitable' ),
433 'default' => $default_gateway,
434 'gateways' => $gateways,
435 'priority' => 60,
436 );
437 }
438
439 return $fields;
440 }
441
442 /**
443 * Include a paragraph showing the currently set donation
444 * amount before the amount form, if one is set.
445 *
446 * @since 1.4.14
447 * @since 1.5.0 $fields argument deprecated and return
448 * type changed to string.
449 *
450 * @return string
451 */
452 public function maybe_show_current_donation_amount() {
453 if ( ! $this->get_campaign() ) {
454 return '';
455 }
456
457 $amount = $this->get_campaign()->get_donation_amount_in_session();
458
459 if ( ! $amount ) {
460 $content = charitable_template_from_session_content(
461 'donation_form_current_amount_text',
462 array(
463 'campaign_id' => $this->get_campaign()->ID,
464 'form_id' => $this->get_form_identifier(),
465 ),
466 ''
467 );
468 } else {
469 $content = charitable_template_donation_form_current_amount_text( $amount, $this->get_form_identifier(), $this->get_campaign()->ID );
470 }
471
472 return $content;
473 }
474
475 /**
476 * Add credit card fields to the donation form if this gateway requires it.
477 *
478 * @since 1.0.0
479 *
480 * @param array[] $fields Current gateway fields. Deprecated as of 1.5.0.
481 * @param Charitable_Gateway $gateway Instance of `Charitable_Gateway`.
482 * @return array[]
483 */
484 public function add_credit_card_fields( $fields, Charitable_Gateway $gateway ) {
485 if ( $gateway->supports( 'credit-card' ) ) {
486 $fields = array_merge( $fields, $gateway->get_credit_card_fields() );
487 }
488
489 return $fields;
490 }
491
492 /**
493 * Render the donation form.
494 *
495 * @since 1.0.0
496 *
497 * @return void
498 */
499 public function render() {
500 charitable_template(
501 'donation-form/form-donation.php',
502 array(
503 'campaign' => $this->get_campaign(),
504 'form' => $this,
505 )
506 );
507 }
508
509 /**
510 * Return the validated donation ID.
511 *
512 * The donation is picked from a GET parameter or a POST. Either way,
513 * the donation ID is checked to ensure it corresponds to a donation
514 * and one that the current user should have access to.
515 *
516 * @since 1.5.14
517 *
518 * @return int
519 */
520 public function get_validated_donation_id() {
521 if ( array_key_exists( 'donation_id', $_GET ) ) {
522 $donation_id = $_GET['donation_id'];
523 } elseif ( array_key_exists( 'ID', $_POST ) ) {
524 $donation_id = $_POST['ID'];
525 } else {
526 return 0;
527 }
528
529 if ( charitable_user_can_access_donation( $donation_id ) ) {
530 return $donation_id;
531 }
532
533 return 0;
534 }
535
536 /**
537 * Adds hidden fields to the start of the donation form.
538 *
539 * @since 1.5.0
540 *
541 * @return array
542 */
543 public function get_hidden_fields() {
544 $fields = parent::get_hidden_fields();
545
546 if ( ! is_null( $this->campaign ) ) {
547 $fields['campaign_id'] = $this->campaign->ID;
548 $fields['description'] = get_the_title( $this->campaign->ID );
549 }
550
551 $fields['ID'] = $this->get_validated_donation_id();
552
553 /**
554 * Filter the hidden fields in the donation form.
555 *
556 * @since 1.0.0
557 *
558 * @param string[] $fields The hidden fields as key=>value pairs.
559 * @param Charitable_Donation_Form $form The donation form instance.
560 */
561 return apply_filters( 'charitable_donation_form_hidden_fields', $fields, $this );
562 }
563
564 /**
565 * Set the gateway as a hidden field when there is only one gateway.
566 *
567 * @since 1.0.0
568 *
569 * @param string[] $fields The hidden fields as key=>value pairs.
570 * @return string[]
571 */
572 public function add_hidden_gateway_field( $fields ) {
573 $gateways = charitable_get_helper( 'gateways' )->get_active_gateways();
574
575 if ( count( $gateways ) !== 1 ) {
576 return $fields;
577 }
578
579 $gateway_keys = array_keys( $gateways );
580
581 $fields['gateway'] = $gateway_keys[0];
582
583 return $fields;
584 }
585
586 /**
587 * Add a password field to the end of the form.
588 *
589 * @since 1.0.0
590 *
591 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
592 * @return void
593 */
594 public function add_password_field( $form ) {
595 if ( ! $form->is_current_form( $this->id ) ) {
596 return;
597 }
598
599 /* Make sure we are not logged in. */
600 if ( 0 !== wp_get_current_user()->ID ) {
601 return;
602 }
603
604 charitable_template( 'donation-form/user-login-fields.php' );
605 }
606
607 /**
608 * Validate the form submission.
609 *
610 * @since 1.0.0
611 *
612 * @return boolean
613 */
614 public function validate_submission() {
615 /* If we have already validated the submission, return the value. */
616 if ( $this->validated ) {
617 return $this->valid;
618 }
619
620 $this->validated = true;
621
622 $this->valid = $this->validate_security_check()
623 && $this->check_required_fields( $this->get_merged_fields() )
624 && $this->validate_email()
625 && $this->validate_amount()
626 && $this->validate_gateway();
627
628 /**
629 * Filter the overall validation result.
630 *
631 * @since 1.0.0
632 *
633 * @param boolean $ret The result to be returned. True or False.
634 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
635 */
636 $this->valid = apply_filters( 'charitable_validate_donation_form_submission', $this->valid, $this );
637
638 return $this->valid;
639 }
640
641 /**
642 * Checks whether the security checks (nonce and honeypot) pass.
643 *
644 * @since 1.4.6
645 *
646 * @return boolean
647 */
648 public function validate_security_check() {
649 $ret = true;
650
651 if ( ! $this->validate_nonce() || ! $this->validate_honeypot() ) {
652 charitable_get_notices()->add_error(
653 __( 'Unfortunately, we were unable to verify your form submission. Please reload the page and try again.', 'charitable' )
654 );
655
656 $ret = false;
657 }
658
659 /**
660 * Filter the security validation result.
661 *
662 * @since 1.4.7
663 *
664 * @param boolean $ret The result to be returned. True or False.
665 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
666 */
667 return apply_filters( 'charitable_validate_donation_form_submission_security_check', $ret, $this );
668 }
669
670 /**
671 * Checks whether the submitted email is valid.
672 *
673 * @since 1.4.6
674 *
675 * @return boolean
676 */
677 public function validate_email() {
678 $ret = true;
679
680 /* Don't process donations with dummy emails. */
681 $submitted = $this->get_submitted_value( 'email' );
682 if ( ! is_null( $submitted ) && ! is_email( $submitted ) ) {
683 charitable_get_notices()->add_error( sprintf(
684 /* translators: %s: email address */
685 __( '%s is not a valid email address.', 'charitable' ),
686 $submitted
687 ) );
688
689 $ret = false;
690 }
691
692 /**
693 * Filter the email validation result.
694 *
695 * @since 1.4.7
696 *
697 * @param boolean $ret The result to be returned. True or False.
698 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
699 */
700 return apply_filters( 'charitable_validate_donation_form_submission_email_check', $ret, $this );
701 }
702
703 /**
704 * Checks whether the submitted gateway is valid.
705 *
706 * @since 1.4.6
707 *
708 * @return boolean
709 */
710 public function validate_gateway() {
711 $ret = true;
712
713 /* Validate the gateway. */
714 if ( ! Charitable_Gateways::get_instance()->is_valid_gateway( $this->get_submitted_value( 'gateway' ) ) ) {
715 charitable_get_notices()->add_error( __( 'The gateway submitted is not valid.', 'charitable' ) );
716 $ret = false;
717 }
718
719 /**
720 * Filter the gateway validation result.
721 *
722 * @since 1.4.7
723 *
724 * @param boolean $ret The result to be returned. True or False.
725 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
726 */
727 return apply_filters( 'charitable_validate_donation_form_submission_gateway_check', $ret, $this );
728
729 }
730
731 /**
732 * Checks whether the set amount is valid.
733 *
734 * @since 1.4.6
735 *
736 * @return boolean
737 */
738 public function validate_amount() {
739 $ret = true;
740
741 /* Ensure that a valid amount has been submitted. */
742 $submitted = $this->get_submitted_values();
743 $minimum = charitable_get_minimum_donation_amount( $submitted['campaign_id'] );
744 $maximum = charitable_get_maximum_donation_amount( $submitted['campaign_id'] );
745 $amount = self::get_donation_amount();
746
747 if ( $minimum > 0 && $amount < $minimum ) {
748 charitable_get_notices()->add_error(
749 sprintf(
750 __( 'You must donate more than %s.', 'charitable' ),
751 charitable_format_money( $minimum, false, true )
752 )
753 );
754
755 $ret = false;
756 } elseif ( $maximum && $maximum < $amount ) {
757 charitable_get_notices()->add_error(
758 sprintf(
759 __( 'The maximum donation amount permitted is %s.', 'charitable' ),
760 charitable_format_money( $maximum, false, true )
761 )
762 );
763
764 $ret = false;
765 } elseif ( $minimum === 0 && $amount <= 0 && ! apply_filters( 'charitable_permit_0_donation', false ) ) {
766 charitable_get_notices()->add_error( sprintf(
767 __( 'You must donate more than %s.', 'charitable' ),
768 charitable_format_money( $minimum )
769 ) );
770
771 $ret = false;
772 }
773
774 /**
775 * Filter the amount validation result.
776 *
777 * @since 1.4.7
778 *
779 * @param boolean $ret The result to be returned. True or False.
780 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
781 */
782 return apply_filters( 'charitable_validate_donation_form_submission_amount_check', $ret, $this );
783
784 }
785
786 /**
787 * Return the donation values.
788 *
789 * @since 1.0.0
790 *
791 * @return array
792 */
793 public function get_donation_values() {
794 $submitted = $this->get_submitted_values();
795 $values = array(
796 'ID' => $this->get_validated_donation_id(),
797 'user_id' => get_current_user_id(),
798 'gateway' => $submitted['gateway'],
799 'campaigns' => array(
800 array(
801 'campaign_id' => $submitted['campaign_id'],
802 'amount' => self::get_donation_amount(),
803 ),
804 ),
805 );
806
807 if ( 0 !== $values['ID'] ) {
808 $values['donation_key'] = charitable_get_donation( $values['ID'] )->get_donation_key();
809 }
810
811 foreach ( $this->get_merged_fields() as $key => $field ) {
812 if ( isset( $field['data_type'] ) || 'gateways' == $key ) {
813 if ( 'gateways' == $key ) {
814 foreach ( $field as $gateway_id => $gateway_fields ) {
815 foreach ( $gateway_fields as $key => $field ) {
816 if ( ! isset( $field['type'] ) ) {
817 continue;
818 }
819
820 $field_type = $field['type'];
821 $default = 'checkbox' == $field_type ? false : '';
822 $value = isset( $submitted[ $key ] ) ? $submitted[ $key ] : $default;
823
824 /* Strip extra spaces from the credit card number. */
825 if ( 'cc_number' == $key ) {
826 $value = trim( str_replace( ' ', '', $value ) );
827 }
828
829 $values['gateways'][ $gateway_id ][ $key ] = $value;
830 }
831 }
832 } elseif ( isset( $field['type'] ) ) {
833 $data_type = $field['data_type'];
834 $field_type = $field['type'];
835 $default = 'checkbox' == $field_type ? false : '';
836
837 $values[ $data_type ][ $key ] = isset( $submitted[ $key ] ) ? $submitted[ $key ] : $default;
838 }
839 }
840 }
841
842 /**
843 * Filter the submitted donation form values, before any processing happens.
844 *
845 * @since 1.0.0
846 *
847 * @param array $values The structured submitted values.
848 * @param array $submitted The raw submitted values (i.e. $_POST).
849 * @param Charitable_Donation_Form $form This instance of `Charitable_Donation_Form`.
850 */
851 return apply_filters( 'charitable_donation_form_submission_values', $values, $submitted, $this );
852 }
853
854 /**
855 * Returns all fields as a merged array.
856 *
857 * @since 1.0.0
858 *
859 * @return array[]
860 */
861 public function get_merged_fields() {
862 if ( ! isset( $this->merged_fields ) ) {
863 $this->merged_fields = array();
864
865 foreach ( $this->get_fields() as $section_id => $section ) {
866 $this->merged_fields = array_merge( $this->merged_fields, $this->get_fields_from_section( $section_id, $section ) );
867 }
868 }
869
870 return $this->merged_fields;
871 }
872
873 /**
874 * Return the fields from a particular section.
875 *
876 * This will loop recursively, so if the section has a "fields" property,
877 * it will loop over those fields and call itself again for each of those.
878 *
879 * @since 1.6.0
880 *
881 * @param string $section_id The key of the section, or field.
882 * @param array $section The section arguments.
883 * @return array
884 */
885 public function get_fields_from_section( $section_id, $section ) {
886 if ( 'payment_fields' == $section_id ) {
887 $section_fields = array();
888
889 foreach ( $section['gateways'] as $gateway_id => $gateway_section ) {
890 if ( isset( $gateway_section['fields'] ) ) {
891 $section_fields['gateways'][ $gateway_id ] = $gateway_section['fields'];
892 }
893 }
894
895 return $section_fields;
896 }
897
898 if ( array_key_exists( 'fields', $section ) ) {
899 $section_fields = array();
900
901 foreach ( $section['fields'] as $section_id => $section ) {
902 $section_fields = array_merge( $section_fields, $this->get_fields_from_section( $section_id, $section ) );
903 }
904
905 return $section_fields;
906 }
907
908 return array( $section_id => $section );
909 }
910
911 /**
912 * Checks whether the user has all required fields.
913 *
914 * @since 1.2.0
915 *
916 * @return boolean
917 */
918 public function user_has_required_fields() {
919 if ( ! isset( $this->user_has_required_fields ) ) {
920
921 foreach ( $this->get_user_fields() as $field ) {
922
923 if ( ! isset( $field['required'] ) || false == $field['required'] ) {
924 continue;
925 }
926
927 if ( empty( $field['value'] ) ) {
928 $this->user_has_required_fields = false;
929 return $this->user_has_required_fields;
930 }
931 }
932
933 $this->user_has_required_fields = true;
934 }
935
936 return $this->user_has_required_fields;
937 }
938
939 /**
940 * Maybe add terms and conditions fields to the form.
941 *
942 * @since 1.6.0
943 *
944 * @param array $fields The registered donation form fields.
945 * @return array
946 */
947 public function maybe_add_terms_conditions_fields( $fields ) {
948 $terms_fields = array();
949 $show_consent = (int) charitable_get_option( 'contact_consent', 0 ) && Charitable_Upgrade::get_instance()->upgrade_has_been_completed( 'upgrade_donor_tables' );
950
951 if ( charitable_is_privacy_policy_activated() ) {
952 $terms_fields['privacy_policy_text'] = array(
953 'type' => 'content',
954 'content' => '<p class="charitable-privacy-policy-text">' . charitable_get_privacy_policy_field_text() . '</p>',
955 'priority' => 4,
956 );
957 }
958
959 if ( charitable_is_contact_consent_activated() ) {
960 $terms_fields['contact_consent'] = array(
961 'type' => 'checkbox',
962 'label' => charitable_get_option( 'contact_consent_label', __( 'Yes, I am happy for you to contact me via email or phone.', 'charitable' ) ),
963 'priority' => 8,
964 'required' => false,
965 'data_type' => 'meta',
966 );
967 }
968
969 if ( charitable_is_terms_and_conditions_activated() ) {
970 $terms_fields['terms_text'] = array(
971 'type' => 'content',
972 'content' => '<div class="charitable-terms-text">' . charitable_get_terms_and_conditions() . '</div>',
973 'priority' => 12,
974 );
975
976 $terms_fields['accept_terms'] = array(
977 'type' => 'checkbox',
978 'label' => charitable_get_terms_and_conditions_field_label(),
979 'priority' => 16,
980 'required' => true,
981 'data_type' => 'meta',
982 );
983 }
984
985 /**
986 * Filter the terms and conditions fields.
987 *
988 * @since 1.6.0
989 *
990 * @param array $terms_fields List of terms fields.
991 * @param Charitable_Donation_Form $form Instance of `Charitable_Donation_Form`.
992 */
993 $terms_fields = apply_filters( 'charitable_donation_form_terms_fields', $terms_fields, $this );
994
995 if ( empty( $terms_fields ) ) {
996 return $fields;
997 }
998
999 return array_merge( $fields, array(
1000 'terms_fields' => array(
1001 'legend' => __( 'Terms and Conditions', 'charitable' ),
1002 'type' => 'fieldset',
1003 'fields' => $terms_fields,
1004 'priority' => 80,
1005 ),
1006 ) );
1007 }
1008
1009 /**
1010 * Return the terms and conditions text.
1011 *
1012 * @deprecated 2.0.0
1013 *
1014 * @since 1.6.0
1015 * @since 1.6.14 Deprecated.
1016 *
1017 * @param int $terms_page The ID of the terms page.
1018 * @return string
1019 */
1020 public function get_terms( $terms_page ) {
1021 charitable_get_deprecated()->deprecated_function(
1022 __METHOD__,
1023 '1.6.14',
1024 'charitable_get_terms_and_conditions'
1025 );
1026
1027 return charitable_get_terms_and_conditions();
1028 }
1029
1030 /**
1031 * Return the terms text, with [terms] replaced by a link to the terms and conditions.
1032 *
1033 * @deprecated 2.0.0
1034 *
1035 * @since 1.6.0
1036 * @since 1.6.14 Deprecated.
1037 *
1038 * @param int $terms_page The ID of the terms page.
1039 * @return string
1040 */
1041 public function get_parsed_terms_text( $terms_page ) {
1042 charitable_get_deprecated()->deprecated_function(
1043 __METHOD__,
1044 '1.6.14',
1045 'charitable_get_terms_and_conditions_field_label'
1046 );
1047
1048 return charitable_get_terms_and_conditions_field_label();
1049 }
1050
1051 /**
1052 * Return the privacy policy text, with [privacy_policy] replaced by a link to the privacy policy page.
1053 *
1054 * @deprecated 2.0.0
1055 *
1056 * @since 1.6.0
1057 * @since 1.6.14 Deprecated.
1058 *
1059 * @param int $privacy_page The ID of the privacy policy page.
1060 * @return string
1061 */
1062 public function get_parsed_privacy_text( $privacy_page ) {
1063 charitable_get_deprecated()->deprecated_function(
1064 __METHOD__,
1065 '1.6.14',
1066 'charitable_get_privacy_policy_field_text'
1067 );
1068
1069 return charitable_get_privacy_policy_field_text();
1070 }
1071
1072 /**
1073 * Returns whether the form should hide the user fields.
1074 *
1075 * @since 1.6.0
1076 *
1077 * @return boolean
1078 */
1079 public function should_hide_user_fields() {
1080 $hide_fields = $this->get_user() && $this->user_has_required_fields() && ! is_customize_preview();
1081
1082 /**
1083 * Filter whether the user fields should be hidden.
1084 *
1085 * @since 1.6.0
1086 *
1087 * @param boolean $hide_fields Whether the fields should be hidden.
1088 * @param Charitable_Donation_Form $form The instance of `Charitable_Donation_Form`.
1089 */
1090 return apply_filters( 'charitable_hide_fields_for_logged_in_users', $hide_fields, $this );
1091 }
1092
1093 /**
1094 * Return the donation amount.
1095 *
1096 * @since 1.0.0
1097 *
1098 * @return float
1099 */
1100 public static function get_donation_amount() {
1101 $amount = isset( $_POST['donation_amount'] ) ? $_POST['donation_amount'] : 0;
1102
1103 if ( 0 === $amount || 'custom' == $amount ) {
1104 $amount = isset( $_POST['custom_donation_amount'] ) ? $_POST['custom_donation_amount'] : 0;
1105 }
1106
1107 /**
1108 * Only sanitize the amount if it's a number (no letters).
1109 *
1110 * With Recurring Donations, it's possible that amount might be 'recurring-custom',
1111 * which should not be sanitized.
1112 */
1113 if ( ! is_string( $amount ) || ! preg_match( '/[A-Za-z]/', $amount ) ) {
1114 $amount = charitable_get_currency_helper()->sanitize_monetary_amount( (string) $amount );
1115 }
1116
1117 /**
1118 * Filter the donation amount.
1119 *
1120 * @since 1.3.0
1121 *
1122 * @param float|WP_Error $amount The donation amount.
1123 */
1124 return apply_filters( 'charitable_donation_form_amount', $amount );
1125 }
1126
1127 /**
1128 * Set up payment fields based on the gateways that are installed and which one is default.
1129 *
1130 * @since 1.0.0
1131 *
1132 * @return void
1133 */
1134 protected function setup_payment_fields() {
1135 $active_gateways = charitable_get_helper( 'gateways' )->get_active_gateways();
1136 $has_gateways = apply_filters( 'charitable_has_active_gateways', ! empty( $active_gateways ) );
1137
1138 /* If no gateways have been selected, display a notice and return the fields */
1139 if ( ! $has_gateways ) {
1140 charitable_get_notices()->add_error( $this->get_no_active_gateways_notice() );
1141 return;
1142 }
1143
1144 if ( count( $active_gateways ) == 1 ) {
1145 add_filter( 'charitable_donation_form_hidden_fields', array( $this, 'add_hidden_gateway_field' ) );
1146 }
1147
1148 add_action( 'charitable_donation_form_fields', array( $this, 'add_payment_fields' ) );
1149 }
1150
1151 /**
1152 * Set a field's initial value.
1153 *
1154 * @since 1.5.0
1155 *
1156 * @param array $field Field definition.
1157 * @param string $key The key of the field.
1158 * @return array
1159 */
1160 protected function set_field_value( $field, $key ) {
1161 $donation_id = $this->get_validated_donation_id();
1162 $submitted = isset( $_POST[ $key ] ) ? $_POST[ $key ] : null;
1163
1164 /* Mark a checkbox as checked. */
1165 if ( 'checkbox' === $field['type'] ) {
1166 $checked_value = isset( $field['value'] ) ? $field['value'] : 1;
1167
1168 if ( ! is_null( $submitted ) ) {
1169 $field['checked'] = $submitted == $checked_value;
1170 } elseif( $donation_id ) {
1171 $field['checked'] = charitable_get_donation( $donation_id )->get( $key ) == $checked_value;
1172 }
1173 } else {
1174 if ( ! is_null( $submitted ) ) {
1175 $field['value'] = $submitted;
1176 } elseif ( $donation_id ) {
1177 $donation = charitable_get_donation( $donation_id );
1178 $field['value'] = $donation ? $donation->get( $key ) : '';
1179 }
1180 }
1181
1182 if ( array_key_exists( 'value', $field ) ) {
1183 return $field;
1184 }
1185
1186 if ( 'user' == $field['data_type'] ) {
1187 $field['value'] = $this->get_user_value( $key, $field['default'] );
1188 } else {
1189 $field['value'] = $field['default'];
1190 }
1191
1192 return $field;
1193 }
1194
1195 /**
1196 * Given a notice and an additional action that requires
1197 * admin credentials, return the appropriate notice for
1198 * the current user.
1199 *
1200 * @since 1.5.0
1201 *
1202 * @return string
1203 */
1204 protected function get_credentialed_notice( $notice, $admin_action ) {
1205 if ( current_user_can( 'manage_charitable_settings' ) ) {
1206 $notice .= '&nbsp;' . $admin_action;
1207 }
1208
1209 return $notice;
1210 }
1211
1212 /**
1213 * A formatted notice to advise that there are no gateways active.
1214 *
1215 * @since 1.0.0
1216 *
1217 * @return string
1218 */
1219 protected function get_no_active_gateways_notice() {
1220 $notice = $this->get_credentialed_notice(
1221 __( 'There are no active payment gateways.', 'charitable' ),
1222 sprintf( '<a href="%s">%s</a>.', admin_url( 'admin.php?page=charitable-settings&tab=gateways' ), _x( 'Enable one now', 'enable payment gateway', 'charitable' ) )
1223 );
1224
1225 return apply_filters( 'charitable_no_active_gateways_notice', $notice, current_user_can( 'manage_charitable_settings' ) );
1226 }
1227
1228 /**
1229 * Determine the status of Test Mode and display an alert if it is active
1230 *
1231 * @since 1.4.7
1232 *
1233 * @return void
1234 */
1235 protected function check_test_mode() {
1236 $in_test_mode = charitable_get_option( 'test_mode', 0 );
1237
1238 /* If test mode is enabled, and current user is an admin, display an alert on the form. */
1239 if ( $in_test_mode && current_user_can( 'manage_charitable_settings' ) ) {
1240 charitable_get_notices()->add_error( $this->get_test_mode_active_notice() );
1241 }
1242 }
1243
1244 /**
1245 * A formatted notice to advise that Test Mode is active.
1246 *
1247 * @since 1.4.7
1248 *
1249 * @return string
1250 */
1251 protected function get_test_mode_active_notice() {
1252 $notice = $this->get_credentialed_notice(
1253 __( 'Test mode is active.', 'charitable' ),
1254 sprintf( '<a href="%s">%s</a>.',
1255 admin_url( 'admin.php?page=charitable-settings&tab=gateways' ),
1256 __( 'Disable Test Mode', 'charitable' )
1257 )
1258 );
1259
1260 return apply_filters( 'charitable_test_mode_active_notice', $notice, current_user_can( 'manage_charitable_settings' ) );
1261 }
1262
1263 /**
1264 * Return the donor value fields.
1265 *
1266 * @since 1.0.0
1267 *
1268 * @param array $submitted The submitted values.
1269 * @return string[]
1270 */
1271 protected function get_donor_value_fields( $submitted ) {
1272 $donor_fields = array();
1273
1274 if ( isset( $submitted['first_name'] ) ) {
1275 $donor_fields['first_name'] = $submitted['first_name'];
1276 }
1277
1278 if ( isset( $submitted['last_name'] ) ) {
1279 $donor_fields['last_name'] = $submitted['last_name'];
1280 }
1281
1282 if ( isset( $submitted['user_email'] ) ) {
1283 $donor_fields['email'] = $submitted['user_email'];
1284 }
1285
1286 return $donor_fields;
1287 }
1288
1289 /**
1290 * Checks whether the form submission contains profile fields.
1291 *
1292 * @since 1.0.0
1293 *
1294 * @return boolean
1295 */
1296 protected function has_profile_fields( $submitted, $user_fields ) {
1297 foreach ( $user_fields as $key => $field ) {
1298 if ( $field['requires_registration'] && isset( $submitted[ $key ] ) ) {
1299 return true;
1300 }
1301 }
1302
1303 return false;
1304 }
1305
1306 /**
1307 * Returns true if required fields are missing.
1308 *
1309 * @since 1.0.0
1310 *
1311 * @param array $required_fields
1312 * @return boolean
1313 */
1314 protected function is_missing_required_fields( $required_fields ) {
1315 if ( is_user_logged_in() ) {
1316 return false;
1317 }
1318
1319 if ( is_null( $this->get_submitted_value( 'gateway' ) ) ) {
1320 charitable_get_notices()->add_error( sprintf( '<p>%s</p>',
1321 __( 'Your donation could not be processed. No payment gateway was selected.', 'charitable' )
1322 ) );
1323
1324 return false;
1325 }
1326
1327 return ! $this->check_required_fields( $required_fields );
1328 }
1329
1330 /**
1331 * Use custom template for some form fields.
1332 *
1333 * @since 1.0.0
1334 * @since 1.5.0 Deprecated. This is handled by `Charitable_Public_Form_View` now.
1335 *
1336 * @param string|false $custom_template
1337 * @return string|false|Charitable_Template
1338 */
1339 public function use_custom_templates( $custom_template ) {
1340 charitable_get_deprecated()->deprecated_function(
1341 __METHOD__,
1342 '1.5.0'
1343 );
1344
1345 return $custom_template;
1346 }
1347 }
1348
1349 endif;
1350