PluginProbe
Gutenberg / 4.0.0
Gutenberg v4.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 / meta-box-partial-page.php

meta-box-partial-page.php in Gutenberg 4.0.0, at lib/meta-box-partial-page.php

388 lines 12.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Initialization and wp-admin integration for the Gutenberg editor plugin.
4 *
5 * @package gutenberg
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 die( 'Silence is golden.' );
10 }
11
12 /**
13 * Page loaded when saving the meta boxes.
14 * The HTML returned by this page is irrelevant, it's being called in AJAX ignoring its output
15 *
16 * @since 1.8.0
17 */
18 function gutenberg_meta_box_save() {
19 /**
20 * Needs classic editor to be active.
21 *
22 * @see https://github.com/WordPress/gutenberg/commit/bdf94e65ac0c10b3ce5d8e214f0c9e1081997d9b
23 */
24 if ( ! isset( $_REQUEST['classic-editor'] ) ) {
25 return;
26 }
27
28 /**
29 * The meta_box param as long as it is set on the wp-admin/post.php request
30 * will trigger this partial page.
31 *
32 * Essentially all that happens is we try to load in the scripts from admin_head
33 * and admin_footer to mimic the assets for a typical post.php.
34 *
35 * @in_the_future Hopefully the meta box param can be changed to a location,
36 * or contenxt, so that we can use this API to render meta boxes that appear,
37 * in the sidebar vs. regular content, or core meta boxes vs others. For now
38 * a request like http://local.wordpress.dev/wp-admin/post.php?post=40007&action=edit&meta_box=taco
39 * works just fine!
40 */
41 if ( ! isset( $_REQUEST['meta_box'] ) || 'post.php' !== $GLOBALS['pagenow'] ) {
42 return;
43 }
44
45 // Ths action is not needed since it's an XHR call.
46 remove_action( 'admin_head', 'wp_admin_canonical_url' );
47 the_gutenberg_metaboxes();
48 }
49
50 add_action( 'do_meta_boxes', 'gutenberg_meta_box_save', 1000 );
51
52 /**
53 * Allows the meta box endpoint to correctly redirect to the meta box endpoint
54 * when a post is saved.
55 *
56 * @since 1.5.0
57 *
58 * @param string $location The location of the meta box, 'side', 'normal'.
59 * @param int $post_id Post ID.
60 *
61 * @hooked redirect_post_location priority 10
62 */
63 function gutenberg_meta_box_save_redirect( $location, $post_id ) {
64 if ( isset( $_REQUEST['gutenberg_meta_boxes'] ) ) {
65 $location = add_query_arg(
66 array(
67 'meta_box' => true,
68 'action' => 'edit',
69 'classic-editor' => true,
70 'post' => $post_id,
71 ),
72 admin_url( 'post.php' )
73 );
74 }
75
76 return $location;
77 }
78
79 add_filter( 'redirect_post_location', 'gutenberg_meta_box_save_redirect', 10, 2 );
80
81 /**
82 * Filter out core meta boxes as well as the post thumbnail.
83 *
84 * @since 1.5.0
85 *
86 * @param array $meta_boxes Meta box data.
87 * @return array Meta box data without core meta boxes.
88 */
89 function gutenberg_filter_meta_boxes( $meta_boxes ) {
90 $core_side_meta_boxes = array(
91 'submitdiv',
92 'formatdiv',
93 'pageparentdiv',
94 'postimagediv',
95 );
96
97 $custom_taxonomies = get_taxonomies(
98 array(
99 'show_ui' => true,
100 ),
101 'objects'
102 );
103
104 // Following the same logic as meta box generation in:
105 // https://github.com/WordPress/wordpress-develop/blob/c896326/src/wp-admin/edit-form-advanced.php#L288-L292.
106 foreach ( $custom_taxonomies as $custom_taxonomy ) {
107 $core_side_meta_boxes [] = $custom_taxonomy->hierarchical ?
108 $custom_taxonomy->name . 'div' :
109 'tagsdiv-' . $custom_taxonomy->name;
110 }
111
112 $core_normal_meta_boxes = array(
113 'revisionsdiv',
114 'postexcerpt',
115 'trackbacksdiv',
116 'postcustom',
117 'commentstatusdiv',
118 'commentsdiv',
119 'slugdiv',
120 'authordiv',
121 );
122
123 $taxonomy_callbacks_to_unset = array(
124 'post_tags_meta_box',
125 'post_categories_meta_box',
126 );
127
128 foreach ( $meta_boxes as $page => $contexts ) {
129 foreach ( $contexts as $context => $priorities ) {
130 foreach ( $priorities as $priority => $boxes ) {
131 foreach ( $boxes as $name => $data ) {
132 if ( 'normal' === $context && in_array( $name, $core_normal_meta_boxes ) ) {
133 unset( $meta_boxes[ $page ][ $context ][ $priority ][ $name ] );
134 } elseif ( 'side' === $context && in_array( $name, $core_side_meta_boxes ) ) {
135 unset( $meta_boxes[ $page ][ $context ][ $priority ][ $name ] );
136 }
137 // Filter out any taxonomies as Gutenberg already provides JS alternative.
138 if ( isset( $data['callback'] ) && in_array( $data['callback'], $taxonomy_callbacks_to_unset ) ) {
139 unset( $meta_boxes[ $page ][ $context ][ $priority ][ $name ] );
140 }
141 // Filter out meta boxes that are just registered for back compat.
142 if ( isset( $data['args']['__back_compat_meta_box'] ) && $data['args']['__back_compat_meta_box'] ) {
143 unset( $meta_boxes[ $page ][ $context ][ $priority ][ $name ] );
144 }
145 }
146 }
147 }
148 }
149
150 return $meta_boxes;
151 }
152
153 add_filter( 'filter_gutenberg_meta_boxes', 'gutenberg_filter_meta_boxes' );
154
155 /**
156 * Go through the global metaboxes, and override the render callback, so we can trigger our warning if needed.
157 *
158 * @since 1.8.0
159 */
160 function gutenberg_intercept_meta_box_render() {
161 global $wp_meta_boxes;
162
163 foreach ( $wp_meta_boxes as $post_type => $contexts ) {
164 foreach ( $contexts as $context => $priorities ) {
165 foreach ( $priorities as $priority => $boxes ) {
166 foreach ( $boxes as $id => $box ) {
167 if ( ! is_array( $box ) ) {
168 continue;
169 }
170 if ( ! is_array( $wp_meta_boxes[ $post_type ][ $context ][ $priority ][ $id ]['args'] ) ) {
171 $wp_meta_boxes[ $post_type ][ $context ][ $priority ][ $id ]['args'] = array();
172 }
173 if ( ! isset( $wp_meta_boxes[ $post_type ][ $context ][ $priority ][ $id ]['args']['__original_callback'] ) ) {
174 $wp_meta_boxes[ $post_type ][ $context ][ $priority ][ $id ]['args']['__original_callback'] = $box['callback'];
175 $wp_meta_boxes[ $post_type ][ $context ][ $priority ][ $id ]['callback'] = 'gutenberg_override_meta_box_callback';
176 }
177 }
178 }
179 }
180 }
181 }
182 add_action( 'submitpost_box', 'gutenberg_intercept_meta_box_render' );
183 add_action( 'submitpage_box', 'gutenberg_intercept_meta_box_render' );
184 add_action( 'edit_page_form', 'gutenberg_intercept_meta_box_render' );
185 add_action( 'edit_form_advanced', 'gutenberg_intercept_meta_box_render' );
186
187 /**
188 * Check if this metabox only exists for back compat purposes, show a warning if it doesn't.
189 *
190 * @since 1.8.0
191 *
192 * @param mixed $object The object being operated on, on this screen.
193 * @param array $box The current meta box definition.
194 */
195 function gutenberg_override_meta_box_callback( $object, $box ) {
196 $callback = $box['args']['__original_callback'];
197 unset( $box['args']['__original_callback'] );
198
199 $block_compatible = true;
200 if ( isset( $box['args']['__block_editor_compatible_meta_box'] ) ) {
201 $block_compatible = (bool) $box['args']['__block_editor_compatible_meta_box'];
202 unset( $box['args']['__block_editor_compatible_meta_box'] );
203 }
204
205 if ( isset( $box['args']['__back_compat_meta_box'] ) ) {
206 $block_compatible |= (bool) $box['args']['__back_compat_meta_box'];
207 unset( $box['args']['__back_compat_meta_box'] );
208 }
209
210 if ( ! $block_compatible ) {
211 gutenberg_show_meta_box_warning( $callback );
212 }
213
214 call_user_func( $callback, $object, $box );
215 }
216
217 /**
218 * Display a warning in the metabox that the current plugin is causing the fallback to the old editor.
219 *
220 * @since 1.8.0
221 *
222 * @param callable $callback The function that a plugin has defined to render a meta box.
223 */
224 function gutenberg_show_meta_box_warning( $callback ) {
225 // Only show the warning when WP_DEBUG is enabled.
226 if ( ! WP_DEBUG ) {
227 return;
228 }
229
230 // Don't show in the Gutenberg meta box UI.
231 if ( ! isset( $_REQUEST['classic-editor'] ) ) {
232 return;
233 }
234
235 if ( is_array( $callback ) ) {
236 $reflection = new ReflectionMethod( $callback[0], $callback[1] );
237 } else {
238 $reflection = new ReflectionFunction( $callback );
239 }
240
241 if ( $reflection->isInternal() ) {
242 return;
243 }
244
245 $filename = $reflection->getFileName();
246 if ( strpos( $filename, WP_PLUGIN_DIR ) !== 0 ) {
247 return;
248 }
249
250 $filename = str_replace( WP_PLUGIN_DIR, '', $filename );
251 $filename = preg_replace( '|^/([^/]*/).*$|', '\\1', $filename );
252
253 $plugins = get_plugins();
254 foreach ( $plugins as $name => $plugin ) {
255 if ( strpos( $name, $filename ) === 0 ) {
256 ?>
257 <div class="error inline">
258 <p>
259 <?php
260 /* translators: %s is the name of the plugin that generated this meta box. */
261 printf( __( 'Gutenberg incompatible meta box, from the "%s" plugin.', 'gutenberg' ), $plugin['Name'] );
262 ?>
263 </p>
264 </div>
265 <?php
266 }
267 }
268 }
269
270 /**
271 * Renders the WP meta boxes forms.
272 *
273 * @since 1.8.0
274 */
275 function the_gutenberg_metaboxes() {
276 global $post, $current_screen, $wp_meta_boxes;
277
278 // Handle meta box state.
279 $_original_meta_boxes = $wp_meta_boxes;
280
281 /**
282 * Fires right before the meta boxes are rendered.
283 *
284 * This allows for the filtering of meta box data, that should already be
285 * present by this point. Do not use as a means of adding meta box data.
286 *
287 * By default gutenberg_filter_meta_boxes() is hooked in and can be
288 * unhooked to restore core meta boxes.
289 *
290 * @param array $wp_meta_boxes Global meta box state.
291 */
292 $wp_meta_boxes = apply_filters( 'filter_gutenberg_meta_boxes', $wp_meta_boxes );
293 $locations = array( 'side', 'normal', 'advanced' );
294 $active_meta_box_locations = array();
295 // Render meta boxes.
296 ?>
297 <form class="metabox-base-form">
298 <?php gutenberg_meta_box_post_form_hidden_fields( $post ); ?>
299 </form>
300 <?php foreach ( $locations as $location ) : ?>
301 <form class="metabox-location-<?php echo esc_attr( $location ); ?>">
302 <div id="poststuff" class="sidebar-open">
303 <div id="postbox-container-2" class="postbox-container">
304 <?php
305 $number_of_meta_boxes = do_meta_boxes(
306 $current_screen,
307 $location,
308 $post
309 );
310
311 if ( $number_of_meta_boxes > 0 ) {
312 $active_meta_box_locations[] = $location;
313 }
314 ?>
315 </div>
316 </div>
317 </form>
318 <?php endforeach; ?>
319 <?php
320
321 /**
322 * Sadly we probably can not add this data directly into editor settings.
323 *
324 * ACF and other meta boxes need admin_head to fire for meta box registry.
325 * admin_head fires after admin_enqueue_scripts which is where we create our
326 * editor instance. If a cleaner solution can be imagined, please change
327 * this, and try to get this data to load directly into the editor settings.
328 */
329 $script = 'window._wpLoadGutenbergEditor.then( function() {
330 wp.data.dispatch( \'core/edit-post\' ).setActiveMetaBoxLocations( ' . wp_json_encode( $active_meta_box_locations ) . ' );
331 } );';
332
333 wp_add_inline_script( 'wp-edit-post', $script );
334
335 /**
336 * When `wp-edit-post` is output in the `<head>`, the inline script needs to be manually printed. Otherwise,
337 * metaboxes will not display because inline scripts for `wp-edit-post` will not be printed again after this point.
338 *
339 * @see https://github.com/WordPress/gutenberg/issues/6963
340 */
341 if ( wp_script_is( 'wp-edit-post', 'done' ) ) {
342 printf( "<script type='text/javascript'>\n%s\n</script>\n", trim( $script ) );
343 }
344
345 // Reset meta box data.
346 $wp_meta_boxes = $_original_meta_boxes;
347 }
348
349 /**
350 * Renders the hidden form required for the meta boxes form.
351 *
352 * @param WP_Post $post Current post object.
353 *
354 * @since 1.8.0
355 */
356 function gutenberg_meta_box_post_form_hidden_fields( $post ) {
357 $form_extra = '';
358 if ( 'auto-draft' === $post->post_status ) {
359 $form_extra .= "<input type='hidden' id='auto_draft' name='auto_draft' value='1' />";
360 }
361 $form_action = 'editpost';
362 $nonce_action = 'update-post_' . $post->ID;
363 $form_extra .= "<input type='hidden' id='post_ID' name='post_ID' value='" . esc_attr( $post->ID ) . "' />";
364 $referer = wp_get_referer();
365 $current_user = wp_get_current_user();
366 $user_id = $current_user->ID;
367 wp_nonce_field( $nonce_action );
368 ?>
369 <input type="hidden" id="user-id" name="user_ID" value="<?php echo (int) $user_id; ?>" />
370 <input type="hidden" id="hiddenaction" name="action" value="<?php echo esc_attr( $form_action ); ?>" />
371 <input type="hidden" id="originalaction" name="originalaction" value="<?php echo esc_attr( $form_action ); ?>" />
372 <input type="hidden" id="post_type" name="post_type" value="<?php echo esc_attr( $post->post_type ); ?>" />
373 <input type="hidden" id="original_post_status" name="original_post_status" value="<?php echo esc_attr( $post->post_status ); ?>" />
374 <input type="hidden" id="referredby" name="referredby" value="<?php echo $referer ? esc_url( $referer ) : ''; ?>" />
375 <!-- These fields are not part of the standard post form. Used to redirect back to this page on save. -->
376 <input type="hidden" name="gutenberg_meta_boxes" value="gutenberg_meta_boxes" />
377
378 <?php
379 if ( 'draft' !== get_post_status( $post ) ) {
380 wp_original_referer_field( true, 'previous' );
381 }
382 echo $form_extra;
383 wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
384 wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
385 // Permalink title nonce.
386 wp_nonce_field( 'samplepermalink', 'samplepermalinknonce', false );
387 }
388