| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | /** |
| 3 | - * Functions to register client-side assets (scripts and stylesheets) specific | |
| 4 | - * for the Gutenberg editor plugin. | |
| 3 | + * Functions to register client-side assets (scripts and stylesheets) for the | |
| 4 | + * Gutenberg editor plugin. | |
| 5 | 5 | * |
| 6 | 6 | * @package gutenberg |
| 7 | 7 | */ |
| 8 | 8 | |
| @@ -12,358 +12,585 @@ | ||
| 12 | 12 | |
| 13 | 13 | /** |
| 14 | 14 | * Retrieves the root plugin path. |
| 15 | 15 | * |
| 16 | + * @return string Root path to the gutenberg plugin. | |
| 17 | + * | |
| 16 | 18 | * @since 0.1.0 |
| 17 | - * | |
| 18 | - * @return string Root path to the gutenberg plugin. | |
| 19 | 19 | */ |
| 20 | 20 | function gutenberg_dir_path() { |
| 21 | - return plugin_dir_path( __DIR__ ); | |
| 21 | + return plugin_dir_path( dirname( __FILE__ ) ); | |
| 22 | 22 | } |
| 23 | 23 | |
| 24 | 24 | /** |
| 25 | 25 | * Retrieves a URL to a file in the gutenberg plugin. |
| 26 | 26 | * |
| 27 | - * @since 0.1.0 | |
| 28 | - * | |
| 29 | 27 | * @param string $path Relative path of the desired file. |
| 30 | 28 | * |
| 31 | 29 | * @return string Fully qualified URL pointing to the desired file. |
| 30 | + * | |
| 31 | + * @since 0.1.0 | |
| 32 | 32 | */ |
| 33 | 33 | function gutenberg_url( $path ) { |
| 34 | - return plugins_url( $path, __DIR__ ); | |
| 34 | + return plugins_url( $path, dirname( __FILE__ ) ); | |
| 35 | 35 | } |
| 36 | 36 | |
| 37 | 37 | /** |
| 38 | - * Filters the default translation file load behavior to load the Gutenberg | |
| 39 | - * plugin translation file, if available. | |
| 38 | + * Returns contents of an inline script used in appending polyfill scripts for | |
| 39 | + * browsers which fail the provided tests. The provided array is a mapping from | |
| 40 | + * a condition to verify feature support to its polyfill script handle. | |
| 40 | 41 | * |
| 41 | - * @param string|false $file Path to the translation file to load. False if | |
| 42 | - * there isn't one. | |
| 43 | - * @param string $handle Name of the script to register a translation | |
| 44 | - * domain to. | |
| 45 | - * | |
| 46 | - * @return string|false Filtered path to the Gutenberg translation file, if | |
| 47 | - * available. | |
| 42 | + * @param array $tests Features to detect. | |
| 43 | + * @return string Conditional polyfill inline script. | |
| 48 | 44 | */ |
| 49 | -function gutenberg_override_translation_file( $file, $handle ) { | |
| 50 | - if ( ! $file ) { | |
| 51 | - return $file; | |
| 52 | - } | |
| 45 | +function gutenberg_get_script_polyfill( $tests ) { | |
| 46 | + global $wp_scripts; | |
| 53 | 47 | |
| 54 | - // Ignore scripts whose handle does not have the "wp-" prefix. | |
| 55 | - if ( ! str_starts_with( $handle, 'wp-' ) ) { | |
| 56 | - return $file; | |
| 48 | + $polyfill = ''; | |
| 49 | + foreach ( $tests as $test => $handle ) { | |
| 50 | + if ( ! array_key_exists( $handle, $wp_scripts->registered ) ) { | |
| 51 | + continue; | |
| 52 | + } | |
| 53 | + | |
| 54 | + $polyfill .= ( | |
| 55 | + // Test presence of feature... | |
| 56 | + '( ' . $test . ' ) || ' . | |
| 57 | + // ...appending polyfill on any failures. Cautious viewers may balk | |
| 58 | + // at the `document.write`. Its caveat of synchronous mid-stream | |
| 59 | + // blocking write is exactly the behavior we need though. | |
| 60 | + 'document.write( \'<script src="' . | |
| 61 | + esc_url( $wp_scripts->registered[ $handle ]->src ) . | |
| 62 | + '"></scr\' + \'ipt>\' );' | |
| 63 | + ); | |
| 57 | 64 | } |
| 58 | 65 | |
| 59 | - // Ignore scripts that are not found in the expected `build/scripts/` location. | |
| 60 | - $script_path = gutenberg_dir_path() . 'build/scripts/' . substr( $handle, 3 ) . '/index.min.js'; | |
| 61 | - if ( ! file_exists( $script_path ) ) { | |
| 62 | - return $file; | |
| 63 | - } | |
| 66 | + return $polyfill; | |
| 67 | +} | |
| 64 | 68 | |
| 65 | - /* | |
| 66 | - * The default file will be in the plugins language directory, omitting the | |
| 67 | - * domain since Gutenberg assigns the script translations as the default. | |
| 68 | - * | |
| 69 | - * Example: /www/wp-content/languages/plugins/de_DE-07d88e6a803e01276b9bfcc1203e862e.json | |
| 70 | - * | |
| 71 | - * The logic of `load_script_textdomain` is such that it will assume to | |
| 72 | - * search in the plugins language directory, since the assigned source of | |
| 73 | - * the overridden Gutenberg script originates in the plugins directory. | |
| 74 | - * | |
| 75 | - * The plugin translation files each begin with the slug of the plugin, so | |
| 76 | - * it's a simple matter of prepending the Gutenberg plugin slug. | |
| 69 | +if ( ! function_exists( 'register_tinymce_scripts' ) ) { | |
| 70 | + /** | |
| 71 | + * Registers the main TinyMCE scripts. | |
| 77 | 72 | */ |
| 78 | - $path_parts = pathinfo( $file ); | |
| 79 | - $plugin_translation_file = ( | |
| 80 | - $path_parts['dirname'] . | |
| 81 | - '/gutenberg-' . | |
| 82 | - $path_parts['basename'] | |
| 83 | - ); | |
| 73 | + function register_tinymce_scripts() { | |
| 74 | + global $tinymce_version, $concatenate_scripts, $compress_scripts; | |
| 75 | + if ( ! isset( $concatenate_scripts ) ) { | |
| 76 | + script_concat_settings(); | |
| 77 | + } | |
| 78 | + $suffix = SCRIPT_DEBUG ? '' : '.min'; | |
| 79 | + $compressed = $compress_scripts && $concatenate_scripts && isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) | |
| 80 | + && false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' ); | |
| 81 | + // Load tinymce.js when running from /src, otherwise load wp-tinymce.js.gz (in production) or | |
| 82 | + // tinymce.min.js (when SCRIPT_DEBUG is true). | |
| 83 | + $mce_suffix = false !== strpos( get_bloginfo( 'version' ), '-src' ) ? '' : '.min'; | |
| 84 | + if ( $compressed ) { | |
| 85 | + gutenberg_override_script( 'wp-tinymce', includes_url( 'js/tinymce/' ) . 'wp-tinymce.php', array(), $tinymce_version ); | |
| 86 | + } else { | |
| 87 | + gutenberg_override_script( 'wp-tinymce-root', includes_url( 'js/tinymce/' ) . "tinymce{$mce_suffix}.js", array(), $tinymce_version ); | |
| 88 | + gutenberg_override_script( 'wp-tinymce', includes_url( 'js/tinymce/' ) . "plugins/compat3x/plugin{$suffix}.js", array( 'wp-tinymce-root' ), $tinymce_version ); | |
| 89 | + } | |
| 84 | 90 | |
| 85 | - return $plugin_translation_file; | |
| 91 | + gutenberg_override_script( 'wp-tinymce-lists', includes_url( 'js/tinymce/' ) . "plugins/lists/plugin{$suffix}.js", array( 'wp-tinymce' ), $tinymce_version ); | |
| 92 | + } | |
| 86 | 93 | } |
| 87 | -add_filter( 'load_script_translation_file', 'gutenberg_override_translation_file', 10, 2 ); | |
| 88 | 94 | |
| 89 | 95 | /** |
| 90 | - * Adds the 'editor' dependency to wp-block-library, required by the Classic block. | |
| 96 | + * Registers a script according to `wp_register_script`. Honors this request by | |
| 97 | + * deregistering any script by the same handler before registration. | |
| 91 | 98 | * |
| 92 | - * @param WP_Scripts $scripts WP_Scripts instance. | |
| 99 | + * @since 4.1.0 | |
| 100 | + * | |
| 101 | + * @param string $handle Name of the script. Should be unique. | |
| 102 | + * @param string $src Full URL of the script, or path of the script relative to the WordPress root directory. | |
| 103 | + * @param array $deps Optional. An array of registered script handles this script depends on. Default empty array. | |
| 104 | + * @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL | |
| 105 | + * as a query string for cache busting purposes. If version is set to false, a version | |
| 106 | + * number is automatically added equal to current installed WordPress version. | |
| 107 | + * If set to null, no version is added. | |
| 108 | + * @param bool $in_footer Optional. Whether to enqueue the script before </body> instead of in the <head>. | |
| 109 | + * Default 'false'. | |
| 93 | 110 | */ |
| 94 | -function gutenberg_register_block_library_script_special_case( $scripts ) { | |
| 95 | - $handle = 'wp-block-library'; | |
| 96 | - $script = $scripts->query( $handle, 'registered' ); | |
| 97 | - if ( ! in_array( 'editor', $script->deps, true ) ) { | |
| 98 | - $script->deps[] = 'editor'; | |
| 99 | - } | |
| 111 | +function gutenberg_override_script( $handle, $src, $deps = array(), $ver = false, $in_footer = false ) { | |
| 112 | + wp_deregister_script( $handle ); | |
| 113 | + wp_register_script( $handle, $src, $deps, $ver, $in_footer ); | |
| 100 | 114 | } |
| 101 | -add_action( 'wp_default_scripts', 'gutenberg_register_block_library_script_special_case', 11 ); | |
| 102 | 115 | |
| 103 | 116 | /** |
| 104 | - * Registers WordPress package styles with complex requirements. | |
| 117 | + * Registers a style according to `wp_register_style`. Honors this request by | |
| 118 | + * deregistering any style by the same handler before registration. | |
| 105 | 119 | * |
| 106 | - * Simple styles (main style.css with inferred dependencies) are auto-registered | |
| 107 | - * via build/styles.php at default priority (10). This function runs at priority 15 to handle: | |
| 108 | - * - Multiple style files per package (content.css, classic.css, etc.) | |
| 109 | - * - Non-WordPress dependencies (dashicons, common, forms) | |
| 110 | - * - Custom handles that don't match wp-{package} pattern | |
| 111 | - * - Conditional dependencies based on theme/settings | |
| 112 | - * - Dynamic filename logic | |
| 120 | + * @since 4.1.0 | |
| 113 | 121 | * |
| 114 | - * These override calls will replace the auto-registered versions as needed. | |
| 122 | + * @param string $handle Name of the stylesheet. Should be unique. | |
| 123 | + * @param string $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory. | |
| 124 | + * @param array $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array. | |
| 125 | + * @param string|bool|null $ver Optional. String specifying stylesheet version number, if it has one, which is added to the URL | |
| 126 | + * as a query string for cache busting purposes. If version is set to false, a version | |
| 127 | + * number is automatically added equal to current installed WordPress version. | |
| 128 | + * If set to null, no version is added. | |
| 129 | + * @param string $media Optional. The media for which this stylesheet has been defined. | |
| 130 | + * Default 'all'. Accepts media types like 'all', 'print' and 'screen', or media queries like | |
| 131 | + * '(orientation: portrait)' and '(max-width: 640px)'. | |
| 132 | + */ | |
| 133 | +function gutenberg_override_style( $handle, $src, $deps = array(), $ver = false, $media = 'all' ) { | |
| 134 | + wp_deregister_style( $handle ); | |
| 135 | + wp_register_style( $handle, $src, $deps, $ver, $media ); | |
| 136 | +} | |
| 137 | + | |
| 138 | +/** | |
| 139 | + * Registers all the WordPress packages scripts that are in the standardized | |
| 140 | + * `build/` location. | |
| 115 | 141 | * |
| 116 | - * @since 6.7.0 | |
| 117 | - * | |
| 118 | - * @global array $editor_styles | |
| 119 | - * | |
| 120 | - * @param WP_Styles $styles WP_Styles instance. | |
| 142 | + * @since 4.5.0 | |
| 121 | 143 | */ |
| 122 | -function gutenberg_register_packages_styles( $styles ) { | |
| 123 | - // When in production, use the plugin's version as the asset version; | |
| 124 | - // else (for development or test) default to use the current time. | |
| 125 | - $version = defined( 'GUTENBERG_VERSION' ) && ! SCRIPT_DEBUG ? GUTENBERG_VERSION : time(); | |
| 126 | - $suffix = SCRIPT_DEBUG ? '' : '.min'; | |
| 144 | +function gutenberg_register_packages_scripts() { | |
| 145 | + $packages_dependencies = include dirname( __FILE__ ) . '/packages-dependencies.php'; | |
| 127 | 146 | |
| 128 | - // wp-components: add dashicons (icon font dependency) | |
| 129 | - $styles->query( 'wp-components', 'registered' )->deps[] = 'dashicons'; | |
| 147 | + foreach ( $packages_dependencies as $handle => $dependencies ) { | |
| 148 | + // Remove `wp-` prefix from the handle to get the package's name. | |
| 149 | + $package_name = strpos( $handle, 'wp-' ) === 0 ? substr( $handle, 3 ) : $handle; | |
| 150 | + $path = "build/$package_name/index.js"; | |
| 151 | + gutenberg_override_script( | |
| 152 | + $handle, | |
| 153 | + gutenberg_url( $path ), | |
| 154 | + array_merge( $dependencies, array( 'wp-polyfill' ) ), | |
| 155 | + filemtime( gutenberg_dir_path() . $path ), | |
| 156 | + true | |
| 157 | + ); | |
| 158 | + } | |
| 159 | +} | |
| 130 | 160 | |
| 131 | - // wp-edit-post: add wp-edit-blocks (custom handle not auto-inferred) | |
| 132 | - $styles->query( 'wp-edit-post', 'registered' )->deps[] = 'wp-edit-blocks'; | |
| 161 | +/** | |
| 162 | + * Registers common scripts and styles to be used as dependencies of the editor | |
| 163 | + * and plugins. | |
| 164 | + * | |
| 165 | + * @since 0.1.0 | |
| 166 | + */ | |
| 167 | +function gutenberg_register_scripts_and_styles() { | |
| 168 | + gutenberg_register_vendor_scripts(); | |
| 133 | 169 | |
| 134 | - // wp-edit-site: add core WP styles and custom handles | |
| 135 | - $edit_site_style = $styles->query( 'wp-edit-site', 'registered' ); | |
| 136 | - $edit_site_style->deps[] = 'common'; | |
| 137 | - $edit_site_style->deps[] = 'forms'; | |
| 138 | - $edit_site_style->deps[] = 'wp-block-library-editor'; | |
| 170 | + register_tinymce_scripts(); | |
| 139 | 171 | |
| 140 | - // wp-edit-widgets: add wp-edit-blocks (custom handle not auto-inferred) | |
| 141 | - $styles->query( 'wp-edit-widgets', 'registered' )->deps[] = 'wp-edit-blocks'; | |
| 172 | + wp_add_inline_script( | |
| 173 | + 'wp-polyfill', | |
| 174 | + gutenberg_get_script_polyfill( | |
| 175 | + array( | |
| 176 | + '\'fetch\' in window' => 'wp-polyfill-fetch', | |
| 177 | + 'document.contains' => 'wp-polyfill-node-contains', | |
| 178 | + 'window.FormData && window.FormData.prototype.keys' => 'wp-polyfill-formdata', | |
| 179 | + 'Element.prototype.matches && Element.prototype.closest' => 'wp-polyfill-element-closest', | |
| 180 | + ), | |
| 181 | + 'after' | |
| 182 | + ) | |
| 183 | + ); | |
| 142 | 184 | |
| 143 | - // wp-customize-widgets: add wp-edit-blocks (custom handle not auto-inferred) | |
| 144 | - $styles->query( 'wp-customize-widgets', 'registered' )->deps[] = 'wp-edit-blocks'; | |
| 185 | + gutenberg_register_packages_scripts(); | |
| 145 | 186 | |
| 146 | - // Register wp-theme (Design System tokens from @wordpress/theme) as a | |
| 147 | - // dependency of wp-base-styles so its `:root` token block loads | |
| 148 | - // everywhere wp-base-styles does, including the editor iframe via | |
| 149 | - // $wp_edit_blocks_dependencies below. | |
| 150 | - gutenberg_override_style( | |
| 151 | - $styles, | |
| 152 | - 'wp-theme', | |
| 153 | - gutenberg_url( 'build/styles/theme/design-tokens' . $suffix . '.css' ), | |
| 154 | - array(), | |
| 155 | - $version | |
| 187 | + // Inline scripts. | |
| 188 | + wp_add_inline_script( | |
| 189 | + 'wp-api-fetch', | |
| 190 | + sprintf( | |
| 191 | + 'wp.apiFetch.use( wp.apiFetch.createNonceMiddleware( "%s" ) );', | |
| 192 | + ( wp_installing() && ! is_multisite() ) ? '' : wp_create_nonce( 'wp_rest' ) | |
| 193 | + ), | |
| 194 | + 'after' | |
| 156 | 195 | ); |
| 157 | - $styles->add_data( 'wp-theme', 'rtl', 'replace' ); | |
| 158 | - $styles->add_data( 'wp-theme', 'suffix', $suffix ); | |
| 159 | - $styles->add_data( 'wp-theme', 'path', gutenberg_dir_path() . 'build/styles/theme/design-tokens' . $suffix . '.css' ); | |
| 196 | + wp_add_inline_script( | |
| 197 | + 'wp-api-fetch', | |
| 198 | + sprintf( | |
| 199 | + 'wp.apiFetch.use( wp.apiFetch.createRootURLMiddleware( "%s" ) );', | |
| 200 | + esc_url_raw( get_rest_url() ) | |
| 201 | + ), | |
| 202 | + 'after' | |
| 203 | + ); | |
| 204 | + wp_add_inline_script( | |
| 205 | + 'wp-data', | |
| 206 | + implode( | |
| 207 | + "\n", | |
| 208 | + array( | |
| 209 | + '( function() {', | |
| 210 | + ' var userId = ' . get_current_user_ID() . ';', | |
| 211 | + ' var storageKey = "WP_DATA_USER_" + userId;', | |
| 212 | + ' wp.data', | |
| 213 | + ' .use( wp.data.plugins.persistence, { storageKey: storageKey } )', | |
| 214 | + ' .use( wp.data.plugins.controls );', | |
| 215 | + '} )()', | |
| 216 | + ) | |
| 217 | + ) | |
| 218 | + ); | |
| 219 | + global $wp_locale; | |
| 220 | + wp_add_inline_script( | |
| 221 | + 'wp-date', | |
| 222 | + sprintf( | |
| 223 | + 'wp.date.setSettings( %s );', | |
| 224 | + wp_json_encode( | |
| 225 | + array( | |
| 226 | + 'l10n' => array( | |
| 227 | + 'locale' => get_user_locale(), | |
| 228 | + 'months' => array_values( $wp_locale->month ), | |
| 229 | + 'monthsShort' => array_values( $wp_locale->month_abbrev ), | |
| 230 | + 'weekdays' => array_values( $wp_locale->weekday ), | |
| 231 | + 'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ), | |
| 232 | + 'meridiem' => (object) $wp_locale->meridiem, | |
| 233 | + 'relative' => array( | |
| 234 | + /* translators: %s: duration */ | |
| 235 | + 'future' => __( '%s from now', 'default' ), | |
| 236 | + /* translators: %s: duration */ | |
| 237 | + 'past' => __( '%s ago', 'default' ), | |
| 238 | + ), | |
| 239 | + ), | |
| 240 | + 'formats' => array( | |
| 241 | + 'time' => get_option( 'time_format', __( 'g:i a', 'default' ) ), | |
| 242 | + 'date' => get_option( 'date_format', __( 'F j, Y', 'default' ) ), | |
| 243 | + 'datetime' => __( 'F j, Y g:i a', 'default' ), | |
| 244 | + 'datetimeAbbreviated' => __( 'M j, Y g:i a', 'default' ), | |
| 245 | + ), | |
| 246 | + 'timezone' => array( | |
| 247 | + 'offset' => get_option( 'gmt_offset', 0 ), | |
| 248 | + 'string' => get_option( 'timezone_string', 'UTC' ), | |
| 249 | + ), | |
| 250 | + ) | |
| 251 | + ) | |
| 252 | + ), | |
| 253 | + 'after' | |
| 254 | + ); | |
| 255 | + wp_add_inline_script( | |
| 256 | + 'moment', | |
| 257 | + sprintf( | |
| 258 | + "moment.locale( '%s', %s );", | |
| 259 | + get_user_locale(), | |
| 260 | + wp_json_encode( | |
| 261 | + array( | |
| 262 | + 'months' => array_values( $wp_locale->month ), | |
| 263 | + 'monthsShort' => array_values( $wp_locale->month_abbrev ), | |
| 264 | + 'weekdays' => array_values( $wp_locale->weekday ), | |
| 265 | + 'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ), | |
| 266 | + 'week' => array( | |
| 267 | + 'dow' => (int) get_option( 'start_of_week', 0 ), | |
| 268 | + ), | |
| 269 | + 'longDateFormat' => array( | |
| 270 | + 'LT' => get_option( 'time_format', __( 'g:i a', 'default' ) ), | |
| 271 | + 'LTS' => null, | |
| 272 | + 'L' => null, | |
| 273 | + 'LL' => get_option( 'date_format', __( 'F j, Y', 'default' ) ), | |
| 274 | + 'LLL' => __( 'F j, Y g:i a', 'default' ), | |
| 275 | + 'LLLL' => null, | |
| 276 | + ), | |
| 277 | + ) | |
| 278 | + ) | |
| 279 | + ), | |
| 280 | + 'after' | |
| 281 | + ); | |
| 282 | + // Loading the old editor and its config to ensure the classic block works as expected. | |
| 283 | + wp_add_inline_script( | |
| 284 | + 'editor', | |
| 285 | + 'window.wp.oldEditor = window.wp.editor;', | |
| 286 | + 'after' | |
| 287 | + ); | |
| 160 | 288 | |
| 161 | - // Register wp-base-styles and add it to the already registered wp-admin stylesheet | |
| 162 | - gutenberg_override_style( | |
| 163 | - $styles, | |
| 164 | - 'wp-base-styles', | |
| 165 | - gutenberg_url( 'build/styles/base-styles/admin-schemes' . $suffix . '.css' ), | |
| 166 | - array( 'wp-theme' ), | |
| 167 | - $version | |
| 289 | + $tinymce_plugins = array( | |
| 290 | + 'charmap', | |
| 291 | + 'colorpicker', | |
| 292 | + 'hr', | |
| 293 | + 'lists', | |
| 294 | + 'media', | |
| 295 | + 'paste', | |
| 296 | + 'tabfocus', | |
| 297 | + 'textcolor', | |
| 298 | + 'fullscreen', | |
| 299 | + 'wordpress', | |
| 300 | + 'wpautoresize', | |
| 301 | + 'wpeditimage', | |
| 302 | + 'wpemoji', | |
| 303 | + 'wpgallery', | |
| 304 | + 'wplink', | |
| 305 | + 'wpdialogs', | |
| 306 | + 'wptextpattern', | |
| 307 | + 'wpview', | |
| 168 | 308 | ); |
| 169 | - $styles->add_data( 'wp-base-styles', 'rtl', 'replace' ); | |
| 170 | - $styles->add_data( 'wp-base-styles', 'suffix', $suffix ); | |
| 171 | - $styles->add_data( 'wp-base-styles', 'path', gutenberg_dir_path() . 'build/styles/base-styles/admin-schemes' . $suffix . '.css' ); | |
| 172 | - $styles->query( 'wp-admin', 'registered' )->deps[] = 'wp-base-styles'; | |
| 309 | + $tinymce_plugins = apply_filters( 'tiny_mce_plugins', $tinymce_plugins, 'classic-block' ); | |
| 310 | + $tinymce_plugins = array_unique( $tinymce_plugins ); | |
| 173 | 311 | |
| 174 | - gutenberg_override_style( | |
| 175 | - $styles, | |
| 176 | - 'wp-block-editor-content', | |
| 177 | - gutenberg_url( 'build/styles/block-editor/content' . $suffix . '.css' ), | |
| 178 | - array( 'wp-components' ), | |
| 179 | - $version | |
| 312 | + $toolbar1 = array( | |
| 313 | + 'formatselect', | |
| 314 | + 'bold', | |
| 315 | + 'italic', | |
| 316 | + 'bullist', | |
| 317 | + 'numlist', | |
| 318 | + 'blockquote', | |
| 319 | + 'alignleft', | |
| 320 | + 'aligncenter', | |
| 321 | + 'alignright', | |
| 322 | + 'link', | |
| 323 | + 'unlink', | |
| 324 | + 'wp_more', | |
| 325 | + 'spellchecker', | |
| 326 | + 'wp_add_media', | |
| 327 | + 'kitchensink', | |
| 180 | 328 | ); |
| 181 | - $styles->add_data( 'wp-block-editor-content', 'rtl', 'replace' ); | |
| 182 | - $styles->add_data( 'wp-block-editor-content', 'suffix', $suffix ); | |
| 329 | + $toolbar1 = apply_filters( 'mce_buttons', $toolbar1, 'classic-block' ); | |
| 183 | 330 | |
| 184 | - $block_library_filename = wp_should_load_separate_core_block_assets() ? 'common' : 'style'; | |
| 185 | - gutenberg_override_style( | |
| 186 | - $styles, | |
| 187 | - 'wp-block-library', | |
| 188 | - gutenberg_url( 'build/styles/block-library/' . $block_library_filename . $suffix . '.css' ), | |
| 189 | - array(), | |
| 190 | - $version | |
| 331 | + $toolbar2 = array( | |
| 332 | + 'strikethrough', | |
| 333 | + 'hr', | |
| 334 | + 'forecolor', | |
| 335 | + 'pastetext', | |
| 336 | + 'removeformat', | |
| 337 | + 'charmap', | |
| 338 | + 'outdent', | |
| 339 | + 'indent', | |
| 340 | + 'undo', | |
| 341 | + 'redo', | |
| 342 | + 'wp_help', | |
| 191 | 343 | ); |
| 192 | - $styles->add_data( 'wp-block-library', 'rtl', 'replace' ); | |
| 193 | - $styles->add_data( 'wp-block-library', 'suffix', $suffix ); | |
| 194 | - $styles->add_data( 'wp-block-library', 'path', gutenberg_dir_path() . 'build/styles/block-library/' . $block_library_filename . $suffix . '.css' ); | |
| 344 | + $toolbar2 = apply_filters( 'mce_buttons_2', $toolbar2, 'classic-block' ); | |
| 195 | 345 | |
| 196 | - // Only add CONTENT styles here that should be enqueued in the iframe! | |
| 197 | - $wp_edit_blocks_dependencies = array( | |
| 198 | - // Design System tokens load first so the `:root` CSS custom | |
| 199 | - // properties are defined before any consuming stylesheet reads | |
| 200 | - // them inside the editor iframe. | |
| 201 | - 'wp-theme', | |
| 202 | - 'wp-components', | |
| 203 | - // This need to be added before the block library styles, | |
| 204 | - // The block library styles override the "reset" styles. | |
| 205 | - 'wp-reset-editor-styles', | |
| 206 | - 'wp-block-library', | |
| 207 | - // Until #37466, we can't specifically add them as editor styles yet, | |
| 208 | - // so we must hard-code it here as a dependency. | |
| 209 | - 'wp-block-editor-content', | |
| 210 | - 'wp-base-styles', | |
| 346 | + $toolbar3 = apply_filters( 'mce_buttons_3', array(), 'classic-block' ); | |
| 347 | + $toolbar4 = apply_filters( 'mce_buttons_4', array(), 'classic-block' ); | |
| 348 | + | |
| 349 | + $external_plugins = apply_filters( 'mce_external_plugins', array(), 'classic-block' ); | |
| 350 | + | |
| 351 | + $tinymce_settings = array( | |
| 352 | + 'plugins' => implode( ',', $tinymce_plugins ), | |
| 353 | + 'toolbar1' => implode( ',', $toolbar1 ), | |
| 354 | + 'toolbar2' => implode( ',', $toolbar2 ), | |
| 355 | + 'toolbar3' => implode( ',', $toolbar3 ), | |
| 356 | + 'toolbar4' => implode( ',', $toolbar4 ), | |
| 357 | + 'external_plugins' => wp_json_encode( $external_plugins ), | |
| 358 | + 'classic_block_editor' => true, | |
| 211 | 359 | ); |
| 360 | + $tinymce_settings = apply_filters( 'tiny_mce_before_init', $tinymce_settings, 'classic-block' ); | |
| 212 | 361 | |
| 213 | - // Only load the default layout and margin styles for themes without theme.json file. | |
| 214 | - if ( ! wp_theme_has_theme_json() ) { | |
| 215 | - $wp_edit_blocks_dependencies[] = 'wp-editor-classic-layout-styles'; | |
| 362 | + // Do "by hand" translation from PHP array to js object. | |
| 363 | + // Prevents breakage in some custom settings. | |
| 364 | + $init_obj = ''; | |
| 365 | + foreach ( $tinymce_settings as $key => $value ) { | |
| 366 | + if ( is_bool( $value ) ) { | |
| 367 | + $val = $value ? 'true' : 'false'; | |
| 368 | + $init_obj .= $key . ':' . $val . ','; | |
| 369 | + continue; | |
| 370 | + } elseif ( ! empty( $value ) && is_string( $value ) && ( | |
| 371 | + ( '{' == $value{0} && '}' == $value{strlen( $value ) - 1} ) || | |
| 372 | + ( '[' == $value{0} && ']' == $value{strlen( $value ) - 1} ) || | |
| 373 | + preg_match( '/^\(?function ?\(/', $value ) ) ) { | |
| 374 | + | |
| 375 | + $init_obj .= $key . ':' . $value . ','; | |
| 376 | + continue; | |
| 377 | + } | |
| 378 | + $init_obj .= $key . ':"' . $value . '",'; | |
| 216 | 379 | } |
| 217 | 380 | |
| 218 | - global $editor_styles; | |
| 219 | - if ( current_theme_supports( 'wp-block-styles' ) && ( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 ) ) { | |
| 220 | - // Include opinionated block styles if the theme supports block styles and no $editor_styles are declared, so the editor never appears broken. | |
| 221 | - $wp_edit_blocks_dependencies[] = 'wp-block-library-theme'; | |
| 381 | + $init_obj = '{' . trim( $init_obj, ' ,' ) . '}'; | |
| 382 | + | |
| 383 | + $script = 'window.wpEditorL10n = { | |
| 384 | + tinymce: { | |
| 385 | + baseURL: ' . wp_json_encode( includes_url( 'js/tinymce' ) ) . ', | |
| 386 | + suffix: ' . ( SCRIPT_DEBUG ? '""' : '".min"' ) . ', | |
| 387 | + settings: ' . $init_obj . ', | |
| 388 | + } | |
| 389 | + }'; | |
| 390 | + | |
| 391 | + wp_add_inline_script( 'wp-block-library', $script, 'before' ); | |
| 392 | + | |
| 393 | + // Editor Styles. | |
| 394 | + // This empty stylesheet is defined to ensure backwards compatibility. | |
| 395 | + gutenberg_override_style( 'wp-blocks', false ); | |
| 396 | + $fonts_url = ''; | |
| 397 | + | |
| 398 | + /* | |
| 399 | + * Translators: Use this to specify the proper Google Font name and variants | |
| 400 | + * to load that is supported by your language. Do not translate. | |
| 401 | + * Set to 'off' to disable loading. | |
| 402 | + */ | |
| 403 | + $font_family = _x( 'Noto Serif:400,400i,700,700i', 'Google Font Name and Variants', 'gutenberg' ); | |
| 404 | + if ( 'off' !== $font_family ) { | |
| 405 | + $query_args = array( | |
| 406 | + 'family' => urlencode( $font_family ), | |
| 407 | + ); | |
| 408 | + $fonts_url = esc_url_raw( add_query_arg( $query_args, 'https://fonts.googleapis.com/css' ) ); | |
| 222 | 409 | } |
| 223 | 410 | |
| 224 | 411 | gutenberg_override_style( |
| 225 | - $styles, | |
| 226 | - 'wp-reset-editor-styles', | |
| 227 | - gutenberg_url( 'build/styles/block-library/reset' . $suffix . '.css' ), | |
| 228 | - array( 'common', 'forms' ), // Make sure the reset is loaded after the default WP Admin styles. | |
| 229 | - $version | |
| 412 | + 'wp-editor-font', | |
| 413 | + $fonts_url, | |
| 414 | + array(), | |
| 415 | + null | |
| 230 | 416 | ); |
| 231 | - $styles->add_data( 'wp-reset-editor-styles', 'rtl', 'replace' ); | |
| 232 | - $styles->add_data( 'wp-reset-editor-styles', 'suffix', $suffix ); | |
| 233 | 417 | |
| 234 | 418 | gutenberg_override_style( |
| 235 | - $styles, | |
| 236 | - 'wp-editor-classic-layout-styles', | |
| 237 | - gutenberg_url( 'build/styles/edit-post/classic' . $suffix . '.css' ), | |
| 419 | + 'wp-editor', | |
| 420 | + gutenberg_url( 'build/editor/style.css' ), | |
| 421 | + array( 'wp-components', 'wp-editor-font', 'wp-nux' ), | |
| 422 | + filemtime( gutenberg_dir_path() . 'build/editor/style.css' ) | |
| 423 | + ); | |
| 424 | + wp_style_add_data( 'wp-editor', 'rtl', 'replace' ); | |
| 425 | + | |
| 426 | + gutenberg_override_style( | |
| 427 | + 'wp-edit-post', | |
| 428 | + gutenberg_url( 'build/edit-post/style.css' ), | |
| 429 | + array( 'wp-components', 'wp-editor', 'wp-edit-blocks', 'wp-block-library', 'wp-nux' ), | |
| 430 | + filemtime( gutenberg_dir_path() . 'build/edit-post/style.css' ) | |
| 431 | + ); | |
| 432 | + wp_style_add_data( 'wp-edit-post', 'rtl', 'replace' ); | |
| 433 | + | |
| 434 | + gutenberg_override_style( | |
| 435 | + 'wp-components', | |
| 436 | + gutenberg_url( 'build/components/style.css' ), | |
| 238 | 437 | array(), |
| 239 | - $version | |
| 438 | + filemtime( gutenberg_dir_path() . 'build/components/style.css' ) | |
| 240 | 439 | ); |
| 241 | - $styles->add_data( 'wp-editor-classic-layout-styles', 'rtl', 'replace' ); | |
| 242 | - $styles->add_data( 'wp-editor-classic-layout-styles', 'suffix', $suffix ); | |
| 440 | + wp_style_add_data( 'wp-components', 'rtl', 'replace' ); | |
| 243 | 441 | |
| 244 | 442 | gutenberg_override_style( |
| 245 | - $styles, | |
| 246 | - 'wp-block-library-editor', | |
| 247 | - gutenberg_url( 'build/styles/block-library/editor' . $suffix . '.css' ), | |
| 443 | + 'wp-block-library', | |
| 444 | + gutenberg_url( 'build/block-library/style.css' ), | |
| 445 | + current_theme_supports( 'wp-block-styles' ) ? array( 'wp-block-library-theme' ) : array(), | |
| 446 | + filemtime( gutenberg_dir_path() . 'build/block-library/style.css' ) | |
| 447 | + ); | |
| 448 | + wp_style_add_data( 'wp-block-library', 'rtl', 'replace' ); | |
| 449 | + | |
| 450 | + gutenberg_override_style( | |
| 451 | + 'wp-format-library', | |
| 452 | + gutenberg_url( 'build/format-library/style.css' ), | |
| 248 | 453 | array(), |
| 249 | - $version | |
| 454 | + filemtime( gutenberg_dir_path() . 'build/format-library/style.css' ) | |
| 250 | 455 | ); |
| 251 | - $styles->add_data( 'wp-block-library-editor', 'rtl', 'replace' ); | |
| 252 | - $styles->add_data( 'wp-block-library-editor', 'suffix', $suffix ); | |
| 456 | + wp_style_add_data( 'wp-format-library', 'rtl', 'replace' ); | |
| 253 | 457 | |
| 254 | 458 | gutenberg_override_style( |
| 255 | - $styles, | |
| 256 | 459 | 'wp-edit-blocks', |
| 257 | - gutenberg_url( 'build/styles/block-library/editor' . $suffix . '.css' ), | |
| 258 | - $wp_edit_blocks_dependencies, | |
| 259 | - $version | |
| 460 | + gutenberg_url( 'build/block-library/editor.css' ), | |
| 461 | + array( | |
| 462 | + 'wp-components', | |
| 463 | + 'wp-editor', | |
| 464 | + 'wp-block-library', | |
| 465 | + // Always include visual styles so the editor never appears broken. | |
| 466 | + 'wp-block-library-theme', | |
| 467 | + ), | |
| 468 | + filemtime( gutenberg_dir_path() . 'build/block-library/editor.css' ) | |
| 260 | 469 | ); |
| 261 | - $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' ); | |
| 262 | - $styles->add_data( 'wp-edit-blocks', 'suffix', $suffix ); | |
| 470 | + wp_style_add_data( 'wp-edit-blocks', 'rtl', 'replace' ); | |
| 263 | 471 | |
| 264 | 472 | gutenberg_override_style( |
| 265 | - $styles, | |
| 473 | + 'wp-nux', | |
| 474 | + gutenberg_url( 'build/nux/style.css' ), | |
| 475 | + array( 'wp-components' ), | |
| 476 | + filemtime( gutenberg_dir_path() . 'build/nux/style.css' ) | |
| 477 | + ); | |
| 478 | + wp_style_add_data( 'wp-nux', 'rtl', 'replace' ); | |
| 479 | + | |
| 480 | + gutenberg_override_style( | |
| 266 | 481 | 'wp-block-library-theme', |
| 267 | - gutenberg_url( 'build/styles/block-library/theme' . $suffix . '.css' ), | |
| 482 | + gutenberg_url( 'build/block-library/theme.css' ), | |
| 268 | 483 | array(), |
| 269 | - $version | |
| 484 | + filemtime( gutenberg_dir_path() . 'build/block-library/theme.css' ) | |
| 270 | 485 | ); |
| 271 | - $styles->add_data( 'wp-block-library-theme', 'rtl', 'replace' ); | |
| 272 | - $styles->add_data( 'wp-block-library-theme', 'suffix', $suffix ); | |
| 486 | + wp_style_add_data( 'wp-block-library-theme', 'rtl', 'replace' ); | |
| 273 | 487 | |
| 274 | 488 | gutenberg_override_style( |
| 275 | - $styles, | |
| 276 | - 'classic-theme-styles', | |
| 277 | - gutenberg_url( 'build/styles/block-library/classic' . $suffix . '.css' ), | |
| 278 | - array(), | |
| 279 | - $version | |
| 489 | + 'wp-list-reusable-blocks', | |
| 490 | + gutenberg_url( 'build/list-reusable-blocks/style.css' ), | |
| 491 | + array( 'wp-components' ), | |
| 492 | + filemtime( gutenberg_dir_path() . 'build/list-reusable-blocks/style.css' ) | |
| 280 | 493 | ); |
| 281 | - $styles->add_data( 'classic-theme-styles', 'rtl', 'replace' ); | |
| 282 | - $styles->add_data( 'classic-theme-styles', 'suffix', $suffix ); | |
| 283 | - $styles->add_data( 'classic-theme-styles', 'path', gutenberg_dir_path() . 'build/styles/block-library/classic' . $suffix . '.css' ); | |
| 494 | + wp_style_add_data( 'wp-list-reusable-block', 'rtl', 'replace' ); | |
| 495 | + | |
| 496 | + if ( defined( 'GUTENBERG_LIVE_RELOAD' ) && GUTENBERG_LIVE_RELOAD ) { | |
| 497 | + $live_reload_url = ( GUTENBERG_LIVE_RELOAD === true ) ? 'http://localhost:35729/livereload.js' : GUTENBERG_LIVE_RELOAD; | |
| 498 | + | |
| 499 | + wp_enqueue_script( | |
| 500 | + 'gutenberg-live-reload', | |
| 501 | + $live_reload_url | |
| 502 | + ); | |
| 503 | + } | |
| 504 | + | |
| 505 | + // Temporary backward compatibility for `wp-polyfill-ecmascript`, which has | |
| 506 | + // since been absorbed into `wp-polyfill`. | |
| 507 | + // | |
| 508 | + // [TODO][REMOVEME] To be removed in Gutenberg v4.5. | |
| 509 | + gutenberg_override_script( | |
| 510 | + 'wp-polyfill-ecmascript', | |
| 511 | + null, | |
| 512 | + array( | |
| 513 | + 'wp-polyfill', | |
| 514 | + 'wp-deprecated', | |
| 515 | + ) | |
| 516 | + ); | |
| 517 | + wp_script_add_data( | |
| 518 | + 'wp-polyfill-ecmascript', | |
| 519 | + 'data', | |
| 520 | + 'wp.deprecated( "wp-polyfill-ecmascript script handle", { plugin: "Gutenberg", version: "4.5" } );' | |
| 521 | + ); | |
| 284 | 522 | } |
| 285 | -add_action( 'wp_default_styles', 'gutenberg_register_packages_styles', 15 ); | |
| 523 | +add_action( 'wp_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 ); | |
| 524 | +add_action( 'admin_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 ); | |
| 286 | 525 | |
| 287 | 526 | /** |
| 288 | - * Fetches, processes and compiles stored core styles, then combines and renders them to the page. | |
| 289 | - * Styles are stored via the Style Engine API. | |
| 527 | + * Append result of internal request to REST API for purpose of preloading | |
| 528 | + * data to be attached to the page. Expected to be called in the context of | |
| 529 | + * `array_reduce`. | |
| 290 | 530 | * |
| 291 | - * This hook also exists, and should be backported to Core in future versions. | |
| 292 | - * However, it is envisaged that Gutenberg will continue to use the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes to aid continuous development. | |
| 293 | - * | |
| 294 | - * @since 6.1 | |
| 295 | - * | |
| 296 | - * @link https://developer.wordpress.org/block-editor/reference-guides/packages/packages-style-engine/ | |
| 297 | - * | |
| 298 | - * @param array $options { | |
| 299 | - * Optional. An array of options to pass to gutenberg_style_engine_get_stylesheet_from_context(). Default empty array. | |
| 300 | - * | |
| 301 | - * @type bool $optimize Whether to optimize the CSS output, e.g., combine rules. Default is `false`. | |
| 302 | - * @type bool $prettify Whether to add new lines and indents to output. Default is the test of whether the global constant `SCRIPT_DEBUG` is defined. | |
| 303 | - * } | |
| 304 | - * | |
| 305 | - * @return void | |
| 531 | + * @param array $memo Reduce accumulator. | |
| 532 | + * @param string $path REST API path to preload. | |
| 533 | + * @return array Modified reduce accumulator. | |
| 306 | 534 | */ |
| 307 | -function gutenberg_enqueue_stored_styles( $options = array() ) { | |
| 308 | - $is_block_theme = wp_is_block_theme(); | |
| 309 | - $is_classic_theme = ! $is_block_theme; | |
| 535 | +function gutenberg_preload_api_request( $memo, $path ) { | |
| 310 | 536 | |
| 311 | - /* | |
| 312 | - * For block themes, print stored styles in the header. | |
| 313 | - * For classic themes, in the footer. | |
| 314 | - */ | |
| 315 | - if ( | |
| 316 | - ( $is_block_theme && doing_action( 'wp_footer' ) ) || | |
| 317 | - ( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) ) | |
| 318 | - ) { | |
| 319 | - return; | |
| 537 | + // array_reduce() doesn't support passing an array in PHP 5.2 | |
| 538 | + // so we need to make sure we start with one. | |
| 539 | + if ( ! is_array( $memo ) ) { | |
| 540 | + $memo = array(); | |
| 320 | 541 | } |
| 321 | 542 | |
| 322 | - $core_styles_keys = array( 'block-supports' ); | |
| 323 | - $compiled_core_stylesheet = ''; | |
| 324 | - $style_tag_id = 'core'; | |
| 325 | - foreach ( $core_styles_keys as $style_key ) { | |
| 326 | - // Adds comment if code is prettified to identify core styles sections in debugging. | |
| 327 | - $should_prettify = isset( $options['prettify'] ) ? true === $options['prettify'] : SCRIPT_DEBUG; | |
| 328 | - if ( $should_prettify ) { | |
| 329 | - $compiled_core_stylesheet .= "/**\n * Core styles: $style_key\n */\n"; | |
| 543 | + if ( empty( $path ) ) { | |
| 544 | + return $memo; | |
| 545 | + } | |
| 546 | + | |
| 547 | + $method = 'GET'; | |
| 548 | + if ( is_array( $path ) && 2 === count( $path ) ) { | |
| 549 | + $method = end( $path ); | |
| 550 | + $path = reset( $path ); | |
| 551 | + | |
| 552 | + if ( ! in_array( $method, array( 'GET', 'OPTIONS' ), true ) ) { | |
| 553 | + $method = 'GET'; | |
| 330 | 554 | } |
| 331 | - // Chains core store ids to signify what the styles contain. | |
| 332 | - $style_tag_id .= '-' . $style_key; | |
| 333 | - $compiled_core_stylesheet .= gutenberg_style_engine_get_stylesheet_from_context( $style_key, $options ); | |
| 334 | 555 | } |
| 335 | 556 | |
| 336 | - // Combines Core styles. | |
| 337 | - if ( ! empty( $compiled_core_stylesheet ) ) { | |
| 338 | - wp_register_style( $style_tag_id, false, array(), true ); | |
| 339 | - wp_add_inline_style( $style_tag_id, $compiled_core_stylesheet ); | |
| 340 | - wp_enqueue_style( $style_tag_id ); | |
| 557 | + $path_parts = parse_url( $path ); | |
| 558 | + if ( false === $path_parts ) { | |
| 559 | + return $memo; | |
| 341 | 560 | } |
| 342 | 561 | |
| 343 | - // If there are any other stores registered by themes etc., print them out. | |
| 344 | - $additional_stores = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_stores(); | |
| 345 | - | |
| 346 | - /* | |
| 347 | - * Since the corresponding action hook in Core is removed below, | |
| 348 | - * this function should still honour any styles stored using the Core Style Engine store. | |
| 349 | - */ | |
| 350 | - if ( class_exists( 'WP_Style_Engine_CSS_Rules_Store' ) ) { | |
| 351 | - $additional_stores = array_merge( $additional_stores, WP_Style_Engine_CSS_Rules_Store::get_stores() ); | |
| 562 | + $request = new WP_REST_Request( $method, $path_parts['path'] ); | |
| 563 | + if ( ! empty( $path_parts['query'] ) ) { | |
| 564 | + parse_str( $path_parts['query'], $query_params ); | |
| 565 | + $request->set_query_params( $query_params ); | |
| 352 | 566 | } |
| 353 | 567 | |
| 354 | - foreach ( array_keys( $additional_stores ) as $store_name ) { | |
| 355 | - if ( in_array( $store_name, $core_styles_keys, true ) ) { | |
| 356 | - continue; | |
| 568 | + $response = rest_do_request( $request ); | |
| 569 | + if ( 200 === $response->status ) { | |
| 570 | + $server = rest_get_server(); | |
| 571 | + $data = (array) $response->get_data(); | |
| 572 | + $links = $server->get_compact_response_links( $response ); | |
| 573 | + if ( ! empty( $links ) ) { | |
| 574 | + $data['_links'] = $links; | |
| 357 | 575 | } |
| 358 | - $styles = gutenberg_style_engine_get_stylesheet_from_context( $store_name, $options ); | |
| 359 | - if ( ! empty( $styles ) ) { | |
| 360 | - $key = "wp-style-engine-$store_name"; | |
| 361 | - wp_register_style( $key, false, array(), true ); | |
| 362 | - wp_add_inline_style( $key, $styles ); | |
| 363 | - wp_enqueue_style( $key ); | |
| 576 | + | |
| 577 | + if ( 'OPTIONS' === $method ) { | |
| 578 | + $response = rest_send_allow_header( $response, $server, $request ); | |
| 579 | + | |
| 580 | + $memo[ $method ][ $path ] = array( | |
| 581 | + 'body' => $data, | |
| 582 | + 'headers' => $response->headers, | |
| 583 | + ); | |
| 584 | + } else { | |
| 585 | + $memo[ $path ] = array( | |
| 586 | + 'body' => $data, | |
| 587 | + 'headers' => $response->headers, | |
| 588 | + ); | |
| 364 | 589 | } |
| 365 | 590 | } |
| 591 | + | |
| 592 | + return $memo; | |
| 366 | 593 | } |
| 367 | 594 | |
| 368 | 595 | /** |
| 369 | 596 | * Registers vendor JavaScript files to be used as dependencies of the editor |
| @@ -371,106 +598,808 @@ | ||
| 371 | 598 | * |
| 372 | 599 | * This function is called from a script during the plugin build process, so it |
| 373 | 600 | * should not call any WordPress PHP functions. |
| 374 | 601 | * |
| 375 | - * @since 13.0 | |
| 602 | + * @since 0.1.0 | |
| 603 | + */ | |
| 604 | +function gutenberg_register_vendor_scripts() { | |
| 605 | + $suffix = SCRIPT_DEBUG ? '' : '.min'; | |
| 606 | + | |
| 607 | + // Vendor Scripts. | |
| 608 | + $react_suffix = ( SCRIPT_DEBUG ? '.development' : '.production' ) . $suffix; | |
| 609 | + | |
| 610 | + gutenberg_register_vendor_script( | |
| 611 | + 'react', | |
| 612 | + 'https://unpkg.com/react@16.6.3/umd/react' . $react_suffix . '.js', | |
| 613 | + array( 'wp-polyfill' ) | |
| 614 | + ); | |
| 615 | + gutenberg_register_vendor_script( | |
| 616 | + 'react-dom', | |
| 617 | + 'https://unpkg.com/react-dom@16.6.3/umd/react-dom' . $react_suffix . '.js', | |
| 618 | + array( 'react' ) | |
| 619 | + ); | |
| 620 | + $moment_script = SCRIPT_DEBUG ? 'moment.js' : 'min/moment.min.js'; | |
| 621 | + gutenberg_register_vendor_script( | |
| 622 | + 'moment', | |
| 623 | + 'https://unpkg.com/moment@2.22.1/' . $moment_script, | |
| 624 | + array() | |
| 625 | + ); | |
| 626 | + gutenberg_register_vendor_script( | |
| 627 | + 'lodash', | |
| 628 | + 'https://unpkg.com/lodash@4.17.5/lodash' . $suffix . '.js' | |
| 629 | + ); | |
| 630 | + wp_add_inline_script( 'lodash', 'window.lodash = _.noConflict();' ); | |
| 631 | + gutenberg_register_vendor_script( | |
| 632 | + 'wp-polyfill-fetch', | |
| 633 | + 'https://unpkg.com/whatwg-fetch@3.0.0/dist/fetch.umd.js' | |
| 634 | + ); | |
| 635 | + gutenberg_register_vendor_script( | |
| 636 | + 'wp-polyfill-formdata', | |
| 637 | + 'https://unpkg.com/formdata-polyfill@3.0.9/formdata.min.js' | |
| 638 | + ); | |
| 639 | + gutenberg_register_vendor_script( | |
| 640 | + 'wp-polyfill-node-contains', | |
| 641 | + 'https://unpkg.com/polyfill-library@3.26.0-0/polyfills/Node/prototype/contains/polyfill.js' | |
| 642 | + ); | |
| 643 | + gutenberg_register_vendor_script( | |
| 644 | + 'wp-polyfill-element-closest', | |
| 645 | + 'https://unpkg.com/element-closest@2.0.2/element-closest.js' | |
| 646 | + ); | |
| 647 | + gutenberg_register_vendor_script( | |
| 648 | + 'wp-polyfill', | |
| 649 | + 'https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.0.0/polyfill' . $suffix . '.js' | |
| 650 | + ); | |
| 651 | +} | |
| 652 | + | |
| 653 | +/** | |
| 654 | + * Retrieves a unique and reasonably short and human-friendly filename for a | |
| 655 | + * vendor script based on a URL and the script handle. | |
| 376 | 656 | * |
| 377 | - * @param WP_Scripts $scripts WP_Scripts instance. | |
| 657 | + * @param string $handle The name of the script. | |
| 658 | + * @param string $src Full URL of the external script. | |
| 659 | + * | |
| 660 | + * @return string Script filename suitable for local caching. | |
| 661 | + * | |
| 662 | + * @since 0.1.0 | |
| 378 | 663 | */ |
| 379 | -function gutenberg_register_vendor_scripts( $scripts ) { | |
| 380 | - $extension = SCRIPT_DEBUG ? '.js' : '.min.js'; | |
| 381 | - $vendors_dir = gutenberg_dir_path() . 'build/scripts/vendors/'; | |
| 664 | +function gutenberg_vendor_script_filename( $handle, $src ) { | |
| 665 | + $match_tinymce_plugin = preg_match( | |
| 666 | + '@tinymce.*/plugins/([^/]+)/plugin(\.min)?\.js$@', | |
| 667 | + $src, | |
| 668 | + $tinymce_plugin_pieces | |
| 669 | + ); | |
| 670 | + if ( $match_tinymce_plugin ) { | |
| 671 | + $prefix = 'tinymce-plugin-' . $tinymce_plugin_pieces[1]; | |
| 672 | + $suffix = isset( $tinymce_plugin_pieces[2] ) ? $tinymce_plugin_pieces[2] : ''; | |
| 673 | + } else { | |
| 674 | + $filename = basename( $src ); | |
| 675 | + $match = preg_match( | |
| 676 | + '/^' | |
| 677 | + . '(?P<ignore>.*?)' | |
| 678 | + . '(?P<suffix>\.min)?' | |
| 679 | + . '(?P<extension>\.js)' | |
| 680 | + . '(?P<extra>.*)' | |
| 681 | + . '$/', | |
| 682 | + $filename, | |
| 683 | + $filename_pieces | |
| 684 | + ); | |
| 382 | 685 | |
| 383 | - // When the React 19 experiment is enabled, register React 19 vendor | |
| 384 | - // scripts under the `react`, `react-dom`, and `react-jsx-runtime` handles. | |
| 385 | - $use_react_19 = gutenberg_is_experiment_enabled( 'gutenberg-react-19' ); | |
| 686 | + $prefix = $handle; | |
| 687 | + $suffix = $match ? $filename_pieces['suffix'] : ''; | |
| 688 | + } | |
| 386 | 689 | |
| 387 | - $vendor_handles = array( 'react', 'react-dom', 'react-jsx-runtime' ); | |
| 690 | + $hash = substr( md5( $src ), 0, 8 ); | |
| 388 | 691 | |
| 389 | - foreach ( $vendor_handles as $handle ) { | |
| 390 | - $source = $use_react_19 ? $handle . '-19' : $handle; | |
| 391 | - $asset_file = $vendors_dir . $source . '.min.asset.php'; | |
| 392 | - $asset = file_exists( $asset_file ) ? require $asset_file : array(); | |
| 393 | - $dependencies = $asset['dependencies'] ?? array(); | |
| 394 | - $version = $asset['version'] ?? '0'; | |
| 692 | + return "${prefix}${suffix}.${hash}.js"; | |
| 693 | +} | |
| 395 | 694 | |
| 396 | - gutenberg_override_script( | |
| 397 | - $scripts, | |
| 398 | - $handle, | |
| 399 | - gutenberg_url( 'build/scripts/vendors/' . $source . $extension ), | |
| 400 | - $dependencies, | |
| 401 | - $version | |
| 402 | - ); | |
| 695 | +/** | |
| 696 | + * Registers a vendor script from a URL, preferring a locally cached version if | |
| 697 | + * possible, or downloading it if the cached version is unavailable or | |
| 698 | + * outdated. | |
| 699 | + * | |
| 700 | + * @param string $handle Name of the script. | |
| 701 | + * @param string $src Full URL of the external script. | |
| 702 | + * @param array $deps Optional. An array of registered script handles this | |
| 703 | + * script depends on. | |
| 704 | + * | |
| 705 | + * @since 0.1.0 | |
| 706 | + */ | |
| 707 | +function gutenberg_register_vendor_script( $handle, $src, $deps = array() ) { | |
| 708 | + if ( defined( 'GUTENBERG_LOAD_VENDOR_SCRIPTS' ) && ! GUTENBERG_LOAD_VENDOR_SCRIPTS ) { | |
| 709 | + return; | |
| 403 | 710 | } |
| 404 | 711 | |
| 405 | - // WordPress Core in `wp_register_development_scripts` sets `wp-react-refresh-entry` | |
| 406 | - // as a dependency to `react` when `SCRIPT_DEBUG` is true. Preserve that here. | |
| 407 | - if ( SCRIPT_DEBUG ) { | |
| 408 | - $react = $scripts->query( 'react', 'registered' ); | |
| 409 | - if ( $react && ! in_array( 'wp-react-refresh-entry', $react->deps, true ) ) { | |
| 410 | - $react->deps[] = 'wp-react-refresh-entry'; | |
| 712 | + $filename = gutenberg_vendor_script_filename( $handle, $src ); | |
| 713 | + | |
| 714 | + if ( defined( 'GUTENBERG_LIST_VENDOR_ASSETS' ) && GUTENBERG_LIST_VENDOR_ASSETS ) { | |
| 715 | + echo "$src|$filename\n"; | |
| 716 | + return; | |
| 717 | + } | |
| 718 | + | |
| 719 | + $full_path = gutenberg_dir_path() . 'vendor/' . $filename; | |
| 720 | + | |
| 721 | + $needs_fetch = ( | |
| 722 | + defined( 'GUTENBERG_DEVELOPMENT_MODE' ) && GUTENBERG_DEVELOPMENT_MODE && ( | |
| 723 | + ! file_exists( $full_path ) || | |
| 724 | + time() - filemtime( $full_path ) >= DAY_IN_SECONDS | |
| 725 | + ) | |
| 726 | + ); | |
| 727 | + | |
| 728 | + if ( $needs_fetch ) { | |
| 729 | + // Determine whether we can write to this file. If not, don't waste | |
| 730 | + // time doing a network request. | |
| 731 | + // @codingStandardsIgnoreStart | |
| 732 | + $f = @fopen( $full_path, 'a' ); | |
| 733 | + // @codingStandardsIgnoreEnd | |
| 734 | + if ( ! $f ) { | |
| 735 | + // Failed to open the file for writing, probably due to server | |
| 736 | + // permissions. Enqueue the script directly from the URL instead. | |
| 737 | + gutenberg_override_script( $handle, $src, $deps, null ); | |
| 738 | + return; | |
| 411 | 739 | } |
| 740 | + fclose( $f ); | |
| 741 | + $response = wp_remote_get( $src ); | |
| 742 | + if ( wp_remote_retrieve_response_code( $response ) === 200 ) { | |
| 743 | + $f = fopen( $full_path, 'w' ); | |
| 744 | + fwrite( $f, wp_remote_retrieve_body( $response ) ); | |
| 745 | + fclose( $f ); | |
| 746 | + } elseif ( ! filesize( $full_path ) ) { | |
| 747 | + // The request failed. If the file is already cached, continue to | |
| 748 | + // use this file. If not, then unlink the 0 byte file, and enqueue | |
| 749 | + // the script directly from the URL. | |
| 750 | + gutenberg_override_script( $handle, $src, $deps, null ); | |
| 751 | + unlink( $full_path ); | |
| 752 | + return; | |
| 753 | + } | |
| 412 | 754 | } |
| 755 | + gutenberg_override_script( | |
| 756 | + $handle, | |
| 757 | + gutenberg_url( 'vendor/' . $filename ), | |
| 758 | + $deps, | |
| 759 | + null | |
| 760 | + ); | |
| 413 | 761 | } |
| 414 | -add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' ); | |
| 415 | 762 | |
| 416 | 763 | /** |
| 417 | - * Registers or re-registers Gutenberg Script Modules. | |
| 764 | + * Prepares server-registered blocks for JavaScript, returning an associative | |
| 765 | + * array of registered block data keyed by block name. Data includes properties | |
| 766 | + * of a block relevant for client registration. | |
| 418 | 767 | * |
| 419 | - * Script modules that are registered by Core will be re-registered by Gutenberg. | |
| 768 | + * @return array An associative array of registered block data. | |
| 769 | + */ | |
| 770 | +function gutenberg_prepare_blocks_for_js() { | |
| 771 | + $block_registry = WP_Block_Type_Registry::get_instance(); | |
| 772 | + $blocks = array(); | |
| 773 | + $keys_to_pick = array( 'title', 'description', 'icon', 'category', 'keywords', 'supports', 'attributes' ); | |
| 774 | + | |
| 775 | + foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) { | |
| 776 | + foreach ( $keys_to_pick as $key ) { | |
| 777 | + if ( ! isset( $block_type->{ $key } ) ) { | |
| 778 | + continue; | |
| 779 | + } | |
| 780 | + | |
| 781 | + if ( ! isset( $blocks[ $block_name ] ) ) { | |
| 782 | + $blocks[ $block_name ] = array(); | |
| 783 | + } | |
| 784 | + | |
| 785 | + $blocks[ $block_name ][ $key ] = $block_type->{ $key }; | |
| 786 | + } | |
| 787 | + } | |
| 788 | + | |
| 789 | + return $blocks; | |
| 790 | +} | |
| 791 | + | |
| 792 | +/** | |
| 793 | + * Handles the enqueueing of block scripts and styles that are common to both | |
| 794 | + * the editor and the front-end. | |
| 420 | 795 | * |
| 421 | - * @since 19.3.0 | |
| 796 | + * Note: This function must remain *before* | |
| 797 | + * `gutenberg_editor_scripts_and_styles` so that editor-specific stylesheets | |
| 798 | + * are loaded last. | |
| 799 | + * | |
| 800 | + * @since 0.4.0 | |
| 422 | 801 | */ |
| 423 | -function gutenberg_define_interactivity_modules_support() { | |
| 424 | - // Load the auto-generated module registry. | |
| 425 | - $modules_registry_file = gutenberg_dir_path() . 'build/modules/index.php'; | |
| 426 | - if ( ! file_exists( $modules_registry_file ) ) { | |
| 802 | +function gutenberg_common_scripts_and_styles() { | |
| 803 | + if ( ! is_gutenberg_page() && is_admin() ) { | |
| 427 | 804 | return; |
| 428 | 805 | } |
| 429 | 806 | |
| 430 | - $modules = require $modules_registry_file; | |
| 807 | + // Enqueue basic styles built out of Gutenberg through `npm build`. | |
| 808 | + wp_enqueue_style( 'wp-block-library' ); | |
| 431 | 809 | |
| 432 | - // Add client navigation support to block library modules. | |
| 433 | - foreach ( $modules as $module ) { | |
| 434 | - if ( str_starts_with( $module['id'], '@wordpress/block-library' ) && method_exists( 'WP_Interactivity_API', 'add_client_navigation_support_to_script_module' ) ) { | |
| 435 | - wp_interactivity()->add_client_navigation_support_to_script_module( $module['id'] ); | |
| 810 | + /* | |
| 811 | + * Enqueue block styles built through plugins. This lives in a separate | |
| 812 | + * action for a couple of reasons: (1) we want to load these assets | |
| 813 | + * (usually stylesheets) in *both* frontend and editor contexts, and (2) | |
| 814 | + * one day we may need to be smarter about whether assets are included | |
| 815 | + * based on whether blocks are actually present on the page. | |
| 816 | + */ | |
| 817 | + | |
| 818 | + /** | |
| 819 | + * Fires after enqueuing block assets for both editor and front-end. | |
| 820 | + * | |
| 821 | + * Call `add_action` on any hook before 'wp_enqueue_scripts'. | |
| 822 | + * | |
| 823 | + * In the function call you supply, simply use `wp_enqueue_script` and | |
| 824 | + * `wp_enqueue_style` to add your functionality to the Gutenberg editor. | |
| 825 | + * | |
| 826 | + * @since 0.4.0 | |
| 827 | + */ | |
| 828 | + do_action( 'enqueue_block_assets' ); | |
| 829 | +} | |
| 830 | +add_action( 'wp_enqueue_scripts', 'gutenberg_common_scripts_and_styles' ); | |
| 831 | +add_action( 'admin_enqueue_scripts', 'gutenberg_common_scripts_and_styles' ); | |
| 832 | + | |
| 833 | +/** | |
| 834 | + * Enqueues registered block scripts and styles, depending on current rendered | |
| 835 | + * context (only enqueuing editor scripts while in context of the editor). | |
| 836 | + * | |
| 837 | + * @since 2.0.0 | |
| 838 | + */ | |
| 839 | +function gutenberg_enqueue_registered_block_scripts_and_styles() { | |
| 840 | + $is_editor = ( 'enqueue_block_editor_assets' === current_action() ); | |
| 841 | + | |
| 842 | + $block_registry = WP_Block_Type_Registry::get_instance(); | |
| 843 | + foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) { | |
| 844 | + // Front-end styles. | |
| 845 | + if ( ! empty( $block_type->style ) ) { | |
| 846 | + wp_enqueue_style( $block_type->style ); | |
| 436 | 847 | } |
| 848 | + | |
| 849 | + // Front-end script. | |
| 850 | + if ( ! empty( $block_type->script ) ) { | |
| 851 | + wp_enqueue_script( $block_type->script ); | |
| 852 | + } | |
| 853 | + | |
| 854 | + // Editor styles. | |
| 855 | + if ( $is_editor && ! empty( $block_type->editor_style ) ) { | |
| 856 | + wp_enqueue_style( $block_type->editor_style ); | |
| 857 | + } | |
| 858 | + | |
| 859 | + // Editor script. | |
| 860 | + if ( $is_editor && ! empty( $block_type->editor_script ) ) { | |
| 861 | + wp_enqueue_script( $block_type->editor_script ); | |
| 862 | + } | |
| 437 | 863 | } |
| 438 | 864 | } |
| 439 | -remove_action( 'wp_default_scripts', 'wp_define_interactivity_modules_support' ); | |
| 440 | -add_action( 'wp_default_scripts', 'gutenberg_define_interactivity_modules_support' ); | |
| 865 | +add_action( 'enqueue_block_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); | |
| 866 | +add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); | |
| 441 | 867 | |
| 442 | 868 | /** |
| 443 | - * Always remove the Core action hook while gutenberg_enqueue_stored_styles() exists to avoid styles being printed twice. | |
| 444 | - * This is also because gutenberg_enqueue_stored_styles uses the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes, | |
| 445 | - * which are in continuous development and generally ahead of Core. | |
| 869 | + * Assigns a default editor template with a default block by post format, if | |
| 870 | + * not otherwise assigned for a new post of type "post". | |
| 871 | + * | |
| 872 | + * @param array $settings Default editor settings. | |
| 873 | + * @param WP_Post $post Post being edited. | |
| 874 | + * | |
| 875 | + * @return array Filtered block editor settings. | |
| 446 | 876 | */ |
| 447 | -remove_action( 'wp_enqueue_scripts', 'wp_enqueue_stored_styles' ); | |
| 448 | -remove_action( 'wp_footer', 'wp_enqueue_stored_styles', 1 ); | |
| 877 | +function gutenberg_default_post_format_template( $settings, $post ) { | |
| 878 | + // Only assign template for new posts without explicitly assigned template. | |
| 879 | + $is_new_post = 'auto-draft' === $post->post_status; | |
| 880 | + if ( $is_new_post && ! isset( $settings['template'] ) && 'post' === $post->post_type ) { | |
| 881 | + switch ( get_post_format() ) { | |
| 882 | + case 'audio': | |
| 883 | + $default_block_name = 'core/audio'; | |
| 884 | + break; | |
| 885 | + case 'gallery': | |
| 886 | + $default_block_name = 'core/gallery'; | |
| 887 | + break; | |
| 888 | + case 'image': | |
| 889 | + $default_block_name = 'core/image'; | |
| 890 | + break; | |
| 891 | + case 'quote': | |
| 892 | + $default_block_name = 'core/quote'; | |
| 893 | + break; | |
| 894 | + case 'video': | |
| 895 | + $default_block_name = 'core/video'; | |
| 896 | + break; | |
| 897 | + } | |
| 449 | 898 | |
| 450 | -// Enqueue stored styles. | |
| 451 | -add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_stored_styles' ); | |
| 452 | -add_action( 'wp_footer', 'gutenberg_enqueue_stored_styles', 1 ); | |
| 899 | + if ( isset( $default_block_name ) ) { | |
| 900 | + $settings['template'] = array( array( $default_block_name ) ); | |
| 901 | + } | |
| 902 | + } | |
| 453 | 903 | |
| 454 | -add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_latex_to_mathml_loader' ); | |
| 455 | -function gutenberg_enqueue_latex_to_mathml_loader() { | |
| 456 | - wp_enqueue_script_module( '@wordpress/latex-to-mathml/loader' ); | |
| 904 | + return $settings; | |
| 457 | 905 | } |
| 906 | +add_filter( 'block_editor_settings', 'gutenberg_default_post_format_template', 10, 2 ); | |
| 458 | 907 | |
| 459 | 908 | /** |
| 460 | - * Enqueue the vips loader script module in the block editor. | |
| 909 | + * Retrieve a stored autosave that is newer than the post save. | |
| 461 | 910 | * |
| 462 | - * This registers @wordpress/vips/worker as a dynamic dependency in the import map, | |
| 463 | - * enabling on-demand loading of the ~3.8MB WASM-based image processing module | |
| 464 | - * when client-side media processing is triggered via @wordpress/upload-media. | |
| 911 | + * Deletes autosaves that are older than the post save. | |
| 465 | 912 | * |
| 466 | - * @see packages/vips/src/loader.ts | |
| 913 | + * @param WP_Post $post Post object. | |
| 914 | + * @return WP_Post|boolean The post autosave. False if none found. | |
| 467 | 915 | */ |
| 468 | -add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_vips_loader' ); | |
| 469 | -function gutenberg_enqueue_vips_loader() { | |
| 470 | - wp_enqueue_script_module( '@wordpress/vips/loader' ); | |
| 916 | +function gutenberg_get_autosave_newer_than_post_save( $post ) { | |
| 917 | + // Add autosave data if it is newer and changed. | |
| 918 | + $autosave = wp_get_post_autosave( $post->ID ); | |
| 919 | + | |
| 920 | + if ( ! $autosave ) { | |
| 921 | + return false; | |
| 922 | + } | |
| 923 | + | |
| 924 | + // Check if the autosave is newer than the current post. | |
| 925 | + if ( | |
| 926 | + mysql2date( 'U', $autosave->post_modified_gmt, false ) > mysql2date( 'U', $post->post_modified_gmt, false ) | |
| 927 | + ) { | |
| 928 | + return $autosave; | |
| 929 | + } | |
| 930 | + | |
| 931 | + // If the autosave isn't newer, remove it. | |
| 932 | + wp_delete_post_revision( $autosave->ID ); | |
| 933 | + | |
| 934 | + return false; | |
| 471 | 935 | } |
| 472 | 936 | |
| 473 | -add_action( 'admin_enqueue_scripts', 'gutenberg_enqueue_core_abilities' ); | |
| 474 | -function gutenberg_enqueue_core_abilities() { | |
| 475 | - wp_enqueue_script_module( '@wordpress/core-abilities' ); | |
| 937 | +/** | |
| 938 | + * Returns all the block categories. | |
| 939 | + * | |
| 940 | + * @since 2.2.0 | |
| 941 | + * | |
| 942 | + * @param WP_Post $post Post object. | |
| 943 | + * @return Object[] Block categories. | |
| 944 | + */ | |
| 945 | +function gutenberg_get_block_categories( $post ) { | |
| 946 | + $default_categories = array( | |
| 947 | + array( | |
| 948 | + 'slug' => 'common', | |
| 949 | + 'title' => __( 'Common Blocks', 'gutenberg' ), | |
| 950 | + 'icon' => null, | |
| 951 | + ), | |
| 952 | + array( | |
| 953 | + 'slug' => 'formatting', | |
| 954 | + 'title' => __( 'Formatting', 'gutenberg' ), | |
| 955 | + 'icon' => null, | |
| 956 | + ), | |
| 957 | + array( | |
| 958 | + 'slug' => 'layout', | |
| 959 | + 'title' => __( 'Layout Elements', 'gutenberg' ), | |
| 960 | + 'icon' => null, | |
| 961 | + ), | |
| 962 | + array( | |
| 963 | + 'slug' => 'widgets', | |
| 964 | + 'title' => __( 'Widgets', 'gutenberg' ), | |
| 965 | + 'icon' => null, | |
| 966 | + ), | |
| 967 | + array( | |
| 968 | + 'slug' => 'embed', | |
| 969 | + 'title' => __( 'Embeds', 'gutenberg' ), | |
| 970 | + 'icon' => null, | |
| 971 | + ), | |
| 972 | + array( | |
| 973 | + 'slug' => 'reusable', | |
| 974 | + 'title' => __( 'Reusable Blocks', 'gutenberg' ), | |
| 975 | + 'icon' => null, | |
| 976 | + ), | |
| 977 | + ); | |
| 978 | + | |
| 979 | + return apply_filters( 'block_categories', $default_categories, $post ); | |
| 476 | 980 | } |
| 981 | + | |
| 982 | +/** | |
| 983 | + * Loads Gutenberg Locale Data. | |
| 984 | + */ | |
| 985 | +function gutenberg_load_locale_data() { | |
| 986 | + // Prepare Jed locale data. | |
| 987 | + $locale_data = gutenberg_get_jed_locale_data( 'gutenberg' ); | |
| 988 | + wp_add_inline_script( | |
| 989 | + 'wp-i18n', | |
| 990 | + 'wp.i18n.setLocaleData( ' . json_encode( $locale_data ) . ' );' | |
| 991 | + ); | |
| 992 | +} | |
| 993 | + | |
| 994 | +/** | |
| 995 | + * Retrieve The available image sizes for a post | |
| 996 | + * | |
| 997 | + * @return array | |
| 998 | + */ | |
| 999 | +function gutenberg_get_available_image_sizes() { | |
| 1000 | + $size_names = apply_filters( | |
| 1001 | + 'image_size_names_choose', | |
| 1002 | + array( | |
| 1003 | + 'thumbnail' => __( 'Thumbnail', 'gutenberg' ), | |
| 1004 | + 'medium' => __( 'Medium', 'gutenberg' ), | |
| 1005 | + 'large' => __( 'Large', 'gutenberg' ), | |
| 1006 | + 'full' => __( 'Full Size', 'gutenberg' ), | |
| 1007 | + ) | |
| 1008 | + ); | |
| 1009 | + | |
| 1010 | + $all_sizes = array(); | |
| 1011 | + foreach ( $size_names as $size_slug => $size_name ) { | |
| 1012 | + $all_sizes[] = array( | |
| 1013 | + 'slug' => $size_slug, | |
| 1014 | + 'name' => $size_name, | |
| 1015 | + ); | |
| 1016 | + } | |
| 1017 | + | |
| 1018 | + return $all_sizes; | |
| 1019 | +} | |
| 1020 | + | |
| 1021 | +/** | |
| 1022 | + * Scripts & Styles. | |
| 1023 | + * | |
| 1024 | + * Enqueues the needed scripts and styles when visiting the top-level page of | |
| 1025 | + * the Gutenberg editor. | |
| 1026 | + * | |
| 1027 | + * @since 0.1.0 | |
| 1028 | + * | |
| 1029 | + * @param string $hook Screen name. | |
| 1030 | + */ | |
| 1031 | +function gutenberg_editor_scripts_and_styles( $hook ) { | |
| 1032 | + $is_demo = isset( $_GET['gutenberg-demo'] ); | |
| 1033 | + | |
| 1034 | + global $wp_scripts; | |
| 1035 | + | |
| 1036 | + // Add "wp-hooks" as dependency of "heartbeat". | |
| 1037 | + $heartbeat_script = $wp_scripts->query( 'heartbeat', 'registered' ); | |
| 1038 | + if ( $heartbeat_script && ! in_array( 'wp-hooks', $heartbeat_script->deps ) ) { | |
| 1039 | + $heartbeat_script->deps[] = 'wp-hooks'; | |
| 1040 | + } | |
| 1041 | + | |
| 1042 | + // Enqueue heartbeat separately as an "optional" dependency of the editor. | |
| 1043 | + // Heartbeat is used for automatic nonce refreshing, but some hosts choose | |
| 1044 | + // to disable it outright. | |
| 1045 | + wp_enqueue_script( 'heartbeat' ); | |
| 1046 | + | |
| 1047 | + // Transform a "heartbeat-tick" jQuery event into "heartbeat.tick" hook action. | |
| 1048 | + // This removes the need of using jQuery for listening to the event. | |
| 1049 | + wp_add_inline_script( | |
| 1050 | + 'heartbeat', | |
| 1051 | + 'jQuery( document ).on( "heartbeat-tick", function ( event, response ) { wp.hooks.doAction( "heartbeat.tick", response ) } );', | |
| 1052 | + 'after' | |
| 1053 | + ); | |
| 1054 | + | |
| 1055 | + // Ignore Classic Editor's `rich_editing` user option, aka "Disable visual | |
| 1056 | + // editor". Forcing this to be true guarantees that TinyMCE and its plugins | |
| 1057 | + // are available in Gutenberg. Fixes | |
| 1058 | + // https://github.com/WordPress/gutenberg/issues/5667. | |
| 1059 | + $user_can_richedit = user_can_richedit(); | |
| 1060 | + add_filter( 'user_can_richedit', '__return_true' ); | |
| 1061 | + | |
| 1062 | + wp_enqueue_script( 'wp-edit-post' ); | |
| 1063 | + wp_enqueue_script( 'wp-format-library' ); | |
| 1064 | + wp_enqueue_style( 'wp-format-library' ); | |
| 1065 | + | |
| 1066 | + global $post; | |
| 1067 | + | |
| 1068 | + // Set initial title to empty string for auto draft for duration of edit. | |
| 1069 | + // Otherwise, title defaults to and displays as "Auto Draft". | |
| 1070 | + $is_new_post = 'auto-draft' === $post->post_status; | |
| 1071 | + | |
| 1072 | + // Set the post type name. | |
| 1073 | + $post_type = get_post_type( $post ); | |
| 1074 | + $post_type_object = get_post_type_object( $post_type ); | |
| 1075 | + $rest_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name; | |
| 1076 | + | |
| 1077 | + $preload_paths = array( | |
| 1078 | + '/', | |
| 1079 | + '/wp/v2/types?context=edit', | |
| 1080 | + '/wp/v2/taxonomies?per_page=-1&context=edit', | |
| 1081 | + '/wp/v2/themes?status=active', | |
| 1082 | + sprintf( '/wp/v2/%s/%s?context=edit', $rest_base, $post->ID ), | |
| 1083 | + sprintf( '/wp/v2/types/%s?context=edit', $post_type ), | |
| 1084 | + sprintf( '/wp/v2/users/me?post_type=%s&context=edit', $post_type ), | |
| 1085 | + array( '/wp/v2/media', 'OPTIONS' ), | |
| 1086 | + ); | |
| 1087 | + | |
| 1088 | + /** | |
| 1089 | + * Preload common data by specifying an array of REST API paths that will be preloaded. | |
| 1090 | + * | |
| 1091 | + * Filters the array of paths that will be preloaded. | |
| 1092 | + * | |
| 1093 | + * @param array $preload_paths Array of paths to preload | |
| 1094 | + * @param object $post The post resource data. | |
| 1095 | + */ | |
| 1096 | + $preload_paths = apply_filters( 'block_editor_preload_paths', $preload_paths, $post ); | |
| 1097 | + | |
| 1098 | + // Ensure the global $post remains the same after | |
| 1099 | + // API data is preloaded. Because API preloading | |
| 1100 | + // can call the_content and other filters, callbacks | |
| 1101 | + // can unexpectedly modify $post resulting in issues | |
| 1102 | + // like https://github.com/WordPress/gutenberg/issues/7468. | |
| 1103 | + $backup_global_post = $post; | |
| 1104 | + | |
| 1105 | + $preload_data = array_reduce( | |
| 1106 | + $preload_paths, | |
| 1107 | + 'gutenberg_preload_api_request', | |
| 1108 | + array() | |
| 1109 | + ); | |
| 1110 | + | |
| 1111 | + // Restore the global $post as it was before API preloading. | |
| 1112 | + $post = $backup_global_post; | |
| 1113 | + | |
| 1114 | + wp_add_inline_script( | |
| 1115 | + 'wp-api-fetch', | |
| 1116 | + sprintf( 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', wp_json_encode( $preload_data ) ), | |
| 1117 | + 'after' | |
| 1118 | + ); | |
| 1119 | + | |
| 1120 | + wp_add_inline_script( | |
| 1121 | + 'wp-blocks', | |
| 1122 | + sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( gutenberg_get_block_categories( $post ) ) ), | |
| 1123 | + 'after' | |
| 1124 | + ); | |
| 1125 | + | |
| 1126 | + // Assign initial edits, if applicable. These are not initially assigned | |
| 1127 | + // to the persisted post, but should be included in its save payload. | |
| 1128 | + if ( $is_new_post && $is_demo ) { | |
| 1129 | + // Prepopulate with some test content in demo. | |
| 1130 | + ob_start(); | |
| 1131 | + include gutenberg_dir_path() . 'post-content.php'; | |
| 1132 | + $demo_content = ob_get_clean(); | |
| 1133 | + | |
| 1134 | + $initial_edits = array( | |
| 1135 | + 'title' => __( 'Welcome to the Gutenberg Editor', 'gutenberg' ), | |
| 1136 | + 'content' => $demo_content, | |
| 1137 | + ); | |
| 1138 | + } elseif ( $is_new_post ) { | |
| 1139 | + // Override "(Auto Draft)" new post default title with empty string, | |
| 1140 | + // or filtered value. | |
| 1141 | + $initial_edits = array( | |
| 1142 | + 'title' => $post->post_title, | |
| 1143 | + 'content' => $post->post_content, | |
| 1144 | + 'excerpt' => $post->post_excerpt, | |
| 1145 | + ); | |
| 1146 | + } else { | |
| 1147 | + $initial_edits = null; | |
| 1148 | + } | |
| 1149 | + | |
| 1150 | + gutenberg_load_locale_data(); | |
| 1151 | + | |
| 1152 | + // Preload server-registered block schemas. | |
| 1153 | + wp_add_inline_script( | |
| 1154 | + 'wp-blocks', | |
| 1155 | + 'wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . json_encode( gutenberg_prepare_blocks_for_js() ) . ');' | |
| 1156 | + ); | |
| 1157 | + | |
| 1158 | + // Get admin url for handling meta boxes. | |
| 1159 | + $meta_box_url = admin_url( 'post.php' ); | |
| 1160 | + $meta_box_url = add_query_arg( | |
| 1161 | + array( | |
| 1162 | + 'post' => $post->ID, | |
| 1163 | + 'action' => 'edit', | |
| 1164 | + 'classic-editor' => true, | |
| 1165 | + 'meta_box' => true, | |
| 1166 | + ), | |
| 1167 | + $meta_box_url | |
| 1168 | + ); | |
| 1169 | + wp_localize_script( 'wp-editor', '_wpMetaBoxUrl', $meta_box_url ); | |
| 1170 | + | |
| 1171 | + // Initialize the editor. | |
| 1172 | + $gutenberg_theme_support = get_theme_support( 'gutenberg' ); | |
| 1173 | + $align_wide = get_theme_support( 'align-wide' ); | |
| 1174 | + $color_palette = current( (array) get_theme_support( 'editor-color-palette' ) ); | |
| 1175 | + $font_sizes = current( (array) get_theme_support( 'editor-font-sizes' ) ); | |
| 1176 | + | |
| 1177 | + /** | |
| 1178 | + * Filters the allowed block types for the editor, defaulting to true (all | |
| 1179 | + * block types supported). | |
| 1180 | + * | |
| 1181 | + * @param bool|array $allowed_block_types Array of block type slugs, or | |
| 1182 | + * boolean to enable/disable all. | |
| 1183 | + * @param object $post The post resource data. | |
| 1184 | + */ | |
| 1185 | + $allowed_block_types = apply_filters( 'allowed_block_types', true, $post ); | |
| 1186 | + | |
| 1187 | + // Get all available templates for the post/page attributes meta-box. | |
| 1188 | + // The "Default template" array element should only be added if the array is | |
| 1189 | + // not empty so we do not trigger the template select element without any options | |
| 1190 | + // besides the default value. | |
| 1191 | + $available_templates = wp_get_theme()->get_page_templates( get_post( $post->ID ) ); | |
| 1192 | + $available_templates = ! empty( $available_templates ) ? array_merge( | |
| 1193 | + array( | |
| 1194 | + '' => apply_filters( 'default_page_template_title', __( 'Default template', 'gutenberg' ), 'rest-api' ), | |
| 1195 | + ), | |
| 1196 | + $available_templates | |
| 1197 | + ) : $available_templates; | |
| 1198 | + | |
| 1199 | + // Media settings. | |
| 1200 | + $max_upload_size = wp_max_upload_size(); | |
| 1201 | + if ( ! $max_upload_size ) { | |
| 1202 | + $max_upload_size = 0; | |
| 1203 | + } | |
| 1204 | + | |
| 1205 | + // Editor Styles. | |
| 1206 | + global $editor_styles; | |
| 1207 | + $styles = array( | |
| 1208 | + array( | |
| 1209 | + 'css' => file_get_contents( | |
| 1210 | + gutenberg_dir_path() . 'build/editor/editor-styles.css' | |
| 1211 | + ), | |
| 1212 | + ), | |
| 1213 | + ); | |
| 1214 | + | |
| 1215 | + /* Translators: Use this to specify the CSS font family for the default font */ | |
| 1216 | + $locale_font_family = esc_html_x( 'Noto Serif', 'CSS Font Family for Editor Font', 'gutenberg' ); | |
| 1217 | + $styles[] = array( | |
| 1218 | + 'css' => "body { font-family: '$locale_font_family' }", | |
| 1219 | + ); | |
| 1220 | + | |
| 1221 | + if ( $editor_styles && current_theme_supports( 'editor-styles' ) ) { | |
| 1222 | + foreach ( $editor_styles as $style ) { | |
| 1223 | + if ( filter_var( $style, FILTER_VALIDATE_URL ) ) { | |
| 1224 | + $styles[] = array( | |
| 1225 | + 'css' => file_get_contents( $style ), | |
| 1226 | + ); | |
| 1227 | + } else { | |
| 1228 | + $file = get_theme_file_path( $style ); | |
| 1229 | + if ( file_exists( $file ) ) { | |
| 1230 | + $styles[] = array( | |
| 1231 | + 'css' => file_get_contents( $file ), | |
| 1232 | + 'baseURL' => get_theme_file_uri( $style ), | |
| 1233 | + ); | |
| 1234 | + } | |
| 1235 | + } | |
| 1236 | + } | |
| 1237 | + } | |
| 1238 | + | |
| 1239 | + // Lock settings. | |
| 1240 | + $user_id = wp_check_post_lock( $post->ID ); | |
| 1241 | + if ( $user_id ) { | |
| 1242 | + /** | |
| 1243 | + * Filters whether to show the post locked dialog. | |
| 1244 | + * | |
| 1245 | + * Returning a falsey value to the filter will short-circuit displaying the dialog. | |
| 1246 | + * | |
| 1247 | + * @since 3.6.0 | |
| 1248 | + * | |
| 1249 | + * @param bool $display Whether to display the dialog. Default true. | |
| 1250 | + * @param WP_Post $post Post object. | |
| 1251 | + * @param WP_User|bool $user The user id currently editing the post. | |
| 1252 | + */ | |
| 1253 | + if ( apply_filters( 'show_post_locked_dialog', true, $post, $user_id ) ) { | |
| 1254 | + $locked = true; | |
| 1255 | + } | |
| 1256 | + | |
| 1257 | + $user_details = null; | |
| 1258 | + if ( $locked ) { | |
| 1259 | + $user = get_userdata( $user_id ); | |
| 1260 | + $user_details = array( | |
| 1261 | + 'name' => $user->display_name, | |
| 1262 | + ); | |
| 1263 | + $avatar = get_avatar( $user_id, 64 ); | |
| 1264 | + if ( $avatar ) { | |
| 1265 | + if ( preg_match( "|src='([^']+)'|", $avatar, $matches ) ) { | |
| 1266 | + $user_details['avatar'] = $matches[1]; | |
| 1267 | + } | |
| 1268 | + } | |
| 1269 | + } | |
| 1270 | + | |
| 1271 | + $lock_details = array( | |
| 1272 | + 'isLocked' => $locked, | |
| 1273 | + 'user' => $user_details, | |
| 1274 | + ); | |
| 1275 | + } else { | |
| 1276 | + | |
| 1277 | + // Lock the post. | |
| 1278 | + $active_post_lock = wp_set_post_lock( $post->ID ); | |
| 1279 | + $lock_details = array( | |
| 1280 | + 'isLocked' => false, | |
| 1281 | + 'activePostLock' => esc_attr( implode( ':', $active_post_lock ) ), | |
| 1282 | + ); | |
| 1283 | + } | |
| 1284 | + | |
| 1285 | + $editor_settings = array( | |
| 1286 | + 'alignWide' => $align_wide || ! empty( $gutenberg_theme_support[0]['wide-images'] ), // Backcompat. Use `align-wide` outside of `gutenberg` array. | |
| 1287 | + 'availableTemplates' => $available_templates, | |
| 1288 | + 'allowedBlockTypes' => $allowed_block_types, | |
| 1289 | + 'disableCustomColors' => get_theme_support( 'disable-custom-colors' ), | |
| 1290 | + 'disableCustomFontSizes' => get_theme_support( 'disable-custom-font-sizes' ), | |
| 1291 | + 'disablePostFormats' => ! current_theme_supports( 'post-formats' ), | |
| 1292 | + 'titlePlaceholder' => apply_filters( 'enter_title_here', __( 'Add title', 'gutenberg' ), $post ), | |
| 1293 | + 'bodyPlaceholder' => apply_filters( 'write_your_story', __( 'Start writing or type / to choose a block', 'gutenberg' ), $post ), | |
| 1294 | + 'isRTL' => is_rtl(), | |
| 1295 | + 'autosaveInterval' => 10, | |
| 1296 | + 'maxUploadFileSize' => $max_upload_size, | |
| 1297 | + 'allowedMimeTypes' => get_allowed_mime_types(), | |
| 1298 | + 'styles' => $styles, | |
| 1299 | + 'imageSizes' => gutenberg_get_available_image_sizes(), | |
| 1300 | + 'richEditingEnabled' => $user_can_richedit, | |
| 1301 | + | |
| 1302 | + // Ideally, we'd remove this and rely on a REST API endpoint. | |
| 1303 | + 'postLock' => $lock_details, | |
| 1304 | + 'postLockUtils' => array( | |
| 1305 | + 'nonce' => wp_create_nonce( 'lock-post_' . $post->ID ), | |
| 1306 | + 'unlockNonce' => wp_create_nonce( 'update-post_' . $post->ID ), | |
| 1307 | + 'ajaxUrl' => admin_url( 'admin-ajax.php' ), | |
| 1308 | + ), | |
| 1309 | + | |
| 1310 | + // Whether or not to load the 'postcustom' meta box is stored as a user meta | |
| 1311 | + // field so that we're not always loading its assets. | |
| 1312 | + 'enableCustomFields' => (bool) get_user_meta( get_current_user_id(), 'enable_custom_fields', true ), | |
| 1313 | + ); | |
| 1314 | + | |
| 1315 | + $post_autosave = gutenberg_get_autosave_newer_than_post_save( $post ); | |
| 1316 | + if ( $post_autosave ) { | |
| 1317 | + $editor_settings['autosave'] = array( | |
| 1318 | + 'editLink' => add_query_arg( 'gutenberg', true, get_edit_post_link( $post_autosave->ID ) ), | |
| 1319 | + ); | |
| 1320 | + } | |
| 1321 | + | |
| 1322 | + if ( false !== $color_palette ) { | |
| 1323 | + $editor_settings['colors'] = $color_palette; | |
| 1324 | + } | |
| 1325 | + | |
| 1326 | + if ( ! empty( $font_sizes ) ) { | |
| 1327 | + $editor_settings['fontSizes'] = $font_sizes; | |
| 1328 | + } | |
| 1329 | + | |
| 1330 | + if ( ! empty( $post_type_object->template ) ) { | |
| 1331 | + $editor_settings['template'] = $post_type_object->template; | |
| 1332 | + $editor_settings['templateLock'] = ! empty( $post_type_object->template_lock ) ? $post_type_object->template_lock : false; | |
| 1333 | + } | |
| 1334 | + | |
| 1335 | + $init_script = <<<JS | |
| 1336 | + ( function() { | |
| 1337 | + window._wpLoadGutenbergEditor = new Promise( function( resolve ) { | |
| 1338 | + wp.domReady( function() { | |
| 1339 | + resolve( wp.editPost.initializeEditor( 'editor', "%s", %d, %s, %s ) ); | |
| 1340 | + } ); | |
| 1341 | + } ); | |
| 1342 | +} )(); | |
| 1343 | +JS; | |
| 1344 | + | |
| 1345 | + /** | |
| 1346 | + * Filters the settings to pass to the block editor. | |
| 1347 | + * | |
| 1348 | + * @since 3.7.0 | |
| 1349 | + * | |
| 1350 | + * @param array $editor_settings Default editor settings. | |
| 1351 | + * @param WP_Post $post Post being edited. | |
| 1352 | + */ | |
| 1353 | + $editor_settings = apply_filters( 'block_editor_settings', $editor_settings, $post ); | |
| 1354 | + | |
| 1355 | + $script = sprintf( | |
| 1356 | + $init_script, | |
| 1357 | + $post->post_type, | |
| 1358 | + $post->ID, | |
| 1359 | + wp_json_encode( $editor_settings ), | |
| 1360 | + wp_json_encode( $initial_edits ) | |
| 1361 | + ); | |
| 1362 | + wp_add_inline_script( 'wp-edit-post', $script ); | |
| 1363 | + | |
| 1364 | + /** | |
| 1365 | + * Scripts | |
| 1366 | + */ | |
| 1367 | + wp_enqueue_media( | |
| 1368 | + array( | |
| 1369 | + 'post' => $post->ID, | |
| 1370 | + ) | |
| 1371 | + ); | |
| 1372 | + wp_enqueue_editor(); | |
| 1373 | + | |
| 1374 | + /** | |
| 1375 | + * Styles | |
| 1376 | + */ | |
| 1377 | + wp_enqueue_style( 'wp-edit-post' ); | |
| 1378 | + | |
| 1379 | + /** | |
| 1380 | + * Fires after block assets have been enqueued for the editing interface. | |
| 1381 | + * | |
| 1382 | + * Call `add_action` on any hook before 'admin_enqueue_scripts'. | |
| 1383 | + * | |
| 1384 | + * In the function call you supply, simply use `wp_enqueue_script` and | |
| 1385 | + * `wp_enqueue_style` to add your functionality to the Gutenberg editor. | |
| 1386 | + * | |
| 1387 | + * @since 0.4.0 | |
| 1388 | + */ | |
| 1389 | + do_action( 'enqueue_block_editor_assets' ); | |
| 1390 | +} | |
| 1391 | + | |
| 1392 | +/** | |
| 1393 | + * Enqueue the reusable blocks listing page's script | |
| 1394 | + * | |
| 1395 | + * @param string $hook Screen name. | |
| 1396 | + */ | |
| 1397 | +function gutenberg_load_list_reusable_blocks( $hook ) { | |
| 1398 | + $is_reusable_blocks_list_page = 'edit.php' === $hook && isset( $_GET['post_type'] ) && 'wp_block' === $_GET['post_type']; | |
| 1399 | + if ( $is_reusable_blocks_list_page ) { | |
| 1400 | + gutenberg_load_locale_data(); | |
| 1401 | + wp_enqueue_script( 'wp-list-reusable-blocks' ); | |
| 1402 | + wp_enqueue_style( 'wp-list-reusable-blocks' ); | |
| 1403 | + } | |
| 1404 | +} | |
| 1405 | +add_action( 'admin_enqueue_scripts', 'gutenberg_load_list_reusable_blocks' ); | |