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

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