PluginProbe
Gutenberg / 4.3.0
Gutenberg v4.3.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 7.4.0 All 402 releases
gutenberg / lib / compat.php

compat.php in Gutenberg 4.3.0, at lib/compat.php

307 lines 8.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * PHP and WordPress configuration compatibility functions for the Gutenberg
4 * editor plugin.
5 *
6 * @package gutenberg
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 die( 'Silence is golden.' );
11 }
12
13 /**
14 * Splits a UTF-8 string into an array of UTF-8-encoded codepoints.
15 *
16 * @since 0.5.0
17 *
18 * Based on WordPress' _mb_substr() compat function.
19 *
20 * @param string $str The string to split.
21 * @return array
22 */
23 function _gutenberg_utf8_split( $str ) {
24 if ( _wp_can_use_pcre_u() ) {
25 // Use the regex unicode support to separate the UTF-8 characters into
26 // an array.
27 preg_match_all( '/./us', $str, $match );
28 return $match[0];
29 }
30
31 $regex = '/(
32 [\x00-\x7F] # single-byte sequences 0xxxxxxx
33 | [\xC2-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx
34 | \xE0[\xA0-\xBF][\x80-\xBF] # triple-byte sequences 1110xxxx 10xxxxxx * 2
35 | [\xE1-\xEC][\x80-\xBF]{2}
36 | \xED[\x80-\x9F][\x80-\xBF]
37 | [\xEE-\xEF][\x80-\xBF]{2}
38 | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences 11110xxx 10xxxxxx * 3
39 | [\xF1-\xF3][\x80-\xBF]{3}
40 | \xF4[\x80-\x8F][\x80-\xBF]{2}
41 )/x';
42
43 // Start with 1 element instead of 0 since the first thing we do is pop.
44 $chars = array( '' );
45 do {
46 // We had some string left over from the last round, but we counted it
47 // in that last round.
48 array_pop( $chars );
49
50 // Split by UTF-8 character, limit to 1000 characters (last array
51 // element will contain the rest of the string).
52 $pieces = preg_split(
53 $regex,
54 $str,
55 1000,
56 PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
57 );
58
59 $chars = array_merge( $chars, $pieces );
60
61 // If there's anything left over, repeat the loop.
62 if ( count( $pieces ) > 1 ) {
63 $str = array_pop( $pieces );
64 } else {
65 break;
66 }
67 } while ( $str );
68
69 return $chars;
70 }
71
72 /**
73 * Disables wpautop behavior in classic editor when post contains blocks, to
74 * prevent removep from invalidating paragraph blocks.
75 *
76 * @param array $settings Original editor settings.
77 * @param string $editor_id ID for the editor instance.
78 * @return array Filtered settings.
79 */
80 function gutenberg_disable_editor_settings_wpautop( $settings, $editor_id ) {
81 $post = get_post();
82 if ( 'content' === $editor_id && is_object( $post ) && has_blocks( $post ) ) {
83 $settings['wpautop'] = false;
84 }
85
86 return $settings;
87 }
88 add_filter( 'wp_editor_settings', 'gutenberg_disable_editor_settings_wpautop', 10, 2 );
89
90 /**
91 * Add rest nonce to the heartbeat response.
92 *
93 * @param array $response Original heartbeat response.
94 * @return array New heartbeat response.
95 */
96 function gutenberg_add_rest_nonce_to_heartbeat_response_headers( $response ) {
97 $response['rest-nonce'] = wp_create_nonce( 'wp_rest' );
98 return $response;
99 }
100 add_filter( 'wp_refresh_nonces', 'gutenberg_add_rest_nonce_to_heartbeat_response_headers' );
101
102 /**
103 * As a substitute for the default content `wpautop` filter, applies autop
104 * behavior only for posts where content does not contain blocks.
105 *
106 * @param string $content Post content.
107 * @return string Paragraph-converted text if non-block content.
108 */
109 function gutenberg_wpautop( $content ) {
110 if ( has_blocks( $content ) ) {
111 return $content;
112 }
113
114 return wpautop( $content );
115 }
116 remove_filter( 'the_content', 'wpautop' );
117 add_filter( 'the_content', 'gutenberg_wpautop', 6 );
118
119
120 /**
121 * Check if we need to load the block warning in the Classic Editor.
122 *
123 * @since 3.4.0
124 */
125 function gutenberg_check_if_classic_needs_warning_about_blocks() {
126 global $pagenow;
127
128 if ( ! in_array( $pagenow, array( 'post.php', 'post-new.php' ), true ) || ! isset( $_REQUEST['classic-editor'] ) ) {
129 return;
130 }
131
132 $post = get_post();
133 if ( ! $post ) {
134 return;
135 }
136
137 if ( ! has_blocks( $post ) ) {
138 return;
139 }
140
141 // Enqueue the JS we're going to need in the dialog.
142 wp_enqueue_script( 'wp-a11y' );
143 wp_enqueue_script( 'wp-sanitize' );
144
145 add_action( 'admin_footer', 'gutenberg_warn_classic_about_blocks' );
146 }
147 add_action( 'admin_enqueue_scripts', 'gutenberg_check_if_classic_needs_warning_about_blocks' );
148
149 /**
150 * Adds a warning to the Classic Editor when trying to edit a post containing blocks.
151 *
152 * @since 3.4.0
153 */
154 function gutenberg_warn_classic_about_blocks() {
155 $post = get_post();
156
157 $gutenberg_edit_link = get_edit_post_link( $post->ID, 'raw' );
158
159 $classic_edit_link = $gutenberg_edit_link;
160 $classic_edit_link = add_query_arg(
161 array(
162 'classic-editor' => '',
163 'hide-block-warning' => '',
164 ),
165 $classic_edit_link
166 );
167
168 $revisions_link = '';
169 if ( wp_revisions_enabled( $post ) ) {
170 $revisions = wp_get_post_revisions( $post );
171
172 // If there's only one revision, that won't help.
173 if ( count( $revisions ) > 1 ) {
174 reset( $revisions ); // Reset pointer for key().
175 $revisions_link = get_edit_post_link( key( $revisions ) );
176 }
177 }
178 ?>
179 <style type="text/css">
180 #blocks-in-post-dialog .notification-dialog {
181 position: fixed;
182 top: 50%;
183 left: 50%;
184 width: 500px;
185 box-sizing: border-box;
186 transform: translate(-50%, -50%);
187 margin: 0;
188 padding: 25px;
189 max-height: 90%;
190 background: #fff;
191 box-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);
192 line-height: 1.5;
193 z-index: 1000005;
194 overflow-y: auto;
195 }
196
197 @media only screen and (max-height: 480px), screen and (max-width: 450px) {
198 #blocks-in-post-dialog .notification-dialog {
199 top: 0;
200 left: 0;
201 width: 100%;
202 height: 100%;
203 transform: none;
204 max-height: 100%;
205 }
206 }
207 </style>
208
209 <div id="blocks-in-post-dialog" class="notification-dialog-wrap">
210 <div class="notification-dialog-background"></div>
211 <div class="notification-dialog">
212 <div class="blocks-in-post-message">
213 <p><?php _e( 'This post was previously edited in Gutenberg. You can continue in the Classic Editor, but you may lose data and formatting.', 'gutenberg' ); ?></p>
214 <?php
215 if ( $revisions_link ) {
216 ?>
217 <p>
218 <?php
219 /* translators: link to the post revisions page */
220 printf( __( 'You can also <a href="%s">browse previous revisions</a> and restore a version of the post before it was edited in Gutenberg.', 'gutenberg' ), esc_url( $revisions_link ) );
221 ?>
222 </p>
223 <?php
224 } else {
225 ?>
226 <p><strong><?php _e( 'Because this post does not have revisions, you will not be able to revert any changes you make in the Classic Editor.', 'gutenberg' ); ?></strong></p>
227 <?php
228 }
229 ?>
230 </div>
231 <p>
232 <a class="button button-primary blocks-in-post-gutenberg-button" href="<?php echo esc_url( $gutenberg_edit_link ); ?>"><?php _e( 'Edit in Gutenberg', 'gutenberg' ); ?></a>
233 <button type="button" class="button blocks-in-post-classic-button"><?php _e( 'Continue to Classic Editor', 'gutenberg' ); ?></button>
234 </p>
235 </div>
236 </div>
237
238 <script type="text/javascript">
239 /* <![CDATA[ */
240 ( function( $ ) {
241 var dialog = {};
242
243 dialog.init = function() {
244 // The modal
245 dialog.warning = $( '#blocks-in-post-dialog' );
246 // Get the links and buttons within the modal.
247 dialog.warningTabbables = dialog.warning.find( 'a, button' );
248
249 // Get the text within the modal.
250 dialog.rawMessage = dialog.warning.find( '.blocks-in-post-message' ).text();
251
252 // Hide all the #wpwrap content from assistive technologies.
253 $( '#wpwrap' ).attr( 'aria-hidden', 'true' );
254
255 // Detach the warning modal from its position and append it to the body.
256 $( document.body )
257 .addClass( 'modal-open' )
258 .append( dialog.warning.detach() );
259
260 // Reveal the modal and set focus on the Gutenberg button.
261 dialog.warning
262 .removeClass( 'hidden' )
263 .find( '.blocks-in-post-gutenberg-button' ).focus();
264
265 // Attach event handlers.
266 dialog.warningTabbables.on( 'keydown', dialog.constrainTabbing );
267 dialog.warning.on( 'click', '.blocks-in-post-classic-button', dialog.dismissWarning );
268
269 // Make screen readers announce the warning message after a short delay (necessary for some screen readers).
270 setTimeout( function() {
271 wp.a11y.speak( wp.sanitize.stripTags( dialog.rawMessage.replace( /\s+/g, ' ' ) ), 'assertive' );
272 }, 1000 );
273 };
274
275 dialog.constrainTabbing = function( event ) {
276 var firstTabbable, lastTabbable;
277
278 if ( 9 !== event.which ) {
279 return;
280 }
281
282 firstTabbable = dialog.warningTabbables.first()[0];
283 lastTabbable = dialog.warningTabbables.last()[0];
284
285 if ( lastTabbable === event.target && ! event.shiftKey ) {
286 firstTabbable.focus();
287 event.preventDefault();
288 } else if ( firstTabbable === event.target && event.shiftKey ) {
289 lastTabbable.focus();
290 event.preventDefault();
291 }
292 };
293
294 dialog.dismissWarning = function() {
295 // Hide modal.
296 dialog.warning.remove();
297 $( '#wpwrap' ).removeAttr( 'aria-hidden' );
298 $( 'body' ).removeClass( 'modal-open' );
299 };
300
301 $( document ).ready( dialog.init );
302 } )( jQuery );
303 /* ]]> */
304 </script>
305 <?php
306 }
307