diff --git a/.phpactor.json b/.phpactor.json new file mode 100644 index 0000000000000..5cea18e2d4913 --- /dev/null +++ b/.phpactor.json @@ -0,0 +1,4 @@ +{ + "$schema": "/phpactor.schema.json", + "php_code_sniffer.enabled": false +} \ No newline at end of file diff --git a/src/wp-includes/build/pages/site-editor-v2/loader.js b/src/wp-includes/build/pages/site-editor-v2/loader.js new file mode 100644 index 0000000000000..9f7e5db19d51d --- /dev/null +++ b/src/wp-includes/build/pages/site-editor-v2/loader.js @@ -0,0 +1 @@ +// Empty module loader for page dependencies diff --git a/src/wp-includes/build/pages/site-editor-v2/page-wp-admin.php b/src/wp-includes/build/pages/site-editor-v2/page-wp-admin.php new file mode 100644 index 0000000000000..f8be5479cb4d7 --- /dev/null +++ b/src/wp-includes/build/pages/site-editor-v2/page-wp-admin.php @@ -0,0 +1,279 @@ + $path ); + if ( ! empty( $content_module ) ) { + $route['content_module'] = $content_module; + } + if ( ! empty( $route_module ) ) { + $route['route_module'] = $route_module; + } + + $wp_site_editor_v2_wp_admin_routes[] = $route; +} + +/** + * Register a menu item for the site-editor-v2-wp-admin page. + * Note: Menu items are registered but not displayed in single-page mode. + * + * @param string $id Menu item ID. + * @param string $label Display label. + * @param string $to Route path to navigate to. + * @param string $parent_id Optional. Parent menu item ID. + */ +function wp_register_site_editor_v2_wp_admin_menu_item( $id, $label, $to, $parent_id = '' ) { + global $wp_site_editor_v2_wp_admin_menu_items; + + $menu_item = array( + 'id' => $id, + 'label' => $label, + 'to' => $to, + ); + + if ( ! empty( $parent_id ) ) { + $menu_item['parent'] = $parent_id; + } + + $wp_site_editor_v2_wp_admin_menu_items[] = $menu_item; +} + +/** + * Get all registered routes for the site-editor-v2-wp-admin page. + * + * @return array Array of route objects. + */ +function wp_get_site_editor_v2_wp_admin_routes() { + global $wp_site_editor_v2_wp_admin_routes; + return $wp_site_editor_v2_wp_admin_routes ?? array(); +} + +/** + * Get all registered menu items for the site-editor-v2-wp-admin page. + * + * @return array Array of menu item objects. + */ +function wp_get_site_editor_v2_wp_admin_menu_items() { + global $wp_site_editor_v2_wp_admin_menu_items; + return $wp_site_editor_v2_wp_admin_menu_items ?? array(); +} + +/** + * Preload REST API data for the site-editor-v2-wp-admin page. + * Automatically called during page rendering. + */ +function wp_site_editor_v2_wp_admin_preload_data() { + // Define paths to preload - same for all pages + // Please also change packages/core-data/src/entities.js when changing this. + $preload_paths = array( + '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,image_output_formats,jpeg_interlaced,png_interlaced,gif_interlaced,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', + array( '/wp/v2/settings', 'OPTIONS' ), + ); + + // Use rest_preload_api_request to gather the preloaded data + $preload_data = array_reduce( + $preload_paths, + 'rest_preload_api_request', + array() + ); + + // Register the preloading middleware with wp-api-fetch + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', + wp_json_encode( $preload_data ) + ), + 'after' + ); +} + +/** + * Enqueue scripts and styles for the site-editor-v2-wp-admin page. + * Hooked to admin_enqueue_scripts. + * + * @param string $hook_suffix The current admin page. + */ +function wp_site_editor_v2_wp_admin_enqueue_scripts( $hook_suffix ) { + // Check all possible ways this page can be accessed: + // 1. Menu page via admin.php?page=site-editor-v2-wp-admin (plugin) + // 2. Direct file via site-editor-v2.php (Core) - screen ID will be 'site-editor-v2' + $current_screen = get_current_screen(); + $is_our_page = ( + ( isset( $_GET['page'] ) && 'site-editor-v2-wp-admin' === $_GET['page'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended + ( $current_screen && 'site-editor-v2' === $current_screen->id ) + ); + + if ( ! $is_our_page ) { + return; + } + + // Load build constants + $build_constants = require __DIR__ . '/../../constants.php'; + + // Fire init action for extensions to register routes and menu items + do_action( 'site-editor-v2-wp-admin_init' ); + + // Preload REST API data + wp_site_editor_v2_wp_admin_preload_data(); + + // Get all registered routes + $routes = wp_get_site_editor_v2_wp_admin_routes(); + + // Get boot module asset file for dependencies + $asset_file = ABSPATH . WPINC . '/js/dist/script-modules/boot/index.min.asset.php'; + if ( file_exists( $asset_file ) ) { + $asset = require $asset_file; + + // This script serves two purposes: + // 1. It ensures all the globals that are made available to the modules are loaded. + // 2. It initializes the boot module as an inline script. + wp_register_script( 'site-editor-v2-wp-admin-prerequisites', '', $asset['dependencies'], $asset['version'], true ); + + // Add inline script to initialize the app using initSinglePage (no menuItems) + wp_add_inline_script( + 'site-editor-v2-wp-admin-prerequisites', + sprintf( + 'import("@wordpress/boot").then(mod => mod.initSinglePage({mountId: "%s", routes: %s}));', + 'site-editor-v2-wp-admin-app', + wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) + ) + ); + + // Register prerequisites style by filtering script dependencies to find registered styles + $style_dependencies = array_filter( + $asset['dependencies'], + function ( $handle ) { + return wp_style_is( $handle, 'registered' ); + } + ); + wp_register_style( 'site-editor-v2-wp-admin-prerequisites', false, $style_dependencies, $asset['version'] ); + + // Build dependencies for site-editor-v2-wp-admin module + $boot_dependencies = array( + array( + 'import' => 'static', + 'id' => '@wordpress/boot', + ), + ); + + // Add all registered routes as dependencies + foreach ( $routes as $route ) { + if ( isset( $route['route_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'static', + 'id' => $route['route_module'], + ); + } + if ( isset( $route['content_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'dynamic', + 'id' => $route['content_module'], + ); + } + } + + // Dummy script module to ensure dependencies are loaded + wp_register_script_module( + 'site-editor-v2-wp-admin', + $build_constants['build_url'] . 'pages/site-editor-v2/loader.js', + $boot_dependencies + ); + + // Enqueue the boot scripts and styles + wp_enqueue_script( 'site-editor-v2-wp-admin-prerequisites' ); + wp_enqueue_script_module( 'site-editor-v2-wp-admin' ); + wp_enqueue_style( 'site-editor-v2-wp-admin-prerequisites' ); + } +} + +/** + * Render the site-editor-v2-wp-admin page. + * Call this function from add_menu_page or add_submenu_page. + * This renders within the normal WordPress admin interface. + */ +function wp_site_editor_v2_wp_admin_render_page() { + ?> + +
+ $path ); + if ( ! empty( $content_module ) ) { + $route['content_module'] = $content_module; + } + if ( ! empty( $route_module ) ) { + $route['route_module'] = $route_module; + } + + $wp_site_editor_v2_routes[] = $route; +} + +/** + * Register a menu item for the site-editor-v2 page. + * + * @param string $id Menu item ID. + * @param string $label Display label. + * @param string $to Route path to navigate to. + * @param string $parent_id Optional. Parent menu item ID. + * @param string $parent_type Optional. Parent type: 'drilldown' or 'dropdown'. + */ +function wp_register_site_editor_v2_menu_item( $id, $label, $to, $parent_id = '', $parent_type = '' ) { + global $wp_site_editor_v2_menu_items; + + $menu_item = array( + 'id' => $id, + 'label' => $label, + 'to' => $to, + ); + + if ( ! empty( $parent_id ) ) { + $menu_item['parent'] = $parent_id; + } + + if ( ! empty( $parent_type ) && in_array( $parent_type, array( 'drilldown', 'dropdown' ), true ) ) { + $menu_item['parent_type'] = $parent_type; + } + + $wp_site_editor_v2_menu_items[] = $menu_item; +} + +/** + * Get all registered routes for the site-editor-v2 page. + * + * @return array Array of route objects. + */ +function wp_get_site_editor_v2_routes() { + global $wp_site_editor_v2_routes; + return $wp_site_editor_v2_routes ?? array(); +} + +/** + * Get all registered menu items for the site-editor-v2 page. + * + * @return array Array of menu item objects. + */ +function wp_get_site_editor_v2_menu_items() { + global $wp_site_editor_v2_menu_items; + return $wp_site_editor_v2_menu_items ?? array(); +} + +/** + * Preload REST API data for the site-editor-v2 page. + * Automatically called during page rendering. + */ +function wp_site_editor_v2_preload_data() { + // Define paths to preload - same for all pages + // Please also change packages/core-data/src/entities.js when changing this. + $preload_paths = array( + '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,image_output_formats,jpeg_interlaced,png_interlaced,gif_interlaced,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', + array( '/wp/v2/settings', 'OPTIONS' ), + ); + + // Use rest_preload_api_request to gather the preloaded data + $preload_data = array_reduce( + $preload_paths, + 'rest_preload_api_request', + array() + ); + + // Register the preloading middleware with wp-api-fetch + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', + wp_json_encode( $preload_data ) + ), + 'after' + ); +} + +/** + * Render the site-editor-v2 page. + * Call this function from add_menu_page or add_submenu_page. + */ +function wp_site_editor_v2_render_page() { + // Load build constants + $build_constants = require __DIR__ . '/../../constants.php'; + + // Set current screen + set_current_screen(); + + // Remove unwanted deprecated handler + remove_action( 'admin_head', 'wp_admin_bar_header' ); + + // Remove unwanted scripts and styles that were enqueued during `admin_init` + foreach ( wp_scripts()->queue as $script ) { + wp_dequeue_script( $script ); + } + foreach ( wp_styles()->queue as $style ) { + wp_dequeue_style( $style ); + } + + // Fire init action for extensions to register routes and menu items + do_action( 'site-editor-v2_init' ); + + // Enqueue command palette assets for boot-based pages + if ( function_exists( 'wp_enqueue_command_palette_assets' ) ) { + wp_enqueue_command_palette_assets(); + } + + // Preload REST API data + wp_site_editor_v2_preload_data(); + + // Get all registered routes and menu items + $menu_items = wp_get_site_editor_v2_menu_items(); + $routes = wp_get_site_editor_v2_routes(); + + // Get boot module asset file for dependencies + $asset_file = ABSPATH . WPINC . '/js/dist/script-modules/boot/index.min.asset.php'; + if ( file_exists( $asset_file ) ) { + $asset = require $asset_file; + + // This script serves two purposes: + // 1. It ensures all the globals that are made available to the modules are loaded. + // 2. It initializes the boot module as an inline script. + wp_register_script( 'site-editor-v2-prerequisites', '', $asset['dependencies'], $asset['version'], true ); + + // Add inline script to initialize the app + $init_modules = ["@wordpress/edit-site-init"]; + wp_add_inline_script( + 'site-editor-v2-prerequisites', + sprintf( + 'import("@wordpress/boot").then(mod => mod.init({mountId: "%s", menuItems: %s, routes: %s, initModules: %s, dashboardLink: "%s"}));', + 'site-editor-v2-app', + wp_json_encode( $menu_items, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + wp_json_encode( $init_modules, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), + esc_url( admin_url( '/' ) ) + ) + ); + + // Register prerequisites style by filtering script dependencies to find registered styles + $style_dependencies = array_filter( + $asset['dependencies'], + function ( $handle ) { + return wp_style_is( $handle, 'registered' ); + } + ); + wp_register_style( 'site-editor-v2-prerequisites', false, $style_dependencies, $asset['version'] ); + + // Build dependencies for site-editor-v2 module + $boot_dependencies = array( + array( + 'import' => 'static', + 'id' => '@wordpress/boot', + ), + ); + + // Add init modules as static dependencies + $boot_dependencies[] = array( 'import' => 'static', 'id' => '@wordpress/edit-site-init' ); + + // Add all registered routes as dependencies + foreach ( $routes as $route ) { + if ( isset( $route['route_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'static', + 'id' => $route['route_module'], + ); + } + if ( isset( $route['content_module'] ) ) { + $boot_dependencies[] = array( + 'import' => 'dynamic', + 'id' => $route['content_module'], + ); + } + } + + // Dummy script module to ensure dependencies are loaded + wp_register_script_module( + 'site-editor-v2', + $build_constants['build_url'] . 'pages/site-editor-v2/loader.js', + $boot_dependencies + ); + + // Enqueue the boot scripts and styles + wp_enqueue_script( 'site-editor-v2-prerequisites' ); + wp_enqueue_script_module( 'site-editor-v2' ); + wp_enqueue_style( 'site-editor-v2-prerequisites' ); + } + + // Output the HTML + ?> + + > + + + +