PluginProbe
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets / 4.5.6
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets v4.5.6
4.5.6 4.5.5 4.5.4 4.2.1 4.2.2 4.2.3 4.5.0 4.5.2 4.5.3 4.2.0 4.1.18 4.1.17 4.1.16 4.1.15 4.1.14 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.0.9 4.1.0 4.1.1 4.1.2 All 148 releases
← All changes | admin/class-settings-api.php +79 -12 4.2.3 → 4.5.6 View file →
@@ -247,9 +247,16 @@
247 247 }
248 248
249 249 // creates our settings in the options table
250 250 foreach ($this->settings_sections as $section) {
251 - register_setting($section['id'], $section['id'], array($this, 'sanitize_options'));
251 + register_setting(
252 + $section['id'],
253 + $section['id'],
254 + array(
255 + 'type' => 'array',
256 + 'sanitize_callback' => array($this, 'sanitize_options'),
257 + )
258 + );
252 259 }
253 260 }
254 261
255 262 /**
@@ -793,13 +800,17 @@
793 800
794 801 foreach ($options as $option_slug => $option_value) {
795 802 $sanitize_callback = $this->get_sanitize_callback($option_slug);
796 803
797 - // If callback is set, call it
804 + // If a field-specific callback is set, use it.
798 805 if ($sanitize_callback) {
799 806 $options[$option_slug] = call_user_func($sanitize_callback, $option_value);
800 807 continue;
801 808 }
809 +
810 + // Otherwise never store the value raw — apply a safe default so no
811 + // submitted field escapes sanitization (wp.org register_setting rule).
812 + $options[$option_slug] = $this->sanitize_default($option_value);
802 813 }
803 814
804 815 return $options;
805 816 }
@@ -804,8 +815,22 @@
804 815 return $options;
805 816 }
806 817
807 818 /**
819 + * Default sanitizer for settings values without a field-specific callback.
820 + *
821 + * @param mixed $value Raw value.
822 + * @return mixed
823 + */
824 + private function sanitize_default($value) {
825 + if (is_array($value)) {
826 + return array_map(array($this, 'sanitize_default'), $value);
827 + }
828 +
829 + return is_scalar($value) ? sanitize_text_field((string) $value) : '';
830 + }
831 +
832 + /**
808 833 * Get sanitization callback for given option slug
809 834 *
810 835 * @param string $slug option slug
811 836 *
@@ -871,13 +896,30 @@
871 896 $icon = isset($tab['icon']) ? $tab['icon'] : 'dashicons dashicons-screenoptions';
872 897 $html .= sprintf('<li><a href="#%1$s" class="bdt-tab-item" id="bdt-%1$s" data-tab-index="%2$s"><i class="%4$s"></i>%3$s</a></li>', $tab['id'], $count++, $tab['title'], $icon);
873 898 }
874 899
900 + // Extension tabs registered by add-ons (e.g. Ultimate Post Kit Pro). The
901 + // core plugin only provides the extension point; it ships no tabs of its own.
902 + foreach ($this->get_extra_dashboard_tabs() as $tab) {
903 + if (empty($tab['id']) || empty($tab['title'])) {
904 + continue;
905 + }
906 + $icon = isset($tab['icon']) ? $tab['icon'] : 'dashicons dashicons-screenoptions';
907 + $html .= sprintf('<li><a href="#%1$s" class="bdt-tab-item" id="bdt-%1$s" data-tab-index="%2$s"><i class="%4$s"></i>%3$s</a></li>', $tab['id'], $count++, esc_html($tab['title']), esc_attr($icon));
908 + }
909 +
875 910 // License section
876 911 $license_wl_status = UltimatePostKit_Admin_Settings::license_wl_status();
877 912
878 913 if (!defined('BDTUPK_LO') || false == $license_wl_status) {
879 - $html .= sprintf('<li><a href="#%1$s" class="bdt-tab-item" id="bdt-%1$s" data-tab-index="%2$s"><i class="dashicons dashicons-admin-network"></i>%3$s</a></li>', 'ultimate_post_kit_license_settings', $count, esc_html__('License', 'ultimate-post-kit'));
914 + // On the free version this tab shows the "Get Pro" page, not a license form,
915 + // so label it accordingly.
916 + $is_pro_activated = function_exists('_is_upk_pro_activated') ? _is_upk_pro_activated() : false;
917 + $license_tab_title = (true === $is_pro_activated)
918 + ? esc_html__('License', 'ultimate-post-kit')
919 + : esc_html__('Get Pro', 'ultimate-post-kit');
920 +
921 + $html .= sprintf('<li><a href="#%1$s" class="bdt-tab-item" id="bdt-%1$s" data-tab-index="%2$s"><i class="dashicons dashicons-admin-network"></i>%3$s</a></li>', 'ultimate_post_kit_license_settings', $count, $license_tab_title);
880 922 }
881 923
882 924 $html .= '</ul>';
883 925 $html .= '</div>';
@@ -904,8 +946,22 @@
904 946 )
905 947 ));
906 948 }
907 949
950 + /**
951 + * Extra dashboard tabs contributed by add-on plugins.
952 + *
953 + * Neutral extension point: the core plugin renders whatever tabs an add-on
954 + * registers here and ships none of its own. Each item is an array
955 + * [ 'id' => string, 'title' => string, 'icon' => string, 'callback' =>
956 + * callable ] where the callback echoes the tab body.
957 + *
958 + * @return array
959 + */
960 + public function get_extra_dashboard_tabs() {
961 + return (array) apply_filters( 'ultimate_post_kit_dashboard_extra_tabs', array() );
962 + }
963 +
908 964 function ultimate_post_kit_settings_save() {
909 965
910 966 if (!check_ajax_referer('ultimate-post-kit-settings-save-nonce')) {
911 967 wp_send_json_error();
@@ -914,14 +970,30 @@
914 970 if (!current_user_can('manage_options')) {
915 971 return;
916 972 }
917 973
918 - $moudle_id = sanitize_text_field($_POST['id']);
974 + $moudle_id = isset($_POST['id']) ? sanitize_text_field(wp_unslash($_POST['id'])) : '';
919 975
920 976 unset($_POST['id']);
921 977
978 + // Only ever write options inside this plugin's own namespace. Without
979 + // this the option name was fully attacker-chosen, letting a request
980 + // overwrite arbitrary core options (default_role, siteurl, ...).
981 + if ('' === $moudle_id || 0 !== strpos($moudle_id, 'ultimate_post_kit')) {
982 + wp_send_json_error();
983 + }
984 +
922 985 if (isset($_POST[$moudle_id])) {
923 - update_option($moudle_id, $_POST[$moudle_id]);
986 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized below via sanitize_options()/sanitize_default().
987 + $raw_value = wp_unslash($_POST[$moudle_id]);
988 +
989 + // Route the value through the registered per-field sanitizers
990 + // instead of storing raw request data.
991 + $value = is_array($raw_value)
992 + ? $this->sanitize_options($raw_value)
993 + : sanitize_text_field($raw_value);
994 +
995 + update_option($moudle_id, $value);
924 996 }
925 997
926 998 wp_send_json_success();
927 999 }
@@ -935,13 +1007,8 @@
935 1007
936 1008 // Add manually created content sections that don't have settings forms
937 1009 $content_only_sections = [
938 1010 [
939 - 'id' => 'ultimate_post_kit_extra_options',
940 - 'title' => esc_html__('Extra Options', 'ultimate-post-kit'),
941 - 'icon' => 'dashicons dashicons-smiley',
942 - ],
943 - [
944 1011 'id' => 'ultimate_post_kit_analytics_system_req',
945 1012 'title' => esc_html__('System Status', 'ultimate-post-kit'),
946 1013 'icon' => 'dashicons dashicons-chart-bar',
947 1014 ],
@@ -1150,13 +1217,13 @@
1150 1217 }
1151 1218
1152 1219 wp_nonce_field('ultimate-post-kit-settings-save-nonce');
1153 1220
1154 - do_action('wsa_form_top_' . $form['id'], $form);
1221 + do_action('ultimate_post_kit_form_top_' . $form['id'], $form);
1155 1222
1156 1223 $this->do_settings_sections($form['id']);
1157 1224
1158 - do_action('wsa_form_bottom_' . $form['id'], $form);
1225 + do_action('ultimate_post_kit_form_bottom_' . $form['id'], $form);
1159 1226
1160 1227 ?>
1161 1228
1162 1229 </form>