PluginProbe
Gutenberg / 4.7.1
Gutenberg v4.7.1
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 7.4.0 All 402 releases
gutenberg / gutenberg.php

gutenberg.php in Gutenberg 4.7.1, at gutenberg.php

571 lines 15.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Gutenberg
4 * Plugin URI: https://github.com/WordPress/gutenberg
5 * Description: Printing since 1440. This is the development plugin for the new block editor in core.
6 * Version: 4.7.1
7 * Author: Gutenberg Team
8 *
9 * @package gutenberg
10 */
11
12 ### BEGIN AUTO-GENERATED DEFINES
13 define( 'GUTENBERG_VERSION', '4.7.1' );
14 define( 'GUTENBERG_GIT_COMMIT', '2ff13a8e0862d7b6abddf2e8f298fc81bba0ec2c' );
15 ### END AUTO-GENERATED DEFINES
16
17 gutenberg_pre_init();
18
19 /**
20 * Project.
21 *
22 * The main entry point for the Gutenberg editor. Renders the editor on the
23 * wp-admin page for the plugin.
24 *
25 * The gutenberg and gutenberg__editor classNames are left for backward compatibility.
26 *
27 * @since 0.1.0
28 */
29 function the_gutenberg_project() {
30 global $post_type_object;
31 ?>
32 <noscript>
33 <div class="error" style="position:absolute;top:32px;z-index:40"><p>
34 <?php
35 // Using Gutenberg as Plugin.
36 if ( is_plugin_active( 'gutenberg/gutenberg.php' ) ) {
37 $current_url = esc_url( add_query_arg( 'classic-editor', true, $_SERVER['REQUEST_URI'] ) );
38 printf(
39 // Translators: link is to current page specify classic editor.
40 __( 'The Block Editor requires JavaScript. You can use the <a href="%s">Classic Editor</a>.', 'gutenberg' ),
41 $current_url
42 );
43 } else { // Using Gutenberg in Core.
44 printf(
45 /* translators: %s: https://wordpress.org/plugins/classic-editor/ */
46 __( 'The Block Editor requires JavaScript. Please try the <a href="%s">Classic Editor plugin</a>.', 'gutenberg' ),
47 __( 'https://wordpress.org/plugins/classic-editor/', 'gutenberg' )
48 );
49 }
50 ?>
51 </p></div>
52 </noscript>
53 <div class="block-editor gutenberg">
54 <h1 class="screen-reader-text"><?php echo esc_html( $post_type_object->labels->edit_item ); ?></h1>
55 <div id="editor" class="block-editor__container gutenberg__editor"></div>
56 <div id="metaboxes" style="display: none;">
57 <?php the_gutenberg_metaboxes(); ?>
58 </div>
59 </div>
60 <?php
61 }
62
63 /**
64 * Gutenberg's Menu.
65 *
66 * Adds a new wp-admin menu page for the Gutenberg editor.
67 *
68 * @since 0.1.0
69 */
70 function gutenberg_menu() {
71 global $submenu;
72
73 add_menu_page(
74 'Gutenberg',
75 'Gutenberg',
76 'edit_posts',
77 'gutenberg',
78 'the_gutenberg_project',
79 'dashicons-edit'
80 );
81
82 add_submenu_page(
83 'gutenberg',
84 __( 'Demo', 'gutenberg' ),
85 __( 'Demo', 'gutenberg' ),
86 'edit_posts',
87 'gutenberg'
88 );
89
90 if ( current_user_can( 'edit_posts' ) ) {
91 $submenu['gutenberg'][] = array(
92 __( 'Support', 'gutenberg' ),
93 'edit_posts',
94 __( 'https://wordpress.org/support/plugin/gutenberg', 'gutenberg' ),
95 );
96
97 $submenu['gutenberg'][] = array(
98 __( 'Feedback', 'gutenberg' ),
99 'edit_posts',
100 'http://wordpressdotorg.polldaddy.com/s/gutenberg-support',
101 );
102
103 $submenu['gutenberg'][] = array(
104 __( 'Documentation', 'gutenberg' ),
105 'edit_posts',
106 'https://wordpress.org/gutenberg/handbook/',
107 );
108 }
109 }
110 add_action( 'admin_menu', 'gutenberg_menu' );
111
112 /**
113 * Checks whether we're currently loading a Gutenberg page
114 *
115 * @return boolean Whether Gutenberg is being loaded.
116 *
117 * @since 3.1.0
118 */
119 function is_gutenberg_page() {
120 global $post;
121
122 if ( ! is_admin() ) {
123 return false;
124 }
125
126 /*
127 * There have been reports of specialized loading scenarios where `get_current_screen`
128 * does not exist. In these cases, it is safe to say we are not loading Gutenberg.
129 */
130 if ( ! function_exists( 'get_current_screen' ) ) {
131 return false;
132 }
133
134 if ( get_current_screen()->base !== 'post' ) {
135 return false;
136 }
137
138 if ( isset( $_GET['classic-editor'] ) ) {
139 return false;
140 }
141
142 if ( ! gutenberg_can_edit_post( $post ) ) {
143 return false;
144 }
145
146 return true;
147 }
148
149 /**
150 * Display a version notice and deactivate the Gutenberg plugin.
151 *
152 * @since 0.1.0
153 */
154 function gutenberg_wordpress_version_notice() {
155 echo '<div class="error"><p>';
156 _e( 'Gutenberg requires WordPress 4.9.8 or later to function properly. Please upgrade WordPress before activating Gutenberg.', 'gutenberg' );
157 echo '</p></div>';
158
159 deactivate_plugins( array( 'gutenberg/gutenberg.php' ) );
160 }
161
162 /**
163 * Display a build notice.
164 *
165 * @since 0.1.0
166 */
167 function gutenberg_build_files_notice() {
168 echo '<div class="error"><p>';
169 _e( 'Gutenberg development mode requires files to be built. Run <code>npm install</code> to install dependencies, <code>npm run build</code> to build the files or <code>npm run dev</code> to build the files and watch for changes. Read the <a href="https://github.com/WordPress/gutenberg/blob/master/CONTRIBUTING.md">contributing</a> file for more information.', 'gutenberg' );
170 echo '</p></div>';
171 }
172
173 /**
174 * Verify that we can initialize the Gutenberg editor , then load it.
175 *
176 * @since 1.5.0
177 */
178 function gutenberg_pre_init() {
179 if ( defined( 'GUTENBERG_DEVELOPMENT_MODE' ) && GUTENBERG_DEVELOPMENT_MODE && ! file_exists( dirname( __FILE__ ) . '/build/blocks' ) ) {
180 add_action( 'admin_notices', 'gutenberg_build_files_notice' );
181 return;
182 }
183
184 // Get unmodified $wp_version.
185 include ABSPATH . WPINC . '/version.php';
186
187 // Strip '-src' from the version string. Messes up version_compare().
188 $version = str_replace( '-src', '', $wp_version );
189
190 if ( version_compare( $version, '4.9.8', '<' ) ) {
191 add_action( 'admin_notices', 'gutenberg_wordpress_version_notice' );
192 return;
193 }
194
195 require_once dirname( __FILE__ ) . '/lib/load.php';
196
197 if ( function_exists( 'gutenberg_silence_rest_errors' ) ) {
198 gutenberg_silence_rest_errors();
199 }
200
201 add_filter( 'replace_editor', 'gutenberg_init', 10, 2 );
202 }
203
204 /**
205 * Initialize Gutenberg.
206 *
207 * Load API functions, register scripts and actions, etc.
208 *
209 * @param bool $return Whether to replace the editor. Used in the `replace_editor` filter.
210 * @param object $post The post to edit or an auto-draft.
211 * @return bool Whether Gutenberg was initialized.
212 */
213 function gutenberg_init( $return, $post ) {
214 global $title, $post_type;
215
216 if ( true === $return && current_filter() === 'replace_editor' ) {
217 return $return;
218 }
219
220 if ( ! is_gutenberg_page() ) {
221 return false;
222 }
223
224 add_action( 'admin_enqueue_scripts', 'gutenberg_editor_scripts_and_styles' );
225 add_filter( 'screen_options_show_screen', '__return_false' );
226 add_filter( 'admin_body_class', 'gutenberg_add_admin_body_class' );
227
228 $post_type_object = get_post_type_object( $post_type );
229
230 /*
231 * Always force <title> to 'Edit Post' (or equivalent)
232 * because it needs to be in a generic state for both
233 * post-new.php and post.php?post=<id>.
234 */
235 if ( ! empty( $post_type_object ) ) {
236 $title = $post_type_object->labels->edit_item;
237 }
238
239 /*
240 * Remove the emoji script as it is incompatible with both React and any
241 * contenteditable fields.
242 */
243 remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
244
245 /*
246 * Ensure meta box functions are available to third-party code;
247 * includes/meta-boxes is typically loaded from edit-form-advanced.php.
248 */
249 require_once ABSPATH . 'wp-admin/includes/meta-boxes.php';
250 gutenberg_collect_meta_box_data();
251
252 require_once ABSPATH . 'wp-admin/admin-header.php';
253 the_gutenberg_project();
254
255 return true;
256 }
257
258 /**
259 * Redirects the demo page to edit a new post.
260 */
261 function gutenberg_redirect_demo() {
262 global $pagenow;
263
264 if ( 'admin.php' === $pagenow && isset( $_GET['page'] ) && 'gutenberg' === $_GET['page'] ) {
265 wp_safe_redirect( admin_url( 'post-new.php?gutenberg-demo' ) );
266 exit;
267 }
268 }
269 add_action( 'admin_init', 'gutenberg_redirect_demo' );
270
271 /**
272 * Adds the filters to register additional links for the Gutenberg editor in
273 * the post/page screens.
274 *
275 * @since 1.5.0
276 */
277 function gutenberg_add_edit_link_filters() {
278 // For hierarchical post types.
279 add_filter( 'page_row_actions', 'gutenberg_add_edit_link', 10, 2 );
280 // For non-hierarchical post types.
281 add_filter( 'post_row_actions', 'gutenberg_add_edit_link', 10, 2 );
282 }
283 add_action( 'admin_init', 'gutenberg_add_edit_link_filters' );
284
285 /**
286 * Registers an additional link in the post/page screens to edit any post/page in
287 * the Classic editor.
288 *
289 * @since 1.5.0
290 *
291 * @param array $actions Post actions.
292 * @param WP_Post $post Edited post.
293 *
294 * @return array Updated post actions.
295 */
296 function gutenberg_add_edit_link( $actions, $post ) {
297 // Build the classic edit action. See also: WP_Posts_List_Table::handle_row_actions().
298 $title = _draft_or_post_title( $post->ID );
299
300 if ( 'wp_block' === $post->post_type ) {
301 unset( $actions['inline hide-if-no-js'] );
302
303 // Export uses block raw content, which is only returned from the post
304 // REST endpoint via `context=edit`, requiring edit capability.
305 $post_type = get_post_type_object( $post->post_type );
306 if ( ! current_user_can( $post_type->cap->edit_post, $post->ID ) ) {
307 return $actions;
308 }
309
310 $actions['export'] = sprintf(
311 '<button type="button" class="wp-list-reusable-blocks__export button-link" data-id="%s" aria-label="%s">%s</button>',
312 $post->ID,
313 esc_attr(
314 sprintf(
315 /* translators: %s: post title */
316 __( 'Export &#8220;%s&#8221; as JSON', 'gutenberg' ),
317 $title
318 )
319 ),
320 __( 'Export as JSON', 'gutenberg' )
321 );
322 return $actions;
323 }
324
325 if ( ! gutenberg_can_edit_post( $post ) ) {
326 return $actions;
327 }
328
329 $edit_url = get_edit_post_link( $post->ID, 'raw' );
330 $edit_url = add_query_arg( 'classic-editor', '', $edit_url );
331
332 $edit_action = array(
333 'classic' => sprintf(
334 '<a href="%s" aria-label="%s">%s</a>',
335 esc_url( $edit_url ),
336 esc_attr(
337 sprintf(
338 /* translators: %s: post title */
339 __( 'Edit &#8220;%s&#8221; in the classic editor', 'gutenberg' ),
340 $title
341 )
342 ),
343 __( 'Classic Editor', 'gutenberg' )
344 ),
345 );
346
347 // Insert the Classic Edit action after the Edit action.
348 $edit_offset = array_search( 'edit', array_keys( $actions ), true );
349 $actions = array_merge(
350 array_slice( $actions, 0, $edit_offset + 1 ),
351 $edit_action,
352 array_slice( $actions, $edit_offset + 1 )
353 );
354
355 return $actions;
356 }
357
358 /**
359 * Removes the Edit action from the reusable block list's Bulk Actions dropdown.
360 *
361 * @since 3.8.0
362 *
363 * @param array $actions Bulk actions.
364 *
365 * @return array Updated bulk actions.
366 */
367 function gutenberg_block_bulk_actions( $actions ) {
368 unset( $actions['edit'] );
369 return $actions;
370 }
371 add_filter( 'bulk_actions-edit-wp_block', 'gutenberg_block_bulk_actions' );
372
373 /**
374 * Prints the JavaScript to replace the default "Add New" button.$_COOKIE
375 *
376 * @since 1.5.0
377 */
378 function gutenberg_replace_default_add_new_button() {
379 global $typenow;
380
381 if ( 'wp_block' === $typenow ) {
382 ?>
383 <style type="text/css">
384 .page-title-action {
385 display: none;
386 }
387 </style>
388 <?php
389 }
390
391 if ( ! gutenberg_can_edit_post_type( $typenow ) ) {
392 return;
393 }
394
395 ?>
396 <style type="text/css">
397 .split-page-title-action {
398 display: inline-block;
399 }
400
401 .split-page-title-action a,
402 .split-page-title-action a:active,
403 .split-page-title-action .expander:after {
404 padding: 6px 10px;
405 position: relative;
406 top: -3px;
407 text-decoration: none;
408 border: 1px solid #ccc;
409 border-radius: 2px;
410 background: #f7f7f7;
411 text-shadow: none;
412 font-weight: 600;
413 font-size: 13px;
414 line-height: normal; /* IE8-IE11 need this for buttons */
415 color: #0073aa; /* some of these controls are button elements and don't inherit from links */
416 cursor: pointer;
417 outline: 0;
418 }
419
420 .split-page-title-action a:hover,
421 .split-page-title-action .expander:hover:after {
422 border-color: #008EC2;
423 background: #00a0d2;
424 color: #fff;
425 }
426
427 .split-page-title-action a:focus,
428 .split-page-title-action .expander:focus:after {
429 border-color: #5b9dd9;
430 box-shadow: 0 0 2px rgba( 30, 140, 190, 0.8 );
431 }
432
433 .split-page-title-action .expander:after {
434 content: "\f140";
435 font: 400 20px/.5 dashicons;
436 speak: none;
437 top: 1px;
438 <?php if ( is_rtl() ) : ?>
439 right: -1px;
440 <?php else : ?>
441 left: -1px;
442 <?php endif; ?>
443 position: relative;
444 vertical-align: top;
445 text-decoration: none !important;
446 padding: 4px 5px 4px 3px;
447 }
448
449 .split-page-title-action .dropdown {
450 display: none;
451 }
452
453 .split-page-title-action .dropdown.visible {
454 display: block;
455 position: absolute;
456 margin-top: 3px;
457 z-index: 1;
458 }
459
460 .split-page-title-action .dropdown.visible a {
461 display: block;
462 top: 0;
463 margin: -1px 0;
464 <?php if ( is_rtl() ) : ?>
465 padding-left: 9px;
466 <?php else : ?>
467 padding-right: 9px;
468 <?php endif; ?>
469 }
470
471 .split-page-title-action .expander {
472 outline: none;
473 }
474
475 </style>
476 <script type="text/javascript">
477 document.addEventListener( 'DOMContentLoaded', function() {
478 var buttons = document.getElementsByClassName( 'page-title-action' ),
479 button = buttons.item( 0 );
480
481 if ( ! button ) {
482 return;
483 }
484
485 var url = button.href;
486 var urlHasParams = ( -1 !== url.indexOf( '?' ) );
487 var classicUrl = url + ( urlHasParams ? '&' : '?' ) + 'classic-editor';
488
489 var newbutton = '<span id="split-page-title-action" class="split-page-title-action">';
490 newbutton += '<a href="' + url + '">' + button.innerText + '</a>';
491 newbutton += '<span class="expander" tabindex="0" role="button" aria-haspopup="true" aria-label="<?php echo esc_js( __( 'Toggle editor selection menu', 'gutenberg' ) ); ?>"></span>';
492 newbutton += '<span class="dropdown"><a href="' + url + '">Gutenberg</a>';
493 newbutton += '<a href="' + classicUrl + '"><?php echo esc_js( __( 'Classic Editor', 'gutenberg' ) ); ?></a></span></span><span class="page-title-action" style="display:none;"></span>';
494
495 button.insertAdjacentHTML( 'afterend', newbutton );
496 button.parentNode.removeChild( button );
497
498 var expander = document.getElementById( 'split-page-title-action' ).getElementsByClassName( 'expander' ).item( 0 );
499 var dropdown = expander.parentNode.querySelector( '.dropdown' );
500 function toggleDropdown() {
501 dropdown.classList.toggle( 'visible' );
502 }
503 expander.addEventListener( 'click', function( e ) {
504 e.preventDefault();
505 toggleDropdown();
506 } );
507 expander.addEventListener( 'keydown', function( e ) {
508 if ( 13 === e.which || 32 === e.which ) {
509 e.preventDefault();
510 toggleDropdown();
511 }
512 } );
513 } );
514 </script>
515 <?php
516 }
517 add_action( 'admin_print_scripts-edit.php', 'gutenberg_replace_default_add_new_button' );
518
519 /**
520 * Adds the block-editor-page class to the body tag on the Gutenberg page.
521 *
522 * @since 1.5.0
523 *
524 * @param string $classes Space separated string of classes being added to the body tag.
525 * @return string The $classes string, with block-editor-page appended.
526 */
527 function gutenberg_add_admin_body_class( $classes ) {
528 // gutenberg-editor-page is left for backward compatibility.
529 if ( current_theme_supports( 'editor-styles' ) && current_theme_supports( 'dark-editor-style' ) ) {
530 return "$classes block-editor-page gutenberg-editor-page is-fullscreen-mode wp-embed-responsive is-dark-theme";
531 } else {
532 // Default to is-fullscreen-mode to avoid jumps in the UI.
533 return "$classes block-editor-page gutenberg-editor-page is-fullscreen-mode wp-embed-responsive";
534 }
535 }
536
537 /**
538 * Adds attributes to kses allowed tags that aren't in the default list
539 * and that Gutenberg needs to save blocks such as the Gallery block.
540 *
541 * @param array $tags Allowed HTML.
542 * @return array (Maybe) modified allowed HTML.
543 */
544 function gutenberg_kses_allowedtags( $tags ) {
545 if ( isset( $tags['img'] ) ) {
546 $tags['img']['data-link'] = true;
547 $tags['img']['data-id'] = true;
548 }
549 return $tags;
550 }
551
552 add_filter( 'wp_kses_allowed_html', 'gutenberg_kses_allowedtags', 10, 2 );
553
554 /**
555 * Adds the wp-embed-responsive class to the body tag if the theme has opted in to
556 * Gutenberg responsive embeds.
557 *
558 * @since 4.1.0
559 *
560 * @param Array $classes Array of classes being added to the body tag.
561 * @return Array The $classes array, with wp-embed-responsive appended.
562 */
563 function gutenberg_add_responsive_body_class( $classes ) {
564 if ( current_theme_supports( 'responsive-embeds' ) ) {
565 $classes[] = 'wp-embed-responsive';
566 }
567 return $classes;
568 }
569
570 add_filter( 'body_class', 'gutenberg_add_responsive_body_class' );
571