diff --git a/projects/plugins/jetpack/changelog/fix-widget-visibility b/projects/plugins/jetpack/changelog/fix-widget-visibility new file mode 100644 index 0000000000000..4711aa71d1b0e --- /dev/null +++ b/projects/plugins/jetpack/changelog/fix-widget-visibility @@ -0,0 +1,4 @@ +Significance: patch +Type: bugfix + +Allow the use of widget visibility conditions in gutenberg based widget editing diff --git a/projects/plugins/jetpack/modules/widget-visibility/widget-conditions.php b/projects/plugins/jetpack/modules/widget-visibility/widget-conditions.php index 2574690f7367b..bde6d662ce533 100644 --- a/projects/plugins/jetpack/modules/widget-visibility/widget-conditions.php +++ b/projects/plugins/jetpack/modules/widget-visibility/widget-conditions.php @@ -3,31 +3,114 @@ use Automattic\Jetpack\Assets; /** - * Hide or show widgets conditionally. + * Hide or show legacy widgets conditionally. + * + * This class has two responsiblities - administrating the conditions in which legacy widgets may be hidden or shown + * and hiding/showing the legacy widgets on the front-end of the site, depending upon the evaluation of those conditions. + * + * Administrating the conditions can be done in one of four different WordPress screens, plus direct use of the API and + * is supplemented with a legacy widget preview screen. The four different admin screens are + * + * Gutenberg widget experience - widget admin (widgets.php + API + legacy widget preview) + * Gutenberg widget experience - Customizer (customizer screen/API + API + legacy widget preview) + * Classic widget experience - widget admin (widgets.php + admin-ajax XHR requests) + * Classic widget experience - Customizer (customizer screen/API) + * + * An introduction to the API endpoints can be found here: https://make.wordpress.org/core/2021/06/29/rest-api-changes-in-wordpress-5-8/ */ - class Jetpack_Widget_Conditions { static $passed_template_redirect = false; public static function init() { - if ( is_admin() ) { - add_action( 'sidebar_admin_setup', array( __CLASS__, 'widget_admin_setup' ) ); - add_filter( 'widget_update_callback', array( __CLASS__, 'widget_update' ), 10, 3 ); + global $pagenow; + + // The Gutenberg based widget experience will show a preview of legacy widgets by including a URL beginning + // widgets.php?legacy-widget-preview inside an iframe. Previews don't need widget editing loaded and also don't + // want to run the filter - if the widget is filtered out it'll be empty, which would be confusing. + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( isset( $_GET['legacy-widget-preview'] ) ) { + return; + } + + // If action is posted and it's save-widget then it's relevant to widget conditions, otherwise it's something + // else and it's not worth registering hooks. + // phpcs:ignore WordPress.Security.NonceVerification.Missing + if ( isset( $_POST['action'] ) && ! in_array( $_POST['action'], array( 'save-widget', 'update-widget' ), true ) ) { + return; + } + + // API call to *list* the widget types doesn't use editing visibility or display widgets. + if ( false !== strpos( $_SERVER['REQUEST_URI'], '/wp-json/wp/v2/widget-types?' ) ) { + return; + } + + $add_data_assets_to_page = false; + $add_html_to_form = false; + $handle_widget_updates = false; + + $using_classic_experience = ( ! function_exists( 'wp_use_widgets_block_editor' ) || ! wp_use_widgets_block_editor() ); + if ( $using_classic_experience && + ( + is_customize_preview() || 'widgets.php' === $pagenow || + // phpcs:ignore WordPress.Security.NonceVerification.Missing + ( 'admin-ajax.php' === $pagenow && array_key_exists( 'action', $_POST ) && 'save-widget' === $_POST['action'] ) + ) + ) { + $add_data_assets_to_page = true; + $add_html_to_form = true; + $handle_widget_updates = true; + } else { + // On a screen that is hosting the API in the gutenberg editing experience. + if ( is_customize_preview() || 'widgets.php' === $pagenow ) { + $add_data_assets_to_page = true; + } + + // Encoding for a particular widget end point. + if ( 1 === preg_match( '|/wp-json/wp/v2/widget-types/.*/encode|', $_SERVER['REQUEST_URI'] ) ) { + $add_html_to_form = true; + $handle_widget_updates = true; + } + + // Batch API is usually saving but could be anything. + if ( false !== strpos( $_SERVER['REQUEST_URI'], '/wp-json/batch/v1' ) ) { + $handle_widget_updates = true; + $add_html_to_form = true; + } + + // Saving widgets via non-batch API. This isn't used within WordPress but could be used by third parties in theory. + if ( 'GET' !== $_SERVER['REQUEST_METHOD'] && false !== strpos( $_SERVER['REQUEST_URI'], '/wp/v2/widgets' ) ) { + $handle_widget_updates = true; + $add_html_to_form = true; + } + } + + if ( $add_html_to_form ) { add_action( 'in_widget_form', array( __CLASS__, 'widget_conditions_admin' ), 10, 3 ); - } elseif ( ! in_array( $GLOBALS['pagenow'], array( 'wp-login.php', 'wp-register.php' ) ) ) { + } + + if ( $handle_widget_updates ) { + add_filter( 'widget_update_callback', array( __CLASS__, 'widget_update' ), 10, 3 ); + } + + if ( $add_data_assets_to_page ) { + add_action( 'sidebar_admin_setup', array( __CLASS__, 'widget_admin_setup' ) ); + } + + if ( ! $add_html_to_form && ! $handle_widget_updates && ! $add_data_assets_to_page && + ! in_array( $pagenow, array( 'wp-login.php', 'wp-register.php' ), true ) + ) { + // Not hit any known widget admin endpoint, register widget display hooks instead. add_filter( 'widget_display_callback', array( __CLASS__, 'filter_widget' ) ); add_filter( 'sidebars_widgets', array( __CLASS__, 'sidebars_widgets' ) ); add_action( 'template_redirect', array( __CLASS__, 'template_redirect' ) ); } } + /** + * Prepare the interface for editing widgets - loading css, javascript & data + */ public static function widget_admin_setup() { - // Return early if we are not in the block editor. - if ( wp_should_load_block_editor_scripts_and_styles() ) { - return; - } - - wp_enqueue_style( 'widget-conditions', plugins_url( 'widget-conditions/widget-conditions.css', __FILE__ ) ); + wp_enqueue_style( 'widget-conditions', plugins_url( 'widget-conditions/widget-conditions.css', __FILE__ ), array( 'widgets' ), JETPACK__VERSION ); wp_style_add_data( 'widget-conditions', 'rtl', 'replace' ); wp_enqueue_script( 'widget-conditions', @@ -36,7 +119,7 @@ public static function widget_admin_setup() { 'modules/widget-visibility/widget-conditions/widget-conditions.js' ), array( 'jquery', 'jquery-ui-core' ), - 20191128, + JETPACK__VERSION, true ); @@ -295,9 +378,9 @@ public static function get_pages() { /** * Add the widget conditions to each widget in the admin. * - * @param $widget unused. - * @param $return unused. - * @param array $instance The widget settings. + * @param WP_Widget $widget Widget to add conditions settings to. + * @param null $return unused. + * @param array $instance The widget settings. */ public static function widget_conditions_admin( $widget, $return, $instance ) { $conditions = array(); @@ -327,6 +410,9 @@ public static function widget_conditions_admin( $widget, $return, $instance ) { class=" widget-conditional - +