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 / 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.7.0.2, at includes/forms/views/class-charitable-public-form-view.php

668 lines 17.0 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) 2022, 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 ) {
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 $this->enqueue_script();
131 $this->render_notices();
132 $this->render_honeypot();
133 $this->render_hidden_fields();
134 $this->render_fields();
135 }
136
137 /**
138 * Enqueue the forms script.
139 *
140 * @since 1.6.44
141 *
142 * @return void
143 */
144 public function enqueue_script() {
145 if ( ! wp_script_is( 'charitable-forms', 'enqueued' ) ) {
146 wp_enqueue_script( 'charitable-forms' );
147 }
148 }
149
150 /**
151 * Render notices before the form.
152 *
153 * @since 1.5.0
154 *
155 * @return void
156 */
157 public function render_notices() {
158 charitable_template_from_session(
159 'form-fields/notices.php',
160 array(
161 'notices' => charitable_get_notices()->get_notices(),
162 ),
163 'notices'
164 );
165
166 $this->rendered_notices = true;
167
168 /* Clear all notices. */
169 charitable_get_notices()->clear();
170 }
171
172 /**
173 * Render the honeypot fields.
174 *
175 * @since 1.5.0
176 *
177 * @return void
178 */
179 public function render_honeypot() {
180 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() ) );
181
182 $this->rendered_honeypot = true;
183 }
184
185 /**
186 * Render a form's hidden fields.
187 *
188 * @since 1.5.0
189 *
190 * @return boolean True if any fields were rendered. False otherwise.
191 */
192 public function render_hidden_fields() {
193 $fields = $this->form->get_hidden_fields();
194
195 if ( ! is_array( $fields ) || empty( $fields ) ) {
196 return false;
197 }
198
199 array_walk( $fields, array( $this, 'render_hidden_field' ) );
200
201 $this->rendered_hidden_fields = true;
202 }
203
204 /**
205 * Render all of a form's fields.
206 *
207 * @since 1.5.0
208 *
209 * @param array $fields Optional. A set of fields to display. If not set,
210 * we will look for fields in the form's `get_fields()`
211 * method (if it has one).
212 * @return boolean True if any fields were rendered. False otherwsie.
213 */
214 public function render_fields( $fields = array() ) {
215 if ( empty( $fields ) ) {
216 $fields = $this->form->get_fields();
217
218 /* If still missing fields, return false and throw an error. */
219 if ( empty( $fields ) ) {
220 charitable_get_deprecated()->doing_it_wrong(
221 __METHOD__,
222 __( 'There are no fields to render.', 'charitable' ),
223 '1.5.0'
224 );
225 return false;
226 }
227 }
228
229 $i = 1;
230
231 foreach ( $fields as $key => $field ) {
232 $this->render_field(
233 $field,
234 $key,
235 array(
236 'index' => $i,
237 )
238 );
239
240 $i += $this->increment_index( $field, $key, $i );
241 }
242 }
243
244 /**
245 * Render a hidden field.
246 *
247 * @since 1.5.0
248 *
249 * @param string|array $value Field value.
250 * @param string $key Field key.
251 * @return void
252 */
253 public function render_hidden_field( $value, $key ) {
254 $attrs = '';
255
256 if ( is_array( $value ) ) {
257 if ( ! array_key_exists( 'value', $value ) ) {
258 return;
259 }
260
261 $args = $value;
262 $value = $args['value'];
263
264 unset( $args['value'] );
265
266 $attrs = charitable_get_arbitrary_attributes( array( 'attrs' => $args ) );
267 }
268
269 printf( '<input type="hidden" name="%s" value="%s" %s />', esc_attr( $key ), esc_attr( $value ), $attrs );
270 }
271
272 /**
273 * Render a specific form field.
274 *
275 * @since 1.5.0
276 *
277 * @param array $field Field definition.
278 * @param string $key Field key.
279 * @param array $args Mixed array of arguments.
280 * @return boolean False if the field was not rendered. True otherwise.
281 */
282 public function render_field( $field, $key, $args = array() ) {
283 if ( ! array_key_exists( 'type', $field ) ) {
284 return false;
285 }
286
287 /* Get our index and namespace, with defaults if they're not provided. */
288 $namespace = array_key_exists( 'namespace', $args ) ? $args['namespace'] : null;
289 $index = array_key_exists( 'index', $args ) ? $args['index'] : 0;
290
291 /* Set up some form attributes. */
292 $field['key'] = $this->get_field_name( $key, $namespace, $index );
293 $field['attrs'] = array_key_exists( 'attrs', $field ) ? $field['attrs'] : array();
294
295 /**
296 * Do something before the field is rendered.
297 *
298 * @since 1.0.0
299 * @since 1.5.4 Added $from_template parameter to avoid backwards compatibility issues.
300 *
301 * @param array $field Field definition.
302 * @param string $key The field's key.
303 * @param Charitable_Form $form The Charitable_Form object.
304 * @param int $index The current index.
305 * @param null $namespace Unused. Namespace for the form field's name attribute.
306 * @param false $from_template Whether the method was called from a template file.
307 */
308 do_action( 'charitable_form_field', $field, $key, $this->form, $index, null, false );
309
310 /* Get the template and make sure it's valid. */
311 $template = $this->get_field_template( $field, $index );
312
313 if ( ! $template ) {
314 return false;
315 }
316
317 $template->set_view_args( array(
318 'form' => $this->form,
319 'field' => $field,
320 'classes' => $this->get_field_classes( $field, $index ),
321 ) );
322
323 $template->render();
324
325 return true;
326 }
327
328 /**
329 * Return the key for a particular field.
330 *
331 * @since 1.5.0
332 *
333 * @param string $key Field key.
334 * @param string|null $namespace Namespace for the form field's name attribute.
335 * @param int $index The current index.
336 * @return string
337 */
338 protected function get_field_name( $key, $namespace = null, $index = 0 ) {
339 $name = $key;
340
341 if ( ! is_null( $namespace ) ) {
342 $name = $namespace . '[' . $name . ']';
343 }
344
345 /**
346 * Filter the name attribute to be used for the field.
347 *
348 * @since 1.0.0
349 *
350 * @param string $name The name attribute.
351 * @param string $key The field's key.
352 * @param string|null $namespace Namespace for the field's attribute.
353 * @param Charitable_Form $form The Charitable_Form object.
354 * @param int $index The current index.
355 */
356 return apply_filters( 'charitable_form_field_key', $name, $key, $namespace, $this->form, $index );
357 }
358
359 /**
360 * Return a field template.
361 *
362 * @since 1.5.0
363 *
364 * @param array $field Field definition.
365 * @param int $index The current index.
366 * @return Charitable_Template|false Returns a template if the template file exists. If it doesn't returns false.
367 */
368 public function get_field_template( $field, $index ) {
369 $template = $this->get_custom_template( $field['type'] );
370
371 /**
372 * Filter the template to be used for the form.
373 *
374 * Any callback hooking into this filter should return a `Charitable_Template`
375 * instance. Anything else will be ignored.
376 *
377 * @since 1.0.0
378 *
379 * @param false|Charitable_Template $template False by default.
380 * @param array $field Field definition.
381 * @param Charitable_Form $form The Charitable_Form object.
382 * @param int $index The current index.
383 */
384 $template = apply_filters( 'charitable_form_field_template', $template, $field, $this->form, $index );
385
386 /* Fall back to default Charitable_Template if no template returned or if template was not object of 'Charitable_Template' class. */
387 if ( ! $this->is_valid_template( $template ) ) {
388 $template = new Charitable_Template( $this->get_template_name( $field ), false );
389 }
390
391 if ( ! $template->template_file_exists() ) {
392 return false;
393 }
394
395 return $template;
396 }
397
398 /**
399 * Return a custom template for a particular field.
400 *
401 * @since 1.5.0
402 *
403 * @param string $field_type The type of field.
404 * @return false|Charitable_Template False if there is no matching custom template.
405 */
406 public function get_custom_template( $field_type ) {
407 if ( ! array_key_exists( $field_type, $this->custom_field_templates ) ) {
408 return false;
409 }
410
411 $class = $this->custom_field_templates[ $field_type ]['class'];
412 $path = $this->custom_field_templates[ $field_type ]['path'];
413
414 /* Final sanity check to make sure the template class exists. */
415 if ( ! class_exists( $class ) ) {
416 return false;
417 }
418
419 return new $class( $path, false );
420 }
421
422 /**
423 * Return the template name used for this field.
424 *
425 * @since 1.5.0
426 *
427 * @param array $field Field definition.
428 * @return string
429 */
430 public function get_template_name( $field ) {
431 if ( $this->use_default_field_template( $field['type'] ) ) {
432 $template_name = 'form-fields/default.php';
433 } else {
434 $template_name = 'form-fields/' . $field['type'] . '.php';
435 }
436
437 /**
438 * Filter the template name.
439 *
440 * @since 1.0.0
441 *
442 * @param string $template_name Default template name.
443 */
444 return apply_filters( 'charitable_form_field_template_name', $template_name );
445 }
446
447 /**
448 * Checks whether a template is valid.
449 *
450 * @since 1.5.0
451 *
452 * @param mixed $template Template we're checking.
453 * @return boolean
454 */
455 public function is_valid_template( $template ) {
456 return is_object( $template ) && is_a( $template, 'Charitable_Template' );
457 }
458
459 /**
460 * Whether the given field type can use the default field template.
461 *
462 * @since 1.5.0
463 *
464 * @param string $field_type Type of field.
465 * @return boolean
466 */
467 protected function use_default_field_template( $field_type ) {
468 /**
469 * Filter the list of field types that use the default template.
470 *
471 * @since 1.0.0
472 *
473 * @param string[] $types Field types.
474 */
475 $default_field_types = apply_filters( 'charitable_default_template_field_types', array(
476 'text',
477 'email',
478 'password',
479 'date',
480 ) );
481
482 return in_array( $field_type, $default_field_types );
483 }
484
485 /**
486 * Return classes that will be applied to the field.
487 *
488 * @since 1.5.0
489 *
490 * @param array $field Field definition.
491 * @param int $index Field index.
492 * @return string
493 */
494 public function get_field_classes( $field, $index = 0 ) {
495 if ( 'hidden' == $field['type'] ) {
496 return;
497 }
498
499 $classes = $this->get_field_type_classes( $field['type'] );
500
501 if ( array_key_exists( 'class', $field ) ) {
502 $classes[] = $field['class'];
503 }
504
505 if ( array_key_exists( 'required', $field ) && $field['required'] ) {
506 $classes[] = 'required-field';
507 }
508
509 if ( array_key_exists( 'fullwidth', $field ) && $field['fullwidth'] ) {
510 $classes[] = 'fullwidth';
511 } elseif ( $index > 0 ) {
512 $classes[] = $index % 2 ? 'odd' : 'even';
513 }
514
515 /**
516 * Filter the array of classes before it is returned as a string.
517 *
518 * @since 1.0.0
519 *
520 * @param array $classes List of classes.
521 * @param array $field Field definition.
522 * @param int $index The field index.
523 */
524 $classes = apply_filters( 'charitable_form_field_classes', $classes, $field, $index );
525
526 return implode( ' ', $classes );
527 }
528
529 /**
530 * Return array of classes based on the field type.
531 *
532 * @since 1.5.0
533 *
534 * @param string $type Type of field.
535 * @return string[]
536 */
537 public function get_field_type_classes( $type ) {
538 $classes = array();
539
540 switch ( $type ) {
541 case 'paragraph':
542 $classes[] = 'charitable-form-content';
543 break;
544
545 case 'fieldset':
546 case 'donation-amount-wrapper':
547 $classes[] = 'charitable-fieldset';
548 break;
549
550 default:
551 $classes[] = 'charitable-form-field';
552 $classes[] = 'charitable-form-field-' . $type;
553 }
554
555 return $classes;
556 }
557
558 /**
559 * Set how much the index should be incremented by.
560 *
561 * @since 1.5.0
562 *
563 * @param array $field The field definition.
564 * @param string $key The key of the current field. May be empty.
565 * @param int|null $index The current index. May be null.
566 * @return int
567 */
568 public function increment_index( $field, $key = '', $index = null) {
569 if ( ! $this->should_increment( $field ) ) {
570 return 0;
571 }
572
573 /**
574 * Set the default increment.
575 *
576 * @since 1.0.0
577 *
578 * @param int $increment The default increment amount. Defaults to 1.
579 * @param array $field The field definition.
580 * @param string $key The key of the current field.
581 * @param Charitable_Form $form The form we are displaying.
582 * @param int $index The current index.
583 */
584 return apply_filters( 'charitable_form_field_increment', 1, $field, $key, $this->form, $index );
585 }
586
587 /**
588 * Whether the index should be incremented.
589 *
590 * @since 1.5.0
591 *
592 * @param array $field Field definition.
593 * @return boolean
594 */
595 protected function should_increment( $field ) {
596 if ( in_array( $field['type'], array( 'hidden', 'paragraph', 'fieldset' ) ) ) {
597 return false;
598 }
599
600 if ( array_key_exists( 'fullwidth', $field ) && $field['fullwidth'] ) {
601 return false;
602 }
603
604 return true;
605 }
606
607 /**
608 * Set up custom field templates.
609 *
610 * @since 1.5.0
611 *
612 * @return array
613 */
614 protected function init_custom_field_templates() {
615 /**
616 * Filter the custom field templates.
617 *
618 * @since 1.5.0
619 */
620 $templates = apply_filters( 'charitable_public_form_view_custom_field_templates', array(
621 'donation-amount-wrapper' => array(
622 'class' => 'Charitable_Template',
623 'path' => 'donation-form/donation-amount-wrapper.php',
624 ),
625 'donation-amount' => array(
626 'class' => 'Charitable_Template',
627 'path' => 'donation-form/donation-amount.php',
628 ),
629 'donor-fields' => array(
630 'class' => 'Charitable_Template',
631 'path' => 'donation-form/donor-fields.php',
632 ),
633 'details-fields' => array(
634 'class' => 'Charitable_Template',
635 'path' => 'donation-form/details-fields.php',
636 ),
637 'meta-fields' => array(
638 'class' => 'Charitable_Template',
639 'path' => 'donation-form/meta-fields.php',
640 ),
641 'gateway-fields' => array(
642 'class' => 'Charitable_Template',
643 'path' => 'donation-form/gateway-fields.php',
644 ),
645 'cc-expiration' => array(
646 'class' => 'Charitable_Template',
647 'path' => 'donation-form/cc-expiration.php',
648 ),
649 ) );
650
651 return array_filter( $templates, array( $this, 'sanitize_custom_field_template' ) );
652 }
653
654 /**
655 * Filter custom field templates to make sure they all have a class and path.
656 *
657 * @since 1.5.0
658 *
659 * @param array $template The registered template.
660 * @return boolean
661 */
662 protected function sanitize_custom_field_template( $template ) {
663 return is_array( $template ) && array_key_exists( 'path', $template ) && array_key_exists( 'class', $template );
664 }
665 }
666
667 endif;
668