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.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 1.7.0.9 All 211 releases
← All changes | includes/abstracts/abstract-class-charitable-form.php +174 -44 1.6.8 → 1.8.13 View file →
@@ -1,17 +1,20 @@
1 1 <?php
2 2 /**
3 3 * A base class to be extended by specific form classes.
4 4 *
5 - * @package Charitable/Classes/Charitable_Form
6 - * @version 1.0.0
7 - * @author Eric Daams
8 - * @copyright Copyright (c) 2019, Studio 164a
9 - * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
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.8.12.4
10 11 */
11 12
12 13 // Exit if accessed directly.
13 -if ( ! defined( 'ABSPATH' ) ) { exit; }
14 +if ( ! defined( 'ABSPATH' ) ) {
15 + exit;
16 +}
14 17
15 18 if ( ! class_exists( 'Charitable_Form' ) ) :
16 19
17 20 /**
@@ -95,9 +98,10 @@
95 98 charitable_get_deprecated()->doing_it_wrong(
96 99 __METHOD__,
97 100 sprintf(
98 101 /* translators: %s: class name */
99 - __( 'None of the callbacks are required as of Charitable 1.5; use `Charitable_Public_Form_View` instead. Issue encountered in %s class.' ), get_class( $this )
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 )
100 104 ),
101 105 '1.5.0'
102 106 );
103 107
@@ -153,8 +157,9 @@
153 157 *
154 158 * @return array
155 159 */
156 160 public function get_fields() {
161 + return array();
157 162 }
158 163
159 164 /**
160 165 * Return the form action.
@@ -193,13 +198,17 @@
193 198 *
194 199 * @param array $fields The hidden fields as a key=>value array.
195 200 * @param Charitable_Form $form This instance of `Charitable_Form`.
196 201 */
197 - return apply_filters( 'charitable_form_hidden_fields', array(
198 - $this->nonce_name => wp_create_nonce( $this->nonce_action ),
199 - '_wp_http_referer' => wp_unslash( $_SERVER['REQUEST_URI'] ),
200 - 'charitable_action' => $this->form_action,
201 - ), $this );
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 + );
202 211 }
203 212
204 213 /**
205 214 * Output the nonce.
@@ -225,10 +234,10 @@
225 234
226 235 if ( ! $validated ) {
227 236 charitable_get_notices()->add_error( __( 'Unable to submit form. Please try again.', 'charitable' ) );
228 237
229 - if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
230 - error_log(
238 + if ( charitable_is_debug() ) {
239 + error_log( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
231 240 sprintf(
232 241 /* translators: %1$s: nonce name; %2$s: nonce action; %3$s: submitted nonce */
233 242 __( 'Nonce verification failed for nonce with name "%1$s" and action "%2$s". Submitted nonce is "%3$s".', 'charitable' ),
234 243 $this->nonce_name,
@@ -289,9 +298,10 @@
289 298
290 299 /**
291 300 * Check the passed fields to ensure that all required fields have been submitted.
292 301 *
293 - * @since 1.0.0
302 + * @since 1.0.0
303 + * @version 1.8.4.5 Added check for minimum password length.
294 304 *
295 305 * @param array $fields Array of form fields.
296 306 * @param array $submitted Submitted values.
297 307 * @return boolean
@@ -304,11 +314,10 @@
304 314 $ret = true;
305 315 $missing = array();
306 316
307 317 foreach ( $this->get_required_fields( $fields ) as $key => $field ) {
308 -
309 318 /* We already have a value for this field. */
310 - if ( ! empty( $field['value'] ) && 'checkbox' != $field['type'] ) {
319 + if ( ! empty( $field['value'] ) && 'checkbox' !== $field['type'] ) {
311 320 continue;
312 321 }
313 322
314 323 $exists = isset( $submitted[ $key ] );
@@ -319,11 +328,14 @@
319 328 $exists = ! empty( $value ) || ( is_string( $value ) && strlen( $value ) );
320 329 }
321 330
322 331 /* If a value was not provided, check if it's in the $_FILES array. */
332 + // phpcs:disable WordPress.Security.NonceVerification.Missing
323 333 if ( ! $exists ) {
334 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- $_FILES array, validated by isset and empty checks
324 335 $exists = ( 'picture' == $field['type'] && isset( $_FILES[ $key ] ) && ! empty( $_FILES[ $key ]['name'] ) );
325 336 }
337 + // phpcs:enable WordPress.Security.NonceVerification.Missing
326 338
327 339 $exists = apply_filters( 'charitable_required_field_exists', $exists, $key, $field, $submitted, $this );
328 340
329 341 if ( ! $exists ) {
@@ -331,10 +343,28 @@
331 343 $label = isset( $field['label'] ) ? $field['label'] : $key;
332 344
333 345 $missing[] = $label;
334 346 }
335 - }//end foreach
347 + } // end foreach
336 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 +
337 367 $missing = apply_filters( 'charitable_form_missing_fields', $missing, $this, $fields, $submitted );
338 368
339 369 if ( count( $missing ) ) {
340 370
@@ -340,9 +370,10 @@
340 370
341 371 $missing_fields = implode( '</li><li>', $missing );
342 372
343 373 charitable_get_notices()->add_error(
344 - sprintf( '<p>%s</p><ul class="error-list"><li>%s</li></ul>',
374 + sprintf(
375 + '<p>%s</p><ul class="error-list"><li>%s</li></ul>',
345 376 __( 'There were problems with your form submission. The following required fields were not filled out:', 'charitable' ),
346 377 $missing_fields
347 378 )
348 379 );
@@ -368,14 +399,19 @@
368 399 if ( ! isset( $field['type'] ) || 'paragraph' == $field['type'] ) {
369 400 return $ret;
370 401 }
371 402
372 - /* Get the data type. Default to meta if no type is set. */
373 - if ( isset( $field['data_type'] ) ) {
374 - $ret[ $field['data_type'] ][ $key ] = $field['type'];
375 - } else {
376 - $ret[ $key ] = $field['type'];
377 - }
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'];
378 414
379 415 return $ret;
380 416 }
381 417
@@ -386,13 +422,33 @@
386 422 * advantage of the filter on the values.
387 423 *
388 424 * @since 1.0.0
389 425 *
390 - * @return array
426 + * @param array $fields Merged array of all the fields in the form.
427 + * @return array
391 428 */
392 429 public function get_submitted_values() {
393 430 if ( ! isset( $this->submitted ) ) {
394 - $this->submitted = apply_filters( 'charitable_form_submitted_values', $_POST, $this );
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 );
395 451 }
396 452
397 453 return $this->submitted;
398 454 }
@@ -402,9 +458,9 @@
402 458 *
403 459 * @since 1.0.0
404 460 *
405 461 * @param string $key The key to search for.
406 - * @return mixed Submitted value if set. NULL if value was not set.
462 + * @return mixed Submitted value if set. NULL if value was not set.
407 463 */
408 464 public function get_submitted_value( $key ) {
409 465 $submitted = $this->get_submitted_values();
410 466 return isset( $submitted[ $key ] ) ? $submitted[ $key ] : null;
@@ -421,13 +477,12 @@
421 477 * @param array $overrides Override the wp_handle_upload() behavior. Optional.
422 478 * @return int|WP_Error ID of the attachment or a WP_Error object on failure.
423 479 */
424 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';
425 484
426 - require_once( ABSPATH . 'wp-admin/includes/image.php' );
427 - require_once( ABSPATH . 'wp-admin/includes/file.php' );
428 - require_once( ABSPATH . 'wp-admin/includes/media.php' );
429 -
430 485 $overrides = $this->get_file_overrides( $file_key, $overrides );
431 486
432 487 return media_handle_upload( $file_key, $post_id, $post_data, $overrides );
433 488 }
@@ -434,23 +489,31 @@
434 489
435 490 /**
436 491 * Upload a file.
437 492 *
493 + * @since 1.0.0
494 + * @version 1.8.9.1
495 + *
438 496 * @param string $file_key Reference to a single element of `$_FILES`. Call the
439 - * function once for each uploaded file.
497 + * function once for each uploaded file.
440 498 * @param array $overrides Optional. An associative array of names=>values to
441 - * override default variables. Default false.
442 - * @return array|WP_Error On success, returns an associative array of file attributes.
443 - * On failure, returns $overrides['upload_error_handler'](&$file, $message )
444 - * or array( 'error'=>$message ).
445 - * @since 1.0.0
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 ).
446 503 */
447 504 public function upload_file( $file_key, $overrides = array() ) {
505 + require_once ABSPATH . 'wp-admin/includes/file.php';
448 506
449 - require_once( ABSPATH . 'wp-admin/includes/file.php' );
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 + }
450 511
451 512 $overrides = $this->get_file_overrides( $file_key, $overrides );
513 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- $_FILES array, sanitized by wp_handle_upload
452 514 $file = wp_handle_upload( $_FILES[ $file_key ], $overrides );
515 + // phpcs:enable WordPress.Security.NonceVerification.Missing
453 516
454 517 if ( isset( $file['error'] ) ) {
455 518 return new WP_Error( 'upload_error', $file['error'] );
456 519 }
@@ -476,8 +539,9 @@
476 539 'png' => 'image/png',
477 540 'bmp' => 'image/bmp',
478 541 'tif|tiff' => 'image/tiff',
479 542 'ico' => 'image/x-icon',
543 + 'webp' => 'image/webp',
480 544 );
481 545
482 546 $defaults = array(
483 547 'test_form' => false,
@@ -489,8 +553,70 @@
489 553 return $overrides;
490 554 }
491 555
492 556 /**
557 + * Returns all fields as a merged array.
558 + *
559 + * @since 1.6.51
560 + *
561 + * @return array[]
562 + */
563 + public function get_merged_fields() {
564 + $fields = array();
565 +
566 + foreach ( $this->get_fields() as $key => $section ) {
567 + if ( isset( $section['fields'] ) ) {
568 + $fields = array_merge( $fields, $section['fields'] );
569 + } else {
570 + $fields[ $key ] = $section;
571 + }
572 + }
573 +
574 + return $fields;
575 + }
576 +
577 + /**
578 + * Return the form fields, organized by data type.
579 + *
580 + * @since 1.6.51
581 + *
582 + * @return array
583 + */
584 + protected function get_fields_by_data_type() {
585 + $fields = array();
586 +
587 + foreach ( $this->get_merged_fields() as $key => $field ) {
588 + $fields = $this->sort_field_by_data_type( $key, $field, $fields );
589 + }
590 +
591 + return $fields;
592 + }
593 +
594 + /**
595 + * Get an array of valid field options for fields in the form.
596 + *
597 + * @since 1.6.51
598 + *
599 + * @return array
600 + */
601 + protected function get_valid_field_options() {
602 + $options = array();
603 +
604 + foreach ( $this->get_merged_fields() as $key => $field ) {
605 + /* The field has a specific set of options. */
606 + if ( isset( $field['options'] ) ) {
607 + $options[ $key ] = array_keys( $field['options'] );
608 + /* Checkbox fields without a value set should be either 1 or nothing. */
609 + } elseif ( isset( $field['type'] ) && 'checkbox' === $field['type'] ) {
610 + $checked_value = isset( $field['value'] ) ? $field['value'] : 1;
611 + $options[ $key ] = array( $checked_value );
612 + }
613 + }
614 +
615 + return $options;
616 + }
617 +
618 + /**
493 619 * Return the template name used for this field.
494 620 *
495 621 * @deprecated 1.8.0
496 622 *
@@ -506,9 +632,9 @@
506 632 '1.5.0',
507 633 'Charitable_Public_Form_View::get_template_name()'
508 634 );
509 635
510 - return $form->view()->get_template_name( $field );
636 + return $this->view()->get_template_name( $field );
511 637 }
512 638
513 639 /**
514 640 * Checks whether a template is valid.
@@ -685,12 +811,16 @@
685 811 if ( ! $this->view()->rendered_hidden_fields ) {
686 812 $this->view()->render_hidden_fields();
687 813 }
688 814
689 - return $form->view()->render_field( $field, $key, array(
690 - 'index' => $index,
691 - 'namespace' => $namespace,
692 - ) );
815 + return $form->view()->render_field(
816 + $field,
817 + $key,
818 + array(
819 + 'index' => $index,
820 + 'namespace' => $namespace,
821 + )
822 + );
693 823 }
694 824 }
695 825
696 826 endif;