PluginProbe
Gutenberg / 4.1.1
Gutenberg v4.1.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.1.1, at gutenberg.php

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