PluginProbe
Gutenberg / 24.0.0
Gutenberg v24.0.0
24.0.0 23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 All 403 releases
gutenberg / lib / experimental / theme-preview / load.php

load.php in Gutenberg 24.0.0, at lib/experimental/theme-preview/load.php

159 lines 5.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Bootstraps the block theme preview page in wp-admin.
4 *
5 * Part of the extensible site editor experiment: only loaded while the
6 * `gutenberg-extensible-site-editor` experiment is enabled (see lib/load.php).
7 *
8 * @package gutenberg
9 */
10
11 /**
12 * Builds the theme preview page URL for a theme.
13 *
14 * The page previews the theme named by the `wp_theme_preview` query
15 * parameter. Core's `wp-includes/theme-previews.php` reads that parameter on
16 * every request: it filters `stylesheet`/`template` (for users with
17 * `switch_themes`), attaches an apiFetch middleware that forwards the
18 * parameter on every REST request, and prints the activation nonce on
19 * `admin_head` — so the page needs no further wiring beyond carrying the
20 * parameter in its URL.
21 *
22 * @param string $stylesheet Stylesheet (directory name) of the theme to preview.
23 * @return string Theme preview page URL, not escaped for output.
24 */
25 function gutenberg_get_theme_preview_url( $stylesheet ) {
26 static $base = null;
27 if ( null === $base ) {
28 // The page renders the styles route, registered at `/styles`.
29 $base = admin_url( 'admin.php?page=theme-preview-wp-admin&p=%2Fstyles' );
30 }
31
32 return add_query_arg(
33 'wp_theme_preview',
34 // `add_query_arg` does not encode new values, so encode subdirectory
35 // theme stylesheets like `parent/child` here.
36 rawurlencode( $stylesheet ),
37 $base
38 );
39 }
40
41 /**
42 * Registers the hidden wp-admin page that previews a block theme.
43 */
44 function gutenberg_register_theme_preview_admin_page() {
45 // Register with an empty parent to create a hidden admin.php?page= route
46 // without adding an Appearance submenu item for a screen that requires a
47 // `wp_theme_preview` parameter.
48 $hook_suffix = add_submenu_page(
49 '',
50 __( 'Theme Preview', 'gutenberg' ),
51 __( 'Theme Preview', 'gutenberg' ),
52 'switch_themes',
53 'theme-preview-wp-admin',
54 'gutenberg_theme_preview_wp_admin_render_page'
55 );
56
57 if ( $hook_suffix ) {
58 add_action( "load-$hook_suffix", 'gutenberg_theme_preview_wp_admin_prepare_screen' );
59 }
60 }
61
62 /**
63 * Prepares the admin chrome before wp-admin/admin-header.php renders.
64 *
65 * @global string $title The admin page title.
66 * @global string $parent_file The current top-level menu item.
67 * @global string $submenu_file The current submenu item.
68 */
69 function gutenberg_theme_preview_wp_admin_prepare_screen() {
70 global $title, $parent_file, $submenu_file;
71
72 // Hidden pages do not resolve a title from a visible menu item, so set one
73 // before admin-header.php formats the page title.
74 $title = __( 'Theme Preview', 'gutenberg' );
75
76 /*
77 * Take the page out of the hidden `''` submenu bucket it was registered in.
78 * Left in place, get_admin_page_parent() matches it there and resets
79 * $parent_file to '' — and it does so *after* the `parent_file` filter runs,
80 * so filtering cannot win. With no match it preserves a non-empty
81 * $parent_file instead.
82 *
83 * Safe at this point: `load-` fires after the capability check in admin.php,
84 * which needs the page registered, and before the menu is rendered.
85 */
86 remove_submenu_page( '', 'theme-preview-wp-admin' );
87
88 // The preview is reached from the themes screen; keep Appearance current.
89 $parent_file = 'themes.php';
90 $submenu_file = 'themes.php';
91 }
92
93 /**
94 * Points the themes screen's block theme live preview links at the theme
95 * preview page.
96 *
97 * Core builds those links as `site-editor.php?wp_theme_preview=<stylesheet>`;
98 * rewriting them here saves the round trip through site-editor.php that the
99 * redirect below would otherwise take. The active theme's entry is left
100 * alone: it carries no `wp_theme_preview` parameter and links to the site
101 * editor as a customize action.
102 *
103 * @param array $prepared_themes Themes prepared for the themes screen.
104 * @return array Themes with rewritten live preview links.
105 */
106 function gutenberg_use_theme_preview_page_for_live_preview_links( $prepared_themes ) {
107 if ( ! current_user_can( 'switch_themes' ) ) {
108 return $prepared_themes;
109 }
110
111 $current_stylesheet = get_stylesheet();
112 foreach ( $prepared_themes as $stylesheet => $theme_data ) {
113 if (
114 $stylesheet === $current_stylesheet
115 || empty( $theme_data['blockTheme'] )
116 || empty( $theme_data['actions']['customize'] )
117 ) {
118 continue;
119 }
120 $prepared_themes[ $stylesheet ]['actions']['customize'] = esc_url( gutenberg_get_theme_preview_url( $stylesheet ) );
121 }
122
123 return $prepared_themes;
124 }
125
126 /**
127 * Redirects block theme previews from the site editor to the theme preview page.
128 *
129 * The links the filter above rewrites no longer reach site-editor.php; this
130 * catches the remaining entry points — bookmarks, hand-typed URLs, and the
131 * live preview link the theme installer renders after installing a theme.
132 * For users the preview filters ignore, site-editor.php behaves as before.
133 */
134 function gutenberg_redirect_theme_preview_to_theme_preview_page() {
135 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reading the same unauthenticated query arg Core's theme-previews.php reads to decide whether a preview is requested.
136 $stylesheet = isset( $_GET['wp_theme_preview'] ) ? sanitize_text_field( wp_unslash( $_GET['wp_theme_preview'] ) ) : '';
137 if ( '' === $stylesheet ) {
138 return;
139 }
140
141 // Core ignores the preview for users without `switch_themes`; leave them
142 // on the site editor, which keeps showing the active theme.
143 if ( ! current_user_can( 'switch_themes' ) ) {
144 return;
145 }
146
147 wp_safe_redirect( gutenberg_get_theme_preview_url( $stylesheet ) );
148 exit;
149 }
150
151 // The render callback is generated into `build/pages`, which lib/load.php
152 // requires before this file: a build that predates the page has nothing to
153 // register, link, or redirect to.
154 if ( function_exists( 'gutenberg_theme_preview_wp_admin_render_page' ) ) {
155 add_action( 'admin_menu', 'gutenberg_register_theme_preview_admin_page' );
156 add_filter( 'wp_prepare_themes_for_js', 'gutenberg_use_theme_preview_page_for_live_preview_links' );
157 add_action( 'load-site-editor.php', 'gutenberg_redirect_theme_preview_to_theme_preview_page' );
158 }
159