Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/wpb-floating-menu-or-categories/admin/admin-notices.php
+++ b/wpb-floating-menu-or-categories/admin/admin-notices.php
@@ -0,0 +1,66 @@
+<?php
+
+if (! defined('ABSPATH')) exit; // Exit if accessed directly
+
+/**
+ * Display a dismissible admin notice for Menu Icons by ThemeIsle
+ */
+add_action('admin_notices', 'wpb_fmc_menu_icons_notice');
+function wpb_fmc_menu_icons_notice()
+{
+ if (! function_exists('is_plugin_active')) {
+ include_once(ABSPATH . 'wp-admin/includes/plugin.php');
+ }
+
+ // 1. Check if the user has already dismissed this notice
+ if (get_user_meta(get_current_user_id(), 'wpb_fmc_dismissed_menu_icons_notice', true)) {
+ return;
+ }
+
+ // 2. Check if the "Menu Icons" plugin is already active
+ if (is_plugin_active('menu-icons/menu-icons.php')) {
+ return;
+ }
+
+ // 3. Only show to users who can install plugins
+ if (! current_user_can('install_plugins')) {
+ return;
+ }
+
+ $install_url = wp_nonce_url(self_admin_url('update.php?action=install-plugin&plugin=menu-icons'), 'install-plugin_menu-icons');
+?>
+ <div class="notice notice-info is-dismissible wpb-fmc-recommendation-notice">
+ <p>
+ <strong><?php esc_html_e('Pro Tip:', 'wpb-floating-menu-or-categories'); ?></strong>
+ <?php printf(
+ /* translators: %1$s: Plugin name, %2$s: Menu Icons plugin name */
+ esc_html__('The %1$s plugin has %2$s support. If you need menu icons, you can install it to enhance your floating menus.', 'wpb-floating-menu-or-categories'),
+ '<strong>' . esc_html__('WPB Floating Menu or Categories', 'wpb-floating-menu-or-categories') . '</strong>',
+ '<strong>' . esc_html__('Menu Icons by ThemeIsle', 'wpb-floating-menu-or-categories') . '</strong>'
+ ); ?>
+ </p>
+ <p>
+ <a href="<?php echo esc_url($install_url); ?>" class="button button-primary"><?php esc_html_e('Install Menu Icons', 'wpb-floating-menu-or-categories'); ?></a>
+ </p>
+ <script type="text/javascript">
+ jQuery(document).on('click', '.wpb-fmc-recommendation-notice .notice-dismiss', function() {
+ jQuery.post(ajaxurl, {
+ action: 'wpb_fmc_dismiss_notice',
+ nonce: '<?php echo esc_js(wp_create_nonce('wpb_fmc_dismiss_nonce')); ?>'
+ });
+ });
+ </script>
+ </div>
+<?php
+}
+
+/**
+ * Handle the AJAX request to dismiss the notice
+ */
+add_action('wp_ajax_wpb_fmc_dismiss_notice', 'wpb_fmc_dismiss_notice_callback');
+function wpb_fmc_dismiss_notice_callback()
+{
+ check_ajax_referer('wpb_fmc_dismiss_nonce', 'nonce');
+ update_user_meta(get_current_user_id(), 'wpb_fmc_dismissed_menu_icons_notice', '1');
+ wp_die();
+}
--- a/wpb-floating-menu-or-categories/admin/admin-page.php
+++ b/wpb-floating-menu-or-categories/admin/admin-page.php
@@ -1,57 +1,67 @@
<?php
+if (! defined('ABSPATH')) exit; // Exit if accessed directly
+
/**
* WPB Floating Menu or Categories
*/
-wp_enqueue_style( 'wpb_fmc_admin_css' );
-$plugin_data = get_plugin_data( WPB_FMC_PLUGIN_DIR_FILE );
-$version = $plugin_data['Version'];
+wp_enqueue_style('wpb_fmc_admin_css');
+$plugin_data = get_plugin_data(WPB_FMC_PLUGIN_DIR_FILE);
+$version = $plugin_data['Version'];
?>
<div class="wrap wpb-about-wrap">
- <h2 class="nav-tab-wrapper">
- <a href="#wpb_fmc_welcome" class="nav-tab" id="wpb_fmc_welcome-tab"><?php esc_html_e( 'Welcome', WPB_FMC_Domain ) ?></a>
- <a href="#wpb_fmc_use" class="nav-tab" id="wpb_fmc_use-tab"><?php esc_html_e( 'How To Use', WPB_FMC_Domain ) ?></a>
- <a href="#wpb_fmc_shortcode" class="nav-tab" id="wpb_fmc_use-tab"><?php esc_html_e( 'ShortCodes', WPB_FMC_Domain ) ?></a>
- </h2>
- <div class="metabox-holder">
- <div id="wpb_fmc_welcome" class="group">
- <h1><?php esc_html_e( 'WPB Floating Menu or Categories - ' . esc_html( $version ), WPB_FMC_Domain );?></h1>
- <div class="wpb-about-text">
- <?php esc_html_e( 'This plugin helps you by showing floating menu or categories with icons in your site. It comes with a nice and clean design. All the customization options are available in the plugin settings.', WPB_FMC_Domain );?>
- </div>
- </div>
+ <h2 class="nav-tab-wrapper">
+ <a href="#wpb_fmc_welcome" class="nav-tab" id="wpb_fmc_welcome-tab"><?php esc_html_e('Welcome', 'wpb-floating-menu-or-categories') ?></a>
+ <a href="#wpb_fmc_use" class="nav-tab" id="wpb_fmc_use-tab"><?php esc_html_e('How To Use', 'wpb-floating-menu-or-categories') ?></a>
+ <a href="#wpb_fmc_shortcode" class="nav-tab" id="wpb_fmc_use-tab"><?php esc_html_e('ShortCodes', 'wpb-floating-menu-or-categories') ?></a>
+ </h2>
+ <div class="metabox-holder">
+ <div id="wpb_fmc_welcome" class="group">
+ <h1>
+ <?php
+ printf(
+ /* translators: %s: Plugin version number */
+ esc_html__('WPB Floating Menu or Categories - %s', 'wpb-floating-menu-or-categories'),
+ esc_html($version)
+ );
+ ?>
+ </h1>
+ <div class="wpb-about-text">
+ <?php esc_html_e('This plugin helps you by showing floating menu or categories with icons in your site. It comes with a nice and clean design. All the customization options are available in the plugin settings.', 'wpb-floating-menu-or-categories'); ?>
+ </div>
+ </div>
<div id="wpb_fmc_use" class="group">
- <h3><?php esc_html_e( 'How to use:', WPB_FMC_Domain );?></h3>
+ <h3><?php esc_html_e('How to use:', 'wpb-floating-menu-or-categories'); ?></h3>
<ol>
<li>Install it as a regular WordPress plugin</li>
- <li>After installing the plugin, go to this plugin <a href="<?php echo esc_url( get_admin_url() . 'admin.php?page=wpb-floating-menu-or-categories-settings' ); ?>">settings</a> and configure your floating menu or category.</li>
+ <li>After installing the plugin, go to this plugin <a href="<?php echo esc_url(get_admin_url() . 'admin.php?page=wpb-floating-menu-or-categories-settings'); ?>">settings</a> and configure your floating menu or category.</li>
<li>You can use this plugin’s ShortCodes to show the floating menu or categories on a specific page or post on your site.</li>
</ol>
</div>
<div id="wpb_fmc_shortcode" class="group">
- <h3><?php esc_html_e( 'ShortCodes:', WPB_FMC_Domain );?></h3>
+ <h3><?php esc_html_e('ShortCodes:', 'wpb-floating-menu-or-categories'); ?></h3>
<ol>
<li><b>Floating Menu </b><input type="text" value='[wpb-fmc-floating-menu]'></li>
<li><b>Floating Category </b><input type="text" value='[wpb-fmc-floating-category]'></li>
</ol>
- </div>
- </div>
+ </div>
+ </div>
</div>
<div class="clear"></div>
<div class="wpb_wpbean_socials">
- <h4><?php esc_html_e( 'For getting updates of our plugins, features update, WordPress new trend, New web technology etc. Follows Us.', WPB_FMC_Domain );?></h4>
- <a href="https://twitter.com/wpbean" title="Follow us on Twitter" class="wpb_twitter" target="_blank"><?php esc_html_e( 'Follow Us On Twitter', WPB_FMC_Domain );?></a>
- <a href="https://plus.google.com/u/0/+WpBean/posts" title="Follow us on Google+" class="wpb_googleplus" target="_blank"><?php esc_html_e( 'Follow Us On Google Plus', WPB_FMC_Domain );?></a>
- <a href="https://www.facebook.com/wpbean" title="Follow us on Facebook" class="wpb_facebook" target="_blank"><?php esc_html_e( 'Like Us On FaceBook', WPB_FMC_Domain );?></a>
- <a href="https://www.youtube.com/user/wpbean/videos" title="Follow us on Youtube" class="wpb_youtube" target="_blank"><?php esc_html_e( 'Subscribe Us on YouTube', WPB_FMC_Domain );?></a>
- <a href="https://wpbean.com/support/" title="Get Support" class="wpb_support" target="_blank"><?php esc_html_e( 'Get Support', WPB_FMC_Domain );?></a>
- <a href="http://docs.wpbean.com/docs/wpb-floating-menu-or-categories/installing/" title="Documentation" class="wpb_documentation" target="_blank"><?php esc_html_e( 'Online Documentation', WPB_FMC_Domain );?></a>
+ <h4><?php esc_html_e('For getting updates of our plugins, features update, WordPress new trend, New web technology etc. Follows Us.', 'wpb-floating-menu-or-categories'); ?></h4>
+ <a href="https://twitter.com/wpbean" title="Follow us on Twitter" class="wpb_twitter" target="_blank"><?php esc_html_e('Follow Us On Twitter', 'wpb-floating-menu-or-categories'); ?></a>
+ <a href="https://plus.google.com/u/0/+WpBean/posts" title="Follow us on Google+" class="wpb_googleplus" target="_blank"><?php esc_html_e('Follow Us On Google Plus', 'wpb-floating-menu-or-categories'); ?></a>
+ <a href="https://www.facebook.com/wpbean" title="Follow us on Facebook" class="wpb_facebook" target="_blank"><?php esc_html_e('Like Us On FaceBook', 'wpb-floating-menu-or-categories'); ?></a>
+ <a href="https://www.youtube.com/user/wpbean/videos" title="Follow us on Youtube" class="wpb_youtube" target="_blank"><?php esc_html_e('Subscribe Us on YouTube', 'wpb-floating-menu-or-categories'); ?></a>
+ <a href="https://wpbean.com/support/" title="Get Support" class="wpb_support" target="_blank"><?php esc_html_e('Get Support', 'wpb-floating-menu-or-categories'); ?></a>
+ <a href="http://docs.wpbean.com/docs/wpb-floating-menu-or-categories/installing/" title="Documentation" class="wpb_documentation" target="_blank"><?php esc_html_e('Online Documentation', 'wpb-floating-menu-or-categories'); ?></a>
</div>
<script>
@@ -60,36 +70,35 @@
// Switches option sections
$('.group').hide();
var activetab = '';
- if (typeof(localStorage) != 'undefined' ) {
+ if (typeof(localStorage) != 'undefined') {
activetab = localStorage.getItem("activetab");
}
- if (activetab != '' && $(activetab).length ) {
+ if (activetab != '' && $(activetab).length) {
$(activetab).fadeIn();
} else {
$('.group:first').fadeIn();
}
- $('.group .collapsed').each(function(){
+ $('.group .collapsed').each(function() {
$(this).find('input:checked').parent().parent().parent().nextAll().each(
- function(){
- if ($(this).hasClass('last')) {
- $(this).removeClass('hidden');
- return false;
- }
- $(this).filter('.hidden').removeClass('hidden');
- });
+ function() {
+ if ($(this).hasClass('last')) {
+ $(this).removeClass('hidden');
+ return false;
+ }
+ $(this).filter('.hidden').removeClass('hidden');
+ });
});
- if (activetab != '' && $(activetab + '-tab').length ) {
+ if (activetab != '' && $(activetab + '-tab').length) {
$(activetab + '-tab').addClass('nav-tab-active');
- }
- else {
+ } else {
$('.nav-tab-wrapper a:first').addClass('nav-tab-active');
}
$('.nav-tab-wrapper a').click(function(evt) {
$('.nav-tab-wrapper a').removeClass('nav-tab-active');
$(this).addClass('nav-tab-active').blur();
var clicked_group = $(this).attr('href');
- if (typeof(localStorage) != 'undefined' ) {
+ if (typeof(localStorage) != 'undefined') {
localStorage.setItem("activetab", $(this).attr('href'));
}
$('.group').hide();
@@ -97,8 +106,8 @@
evt.preventDefault();
});
- $(".wpb-about-wrap input[type='text']").on("click", function () {
- $(this).select();
- });
- });
-</script>
+ $(".wpb-about-wrap input[type='text']").on("click", function() {
+ $(this).select();
+ });
+ });
+</script>
No newline at end of file
--- a/wpb-floating-menu-or-categories/admin/category-icon.php
+++ b/wpb-floating-menu-or-categories/admin/category-icon.php
@@ -1,102 +1,136 @@
<?php
-if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
+if (! defined('ABSPATH')) exit; // Exit if accessed directly
/* Define class for Categories icon */
-class WPB_FMC_Category_Icons{
+class WPB_FMC_Category_Icons
+{
- /* call default construtor */
- function __construct(){
+ /**
+ * Constructor
+ */
+ public function __construct()
+ {
+ if (is_admin()) {
+ // 1. Get the taxonomy safely
+ $taxonomy = isset($_GET['taxonomy']) ? sanitize_key($_GET['taxonomy']) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+
+ if (! empty($taxonomy)) {
+
+ /* * 2. Silent the Nonce Warning
+ * We are only using this GET variable to register hooks for the UI.
+ * The actual 'Save' action (wpb_fmc_save_iconfield) already has
+ * a nonce check, so this is safe.
+ */
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
- $tax = sanitize_key(@$_REQUEST['taxonomy']);
-
- /* add browse and text field to upload image and add an fontawesome icon */
- add_action( $tax . "_add_form_fields", array($this,'wpb_fmc_add_new_iconfield'), 10, 2 );
- add_action( $tax . "_edit_form_fields", array($this,'wpb_fmc_edit_iconfield'), 10, 2 );
-
- /* save the image or font awesome icon*/
- add_action( "edited_" .$tax, array($this,'wpb_fmc_save_iconfield'), 10, 2 );
- add_action( "create_" . $tax, array($this,'wpb_fmc_save_iconfield'), 10, 2 );
-
- /* show cloumn and their respective icon and images */
- add_filter( 'manage_edit-' .$tax. '_columns', array($this,'wpb_fmc_category_column' )) ;
- add_filter( 'manage_' . $tax. '_custom_column', array($this,'wpb_fmc_category_column_data'),10,3);
+ add_action($taxonomy . "_add_form_fields", array($this, 'wpb_fmc_add_new_iconfield'), 10, 2);
+ add_action($taxonomy . "_edit_form_fields", array($this, 'wpb_fmc_edit_iconfield'), 10, 2);
+ add_action("edited_" . $taxonomy, array($this, 'wpb_fmc_save_iconfield'), 10, 2);
+ add_action("create_" . $taxonomy, array($this, 'wpb_fmc_save_iconfield'), 10, 2);
+
+ add_filter("manage_edit-{$taxonomy}_columns", array($this, 'wpb_fmc_category_column'));
+ add_filter("manage_{$taxonomy}_custom_column", array($this, 'wpb_fmc_category_column_data'), 10, 3);
+ }
+ }
}
/* show cloumn and theie respective icon and images */
- public function wpb_fmc_category_column($columns){
- $columns["wpb_fmc_icon"] = esc_html__("Icon", WPB_FMC_Domain);
+ public function wpb_fmc_category_column($columns)
+ {
+ $columns["wpb_fmc_icon"] = esc_html__("Icon", 'wpb-floating-menu-or-categories');
return $columns;
- }
+ }
/* show cloumn and their respective icon and images */
- public function wpb_fmc_category_column_data( $content, $column, $term_id ){
+ public function wpb_fmc_category_column_data($content, $column, $term_id)
+ {
- if ( $column === 'wpb_fmc_icon' ) {
- $wpb_fmc_term_meta = get_option( "taxonomy_$term_id" );
+ if ($column === 'wpb_fmc_icon') {
+ $wpb_fmc_term_meta = get_option("taxonomy_$term_id");
- if( is_array($wpb_fmc_term_meta) && array_key_exists('wpb_fmc_cat_icons', $wpb_fmc_term_meta) ){
- $content = '<i class="fa-2x '. $wpb_fmc_term_meta['wpb_fmc_cat_icons'] .'"></i>';
- }
- }
+ if (is_array($wpb_fmc_term_meta) && array_key_exists('wpb_fmc_cat_icons', $wpb_fmc_term_meta)) {
+ $content = '<i class="fa-2x ' . esc_attr($wpb_fmc_term_meta['wpb_fmc_cat_icons']) . '"></i>';
+ }
+ }
- return $content;
+ return $content;
}
/* show browse and font awesome icon option while add category */
- public function wpb_fmc_add_new_iconfield(){
- ?>
- <div class="form-field">
- <label for="wpb_fmc_term_meta[wpb_fmc_cat_icons]"><?php _e( 'Icon CSS Class', WPB_FMC_Domain ); ?></label>
+ public function wpb_fmc_add_new_iconfield()
+ {
+?>
+ <div class="form-field">
+ <label for="wpb_fmc_term_meta[wpb_fmc_cat_icons]"><?php esc_html_e('Icon CSS Class', 'wpb-floating-menu-or-categories'); ?></label>
<input type="text" name="wpb_fmc_term_meta[wpb_fmc_cat_icons]" id="wpb_fmc_term_meta[wpb_fmc_cat_icons]" value="">
- <p><?php esc_html_e('You can get it from ', WPB_FMC_Domain) ?><a target="_blank" href="//fontawesome.com/v4.7.0/icons/"><?php esc_html_e('here', WPB_FMC_Domain); ?></a><?php esc_html_e(' e.g.', WPB_FMC_Domain);?> <b>fa fa-car</b></p>
+ <p><?php esc_html_e('You can get it from ', 'wpb-floating-menu-or-categories') ?><a target="_blank" href="//fontawesome.com/v4.7.0/icons/"><?php esc_html_e('here', 'wpb-floating-menu-or-categories'); ?></a><?php esc_html_e(' e.g.', 'wpb-floating-menu-or-categories'); ?> <b>fa fa-car</b></p>
</div>
-
+
<?php
}
// Edit term page
- public function wpb_fmc_edit_iconfield($term) {
-
+ public function wpb_fmc_edit_iconfield($term)
+ {
// put the term ID into a variable
$t_id = $term->term_id;
-
// retrieve the existing value(s) for this meta field. This returns an array
- $wpb_fmc_term_meta = get_option( "taxonomy_$t_id" ); ?>
+ $wpb_fmc_term_meta = get_option("taxonomy_$t_id"); ?>
<tr class="form-field">
- <th scope="row" valign="top"><label for="wpb_fmc_term_meta[wpb_fmc_cat_icons]"><?php _e( 'Icon CSS Class', WPB_FMC_Domain ); ?></label></th>
+ <th scope="row" valign="top"><label for="wpb_fmc_term_meta[wpb_fmc_cat_icons]"><?php esc_html_e('Icon CSS Class', 'wpb-floating-menu-or-categories'); ?></label></th>
<td>
- <input type="text" name="wpb_fmc_term_meta[wpb_fmc_cat_icons]" id="wpb_fmc_term_meta[wpb_fmc_cat_icons]" value="<?php echo esc_attr( $wpb_fmc_term_meta['wpb_fmc_cat_icons'] ) ? esc_attr( $wpb_fmc_term_meta['wpb_fmc_cat_icons'] ) : ''; ?>">
- <p class="description"><?php esc_html_e('You can get more FontAwesome icons from ', WPB_FMC_Domain) ?><a target="_blank" href="//fontawesome.com/v4.7.0/icons/"><?php esc_html_e('here', WPB_FMC_Domain); ?></a><?php esc_html_e(' e.g.', WPB_FMC_Domain);?> <b>fa fa-car</b></p>
+ <input type="text" name="wpb_fmc_term_meta[wpb_fmc_cat_icons]" id="wpb_fmc_term_meta[wpb_fmc_cat_icons]" value="<?php echo esc_attr($wpb_fmc_term_meta['wpb_fmc_cat_icons']) ? esc_attr($wpb_fmc_term_meta['wpb_fmc_cat_icons']) : ''; ?>">
+ <p class="description"><?php esc_html_e('You can get more FontAwesome icons from ', 'wpb-floating-menu-or-categories') ?><a target="_blank" href="//fontawesome.com/v4.7.0/icons/"><?php esc_html_e('here', 'wpb-floating-menu-or-categories'); ?></a><?php esc_html_e(' e.g.', 'wpb-floating-menu-or-categories'); ?> <b>fa fa-car</b></p>
</td>
</tr>
- <?php
+<?php
}
- // Save term
+ // Save term meta (data from edit and add term page)
+ public function wpb_fmc_save_iconfield($term_id)
+ {
+ // 1. Get and sanitize the nonce
+ $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
+
+ // Verify the nonce
+ if (!wp_verify_nonce($nonce, 'update-tag_' . $term_id)) {
+ // If it's a new category, the action name is 'add-tag'
+ if (!wp_verify_nonce($nonce, 'add-tag')) {
+ return;
+ }
+ }
+
+ // 2. Permission check
+ if (!current_user_can('manage_categories')) {
+ return;
+ }
- public function wpb_fmc_save_iconfield( $term_id ) {
- if ( isset( $_POST['wpb_fmc_term_meta'] ) ) {
+ // 3. Process the meta data
+ if (isset($_POST['wpb_fmc_term_meta']) && is_array($_POST['wpb_fmc_term_meta'])) {
- $wpb_fmc_term_meta = get_option( "taxonomy_$term_id" );
- $cat_keys = array_keys( $_POST['wpb_fmc_term_meta'] );
+ // Unslash the whole array at once
+ $posted_meta = wp_unslash($_POST['wpb_fmc_term_meta']); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
- foreach ( $cat_keys as $key ) {
- if ( isset ( $_POST['wpb_fmc_term_meta'][$key] ) ) {
- $wpb_fmc_term_meta[$key] = $_POST['wpb_fmc_term_meta'][$key];
- }
+ $wpb_fmc_term_meta = get_option("taxonomy_$term_id");
+
+ if (!is_array($wpb_fmc_term_meta)) {
+ $wpb_fmc_term_meta = array();
+ }
+
+ foreach ($posted_meta as $key => $value) {
+ // Sanitize keys and values
+ $wpb_fmc_term_meta[sanitize_key($key)] = sanitize_text_field($value);
}
- // Save the option array.
- update_option( "taxonomy_$term_id", $wpb_fmc_term_meta );
+ update_option("taxonomy_$term_id", $wpb_fmc_term_meta);
}
}
-
} /* end class */
/* Intialize the class */
-$templ = new WPB_FMC_Category_Icons();
No newline at end of file
+$templ = new WPB_FMC_Category_Icons();
--- a/wpb-floating-menu-or-categories/admin/class.settings-api.php
+++ b/wpb-floating-menu-or-categories/admin/class.settings-api.php
@@ -1,656 +1,828 @@
<?php
+if (! defined('ABSPATH')) {
+ exit; // Exit if accessed directly
+}
+
/**
* weDevs Settings API wrapper class
*
- * @version 1.3 (27-Sep-2016)
+ * @version 1.4
*
* @author Tareq Hasan <tareq@weDevs.com>
* @link https://tareq.co Tareq Hasan
+ *
+ * Improved for WordPress.org plugin guidelines:
+ * - Replaced deprecated create_function() with anonymous function (PHP 5.3+)
+ * - Escaped all output with context-appropriate functions
+ * - Added nonce verification note (handled by settings_fields())
+ * - Sanitized all input via sanitize_options() and per-field callbacks
+ * - Removed inline phpcs:ignore comments where no longer needed
+ * - Used wp_json_encode() for JS data passing instead of raw PHP string injection
+ * - Added esc_js() / esc_attr() where values touch HTML/JS boundaries
*/
-if ( !class_exists( 'WPB_FMC_WeDevs_Settings_API' ) ):
-class WPB_FMC_WeDevs_Settings_API {
-
- /**
- * settings sections array
- *
- * @var array
- */
- protected $settings_sections = array();
-
- /**
- * Settings fields array
- *
- * @var array
- */
- protected $settings_fields = array();
-
- public function __construct() {
- add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
- }
-
- /**
- * Enqueue scripts and styles
- */
- function admin_enqueue_scripts() {
- wp_enqueue_style( 'wp-color-picker' );
-
- wp_enqueue_media();
- wp_enqueue_script( 'wp-color-picker' );
- wp_enqueue_script( 'jquery' );
- }
-
- /**
- * Set settings sections
- *
- * @param array $sections setting sections array
- */
- function set_sections( $sections ) {
- $this->settings_sections = $sections;
+if (! class_exists('WPB_FMC_WeDevs_Settings_API')) :
- return $this;
- }
-
- /**
- * Add a single section
- *
- * @param array $section
- */
- function add_section( $section ) {
- $this->settings_sections[] = $section;
+ class WPB_FMC_WeDevs_Settings_API
+ {
- return $this;
- }
+ /**
+ * Settings sections array.
+ *
+ * @var array
+ */
+ protected $settings_sections = array();
+
+ /**
+ * Settings fields array.
+ *
+ * @var array
+ */
+ protected $settings_fields = array();
+
+ public function __construct()
+ {
+ add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
+ }
+
+ /**
+ * Enqueue scripts and styles.
+ */
+ public function admin_enqueue_scripts()
+ {
+ wp_enqueue_style('wp-color-picker');
+ wp_enqueue_media();
+ wp_enqueue_script('wp-color-picker');
+ wp_enqueue_script('jquery');
+ }
+
+ /**
+ * Set settings sections.
+ *
+ * @param array $sections Setting sections array.
+ * @return $this
+ */
+ public function set_sections($sections)
+ {
+ $this->settings_sections = $sections;
+ return $this;
+ }
+
+ /**
+ * Add a single section.
+ *
+ * @param array $section
+ * @return $this
+ */
+ public function add_section($section)
+ {
+ $this->settings_sections[] = $section;
+ return $this;
+ }
+
+ /**
+ * Set settings fields.
+ *
+ * @param array $fields Settings fields array.
+ * @return $this
+ */
+ public function set_fields($fields)
+ {
+ $this->settings_fields = $fields;
+ return $this;
+ }
+
+ /**
+ * Add a single field to a section.
+ *
+ * @param string $section Section ID.
+ * @param array $field Field definition.
+ * @return $this
+ */
+ public function add_field($section, $field)
+ {
+ $defaults = array(
+ 'name' => '',
+ 'label' => '',
+ 'desc' => '',
+ 'type' => 'text',
+ );
+
+ $arg = wp_parse_args($field, $defaults);
+ $this->settings_fields[$section][] = $arg;
+
+ return $this;
+ }
+
+ /**
+ * Initialize and register settings sections and fields with WordPress.
+ *
+ * Call this at the `admin_init` hook.
+ */
+ public function admin_init()
+ {
- /**
- * Set settings fields
- *
- * @param array $fields settings fields array
- */
- function set_fields( $fields ) {
- $this->settings_fields = $fields;
-
- return $this;
- }
+ // Register settings sections.
+ foreach ($this->settings_sections as $section) {
- function add_field( $section, $field ) {
- $defaults = array(
- 'name' => '',
- 'label' => '',
- 'desc' => '',
- 'type' => 'text'
- );
-
- $arg = wp_parse_args( $field, $defaults );
- $this->settings_fields[$section][] = $arg;
+ if (false === get_option($section['id'])) {
+ add_option($section['id']);
+ }
- return $this;
- }
+ if (isset($section['desc']) && ! empty($section['desc'])) {
+ // FIXED: Replaced deprecated create_function() with an anonymous function.
+ // Captured $section['desc'] safely via use() — no raw string interpolation.
+ $desc = '<div class="inside">' . wp_kses_post($section['desc']) . '</div>';
+ $callback = function () use ($desc) {
+ echo $desc; // Already sanitized above with wp_kses_post().
+ };
+ } elseif (isset($section['callback'])) {
+ $callback = $section['callback'];
+ } else {
+ $callback = null;
+ }
- /**
- * Initialize and registers the settings sections and fileds to WordPress
- *
- * Usually this should be called at `admin_init` hook.
- *
- * This function gets the initiated settings sections and fields. Then
- * registers them to WordPress and ready for use.
- */
- function admin_init() {
- //register settings sections
- foreach ( $this->settings_sections as $section ) {
- if ( false == get_option( $section['id'] ) ) {
- add_option( $section['id'] );
- }
-
- if ( isset($section['desc']) && !empty($section['desc']) ) {
- $section['desc'] = '<div class="inside">' . $section['desc'] . '</div>';
- $callback = create_function('', 'echo "' . str_replace( '"', '"', $section['desc'] ) . '";');
- } else if ( isset( $section['callback'] ) ) {
- $callback = $section['callback'];
- } else {
- $callback = null;
+ add_settings_section(
+ $section['id'],
+ esc_html($section['title']),
+ $callback,
+ $section['id']
+ );
}
- add_settings_section( $section['id'], $section['title'], $callback, $section['id'] );
- }
+ // Register settings fields.
+ foreach ($this->settings_fields as $section => $field) {
+ foreach ($field as $option) {
+
+ $name = $option['name'];
+ $type = isset($option['type']) ? $option['type'] : 'text';
+ $label = isset($option['label']) ? $option['label'] : '';
+ $callback = isset($option['callback']) ? $option['callback'] : array($this, 'callback_' . $type);
+
+ $args = array(
+ 'id' => $name,
+ 'class' => isset($option['class']) ? sanitize_html_class($option['class']) : sanitize_html_class($name),
+ 'label_for' => "{$section}[{$name}]",
+ 'desc' => isset($option['desc']) ? $option['desc'] : '',
+ 'name' => $label,
+ 'section' => $section,
+ 'size' => isset($option['size']) ? $option['size'] : null,
+ 'options' => isset($option['options']) ? $option['options'] : '',
+ 'std' => isset($option['default']) ? $option['default'] : '',
+ 'sanitize_callback' => isset($option['sanitize_callback']) ? $option['sanitize_callback'] : '',
+ 'type' => $type,
+ 'placeholder' => isset($option['placeholder']) ? $option['placeholder'] : '',
+ 'min' => isset($option['min']) ? $option['min'] : '',
+ 'max' => isset($option['max']) ? $option['max'] : '',
+ 'step' => isset($option['step']) ? $option['step'] : '',
+ );
+
+ add_settings_field(
+ "{$section}[{$name}]",
+ esc_html($label),
+ $callback,
+ $section,
+ $section,
+ $args
+ );
+ }
+ }
- //register settings fields
- foreach ( $this->settings_fields as $section => $field ) {
- foreach ( $field as $option ) {
-
- $name = $option['name'];
- $type = isset( $option['type'] ) ? $option['type'] : 'text';
- $label = isset( $option['label'] ) ? $option['label'] : '';
- $callback = isset( $option['callback'] ) ? $option['callback'] : array( $this, 'callback_' . $type );
-
- $args = array(
- 'id' => $name,
- 'class' => isset( $option['class'] ) ? $option['class'] : $name,
- 'label_for' => "{$section}[{$name}]",
- 'desc' => isset( $option['desc'] ) ? $option['desc'] : '',
- 'name' => $label,
- 'section' => $section,
- 'size' => isset( $option['size'] ) ? $option['size'] : null,
- 'options' => isset( $option['options'] ) ? $option['options'] : '',
- 'std' => isset( $option['default'] ) ? $option['default'] : '',
- 'sanitize_callback' => isset( $option['sanitize_callback'] ) ? $option['sanitize_callback'] : '',
- 'type' => $type,
- 'placeholder' => isset( $option['placeholder'] ) ? $option['placeholder'] : '',
- 'min' => isset( $option['min'] ) ? $option['min'] : '',
- 'max' => isset( $option['max'] ) ? $option['max'] : '',
- 'step' => isset( $option['step'] ) ? $option['step'] : '',
+ // Register settings (creates entries in the options table).
+ foreach ($this->settings_sections as $section) {
+ register_setting(
+ $section['id'],
+ $section['id'],
+ array($this, 'sanitize_options')
);
-
- add_settings_field( "{$section}[{$name}]", $label, $callback, $section, $section, $args );
}
}
- // creates our settings in the options table
- foreach ( $this->settings_sections as $section ) {
- register_setting( $section['id'], $section['id'], array( $this, 'sanitize_options' ) );
- }
- }
+ /**
+ * Get field description markup.
+ *
+ * @param array $args Settings field args.
+ * @return string
+ */
+ public function get_field_description($args)
+ {
+ if (! empty($args['desc'])) {
+ // FIXED: Use wp_kses_post() so basic HTML (links, em, strong) is allowed
+ // but arbitrary/unsafe tags are stripped.
+ $desc = sprintf('<p class="description">%s</p>', wp_kses_post($args['desc']));
+ } else {
+ $desc = '';
+ }
- /**
- * Get field description for display
- *
- * @param array $args settings field args
- */
- public function get_field_description( $args ) {
- if ( ! empty( $args['desc'] ) ) {
- $desc = sprintf( '<p class="description">%s</p>', $args['desc'] );
- } else {
- $desc = '';
+ return $desc;
}
- return $desc;
- }
-
- /**
- * Displays a text field for a settings field
- *
- * @param array $args settings field args
- */
- function callback_text( $args ) {
-
- $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
- $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
- $type = isset( $args['type'] ) ? $args['type'] : 'text';
- $placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
-
- $html = sprintf( '<input type="%1$s" class="%2$s-text" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s/>', $type, $size, $args['section'], $args['id'], $value, $placeholder );
- $html .= $this->get_field_description( $args );
-
- echo $html;
- }
-
- /**
- * Displays a url field for a settings field
- *
- * @param array $args settings field args
- */
- function callback_url( $args ) {
- $this->callback_text( $args );
- }
-
- /**
- * Displays a number field for a settings field
- *
- * @param array $args settings field args
- */
- function callback_number( $args ) {
- $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
- $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
- $type = isset( $args['type'] ) ? $args['type'] : 'number';
- $placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
- $min = ( $args['min'] == '' ) ? '' : ' min="' . $args['min'] . '"';
- $max = ( $args['max'] == '' ) ? '' : ' max="' . $args['max'] . '"';
- $step = ( $args['step'] == '' ) ? '' : ' step="' . $args['step'] . '"';
-
- $html = sprintf( '<input type="%1$s" class="%2$s-number" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s%7$s%8$s%9$s/>', $type, $size, $args['section'], $args['id'], $value, $placeholder, $min, $max, $step );
- $html .= $this->get_field_description( $args );
-
- echo $html;
- }
-
- /**
- * Displays a checkbox for a settings field
- *
- * @param array $args settings field args
- */
- function callback_checkbox( $args ) {
-
- $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
-
- $html = '<fieldset>';
- $html .= sprintf( '<label for="wpuf-%1$s[%2$s]">', $args['section'], $args['id'] );
- $html .= sprintf( '<input type="hidden" name="%1$s[%2$s]" value="off" />', $args['section'], $args['id'] );
- $html .= sprintf( '<input type="checkbox" class="checkbox" id="wpuf-%1$s[%2$s]" name="%1$s[%2$s]" value="on" %3$s />', $args['section'], $args['id'], checked( $value, 'on', false ) );
- $html .= sprintf( '%1$s</label>', $args['desc'] );
- $html .= '</fieldset>';
+ /**
+ * Callback: text / url field.
+ *
+ * @param array $args Settings field args.
+ */
+ public function callback_text($args)
+ {
+ $value = esc_attr($this->get_option($args['id'], $args['section'], $args['std']));
+ $size = isset($args['size']) && ! is_null($args['size']) ? sanitize_html_class($args['size']) : 'regular';
+ $type = isset($args['type']) ? esc_attr($args['type']) : 'text';
+ $placeholder = ! empty($args['placeholder']) ? ' placeholder="' . esc_attr($args['placeholder']) . '"' : '';
+
+ $html = sprintf(
+ '<input type="%1$s" class="%2$s-text" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s/>',
+ $type,
+ $size,
+ esc_attr($args['section']),
+ esc_attr($args['id']),
+ $value,
+ $placeholder
+ );
+ $html .= $this->get_field_description($args);
+
+ echo $html; // $html is fully escaped above.
+ }
+
+ /**
+ * Callback: URL field (delegates to callback_text).
+ *
+ * @param array $args Settings field args.
+ */
+ public function callback_url($args)
+ {
+ $this->callback_text($args);
+ }
+
+ /**
+ * Callback: number field.
+ *
+ * @param array $args Settings field args.
+ */
+ public function callback_number($args)
+ {
+ $value = esc_attr($this->get_option($args['id'], $args['section'], $args['std']));
+ $size = isset($args['size']) && ! is_null($args['size']) ? sanitize_html_class($args['size']) : 'regular';
+ $type = 'number';
+ $placeholder = ! empty($args['placeholder']) ? ' placeholder="' . esc_attr($args['placeholder']) . '"' : '';
+ $min = ('' !== $args['min']) ? ' min="' . esc_attr($args['min']) . '"' : '';
+ $max = ('' !== $args['max']) ? ' max="' . esc_attr($args['max']) . '"' : '';
+ $step = ('' !== $args['step']) ? ' step="' . esc_attr($args['step']) . '"' : '';
+
+ $html = sprintf(
+ '<input type="%1$s" class="%2$s-number" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s%7$s%8$s%9$s/>',
+ $type,
+ $size,
+ esc_attr($args['section']),
+ esc_attr($args['id']),
+ $value,
+ $placeholder,
+ $min,
+ $max,
+ $step
+ );
+ $html .= $this->get_field_description($args);
+
+ echo $html; // $html is fully escaped above.
+ }
+
+ /**
+ * Callback: checkbox field.
+ *
+ * @param array $args Settings field args.
+ */
+ public function callback_checkbox($args)
+ {
+ $value = esc_attr($this->get_option($args['id'], $args['section'], $args['std']));
+
+ $html = '<fieldset>';
+ $html .= sprintf(
+ '<label for="wpuf-%1$s[%2$s]">',
+ esc_attr($args['section']),
+ esc_attr($args['id'])
+ );
+ $html .= sprintf(
+ '<input type="hidden" name="%1$s[%2$s]" value="off" />',
+ esc_attr($args['section']),
+ esc_attr($args['id'])
+ );
+ $html .= sprintf(
+ '<input type="checkbox" class="checkbox" id="wpuf-%1$s[%2$s]" name="%1$s[%2$s]" value="on" %3$s />',
+ esc_attr($args['section']),
+ esc_attr($args['id']),
+ checked($value, 'on', false)
+ );
+ // FIXED: desc is passed through wp_kses_post() to allow safe HTML but strip unsafe tags.
+ $html .= wp_kses_post($args['desc']) . '</label>';
+ $html .= '</fieldset>';
+
+ echo $html; // $html is fully escaped above.
+ }
+
+ /**
+ * Callback: multicheck field.
+ *
+ * @param array $args Settings field args.
+ */
+ public function callback_multicheck($args)
+ {
+ $value = $this->get_option($args['id'], $args['section'], $args['std']);
+ $html = '<fieldset>';
+ $html .= sprintf(
+ '<input type="hidden" name="%1$s[%2$s]" value="" />',
+ esc_attr($args['section']),
+ esc_attr($args['id'])
+ );
+
+ foreach ($args['options'] as $key => $label) {
+ $checked = isset($value[$key]) ? $value[$key] : '0';
+ $html .= sprintf(
+ '<label for="wpuf-%1$s[%2$s][%3$s]">',
+ esc_attr($args['section']),
+ esc_attr($args['id']),
+ esc_attr($key)
+ );
+ $html .= sprintf(
+ '<input type="checkbox" class="checkbox" id="wpuf-%1$s[%2$s][%3$s]" name="%1$s[%2$s][%3$s]" value="%3$s" %4$s />',
+ esc_attr($args['section']),
+ esc_attr($args['id']),
+ esc_attr($key),
+ checked($checked, $key, false)
+ );
+ $html .= esc_html($label) . '</label><br>';
+ }
- echo $html;
- }
+ $html .= $this->get_field_description($args);
+ $html .= '</fieldset>';
- /**
- * Displays a multicheckbox for a settings field
- *
- * @param array $args settings field args
- */
- function callback_multicheck( $args ) {
-
- $value = $this->get_option( $args['id'], $args['section'], $args['std'] );
- $html = '<fieldset>';
- $html .= sprintf( '<input type="hidden" name="%1$s[%2$s]" value="" />', $args['section'], $args['id'] );
- foreach ( $args['options'] as $key => $label ) {
- $checked = isset( $value[$key] ) ? $value[$key] : '0';
- $html .= sprintf( '<label for="wpuf-%1$s[%2$s][%3$s]">', $args['section'], $args['id'], $key );
- $html .= sprintf( '<input type="checkbox" class="checkbox" id="wpuf-%1$s[%2$s][%3$s]" name="%1$s[%2$s][%3$s]" value="%3$s" %4$s />', $args['section'], $args['id'], $key, checked( $checked, $key, false ) );
- $html .= sprintf( '%1$s</label><br>', $label );
+ echo $html; // $html is fully escaped above.
}
- $html .= $this->get_field_description( $args );
- $html .= '</fieldset>';
+ /**
+ * Callback: radio field.
+ *
+ * @param array $args Settings field args.
+ */
+ public function callback_radio($args)
+ {
+ $value = $this->get_option($args['id'], $args['section'], $args['std']);
+ $html = '<fieldset>';
+
+ foreach ($args['options'] as $key => $label) {
+ $html .= sprintf(
+ '<label for="wpuf-%1$s[%2$s][%3$s]">',
+ esc_attr($args['section']),
+ esc_attr($args['id']),
+ esc_attr($key)
+ );
+ $html .= sprintf(
+ '<input type="radio" class="radio" id="wpuf-%1$s[%2$s][%3$s]" name="%1$s[%2$s]" value="%3$s" %4$s />',
+ esc_attr($args['section']),
+ esc_attr($args['id']),
+ esc_attr($key),
+ checked($value, $key, false)
+ );
+ $html .= esc_html($label) . '</label><br>';
+ }
- echo $html;
- }
+ $html .= $this->get_field_description($args);
+ $html .= '</fieldset>';
- /**
- * Displays a radio button for a settings field
- *
- * @param array $args settings field args
- */
- function callback_radio( $args ) {
-
- $value = $this->get_option( $args['id'], $args['section'], $args['std'] );
- $html = '<fieldset>';
-
- foreach ( $args['options'] as $key => $label ) {
- $html .= sprintf( '<label for="wpuf-%1$s[%2$s][%3$s]">', $args['section'], $args['id'], $key );
- $html .= sprintf( '<input type="radio" class="radio" id="wpuf-%1$s[%2$s][%3$s]" name="%1$s[%2$s]" value="%3$s" %4$s />', $args['section'], $args['id'], $key, checked( $value, $key, false ) );
- $html .= sprintf( '%1$s</label><br>', $label );
+ echo $html; // $html is fully escaped above.
}
- $html .= $this->get_field_description( $args );
- $html .= '</fieldset>';
-
- echo $html;
- }
-
- /**
- * Displays a selectbox for a settings field
- *
- * @param array $args settings field args
- */
- function callback_select( $args ) {
+ /**
+ * Callback: select field.
+ *
+ * @param array $args Settings field args.
+ */
+ public function callback_select($args)
+ {
+ $value = esc_attr($this->get_option($args['id'], $args['section'], $args['std']));
+ $size = isset($args['size']) && ! is_null($args['size']) ? sanitize_html_class($args['size']) : 'regular';
+
+ $html = sprintf(
+ '<select class="%1$s" name="%2$s[%3$s]" id="%2$s[%3$s]">',
+ $size,
+ esc_attr($args['section']),
+ esc_attr($args['id'])
+ );
+
+ foreach ($args['options'] as $key => $label) {
+ $html .= sprintf(
+ '<option value="%1$s"%2$s>%3$s</option>',
+ esc_attr($key),
+ selected($value, $key, false),
+ esc_html($label)
+ );
+ }
- $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
- $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
- $html = sprintf( '<select class="%1$s" name="%2$s[%3$s]" id="%2$s[%3$s]">', $size, $args['section'], $args['id'] );
+ $html .= '</select>';
+ $html .= $this->get_field_description($args);
- foreach ( $args['options'] as $key => $label ) {
- $html .= sprintf( '<option value="%s"%s>%s</option>', $key, selected( $value, $key, false ), $label );
+ echo $html; // $html is fully escaped above.
}
- $html .= sprintf( '</select>' );
- $html .= $this->get_field_description( $args );
-
- echo $html;
- }
-
- /**
- * Displays a textarea for a settings field
- *
- * @param array $args settings field args
- */
- function callback_textarea( $args ) {
-
- $value = esc_textarea( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
- $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
- $placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="'.$args['placeholder'].'"';
-
- $html = sprintf( '<textarea rows="5" cols="55" class="%1$s-text" id="%2$s[%3$s]" name="%2$s[%3$s]"%4$s>%5$s</textarea>', $size, $args['section'], $args['id'], $placeholder, $value );
- $html .= $this->get_field_description( $args );
-
- echo $html;
- }
-
- /**
- * Displays the html for a settings field
- *
- * @param array $args settings field args
- * @return string
- */
- function callback_html( $args ) {
- echo $this->get_field_description( $args );
- }
+ /**
+ * Callback: textarea field.
+ *
+ * @param array $args Settings field args.
+ */
+ public function callback_textarea($args)
+ {
+ $value = esc_textarea($this->get_option($args['id'], $args['section'], $args['std']));
+ $size = isset($args['size']) && ! is_null($args['size']) ? sanitize_html_class($args['size']) : 'regular';
+ $placeholder = ! empty($args['placeholder']) ? ' placeholder="' . esc_attr($args['placeholder']) . '"' : '';
+
+ $html = sprintf(
+ '<textarea rows="5" cols="55" class="%1$s-text" id="%2$s[%3$s]" name="%2$s[%3$s]"%4$s>%5$s</textarea>',
+ $size,
+ esc_attr($args['section']),
+ esc_attr($args['id']),
+ $placeholder,
+ $value
+ );
+ $html .= $this->get_field_description($args);
+
+ echo $html; // $html is fully escaped above.
+ }
+
+ /**
+ * Callback: HTML (description-only) field.
+ *
+ * @param array $args Settings field args.
+ */
+ public function callback_html($args)
+ {
+ echo $this->get_field_description($args); // Escaped inside get_field_description().
+ }
+
+ /**
+ * Callback: WP Editor (wysiwyg) field.
+ *
+ * @param array $args Settings field args.
+ */
+ public function callback_wysiwyg($args)
+ {
+ $value = $this->get_option($args['id'], $args['section'], $args['std']);
+ $size = isset($args['size']) && ! is_null($args['size']) ? $args['size'] : '500px';
+
+ // FIXED: esc_attr() on the inline style value.
+ printf('<div style="max-width: %s;">', esc_attr($size));
+
+ $editor_settings = array(
+ 'teeny' => true,
+ 'textarea_name' => $args['section'] . '[' . $args['id'] . ']',
+ 'textarea_rows' => 10,
+ );
- /**
- * Displays a rich text textarea for a settings field
- *
- * @param array $args settings field args
- */
- function callback_wysiwyg( $args ) {
-
- $value = $this->get_option( $args['id'], $args['section'], $args['std'] );
- $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : '500px';
-
- echo '<div style="max-width: ' . $size . ';">';
+ if (isset($args['options']) && is_array($args['options'])) {
+ $editor_settings = array_merge($editor_settings, $args['options']);
+ }
- $editor_settings = array(
- 'teeny' => true,
- 'textarea_name' => $args['section'] . '[' . $args['id'] . ']',
- 'textarea_rows' => 10
- );
+ // wp_editor() handles its own escaping internally.
+ wp_editor($value, $args['section'] . '-' . $args['id'], $editor_settings);
- if ( isset( $args['options'] ) && is_array( $args['options'] ) ) {
- $editor_settings = array_merge( $editor_settings, $args['options'] );
+ echo '</div>';
+ echo $this->get_field_description($args); // Escaped inside get_field_description().
}
- wp_editor( $value, $args['section'] . '-' . $args['id'], $editor_settings );
-
- echo '</div>';
-
- echo $this->get_field_description( $args );
- }
-
- /**
- * Displays a file upload field for a settings field
- *
- * @param array $args settings field args
- */
- function callback_file( $args ) {
-
- $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
- $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
- $id = $args['section'] . '[' . $args['id'] . ']';
- $label = isset( $args['options']['button_label'] ) ? $args['options']['button_label'] : __( 'Choose File' );
-
- $html = sprintf( '<input type="text" class="%1$s-text wpsa-url" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s"/>', $size, $args['section'], $args['id'], $value );
- $html .= '<input type="button" class="button wpsa-browse" value="' . $label . '" />';
- $html .= $this->get_field_description( $args );
-
- echo $html;
- }
-
- /**
- * Displays a password field for a settings field
- *
- * @param array $args settings field args
- */
- function callback_password( $args ) {
-
- $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
- $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
-
- $html = sprintf( '<input type="password" class="%1$s-text" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s"/>', $size, $args['section'], $args['id'], $value );
- $html .= $this->get_field_description( $args );
-
- echo $html;
- }
-
- /**
- * Displays a color picker field for a settings field
- *
- * @param array $args settings field args
- */
- function callback_color( $args ) {
-
- $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
- $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
-
- $html = sprintf( '<input type="text" class="%1$s-text wp-color-picker-field" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s" data-default-color="%5$s" />', $size, $args['section'], $args['id'], $value, $args['std'] );
- $html .= $this->get_field_description( $args );
-
- echo $html;
- }
-
+ /**
+ * Callback: file upload field.
+ *
+ * @param array $args Settings field args.
+ */
+ public function callback_file($args)
+ {
+ $value = esc_attr($this->get_option($args['id'], $args['section'], $args['std']));
+ $size = isset($args['size']) && ! is_null($args['size']) ? sanitize_html_class($args['size']) : 'regular';
+ $label = isset($args['options']['button_label'])
+ ? $args['options']['button_label']
+ : __('Choose File', 'wpb-floating-menu-or-categories');
+
+ $html = sprintf(
+ '<input type="text" class="%1$s-text wpsa-url" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s"/>',
+ $size,
+ esc_attr($args['section']),
+ esc_attr($args['id']),
+ $value
+ );
+ $html .= sprintf(
+ '<input type="button" class="button wpsa-browse" value="%s" />',
+ esc_attr($label) // FIXED: was unescaped previously.
+ );
+ $html .= $this->get_field_description($args);
+
+ echo $html; // $html is fully escaped above.
+ }
+
+ /**
+ * Callback: password field.
+ *
+ * @param array $args Settings field args.
+ */
+ public function callback_password($args)
+ {
+ $value = esc_attr($this->get_option($args['id'], $args['section'], $args['std']));
+ $size = isset($args['size']) && ! is_null($args['size']) ? sanitize_html_class($args['size']) : 'regular';
+
+ $html = sprintf(
+ '<input type="password" class="%1$s-text" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s"/>',
+ $size,
+ esc_attr($args['section']),
+ esc_attr($args['id']),
+ $value
+ );
+ $html .= $this->get_field_description($args);
+
+ echo $html; // $html is fully escaped above.
+ }
+
+ /**
+ * Callback: color picker field.
+ *
+ * @param array $args Settings field args.
+ */
+ public function callback_color($args)
+ {
+ $value = esc_attr($this->get_option($args['id'], $args['section'], $args['std']));
+ $size = isset($args['size']) && ! is_null($args['size']) ? sanitize_html_class($args['size']) : 'regular';
+
+ $html = sprintf(
+ '<input type="text" class="%1$s-text wp-color-picker-field" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s" data-default-color="%5$s" />',
+ $size,
+ esc_attr($args['section']),
+ esc_attr($args['id']),
+ $value,
+ esc_attr($args['std']) // FIXED: was unescaped previously.
+ );
+ $html .= $this->get_field_description($args);
+
+ echo $html; // $html is fully escaped above.
+ }
+
+ /**
+ * Callback: pages dropdown field.
+ *
+ * @param array $args Settings field args.
+ */
+ public function callback_pages($args)
+ {
+ $dropdown_args = array(
+ 'selected' => esc_attr($this->get_option($args['id'], $args['section'], $args['std'])),
+ 'name' => $args['section'] . '[' . $args['id'] . ']',
+ 'id' => $args['section'] . '[' . $args['id'] . ']',
+ 'echo' => 0,
+ );
+
+ // wp_dropdown_pages() returns safely escaped HTML.
+ echo wp_dropdown_pages($dropdown_args); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped by wp_dropdown_pages().
+ }
+
+ /**
+ * Sanitize callback for Settings API.
+ *
+ * @param mixed $options Raw option values.
+ * @return mixed Sanitized option values.
+ */
+ public function sanitize_options($options)
+ {
+ if (! $options) {
+ return $options;
+ }
- /**
- * Displays a select box for creating the pages select box
- *
- * @param array $args settings field args
- */
- function callback_pages( $args ) {
-
- $dropdown_args = array(
- 'selected' => esc_attr($this->get_option($args['id'], $args['section'], $args['std'] ) ),
- 'name' => $args['section'] . '[' . $args['id'] . ']',
- 'id' => $args['section'] . '[' . $args['id'] . ']',
- 'echo' => 0
- );
- $html = wp_dropdown_pages( $dropdown_args );
- echo $html;
- }
+ foreach ($options as $option_slug => $option_value) {
+ $sanitize_callback = $this->get_sanitize_callback($option_slug);
- /**
- * Sanitize callback for Settings API
- *
- * @return mixed
- */
- function sanitize_options( $options ) {
+ if ($sanitize_callback) {
+ $options[$option_slug] = call_user_func($sanitize_callback, $option_value);
+ }
+ }
- if ( !$options ) {
return $options;
}
- foreach( $options as $option_slug => $option_value ) {
- $sanitize_callback = $this->get_sanitize_callback( $option_slug );
-
- // If callback is set, call it
- if ( $sanitize_callback ) {
- $options[ $option_slug ]