1<?php
2/**
3 * Server-side rendering of the `core/legacy-widget` block.
4 *
5 * @package WordPress
6 */
7
8/**
9 * Renders the 'core/legacy-widget' block.
10 *
11 * @param array $attributes The block attributes.
12 *
13 * @return string Rendered block.
14 */
15function render_block_core_legacy_widget( $attributes ) {
16	global $wp_widget_factory;
17
18	if ( isset( $attributes['id'] ) ) {
19		$sidebar_id = wp_find_widgets_sidebar( $attributes['id'] );
20		return wp_render_widget( $attributes['id'], $sidebar_id );
21	}
22
23	if ( ! isset( $attributes['idBase'] ) ) {
24		return '';
25	}
26
27	$id_base = $attributes['idBase'];
28	if ( method_exists( $wp_widget_factory, 'get_widget_key' ) && method_exists( $wp_widget_factory, 'get_widget_object' ) ) {
29		$widget_key    = $wp_widget_factory->get_widget_key( $id_base );
30		$widget_object = $wp_widget_factory->get_widget_object( $id_base );
31	} else {
32		/*
33		 * This file is copied from the published @wordpress/widgets package when WordPress
34		 * Core is built. Because the package is a dependency of both WordPress Core and the
35		 * Gutenberg plugin where the block editor is developed, this fallback condition is
36		 * required until the minimum required version of WordPress for the plugin is raised
37		 * to 5.8.
38		 */
39		$widget_key    = gutenberg_get_widget_key( $id_base );
40		$widget_object = gutenberg_get_widget_object( $id_base );
41	}
42
43	if ( ! $widget_key || ! $widget_object ) {
44		return '';
45	}
46
47	if ( isset( $attributes['instance']['encoded'], $attributes['instance']['hash'] ) ) {
48		$serialized_instance = base64_decode( $attributes['instance']['encoded'] );
49		if ( wp_hash( $serialized_instance ) !== $attributes['instance']['hash'] ) {
50			return '';
51		}
52		$instance = unserialize( $serialized_instance );
53	} else {
54		$instance = array();
55	}
56
57	$args = array(
58		'widget_id'   => $widget_object->id,
59		'widget_name' => $widget_object->name,
60	);
61
62	ob_start();
63	the_widget( $widget_key, $instance, $args );
64	return ob_get_clean();
65}
66
67/**
68 * Registers the 'core/legacy-widget' block.
69 */
70function register_block_core_legacy_widget() {
71	register_block_type_from_metadata(
72		__DIR__ . '/legacy-widget',
73		array(
74			'render_callback' => 'render_block_core_legacy_widget',
75		)
76	);
77}
78
79add_action( 'init', 'register_block_core_legacy_widget' );
80
81/**
82 * Intercepts any request with legacy-widget-preview in the query param and, if
83 * set, renders a page containing a preview of the requested Legacy Widget
84 * block.
85 */
86function handle_legacy_widget_preview_iframe() {
87	if ( empty( $_GET['legacy-widget-preview'] ) ) {
88		return;
89	}
90
91	if ( ! current_user_can( 'edit_theme_options' ) ) {
92		return;
93	}
94
95	define( 'IFRAME_REQUEST', true );
96
97	?>
98	<!doctype html>
99	<html <?php language_attributes(); ?>>
100	<head>
101		<meta charset="<?php bloginfo( 'charset' ); ?>" />
102		<meta name="viewport" content="width=device-width, initial-scale=1" />
103		<link rel="profile" href="https://gmpg.org/xfn/11" />
104		<?php wp_head(); ?>
105		<style>
106			/* Reset theme styles */
107			html, body, #page, #content {
108				padding: 0 !important;
109				margin: 0 !important;
110			}
111		</style>
112	</head>
113	<body <?php body_class(); ?>>
114		<div id="page" class="site">
115			<div id="content" class="site-content">
116				<?php
117				$registry = WP_Block_Type_Registry::get_instance();
118				$block    = $registry->get_registered( 'core/legacy-widget' );
119				echo $block->render( $_GET['legacy-widget-preview'] );
120				?>
121			</div><!-- #content -->
122		</div><!-- #page -->
123		<?php wp_footer(); ?>
124	</body>
125	</html>
126	<?php
127
128	exit;
129}
130
131// Use admin_init instead of init to ensure get_current_screen function is already available.
132// This isn't strictly required, but enables better compatibility with existing plugins.
133// See: https://github.com/WordPress/gutenberg/issues/32624.
134add_action( 'admin_init', 'handle_legacy_widget_preview_iframe', 20 );
135