Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/widget-context/src/WidgetContext.php
+++ b/widget-context/src/WidgetContext.php
@@ -15,6 +15,13 @@
*/
const RULE_KEY_URLS_INVERT = 'urls_invert';
+ /**
+ * Nonce action when saving the individual widget context settings.
+ *
+ * @var string
+ */
+ const SAVE_NONCE_ACTION = 'widget-context-update';
+
private $sidebars_widgets;
private $options_name = 'widget_logic_options'; // Context settings for widgets (visibility, etc)
private $settings_name = 'widget_context_settings'; // Widget Context global settings
@@ -96,9 +103,6 @@
// Register admin settings menu
add_action( 'admin_menu', array( $this, 'widget_context_settings_menu' ) );
- // Register admin settings.
- add_action( 'admin_init', array( $this, 'widget_context_settings_init' ) );
-
// Add quick links to the plugin list.
add_action(
'plugin_action_links_' . $this->plugin->basename(),
@@ -106,8 +110,9 @@
);
}
-
function define_widget_contexts() {
+ register_setting( $this->settings_name, $this->settings_name );
+
$this->context_options = apply_filters(
'widget_context_options',
(array) get_option( $this->options_name, array() )
@@ -161,6 +166,11 @@
// Sort contexts by their weight
uasort( $this->contexts, array( $this, 'sort_context_by_weight' ) );
+
+ if ( $this->is_legacy_widgets_enabled() ) {
+ add_filter( 'gutenberg_use_widgets_block_editor', '__return_false' );
+ add_filter( 'use_widgets_block_editor', '__return_false' );
+ }
}
@@ -279,29 +289,34 @@
function save_widget_context_settings() {
- if ( ! current_user_can( 'edit_theme_options' ) || empty( $_POST ) || ! isset( $_POST['wl'] ) ) {
+ if ( ! current_user_can( 'edit_theme_options' ) || empty( $_POST['wl'] ) || ! is_array( $_POST['wl'] ) ) {
return;
}
- // Delete a widget
- if ( isset( $_POST['delete_widget'] ) && isset( $_POST['the-widget-id'] ) ) {
- unset( $this->context_options[ $_POST['the-widget-id'] ] );
- }
+ // Add and update.
+ foreach ( $_POST['wl'] as $widget_id => $widget_context_input ) {
+ $update_nonce = $this->get_widget_nonce_action( $widget_id );
+
+ if ( ! empty( $_POST[ $update_nonce ] ) && wp_verify_nonce( $_POST[ $update_nonce ], self::SAVE_NONCE_ACTION ) ) {
+ if ( ! isset( $this->context_options[ $widget_id ] ) ) {
+ $this->context_options[ $widget_id ] = array();
+ }
- // Add / Update
- $this->context_options = array_merge( $this->context_options, $_POST['wl'] );
+ if ( ! empty( $_POST['delete_widget'] ) ) { // Delete.
+ unset( $this->context_options[ $widget_id ] );
+ } else { // Update.
+ $this->context_options[ $widget_id ] = $widget_context_input;
+ }
+ }
+ }
- $sidebars_widgets = wp_get_sidebars_widgets();
+ // Get a list of all widget IDs.
$all_widget_ids = array();
-
- // Get a lits of all widget IDs
- foreach ( $sidebars_widgets as $widget_area => $widgets ) {
- foreach ( $widgets as $widget_order => $widget_id ) {
- $all_widget_ids[] = $widget_id;
- }
+ foreach ( wp_get_sidebars_widgets() as $widget_area => $widgets ) {
+ $all_widget_ids = array_merge( $all_widget_ids, array_values( $widgets ) );
}
- // Remove non-existant widget contexts from the settings
+ // Cleanup non-existant widget contexts from the settings.
foreach ( $this->context_options as $widget_id => $widget_context ) {
if ( ! in_array( $widget_id, $all_widget_ids, true ) ) {
unset( $this->context_options[ $widget_id ] );
@@ -636,7 +651,6 @@
$controls_core = array();
foreach ( $this->contexts as $context_name => $context_settings ) {
-
$context_classes = array(
'context-group',
sprintf( 'context-group-%s', esc_attr( $context_name ) ),
@@ -726,6 +740,8 @@
}
}
+ $controls[] = wp_nonce_field( self::SAVE_NONCE_ACTION, $this->get_widget_nonce_action( $widget_id ), false, false );
+
return sprintf(
'<div class="widget-context">
<div class="widget-context-header">
@@ -746,6 +762,17 @@
);
}
+ /**
+ * Get the nonce action for widget context settings.
+ *
+ * @param string $widget_id Widget ID.
+ *
+ * @return string
+ */
+ private function get_widget_nonce_action( $widget_id ) {
+ return 'widget-context--' . $widget_id;
+ }
+
function control_incexc( $control_args ) {
$options = array(
@@ -1047,12 +1074,6 @@
);
}
-
- function widget_context_settings_init() {
- register_setting( $this->settings_name, $this->settings_name );
- }
-
-
/**
* Return a link to the Customize Widgets admin page.
*
@@ -1072,6 +1093,15 @@
return admin_url( 'themes.php?page=widget_context_settings' );
}
+ /**
+ * If the legacy widgets interface is enabled in the plugin settings.
+ *
+ * @return bool
+ */
+ public function is_legacy_widgets_enabled() {
+ return ! empty( $this->context_settings['enable-legacy-widgets'] );
+ }
+
function widget_context_admin_view() {
$context_controls = array();
@@ -1140,6 +1170,21 @@
</p>
</td>
</tr>
+ <tr>
+ <th scrope="row">
+ <?php esc_html_e( 'Widget Interface', 'widget-context' ); ?>
+ </th>
+ <td>
+ <label>
+ <input type="hidden" name="<?php echo esc_attr( $this->settings_name ); ?>[enable-legacy-widgets]" value="0" />
+ <input type="checkbox" name="<?php echo esc_attr( $this->settings_name ); ?>[enable-legacy-widgets]" value="1" <?php checked( $this->context_settings['enable-legacy-widgets'], 1 ); ?> />
+ <?php esc_html_e( 'Enable legacy widget interface', 'widget-context' ); ?>
+ </label>
+ <p class="description">
+ <?php esc_html_e( 'Enable the legacy (non-block) widget interface under "Appearance → Widgets" that was disabled in WordPress 5.8.', 'widget-context' ); ?>
+ </p>
+ </td>
+ </tr>
<tr>
<th scrope="row">
<?php esc_html_e( 'Configure Widgets', 'widget-context' ); ?>
--- a/widget-context/vendor/autoload.php
+++ b/widget-context/vendor/autoload.php
@@ -14,10 +14,7 @@
echo $err;
}
}
- trigger_error(
- $err,
- E_USER_ERROR
- );
+ throw new RuntimeException($err);
}
require_once __DIR__ . '/composer/autoload_real.php';
--- a/widget-context/vendor/composer/InstalledVersions.php
+++ b/widget-context/vendor/composer/InstalledVersions.php
@@ -27,12 +27,23 @@
class InstalledVersions
{
/**
+ * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
+ * @internal
+ */
+ private static $selfDir = null;
+
+ /**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;
/**
+ * @var bool
+ */
+ private static $installedIsLocalDir;
+
+ /**
* @var bool|null
*/
private static $canGetVendors;
@@ -309,6 +320,24 @@
{
self::$installed = $data;
self::$installedByVendor = array();
+
+ // when using reload, we disable the duplicate protection to ensure that self::$installed data is
+ // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
+ // so we have to assume it does not, and that may result in duplicate data being returned when listing
+ // all installed packages for example
+ self::$installedIsLocalDir = false;
+ }
+
+ /**
+ * @return string
+ */
+ private static function getSelfDir()
+ {
+ if (self::$selfDir === null) {
+ self::$selfDir = strtr(__DIR__, '\', '/');
+ }
+
+ return self::$selfDir;
}
/**
@@ -322,19 +351,27 @@
}
$installed = array();
+ $copiedLocalDir = false;
if (self::$canGetVendors) {
+ $selfDir = self::getSelfDir();
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+ $vendorDir = strtr($vendorDir, '\', '/');
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
- $installed[] = self::$installedByVendor[$vendorDir] = $required;
- if (null === self::$installed && strtr($vendorDir.'/composer', '\', '/') === strtr(__DIR__, '\', '/')) {
- self::$installed = $installed[count($installed) - 1];
+ self::$installedByVendor[$vendorDir] = $required;
+ $installed[] = $required;
+ if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
+ self::$installed = $required;
+ self::$installedIsLocalDir = true;
}
}
+ if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
+ $copiedLocalDir = true;
+ }
}
}
@@ -350,7 +387,7 @@
}
}
- if (self::$installed !== array()) {
+ if (self::$installed !== array() && !$copiedLocalDir) {
$installed[] = self::$installed;
}
--- a/widget-context/vendor/composer/installed.php
+++ b/widget-context/vendor/composer/installed.php
@@ -1,9 +1,9 @@
<?php return array(
'root' => array(
'name' => 'kasparsd/widget-context',
- 'pretty_version' => 'dev-master',
- 'version' => 'dev-master',
- 'reference' => 'a2e25bade0a8c6117d67462fa1df8ec6c19f2b31',
+ 'pretty_version' => 'dev-develop',
+ 'version' => 'dev-develop',
+ 'reference' => '1332f3f9829bd0866867e6b1401a508bca6f9ea2',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -11,9 +11,9 @@
),
'versions' => array(
'kasparsd/widget-context' => array(
- 'pretty_version' => 'dev-master',
- 'version' => 'dev-master',
- 'reference' => 'a2e25bade0a8c6117d67462fa1df8ec6c19f2b31',
+ 'pretty_version' => 'dev-develop',
+ 'version' => 'dev-develop',
+ 'reference' => '1332f3f9829bd0866867e6b1401a508bca6f9ea2',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
--- a/widget-context/widget-context.php
+++ b/widget-context/widget-context.php
@@ -3,7 +3,7 @@
* Plugin Name: Widget Context
* Plugin URI: https://widgetcontext.com
* Description: Show or hide widgets depending on the section of the site that is being viewed. Configure the widget visibility rules under the individual widget settings.
- * Version: 1.3.3
+ * Version: 1.4.0
* Author: Kaspars Dambis
* Author URI: https://widgetcontext.com
* Text Domain: widget-context