PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.13
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.13
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 / views / class-charitable-public-form-view.php

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

698 lines 18.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Public form view.
4 *
5 * This is responsible for rendering the output of forms.
6 *
7 * @package Charitable/Forms/Charitable_Public_Form_View
8 * @author David Bisset
9 * @copyright Copyright (c) 2023, WP Charitable LLC
10 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
11 * @since 1.5.0
12 * @version 1.6.44
13 */
14
15 // Exit if accessed directly.
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit;
18 }
19
20 if ( ! class_exists( 'Charitable_Public_Form_View' ) ) :
21
22 /**
23 * Charitable_Public_Form_View.
24 *
25 * @since 1.5.0
26 */
27 class Charitable_Public_Form_View implements Charitable_Form_View_Interface {
28
29 /**
30 * The Form we are responsible for rendering.
31 *
32 * @since 1.5.0
33 *
34 * @var Charitable_Form
35 */
36 protected $form;
37
38 /**
39 * Whether the notices have been rendered.
40 *
41 * @since 1.5.2
42 *
43 * @var boolean
44 */
45 protected $rendered_notices;
46
47 /**
48 * Whether the hidden fields have been rendered.
49 *
50 * @since 1.5.2
51 *
52 * @var boolean
53 */
54 protected $rendered_hidden_fields;
55
56 /**
57 * Whether the honeypot has been rendered.
58 *
59 * @since 1.5.2
60 *
61 * @var boolean
62 */
63 protected $rendered_honeypot;
64
65 /**
66 * List of custom field templates.
67 *
68 * @since 1.5.0
69 *
70 * @var array
71 */
72 protected $custom_field_templates;
73
74 /**
75 * Set up view instance.
76 *
77 * @since 1.5.0
78 *
79 * @param Charitable_Form $form Form object.
80 */
81 public function __construct( Charitable_Form $form ) {
82 $this->form = $form;
83 $this->rendered_honeypot = false;
84 $this->rendered_notices = false;
85 $this->rendered_hidden_fields = false;
86 $this->custom_field_templates = $this->init_custom_field_templates();
87 }
88
89 /**
90 * Return value of a protected class property.
91 *
92 * @since 1.5.2
93 *
94 * @param string $prop The class property.
95 * @return mixed
96 */
97 public function __get( $prop ) {
98 if ( ! property_exists( $this, $prop ) ) {
99 return null;
100 }
101
102 return $this->$prop;
103 }
104
105 /**
106 * Register a custom field template.
107 *
108 * @since 1.5.0
109 *
110 * @param string $field_type The type of field we are registering this template for.
111 * @param string $class The template class name.
112 * @param string $path The path to the template.
113 * @return void
114 */
115 public function register_custom_field_template( $field_type, $class, $path ) { // phpcs:ignore
116 $this->custom_field_templates[ $field_type ] = array(
117 'class' => $class,
118 'path' => $path,
119 );
120 }
121
122 /**
123 * Render a form.
124 *
125 * @since 1.5.0
126 *
127 * @return void
128 */
129 public function render() {
130
131 $form_fields = $this->form->get_fields();
132 $this->enqueue_script();
133 if ( ( isset( $form_fields['donation_fields'] ) && 'bottom' !== charitable_get_option( 'donation_form_notices_display', 'top' ) ) || ! isset( $form_fields['donation_fields'] ) ) {
134 $this->render_notices();
135 }
136 $this->render_honeypot();
137 $this->render_hidden_fields();
138 $this->render_fields();
139 if ( isset( $form_fields['donation_fields'] ) && 'bottom' === charitable_get_option( 'donation_form_notices_display', 'top' ) ) {
140 $this->render_notices();
141 }
142 }
143
144 /**
145 * Enqueue the forms script.
146 *
147 * @since 1.6.44
148 *
149 * @return void
150 */
151 public function enqueue_script() {
152 if ( ! wp_script_is( 'charitable-forms', 'enqueued' ) ) {
153 wp_enqueue_script( 'charitable-forms' );
154 }
155 }
156
157 /**
158 * Render notices before the form.
159 *
160 * @since 1.5.0
161 *
162 * @return void
163 */
164 public function render_notices() {
165 charitable_template_from_session(
166 'form-fields/notices.php',
167 array(
168 'notices' => charitable_get_notices()->get_notices(),
169 ),
170 'notices'
171 );
172
173 $this->rendered_notices = true;
174
175 /* Clear all notices. */
176 charitable_get_notices()->clear();
177 }
178
179 /**
180 * Render the honeypot fields.
181 *
182 * @since 1.5.0
183 *
184 * @return void
185 */
186 public function render_honeypot() {
187 printf( '<input type="hidden" name="charitable_form_id" value="%1$s" autocomplete="off" /><input type="text" name="%1$s" class="charitable-hidden" value="" autocomplete="off" />', esc_attr( $this->form->get_form_identifier() ) );
188
189 $this->rendered_honeypot = true;
190 }
191
192 /**
193 * Render a form's hidden fields.
194 *
195 * @since 1.5.0
196 *
197 * @return boolean True if any fields were rendered. False otherwise.
198 */
199 public function render_hidden_fields() {
200 $fields = $this->form->get_hidden_fields();
201
202 if ( ! is_array( $fields ) || empty( $fields ) ) {
203 return false;
204 }
205
206 array_walk( $fields, array( $this, 'render_hidden_field' ) );
207
208 $this->rendered_hidden_fields = true;
209 }
210
211 /**
212 * Render all of a form's fields.
213 *
214 * @since 1.8.3.4
215 * @since 1.8.3.5
216 *
217 * @param array $fields Optional. A set of fields to display. If not set,
218 * we will look for fields in the form's `get_fields()`
219 * method (if it has one).
220 * @return boolean True if any fields were rendered. False otherwsie.
221 */
222 public function render_fields( $fields = array() ) {
223 if ( empty( $fields ) ) {
224 $fields = $this->form->get_fields();
225
226 /* If still missing fields, return false and throw an error. */
227 if ( empty( $fields ) ) {
228 charitable_get_deprecated()->doing_it_wrong(
229 __METHOD__,
230 __( 'There are no fields to render.', 'charitable' ),
231 '1.5.0'
232 );
233 return false;
234 }
235 }
236
237 $i = 1;
238
239 $form_template = charitable_get_option( 'donation_form_template', false );
240
241 $opening_div = false;
242
243 foreach ( $fields as $key => $field ) {
244 // insert div group for certain fields.
245 if ( $i === 1 && ( 'minimal' === $form_template && ( 'title' === strtolower( $key ) || 'first_name' === strtolower( $key ) ) ) ) {
246 echo '<div class="charitable-form-group charitable-form-group-number-' . esc_attr( $i ) . '" charitable-form-group-' . esc_attr( $key ) . '">';
247 } elseif ( 'minimal' === $form_template && 'first_name' === strtolower( $key ) ) {
248 echo '<div>';
249 $opening_div = true;
250 }
251 $this->render_field(
252 $field,
253 $key,
254 array(
255 'index' => $i,
256 )
257 );
258 if ( 'minimal' === $form_template && 'last_name' === strtolower( $key ) && $opening_div ) {
259 echo '</div>';
260 }
261
262 $i += $this->increment_index( $field, $key, $i );
263 }
264 }
265
266 /**
267 * Render a hidden field.
268 *
269 * @since 1.5.0
270 *
271 * @param string|array $value Field value.
272 * @param string $key Field key.
273 * @return void
274 */
275 public function render_hidden_field( $value, $key ) {
276 $attrs = '';
277
278 if ( is_array( $value ) ) {
279 if ( ! array_key_exists( 'value', $value ) ) {
280 return;
281 }
282
283 $args = $value;
284 $value = $args['value'];
285
286 unset( $args['value'] );
287
288 $attrs = charitable_get_arbitrary_attributes( array( 'attrs' => $args ) );
289 }
290
291 printf( '<input type="hidden" name="%s" value="%s" %s />', esc_attr( $key ), esc_attr( $value ), $attrs ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
292 }
293
294 /**
295 * Render a specific form field.
296 *
297 * @since 1.5.0
298 *
299 * @param array $field Field definition.
300 * @param string $key Field key.
301 * @param array $args Mixed array of arguments.
302 * @return boolean False if the field was not rendered. True otherwise.
303 */
304 public function render_field( $field, $key, $args = array() ) {
305 if ( ! array_key_exists( 'type', $field ) ) {
306 return false;
307 }
308
309 /* Get our index and namespace, with defaults if they're not provided. */
310 $namespace = array_key_exists( 'namespace', $args ) ? $args['namespace'] : null;
311 $index = array_key_exists( 'index', $args ) ? $args['index'] : 0;
312
313 /* Set up some form attributes. */
314 $field['key'] = $this->get_field_name( $key, $namespace, $index );
315 $field['attrs'] = array_key_exists( 'attrs', $field ) ? $field['attrs'] : array();
316
317 /**
318 * Do something before the field is rendered.
319 *
320 * @since 1.0.0
321 * @since 1.5.4 Added $from_template parameter to avoid backwards compatibility issues.
322 *
323 * @param array $field Field definition.
324 * @param string $key The field's key.
325 * @param Charitable_Form $form The Charitable_Form object.
326 * @param int $index The current index.
327 * @param null $namespace Unused. Namespace for the form field's name attribute.
328 * @param false $from_template Whether the method was called from a template file.
329 */
330 do_action( 'charitable_form_field', $field, $key, $this->form, $index, null, false );
331
332 /* Get the template and make sure it's valid. */
333 $template = $this->get_field_template( $field, $index );
334
335 if ( ! $template ) {
336 return false;
337 }
338
339 $template->set_view_args(
340 array(
341 'form' => $this->form,
342 'field' => $field,
343 'classes' => $this->get_field_classes( $field, $index ),
344 )
345 );
346
347 $template->render();
348
349 return true;
350 }
351
352 /**
353 * Return the key for a particular field.
354 *
355 * @since 1.5.0
356 *
357 * @param string $key Field key.
358 * @param string|null $namespace Namespace for the form field's name attribute.
359 * @param int $index The current index.
360 * @return string
361 */
362 protected function get_field_name( $key, $namespace = null, $index = 0 ) { // phpcs:ignore
363 $name = $key;
364
365 if ( ! is_null( $namespace ) ) {
366 $name = $namespace . '[' . $name . ']';
367 }
368
369 /**
370 * Filter the name attribute to be used for the field.
371 *
372 * @since 1.0.0
373 *
374 * @param string $name The name attribute.
375 * @param string $key The field's key.
376 * @param string|null $namespace Namespace for the field's attribute.
377 * @param Charitable_Form $form The Charitable_Form object.
378 * @param int $index The current index.
379 */
380 return apply_filters( 'charitable_form_field_key', $name, $key, $namespace, $this->form, $index );
381 }
382
383 /**
384 * Return a field template.
385 *
386 * @since 1.5.0
387 *
388 * @param array $field Field definition.
389 * @param int $index The current index.
390 * @return Charitable_Template|false Returns a template if the template file exists. If it doesn't returns false.
391 */
392 public function get_field_template( $field, $index ) {
393 $template = $this->get_custom_template( $field['type'] );
394
395 /**
396 * Filter the template to be used for the form.
397 *
398 * Any callback hooking into this filter should return a `Charitable_Template`
399 * instance. Anything else will be ignored.
400 *
401 * @since 1.0.0
402 *
403 * @param false|Charitable_Template $template False by default.
404 * @param array $field Field definition.
405 * @param Charitable_Form $form The Charitable_Form object.
406 * @param int $index The current index.
407 */
408 $template = apply_filters( 'charitable_form_field_template', $template, $field, $this->form, $index );
409
410 /* Fall back to default Charitable_Template if no template returned or if template was not object of 'Charitable_Template' class. */
411 if ( ! $this->is_valid_template( $template ) ) {
412 $template = new Charitable_Template( $this->get_template_name( $field ), false );
413 }
414
415 if ( ! $template->template_file_exists() ) {
416 return false;
417 }
418
419 return $template;
420 }
421
422 /**
423 * Return a custom template for a particular field.
424 *
425 * @since 1.5.0
426 *
427 * @param string $field_type The type of field.
428 * @return false|Charitable_Template False if there is no matching custom template.
429 */
430 public function get_custom_template( $field_type ) {
431 if ( ! array_key_exists( $field_type, $this->custom_field_templates ) ) {
432 return false;
433 }
434
435 $class = $this->custom_field_templates[ $field_type ]['class'];
436 $path = $this->custom_field_templates[ $field_type ]['path'];
437
438 /* Final sanity check to make sure the template class exists. */
439 if ( ! class_exists( $class ) ) {
440 return false;
441 }
442
443 return new $class( $path, false );
444 }
445
446 /**
447 * Return the template name used for this field.
448 *
449 * @since 1.5.0
450 *
451 * @param array $field Field definition.
452 * @return string
453 */
454 public function get_template_name( $field ) {
455 if ( $this->use_default_field_template( $field['type'] ) ) {
456 $template_name = 'form-fields/default.php';
457 } else {
458 $template_name = 'form-fields/' . $field['type'] . '.php';
459 }
460
461 /**
462 * Filter the template name.
463 *
464 * @since 1.0.0
465 *
466 * @param string $template_name Default template name.
467 */
468 return apply_filters( 'charitable_form_field_template_name', $template_name );
469 }
470
471 /**
472 * Checks whether a template is valid.
473 *
474 * @since 1.5.0
475 *
476 * @param mixed $template Template we're checking.
477 * @return boolean
478 */
479 public function is_valid_template( $template ) {
480 return is_object( $template ) && is_a( $template, 'Charitable_Template' );
481 }
482
483 /**
484 * Whether the given field type can use the default field template.
485 *
486 * @since 1.5.0
487 *
488 * @param string $field_type Type of field.
489 * @return boolean
490 */
491 protected function use_default_field_template( $field_type ) {
492 /**
493 * Filter the list of field types that use the default template.
494 *
495 * @since 1.0.0
496 *
497 * @param string[] $types Field types.
498 */
499 $default_field_types = apply_filters(
500 'charitable_default_template_field_types',
501 array(
502 'text',
503 'email',
504 'password',
505 'date',
506 )
507 );
508
509 return in_array( $field_type, $default_field_types );
510 }
511
512 /**
513 * Return classes that will be applied to the field.
514 *
515 * @since 1.5.0
516 *
517 * @param array $field Field definition.
518 * @param int $index Field index.
519 * @return string
520 */
521 public function get_field_classes( $field, $index = 0 ) {
522 if ( 'hidden' == $field['type'] ) {
523 return;
524 }
525
526 $classes = $this->get_field_type_classes( $field['type'] );
527
528 if ( array_key_exists( 'class', $field ) ) {
529 $classes[] = $field['class'];
530 }
531
532 if ( array_key_exists( 'required', $field ) && $field['required'] ) {
533 $classes[] = 'required-field';
534 }
535
536 if ( array_key_exists( 'fullwidth', $field ) && $field['fullwidth'] ) {
537 $classes[] = 'fullwidth';
538 } elseif ( $index > 0 ) {
539 $classes[] = $index % 2 ? 'odd' : 'even';
540 }
541
542 /**
543 * Filter the array of classes before it is returned as a string.
544 *
545 * @since 1.0.0
546 *
547 * @param array $classes List of classes.
548 * @param array $field Field definition.
549 * @param int $index The field index.
550 */
551 $classes = apply_filters( 'charitable_form_field_classes', $classes, $field, $index );
552
553 return implode( ' ', $classes );
554 }
555
556 /**
557 * Return array of classes based on the field type.
558 *
559 * @since 1.5.0
560 *
561 * @param string $type Type of field.
562 * @return string[]
563 */
564 public function get_field_type_classes( $type ) {
565 $classes = array();
566
567 switch ( $type ) {
568 case 'paragraph':
569 $classes[] = 'charitable-form-content';
570 break;
571
572 case 'fieldset':
573 case 'donation-amount-wrapper':
574 $classes[] = 'charitable-fieldset';
575 break;
576
577 default:
578 $classes[] = 'charitable-form-field';
579 $classes[] = 'charitable-form-field-' . $type;
580 }
581
582 return $classes;
583 }
584
585 /**
586 * Set how much the index should be incremented by.
587 *
588 * @since 1.5.0
589 *
590 * @param array $field The field definition.
591 * @param string $key The key of the current field. May be empty.
592 * @param int|null $index The current index. May be null.
593 * @return int
594 */
595 public function increment_index( $field, $key = '', $index = null ) {
596 if ( ! $this->should_increment( $field ) ) {
597 return 0;
598 }
599
600 /**
601 * Set the default increment.
602 *
603 * @since 1.0.0
604 *
605 * @param int $increment The default increment amount. Defaults to 1.
606 * @param array $field The field definition.
607 * @param string $key The key of the current field.
608 * @param Charitable_Form $form The form we are displaying.
609 * @param int $index The current index.
610 */
611 return apply_filters( 'charitable_form_field_increment', 1, $field, $key, $this->form, $index );
612 }
613
614 /**
615 * Whether the index should be incremented.
616 *
617 * @since 1.5.0
618 *
619 * @param array $field Field definition.
620 * @return boolean
621 */
622 protected function should_increment( $field ) {
623 if ( in_array( $field['type'], array( 'hidden', 'paragraph', 'fieldset' ) ) ) {
624 return false;
625 }
626
627 if ( array_key_exists( 'fullwidth', $field ) && $field['fullwidth'] ) {
628 return false;
629 }
630
631 return true;
632 }
633
634 /**
635 * Set up custom field templates.
636 *
637 * @since 1.5.0
638 *
639 * @return array
640 */
641 protected function init_custom_field_templates() {
642 /**
643 * Filter the custom field templates.
644 *
645 * @since 1.5.0
646 */
647 $templates = apply_filters(
648 'charitable_public_form_view_custom_field_templates',
649 array(
650 'donation-amount-wrapper' => array(
651 'class' => 'Charitable_Template',
652 'path' => 'donation-form/donation-amount-wrapper.php',
653 ),
654 'donation-amount' => array(
655 'class' => 'Charitable_Template',
656 'path' => 'donation-form/donation-amount.php',
657 ),
658 'donor-fields' => array(
659 'class' => 'Charitable_Template',
660 'path' => 'donation-form/donor-fields.php',
661 ),
662 'details-fields' => array(
663 'class' => 'Charitable_Template',
664 'path' => 'donation-form/details-fields.php',
665 ),
666 'meta-fields' => array(
667 'class' => 'Charitable_Template',
668 'path' => 'donation-form/meta-fields.php',
669 ),
670 'gateway-fields' => array(
671 'class' => 'Charitable_Template',
672 'path' => 'donation-form/gateway-fields.php',
673 ),
674 'cc-expiration' => array(
675 'class' => 'Charitable_Template',
676 'path' => 'donation-form/cc-expiration.php',
677 ),
678 )
679 );
680
681 return array_filter( $templates, array( $this, 'sanitize_custom_field_template' ) );
682 }
683
684 /**
685 * Filter custom field templates to make sure they all have a class and path.
686 *
687 * @since 1.5.0
688 *
689 * @param array $template The registered template.
690 * @return boolean
691 */
692 protected function sanitize_custom_field_template( $template ) {
693 return is_array( $template ) && array_key_exists( 'path', $template ) && array_key_exists( 'class', $template );
694 }
695 }
696
697 endif;
698