PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.12.3
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.12.3
1.8.13.1 1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 All 212 releases
charitable / includes / abstracts / abstract-class-charitable-form.php

abstract-class-charitable-form.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.12.3, at includes/abstracts/abstract-class-charitable-form.php

826 lines 22.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * A base class to be extended by specific form classes.
4 *
5 * @package Charitable/Classes/Charitable_Form
6 * @author David Bisset
7 * @copyright Copyright (c) 2023, 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.54
11 */
12
13 // Exit if accessed directly.
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 if ( ! class_exists( 'Charitable_Form' ) ) :
19
20 /**
21 * Charitable_Form
22 *
23 * @since 1.0.0
24 */
25 abstract class Charitable_Form {
26
27 /**
28 * Temporary, unique ID of this form.
29 *
30 * @since 1.0.0
31 *
32 * @var string
33 */
34 protected $id;
35
36 /**
37 * Nonce action.
38 *
39 * @since 1.0.0
40 *
41 * @var string
42 */
43 protected $nonce_action = 'charitable_form';
44
45 /**
46 * Nonce name.
47 *
48 * @since 1.0.0
49 *
50 * @var string
51 */
52 protected $nonce_name = '_charitable_form_nonce';
53
54 /**
55 * Form action.
56 *
57 * @since 1.0.0
58 *
59 * @var string
60 */
61 protected $form_action;
62
63 /**
64 * Errors with the form submission.
65 *
66 * @since 1.0.0
67 *
68 * @var array
69 */
70 protected $errors = array();
71
72 /**
73 * Submitted values.
74 *
75 * @since 1.0.0
76 *
77 * @var array
78 */
79 protected $submitted;
80
81 /**
82 * Form View.
83 *
84 * @since 1.5.0
85 *
86 * @var Charitable_Form_View_Interface
87 */
88 protected $view;
89
90 /**
91 * Set up callbacks for actions and filters.
92 *
93 * @since 1.0.0
94 *
95 * @return void
96 */
97 protected function attach_hooks_and_filters() {
98 charitable_get_deprecated()->doing_it_wrong(
99 __METHOD__,
100 sprintf(
101 /* translators: %s: class name */
102 __( 'None of the callbacks are required as of Charitable 1.5; use `Charitable_Public_Form_View` instead. Issue encountered in %s class.', 'charitable' ),
103 get_class( $this )
104 ),
105 '1.5.0'
106 );
107
108 add_action( 'charitable_form_before_fields', array( $this, 'render_error_notices' ) );
109 add_action( 'charitable_form_before_fields', array( $this, 'add_hidden_fields' ) );
110 add_action( 'charitable_form_field', array( $this, 'render_field' ), 10, 6 );
111 add_filter( 'charitable_form_field_increment', array( $this, 'increment_index' ), 10, 2 );
112 }
113
114 /**
115 * Set the Form View object.
116 *
117 * @since 1.5.0
118 *
119 * @param Charitable_Form_View_Interface $form_view An object implementing the `Charitable_Form_View_Interface`.
120 * @return void
121 */
122 public function set_form_view( Charitable_Form_View_Interface $form_view ) {
123 $this->view = $form_view;
124 }
125
126 /**
127 * Return the Form_View object for this form.
128 *
129 * @since 1.5.0
130 *
131 * @return Charitable_Form_View_Interface
132 */
133 public function view() {
134 if ( ! isset( $this->view ) ) {
135 $this->view = new Charitable_Public_Form_View( $this );
136 }
137
138 return $this->view;
139 }
140
141 /**
142 * Compares the ID of the form passed by the action and the current form object to ensure they're the same.
143 *
144 * @since 1.0.0
145 *
146 * @param string $id Current form ID.
147 * @return boolean
148 */
149 public function is_current_form( $id ) {
150 return $id === $this->id;
151 }
152
153 /**
154 * Return the form fields.
155 *
156 * @since 1.5.0
157 *
158 * @return array
159 */
160 public function get_fields() {
161 return array();
162 }
163
164 /**
165 * Return the form action.
166 *
167 * @since 1.3.1
168 *
169 * @return string
170 */
171 public function get_form_action() {
172 return $this->form_action;
173 }
174
175 /**
176 * Return the form ID.
177 *
178 * @since 1.0.0
179 *
180 * @return string
181 */
182 public function get_form_identifier() {
183 return $this->id;
184 }
185
186 /**
187 * Retrieve hidden fields.
188 *
189 * @since 1.5.0
190 *
191 * @return array
192 */
193 public function get_hidden_fields() {
194 /**
195 * Filter hidden fields.
196 *
197 * @since 1.5.0
198 *
199 * @param array $fields The hidden fields as a key=>value array.
200 * @param Charitable_Form $form This instance of `Charitable_Form`.
201 */
202 return apply_filters(
203 'charitable_form_hidden_fields',
204 array(
205 $this->nonce_name => wp_create_nonce( $this->nonce_action ),
206 '_wp_http_referer' => wp_unslash( $_SERVER['REQUEST_URI'] ), // phpcs:ignore
207 'charitable_action' => $this->form_action,
208 ),
209 $this
210 );
211 }
212
213 /**
214 * Output the nonce.
215 *
216 * @since 1.0.0
217 *
218 * @return void
219 */
220 public function nonce_field() {
221 wp_nonce_field( $this->nonce_action, $this->nonce_name );
222 }
223
224 /**
225 * Validate nonce data passed by the submitted form.
226 *
227 * @since 1.0.0
228 *
229 * @return boolean
230 */
231 public function validate_nonce() {
232 $submitted = $this->get_submitted_values();
233 $validated = isset( $submitted[ $this->nonce_name ] ) && wp_verify_nonce( $submitted[ $this->nonce_name ], $this->nonce_action );
234
235 if ( ! $validated ) {
236 charitable_get_notices()->add_error( __( 'Unable to submit form. Please try again.', 'charitable' ) );
237
238 if ( charitable_is_debug() ) {
239 error_log( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
240 sprintf(
241 /* translators: %1$s: nonce name; %2$s: nonce action; %3$s: submitted nonce */
242 __( 'Nonce verification failed for nonce with name "%1$s" and action "%2$s". Submitted nonce is "%3$s".', 'charitable' ),
243 $this->nonce_name,
244 $this->nonce_action,
245 array_key_exists( $this->nonce_name, $submitted ) ? $submitted[ $this->nonce_name ] : ''
246 )
247 );
248 }
249 }
250
251 return $validated;
252 }
253
254 /**
255 * Make sure that the honeypot field is empty.
256 *
257 * @since 1.4.3
258 *
259 * @return boolean
260 */
261 public function validate_honeypot() {
262 $submitted = $this->get_submitted_values();
263
264 if ( ! isset( $submitted['charitable_form_id'] ) ) {
265 return true;
266 }
267
268 $form_id = $submitted['charitable_form_id'];
269
270 return array_key_exists( $form_id, $submitted ) && 0 === strlen( $submitted[ $form_id ] );
271 }
272
273 /**
274 * Callback method used to filter out non-required fields.
275 *
276 * @since 1.0.0
277 *
278 * @param array $field Field definition.
279 * @return array
280 */
281 public function filter_required_fields( $field ) {
282 return isset( $field['required'] ) && true == $field['required'];
283 }
284
285 /**
286 * Filters array returning just the required fields.
287 *
288 * @since 1.0.0
289 *
290 * @param array[] $fields Array of form fields.
291 * @return array[]
292 */
293 public function get_required_fields( $fields ) {
294 $required_fields = array_filter( $fields, array( $this, 'filter_required_fields' ) );
295
296 return $required_fields;
297 }
298
299 /**
300 * Check the passed fields to ensure that all required fields have been submitted.
301 *
302 * @since 1.0.0
303 * @version 1.8.4.5 Added check for minimum password length.
304 *
305 * @param array $fields Array of form fields.
306 * @param array $submitted Submitted values.
307 * @return boolean
308 */
309 public function check_required_fields( $fields, $submitted = array() ) {
310 if ( empty( $submitted ) ) {
311 $submitted = $this->get_submitted_values();
312 }
313
314 $ret = true;
315 $missing = array();
316
317 foreach ( $this->get_required_fields( $fields ) as $key => $field ) {
318 /* We already have a value for this field. */
319 if ( ! empty( $field['value'] ) && 'checkbox' !== $field['type'] ) {
320 continue;
321 }
322
323 $exists = isset( $submitted[ $key ] );
324
325 /* Verify that a value was provided. */
326 if ( $exists ) {
327 $value = $submitted[ $key ];
328 $exists = ! empty( $value ) || ( is_string( $value ) && strlen( $value ) );
329 }
330
331 /* If a value was not provided, check if it's in the $_FILES array. */
332 // phpcs:disable WordPress.Security.NonceVerification.Missing
333 if ( ! $exists ) {
334 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- $_FILES array, validated by isset and empty checks
335 $exists = ( 'picture' == $field['type'] && isset( $_FILES[ $key ] ) && ! empty( $_FILES[ $key ]['name'] ) );
336 }
337 // phpcs:enable WordPress.Security.NonceVerification.Missing
338
339 $exists = apply_filters( 'charitable_required_field_exists', $exists, $key, $field, $submitted, $this );
340
341 if ( ! $exists ) {
342
343 $label = isset( $field['label'] ) ? $field['label'] : $key;
344
345 $missing[] = $label;
346 }
347 } // end foreach
348
349 // If the user_pass field is required we need to confirm a minimum number of characters for security reasons.
350 if ( isset( $fields['user_pass'] ) && ! empty( $fields['user_pass']['required'] ) ) {
351 $min_length = apply_filters( 'charitable_minimum_password_length', 8 );
352
353 if ( isset( $submitted['user_pass'] ) && strlen( $submitted['user_pass'] ) < $min_length ) {
354 charitable_get_notices()->add_error(
355 sprintf(
356 '%s',
357 sprintf(
358 /* translators: %d is the minimum password length required. */
359 __( 'Your password must be at least %d characters long.', 'charitable' ),
360 $min_length
361 )
362 )
363 );
364 }
365 }
366
367 $missing = apply_filters( 'charitable_form_missing_fields', $missing, $this, $fields, $submitted );
368
369 if ( count( $missing ) ) {
370
371 $missing_fields = implode( '</li><li>', $missing );
372
373 charitable_get_notices()->add_error(
374 sprintf(
375 '<p>%s</p><ul class="error-list"><li>%s</li></ul>',
376 __( 'There were problems with your form submission. The following required fields were not filled out:', 'charitable' ),
377 $missing_fields
378 )
379 );
380
381 $ret = false;
382 }
383
384 return $ret;
385 }
386
387 /**
388 * Organize fields by data type, also filtering out unused parameters (we just need the key and the type).
389 *
390 * @since 1.0.0
391 *
392 * @param string $key Key of the field to sort.
393 * @param array $field Field definition.
394 * @param array $ret Return value that we're carrying.
395 * @return array[]
396 */
397 public function sort_field_by_data_type( $key, $field, $ret ) {
398 /* Filter out paragraphs and fields without a type. */
399 if ( ! isset( $field['type'] ) || 'paragraph' == $field['type'] ) {
400 return $ret;
401 }
402
403 /**
404 * Group the fields by data type.
405 *
406 * Where fields don't have a data_type specified, we group
407 * them as 'internal', since we need them to be grouped into a
408 * data type for the data processor to work as expected.
409 *
410 * @since 1.6.51 Added 'internal' data type as fallback.
411 */
412 $data_type = isset( $field['data_type'] ) ? $field['data_type'] : 'internal';
413 $ret[ $data_type ][ $key ] = $field['type'];
414
415 return $ret;
416 }
417
418 /**
419 * Returns the submitted values.
420 *
421 * Use this method instead of accessing the raw $_POST array to take
422 * advantage of the filter on the values.
423 *
424 * @since 1.0.0
425 *
426 * @param array $fields Merged array of all the fields in the form.
427 * @return array
428 */
429 public function get_submitted_values() {
430 if ( ! isset( $this->submitted ) ) {
431 $submitted = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing
432 $fields = $this->get_fields_by_data_type();
433 $options = $this->get_valid_field_options();
434
435 /* Process the submitted data. This handles field sanitization. */
436 $data_processor = new Charitable_Data_Processor( $submitted, $fields, $options );
437
438 foreach ( $data_processor->output() as $data_type_data ) {
439 $submitted = array_merge( $submitted, $data_type_data );
440 }
441
442 /**
443 * Filter the submitted values.
444 *
445 * @since 1.0.0
446 *
447 * @param array $data The submitted data.
448 * @param Charitable_Form $form The form class.
449 */
450 $this->submitted = apply_filters( 'charitable_form_submitted_values', $submitted, $this );
451 }
452
453 return $this->submitted;
454 }
455
456 /**
457 * Returns the submitted value for a particular field.
458 *
459 * @since 1.0.0
460 *
461 * @param string $key The key to search for.
462 * @return mixed Submitted value if set. NULL if value was not set.
463 */
464 public function get_submitted_value( $key ) {
465 $submitted = $this->get_submitted_values();
466 return isset( $submitted[ $key ] ) ? $submitted[ $key ] : null;
467 }
468
469 /**
470 * Uploads a file and attaches it to the given post.
471 *
472 * @since 1.0.0
473 *
474 * @param string $file_key Key of the file input.
475 * @param int $post_id Post ID.
476 * @param array $post_data Overwrite some of the attachment. Optional.
477 * @param array $overrides Override the wp_handle_upload() behavior. Optional.
478 * @return int|WP_Error ID of the attachment or a WP_Error object on failure.
479 */
480 public function upload_post_attachment( $file_key, $post_id, $post_data = array(), $overrides = array() ) {
481 require_once ABSPATH . 'wp-admin/includes/image.php';
482 require_once ABSPATH . 'wp-admin/includes/file.php';
483 require_once ABSPATH . 'wp-admin/includes/media.php';
484
485 $overrides = $this->get_file_overrides( $file_key, $overrides );
486
487 return media_handle_upload( $file_key, $post_id, $post_data, $overrides );
488 }
489
490 /**
491 * Upload a file.
492 *
493 * @since 1.0.0
494 * @version 1.8.9.1
495 *
496 * @param string $file_key Reference to a single element of `$_FILES`. Call the
497 * function once for each uploaded file.
498 * @param array $overrides Optional. An associative array of names=>values to
499 * override default variables. Default false.
500 * @return array|WP_Error On success, returns an associative array of file attributes.
501 * On failure, returns $overrides['upload_error_handler'](&$file, $message )
502 * or array( 'error'=>$message ).
503 */
504 public function upload_file( $file_key, $overrides = array() ) {
505 require_once ABSPATH . 'wp-admin/includes/file.php';
506
507 // phpcs:disable WordPress.Security.NonceVerification.Missing
508 if ( ! isset( $_FILES[ $file_key ] ) ) {
509 return new WP_Error( 'upload_error', __( 'No file was uploaded.', 'charitable' ) );
510 }
511
512 $overrides = $this->get_file_overrides( $file_key, $overrides );
513 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- $_FILES array, sanitized by wp_handle_upload
514 $file = wp_handle_upload( $_FILES[ $file_key ], $overrides );
515 // phpcs:enable WordPress.Security.NonceVerification.Missing
516
517 if ( isset( $file['error'] ) ) {
518 return new WP_Error( 'upload_error', $file['error'] );
519 }
520
521 return $file;
522 }
523
524 /**
525 * Return overrides array for use with upload_file() and upload_post_attachment() methods.
526 *
527 * @since 1.0.0
528 *
529 * @param string $file_key Reference to a single element of `$_FILES`. Call the
530 * function once for each uploaded file.
531 * @param array $overrides Optional. An associative array of names=>values to
532 * override default variables. Default false.
533 * @return array
534 */
535 protected function get_file_overrides( $file_key, $overrides = array() ) {
536 $allowed_mimes = array(
537 'jpg|jpeg|jpe' => 'image/jpeg',
538 'gif' => 'image/gif',
539 'png' => 'image/png',
540 'bmp' => 'image/bmp',
541 'tif|tiff' => 'image/tiff',
542 'ico' => 'image/x-icon',
543 );
544
545 $defaults = array(
546 'test_form' => false,
547 'mimes' => apply_filters( 'charitable_file_' . $file_key . '_allowed_mimes', $allowed_mimes ),
548 );
549
550 $overrides = wp_parse_args( $overrides, $defaults );
551
552 return $overrides;
553 }
554
555 /**
556 * Returns all fields as a merged array.
557 *
558 * @since 1.6.51
559 *
560 * @return array[]
561 */
562 public function get_merged_fields() {
563 $fields = array();
564
565 foreach ( $this->get_fields() as $key => $section ) {
566 if ( isset( $section['fields'] ) ) {
567 $fields = array_merge( $fields, $section['fields'] );
568 } else {
569 $fields[ $key ] = $section;
570 }
571 }
572
573 return $fields;
574 }
575
576 /**
577 * Return the form fields, organized by data type.
578 *
579 * @since 1.6.51
580 *
581 * @return array
582 */
583 protected function get_fields_by_data_type() {
584 $fields = array();
585
586 foreach ( $this->get_merged_fields() as $key => $field ) {
587 $fields = $this->sort_field_by_data_type( $key, $field, $fields );
588 }
589
590 return $fields;
591 }
592
593 /**
594 * Get an array of valid field options for fields in the form.
595 *
596 * @since 1.6.51
597 *
598 * @return array
599 */
600 protected function get_valid_field_options() {
601 $options = array();
602
603 foreach ( $this->get_merged_fields() as $key => $field ) {
604 /* The field has a specific set of options. */
605 if ( isset( $field['options'] ) ) {
606 $options[ $key ] = array_keys( $field['options'] );
607 /* Checkbox fields without a value set should be either 1 or nothing. */
608 } elseif ( isset( $field['type'] ) && 'checkbox' === $field['type'] ) {
609 $checked_value = isset( $field['value'] ) ? $field['value'] : 1;
610 $options[ $key ] = array( $checked_value );
611 }
612 }
613
614 return $options;
615 }
616
617 /**
618 * Return the template name used for this field.
619 *
620 * @deprecated 1.8.0
621 *
622 * @since 1.0.0
623 * @since 1.5.0 Deprecated.
624 *
625 * @param array $field Field definition.
626 * @return string
627 */
628 public function get_template_name( $field ) {
629 charitable_get_deprecated()->deprecated_function(
630 __METHOD__,
631 '1.5.0',
632 'Charitable_Public_Form_View::get_template_name()'
633 );
634
635 return $this->view()->get_template_name( $field );
636 }
637
638 /**
639 * Checks whether a template is valid.
640 *
641 * @deprecated 1.8.0
642 *
643 * @since 1.0.0
644 * @since 1.5.0 Deprecated. Implemented by Form View.
645 *
646 * @param mixed $template Template we're checking.
647 * @return boolean
648 */
649 protected function is_valid_template( $template ) {
650 charitable_get_deprecated()->deprecated_function(
651 __METHOD__,
652 '1.5.0',
653 'Charitable_Public_Form_View::is_valid_template()'
654 );
655
656 return $this->view()->is_valid_template( $template );
657 }
658
659 /**
660 * Whether the given field type can use the default field template.
661 *
662 * @deprecated 1.8.0
663 *
664 * @since 1.0.0
665 * @since 1.5.0 Deprecated. Implemented by Form View.
666 *
667 * @param string $field_type Type of field.
668 * @return boolean
669 */
670 protected function use_default_field_template( $field_type ) {
671 charitable_get_deprecated()->deprecated_function(
672 __METHOD__,
673 '1.5.0',
674 'Charitable_Public_Form_View::use_default_field_template()'
675 );
676
677 return $this->view()->use_default_field_template( $field_type );
678 }
679
680 /**
681 * Set how much the index should be incremented by.
682 *
683 * @deprecated 1.8.0
684 *
685 * @since 1.0.0
686 * @since 1.5.0 Deprecated. Implemented by Form View.
687 *
688 * @param int $increment The number the index should be incremented by.
689 * @param array $field The field definition.
690 * @return int
691 */
692 public function increment_index( $increment, $field ) {
693 charitable_get_deprecated()->deprecated_function(
694 __METHOD__,
695 '1.5.0',
696 'Charitable_Public_Form_View::increment_index()'
697 );
698
699 /**
700 * Remove form's hooked filter.
701 *
702 * Before 1.5, forms used the filter to set the increment level. For
703 * backwards-compatibility purposes, we still provide this method in the
704 * form class, but it calls the Form View. This method shoud
705 * default in the form abstract, but remove it when this function
706 * is called directly.
707 */
708 remove_filter( 'charitable_form_field_increment', array( $this, 'increment_index' ), 10, 2 );
709
710 return $this->view()->increment_index( $field );
711 }
712
713 /**
714 * Display error notices at the start of the form, if there are any.
715 *
716 * @deprecated 1.8.0
717 *
718 * @since 1.0.0
719 * @since 1.5.0 Deprecated. Notices are rendered by the Form View now.
720 *
721 * @param Charitable_Form $form Form object.
722 * @return boolean Whether the notices were rendered.
723 */
724 public function render_error_notices( $form ) {
725 charitable_get_deprecated()->deprecated_function(
726 __METHOD__,
727 '1.5.0',
728 'Charitable_Public_Form_View::render_notices()'
729 );
730
731 if ( ! $form->is_current_form( $this->id ) ) {
732 return false;
733 }
734
735 return $this->view()->render_notices();
736 }
737
738 /**
739 * Adds hidden fields to the start of the donation form.
740 *
741 * @deprecated 1.8.0
742 *
743 * @since 1.0.0
744 * @since 1.5.0 Deprecated. Hidden fields are rendered by the Form View now.
745 *
746 * @param Charitable_Form $form The form object.
747 * @return boolean Whether the output is added.
748 */
749 public function add_hidden_fields( $form ) {
750 charitable_get_deprecated()->deprecated_function(
751 __METHOD__,
752 '1.5.0',
753 'Charitable_Public_Form_View::render_hidden_fields()'
754 );
755
756 if ( ! $form->is_current_form( $this->id ) ) {
757 return false;
758 }
759
760 return $this->view()->render_hidden_fields();
761 }
762
763 /**
764 * Render a form field.
765 *
766 * @deprecated 1.8.0
767 *
768 * @since 1.0.0
769 * @since 1.5.0 Deprecated. Use `Charitable_Public_Form_View` instead.
770 * @since 1.5.4 Added $from_template parameter for backwards compatibility purposes.
771 * Anytime this hook is called from a template using the pre-Charitable 1.5
772 * approach of rendering fields (i.e. do_action( 'charitable_form_field' )),
773 * there will not be a sixth parameter, so this will default to true.
774 *
775 * @param array $field Field definition.
776 * @param string $key Field key.
777 * @param Charitable_Form $form The form object.
778 * @param int $index The current index.
779 * @param string $namespace Namespace for the form field's name attribute.
780 * @param boolean $from_template Whether the method was called from a template file.
781 * @return boolean False if the field was not rendered. True otherwise.
782 */
783 public function render_field( $field, $key, $form, $index = 0, $namespace = null, $from_template = true ) {
784 if ( ! $from_template ) {
785 return false;
786 }
787
788 charitable_get_deprecated()->deprecated_function(
789 __METHOD__,
790 '1.5.0',
791 'Charitable_Public_Form_View::render_field()'
792 );
793
794 if ( ! $form->is_current_form( $this->id ) ) {
795 return false;
796 }
797
798 /**
799 * This was not evoked by the form view so it's likely that notices,
800 * hidden fields and the honeypot also haven't been rendered.
801 */
802 if ( ! $this->view()->rendered_notices ) {
803 $this->view()->render_notices();
804 }
805
806 if ( ! $this->view()->rendered_honeypot ) {
807 $this->view()->render_honeypot();
808 }
809
810 if ( ! $this->view()->rendered_hidden_fields ) {
811 $this->view()->render_hidden_fields();
812 }
813
814 return $form->view()->render_field(
815 $field,
816 $key,
817 array(
818 'index' => $index,
819 'namespace' => $namespace,
820 )
821 );
822 }
823 }
824
825 endif;
826