PluginProbe
WebberZone Top 10 — Popular Posts / 4.5.1
WebberZone Top 10 — Popular Posts v4.5.1
4.5.1 4.5.0 4.4.3 4.4.2 4.4.1 4.4.0 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 trunk 1.0 1.0.1 1.1 1.2 1.3 1.4 1.4.1 1.5 1.5.1 1.5.2 1.5.3 1.6 1.6.1 All 117 releases
← All changes | includes/admin/settings/class-settings-api.php +64 -40 4.4.14.5.1 View file →
@@ -17,9 +17,9 @@
17 17
18 18 /**
19 19 * Settings API wrapper class
20 20 *
21 - * @version 2.9.0
21 + * @version 3.0.0
22 22 * @since 4.0.0
23 23 */
24 24 class Settings_API {
25 25
@@ -27,9 +27,9 @@
27 27 * Current version number
28 28 *
29 29 * @var string
30 30 */
31 - public const VERSION = '2.11.0';
31 + public const VERSION = '3.0.0';
32 32
33 33 /**
34 34 * Settings Key.
35 35 *
@@ -542,9 +542,9 @@
542 542 true
543 543 );
544 544 wp_localize_script(
545 545 "wz-{$this->prefix}-admin",
546 - 'WZSettingsAdmin',
546 + 'WebberSettingsAdmin',
547 547 array(
548 548 'prefix' => $this->prefix,
549 549 'settings_key' => $this->settings_key,
550 550 'strings' => array(
@@ -605,9 +605,9 @@
605 605
606 606 // Localize Tom Select settings.
607 607 wp_localize_script(
608 608 "wz-{$prefix}-tom-select-init",
609 - 'WZTomSelectSettings',
609 + 'WebberTomSelectSettings',
610 610 $args
611 611 );
612 612 wp_enqueue_script( "wz-{$prefix}-tom-select-init" );
613 613
@@ -657,9 +657,9 @@
657 657 $callback = method_exists( $this->settings_form, "callback_{$type}" ) ? array( $this->settings_form, "callback_{$type}" ) : array( $this->settings_form, 'callback_missing' );
658 658
659 659 // Tag header rows so the settings search can group fields under them.
660 660 if ( 'header' === $type ) {
661 - $args['class'] = trim( ( $args['class'] ?? '' ) . ' wz-settings-header-row' );
661 + $args['class'] = trim( ( $args['class'] ?? '' ) . ' wz-settings-header-row', " \t\n\r\0\x0B" );
662 662 }
663 663
664 664 add_settings_field(
665 665 "{$settings_key}[{$id}]", // ID of the settings field. We save it within the settings array.
@@ -676,10 +676,13 @@
676 676 register_setting(
677 677 $settings_key,
678 678 $settings_key,
679 679 array(
680 + 'type' => 'object',
681 + 'default' => $this->settings_defaults(),
680 682 'sanitize_callback' => array( $this, 'settings_sanitize' ),
681 - 'show_in_rest' => true,
683 + // The value is an open-ended map with no REST schema, and settings_sanitize() expects a form submission.
684 + 'show_in_rest' => false,
682 685 )
683 686 );
684 687 }
685 688
@@ -814,28 +817,23 @@
814 817 if ( $setting['id'] !== $key ) {
815 818 continue;
816 819 }
817 820
818 - // Return the callback name.
819 - $sanitize_callback = false;
820 -
821 821 if ( isset( $setting['sanitize_callback'] ) && is_callable( $setting['sanitize_callback'] ) ) {
822 - $sanitize_callback = $setting['sanitize_callback'];
823 - return $sanitize_callback;
822 + return $setting['sanitize_callback'];
824 823 }
825 824
826 - if ( is_callable( array( $settings_sanitize, 'sanitize_' . $setting['type'] . '_field' ) ) ) {
827 - // For repeater fields, create a closure to pass the field configuration.
828 - if ( 'repeater' === $setting['type'] ) {
829 - return function ( $value ) use ( $settings_sanitize, $setting ) {
830 - return $settings_sanitize->sanitize_repeater_field( $value, $setting );
831 - };
832 - }
833 - $sanitize_callback = array( $settings_sanitize, 'sanitize_' . $setting['type'] . '_field' );
834 - return $sanitize_callback;
825 + $method = 'sanitize_' . $setting['type'] . '_field';
826 +
827 + // Field types with no callback of their own must still not store raw input.
828 + if ( ! is_callable( array( $settings_sanitize, $method ) ) ) {
829 + $method = 'sanitize_missing';
835 830 }
836 831
837 - return $sanitize_callback;
832 + // Every callback receives the field configuration so choice fields can validate against their own options.
833 + return function ( $value ) use ( $settings_sanitize, $method, $setting ) {
834 + return $settings_sanitize->$method( $value, $setting );
835 + };
838 836 }
839 837 }
840 838
841 839 return false;
@@ -841,21 +839,40 @@
841 839 return false;
842 840 }
843 841
844 842 /**
843 + * Get the settings keys that are rendered locked (disabled or pro-gated).
844 + *
845 + * @return array Map of settings key => true for each locked setting.
846 + */
847 + public function get_locked_settings() {
848 + $locked = array();
849 +
850 + foreach ( $this->registered_settings as $settings ) {
851 + foreach ( $settings as $setting ) {
852 + if ( isset( $setting['id'] ) && ( ! empty( $setting['disabled'] ) || ! empty( $setting['pro'] ) ) ) {
853 + $locked[ $setting['id'] ] = true;
854 + }
855 + }
856 + }
857 +
858 + return $locked;
859 + }
860 +
861 + /**
845 862 * Sanitize the form data being submitted.
846 863 *
847 - * @param array $input Input unclean array.
864 + * @param mixed $input Unsanitized input. An array for form submissions, but REST and WP-CLI may pass anything.
848 865 * @return array Sanitized array
849 866 */
850 867 public function settings_sanitize( $input ) {
851 - // This should be set if a form is submitted, so let's save it in the $referrer variable.
852 - if ( empty( $_POST['_wp_http_referer'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
853 - return $input;
868 + // Set when a classic form is submitted; used only to pick the active tab below.
869 + $referrer = array();
870 +
871 + if ( ! empty( $_POST['_wp_http_referer'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
872 + parse_str( sanitize_text_field( wp_unslash( $_POST['_wp_http_referer'] ) ), $referrer ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
854 873 }
855 874
856 - parse_str( sanitize_text_field( wp_unslash( $_POST['_wp_http_referer'] ) ), $referrer ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
857 -
858 875 // Check if we need to set to defaults.
859 876 $reset = isset( $_POST['settings_reset'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
860 877
861 878 if ( $reset ) {
@@ -870,13 +887,14 @@
870 887 // Get the various settings we've registered.
871 888 $settings = get_option( $this->settings_key );
872 889 $settings = is_array( $settings ) ? $settings : array();
873 890 $settings_types = $this->get_registered_settings_types();
891 + $locked = $this->get_locked_settings();
874 892
875 893 // Get the tab. This is also our settings' section.
876 894 $tab = $referrer['tab'] ?? $this->default_tab;
877 895
878 - $input = $input ? $input : array();
896 + $input = is_array( $input ) ? $input : array();
879 897
880 898 /**
881 899 * Filter the settings for the tab. e.g. prefix_settings_general_sanitize.
882 900 *
@@ -883,10 +901,10 @@
883 901 * @param array $input Input unclean array
884 902 */
885 903 $input = apply_filters( $this->prefix . '_settings_' . $tab . '_sanitize', $input ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
886 904
887 - // Create an output array by merging the existing settings with the ones submitted.
888 - $output = array_merge( $settings, $input );
905 + // Start from what is stored. Submitted values are merged back in below, once sanitized.
906 + $output = $settings;
889 907
890 908 // Loop through each setting being saved and pass it through a sanitization filter.
891 909 foreach ( $settings_types as $key => $type ) {
892 910 /**
@@ -904,13 +922,9 @@
904 922 $sanitize_callback = $this->get_sanitize_callback( $key );
905 923
906 924 // If callback is set, call it.
907 925 if ( $sanitize_callback ) {
908 - if ( 'sensitive' === $type ) {
909 - $output[ $key ] = call_user_func( $sanitize_callback, $input[ $key ], $key );
910 - } else {
911 - $output[ $key ] = call_user_func( $sanitize_callback, $input[ $key ] );
912 - }
926 + $output[ $key ] = call_user_func( $sanitize_callback, $input[ $key ] );
913 927 continue;
914 928 }
915 929 }
916 930
@@ -915,15 +929,25 @@
915 929 }
916 930
917 931 // Delete any key that is not present when we submit the input array.
918 932 if ( ! isset( $input[ $key ] ) ) {
919 - unset( $output[ $key ] );
933 + // Disabled fields are never submitted, so a missing key must not delete them.
934 + if ( ! isset( $locked[ $key ] ) ) {
935 + unset( $output[ $key ] );
936 + }
920 937 }
938 + }
921 939
922 - // Delete any settings that are no longer part of our registered settings.
923 - if ( array_key_exists( $key, $output ) && ! array_key_exists( $key, $settings_types ) ) {
924 - unset( $output[ $key ] );
925 - }
940 + // Keys added by the tab filter are not registered settings, but must not be stored raw either.
941 + $settings_sanitize = new Settings_Sanitize(
942 + array(
943 + 'settings_key' => $this->settings_key,
944 + 'prefix' => $this->prefix,
945 + )
946 + );
947 +
948 + foreach ( array_diff_key( $input, $settings_types ) as $key => $value ) {
949 + $output[ sanitize_text_field( (string) $key ) ] = $settings_sanitize->sanitize_missing( $value );
926 950 }
927 951
928 952 add_settings_error( $this->prefix . '-notices', '', $this->translation_strings['success_message'], 'updated' );
929 953