diff --git a/lib/compat/wordpress-5.9/class-gutenberg-rest-navigation-controller.php b/lib/compat/wordpress-5.9/class-gutenberg-rest-navigation-controller.php new file mode 100644 index 0000000000000..553e5e629f61d --- /dev/null +++ b/lib/compat/wordpress-5.9/class-gutenberg-rest-navigation-controller.php @@ -0,0 +1,274 @@ +post_type = $post_type; + $this->namespace = 'wp/v2'; + $obj = get_post_type_object( $post_type ); + $this->rest_base = ! empty( $obj->rest_base ) ? $obj->rest_base : $obj->name; + } + + /** + * Registers the controllers routes. + * + * @return void + */ + public function register_routes() { + // Lists all templates. + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_items' ), + 'permission_callback' => array( $this, 'get_items_permissions_check' ), + 'args' => $this->get_collection_params(), + ), + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'create_item' ), + 'permission_callback' => array( $this, 'create_item_permissions_check' ), + 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + + // Lists/updates a single nav item based on the given id. + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/(?P[\/\w-]+)', + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_item' ), + 'permission_callback' => array( $this, 'get_item_permissions_check' ), + 'args' => array( + 'id' => array( + 'description' => __( 'The id of a navigation', 'gutenberg' ), + 'type' => 'string', + ), + ), + ), + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => array( $this, 'update_item' ), + 'permission_callback' => array( $this, 'update_item_permissions_check' ), + 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), + ), + array( + 'methods' => WP_REST_Server::DELETABLE, + 'callback' => array( $this, 'delete_item' ), + 'permission_callback' => array( $this, 'delete_item_permissions_check' ), + 'args' => array( + 'force' => array( + 'type' => 'boolean', + 'default' => false, + 'description' => __( 'Whether to bypass Trash and force deletion.', 'gutenberg' ), + ), + ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + } + + /** + * Checks if the user has permissions to make the request. + * + * @return true|WP_Error True if the request has read access, WP_Error object otherwise. + */ + protected function permissions_check() { + // Verify if the current user has edit_theme_options capability. + // This capability is required to edit/view/delete templates. +// if ( ! current_user_can( 'edit_theme_options' ) ) { +// return new WP_Error( +// 'rest_cannot_manage_templates', +// __( 'Sorry, you are not allowed to access the templates on this site.', 'gutenberg' ), +// array( +// 'status' => rest_authorization_required_code(), +// ) +// ); +// } + + return true; + } + + public function get_item_permissions_check( $request ) { + return true; + } + + public function create_item_permissions_check( $request ) { + return true; + } + + public function update_item_permissions_check( $request ) { + return true; + } + + public function delete_item_permissions_check( $request ) { + return true; + } + + protected function check_update_permission( $post ) { + return true; + } + + public function get_items_permissions_check( $request ) { + return true; + } + + public function check_read_permission( $post ) { + return true; + } + + protected function check_create_permission( $post ) { + return true; + } + + protected function check_delete_permission( $post ) { + return true; + } + + protected function handle_status_param( $post_status, $post_type ) { + return $post_status; + } + + public function prepare_item_for_response( $item, $request ) { + unset( $request['id'] ); + $item = parent::prepare_item_for_response( $item, $request ); // TODO: Change the autogenerated stub + + return $item; + } +// +// public function update_additional_fields_for_object( $post, $request ) { +// echo "update_additional_fields_for_object"; +// print_r(get_post($post->ID)); +// die(print_r($post)); +// } + + public function filter_response_by_context( $data, $context ) { + $data['id'] = $data['slug']; + + return $data; + } + + public function get_item_schema() { + $schema = parent::get_item_schema(); // TODO: Change the autogenerated stub + $schema['properties']['id']['type'] = 'string'; + + return $schema; + } + + + protected function get_post( $id ) { + $wp_query_args = array( + 'name' => $id, + 'post_type' => 'wp_navigation', + 'post_status' => array( 'auto-draft', 'draft', 'publish', 'trash' ), + 'posts_per_page' => 1, + 'no_found_rows' => true, + ); + $template_query = new WP_Query( $wp_query_args ); + $post = $template_query->posts[0]; +// print_r(get_post($post->ID)); +// die(print_r($template_query->posts)); + if ( ! $post ) { + return new WP_Error( + 'rest_post_invalid_id', + __( 'Invalid post ID.' ), + array( 'status' => 404 ) + ); + } + + return $post; + } + + public function create_item( $request ) { + return $this->update_item( $request ); + } + + public function update_item( $request ) { + $navigation = $this->get_post( $request['id'] ); + if ( ! $navigation ) { + return new WP_Error( 'rest_template_not_found', __( 'No templates exist with that id.' ), array( 'status' => 404 ) ); + } + + $changes = $this->prepare_item_for_database( $request ); + + if ( ! is_wp_error( $navigation ) ) { + $result = wp_update_post( wp_slash( (array) $changes ), true ); + } else { + $result = wp_insert_post( wp_slash( (array) $changes ), true ); + } + if ( is_wp_error( $result ) ) { + return $result; + } + + $navigation = $this->get_post( $request['id'] ); +// $fields_update = $this->update_additional_fields_for_object( $navigation, $request ); +// if ( is_wp_error( $fields_update ) ) { +// return $fields_update; +// } + + return $this->prepare_item_for_response( + $navigation, + $request + ); + } + + protected function prepare_item_for_database( $request ) { + $navigation = $this->get_post( $request['id'] ); + $navigation = is_wp_error( $navigation ) ? null : $navigation; + $changes = new stdClass(); + if ( null === $navigation ) { + $changes->post_type = $this->post_type; + $changes->post_status = 'publish'; + $changes->post_name = $request['id']; + } else { + $changes->post_name = $navigation->post_name; + $changes->ID = $navigation->ID; + $changes->post_status = 'publish'; + } + if ( isset( $request['content'] ) ) { + $changes->post_content = $request['content']; + } elseif ( null !== $navigation && 'custom' !== $navigation->source ) { + $changes->post_content = $navigation->content; + } + if ( isset( $request['title'] ) ) { + $changes->post_title = $request['title']; + } elseif ( null !== $navigation && 'custom' !== $navigation->source ) { + $changes->post_title = $navigation->title; + } + if ( isset( $request['description'] ) ) { + $changes->post_excerpt = $request['description']; + } elseif ( null !== $navigation && 'custom' !== $navigation->source ) { + $changes->post_excerpt = $navigation->description; + } + + return $changes; + } + +} diff --git a/lib/load.php b/lib/load.php index 9f57672de7f0a..dce3796bca632 100644 --- a/lib/load.php +++ b/lib/load.php @@ -57,6 +57,7 @@ function gutenberg_is_experiment_enabled( $name ) { require_once __DIR__ . '/class-wp-rest-customizer-nonces.php'; } require_once __DIR__ . '/compat/wordpress-5.9/class-gutenberg-rest-templates-controller.php'; + require_once __DIR__ . '/compat/wordpress-5.9/class-gutenberg-rest-navigation-controller.php'; if ( ! class_exists( 'WP_REST_Block_Editor_Settings_Controller' ) ) { require_once dirname( __FILE__ ) . '/class-wp-rest-block-editor-settings-controller.php'; } diff --git a/lib/navigation.php b/lib/navigation.php index 8413782681b01..90a1dd2ff1b85 100644 --- a/lib/navigation.php +++ b/lib/navigation.php @@ -44,7 +44,7 @@ function gutenberg_register_navigation_post_type() { 'show_in_rest' => true, 'map_meta_cap' => true, 'rest_base' => 'navigation', - 'rest_controller_class' => WP_REST_Posts_Controller::class, + 'rest_controller_class' => Gutenberg_REST_Navigation_Controller::class, 'supports' => array( 'title', 'editor', @@ -291,3 +291,35 @@ function gutenberg_hide_visibility_and_status_for_navigation_posts( $hook ) { } add_action( 'admin_enqueue_scripts', 'gutenberg_hide_visibility_and_status_for_navigation_posts' ); + + +/** + * Sets a custom slug when creating auto-draft navigation. + * This is only needed for auto-drafts created by the regular WP editor. + * If this page is to be removed, this won't be necessary. + * + * @param int $post_id Post ID. + */ +function gutenberg_set_unique_slug_on_create_navigation_post( $post_id ) { + // This is the core function with the same functionality. + if ( function_exists( 'set_unique_slug_on_create_navigation_post' ) ) { + return; + } + + $post = get_post( $post_id ); + if ( 'auto-draft' !== $post->post_status ) { + return; + } + + if ( ! $post->post_name ) { + wp_update_post( + array( + 'ID' => $post_id, + 'post_name' => 'general-menu//' . uniqid(), + ) + ); + } +} + +//add_action( 'save_post_wp_navigation', 'gutenberg_set_unique_slug_on_create_navigation_post' ); + diff --git a/packages/block-library/package.json b/packages/block-library/package.json index f9d17ef35a445..421c282a38ed9 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -68,7 +68,8 @@ "lodash": "^4.17.21", "memize": "^1.1.0", "micromodal": "^0.4.6", - "moment": "^2.22.1" + "moment": "^2.22.1", + "uuid": "^8.3.0" }, "publishConfig": { "access": "public" diff --git a/packages/block-library/src/navigation/block.json b/packages/block-library/src/navigation/block.json index 54e7f90545df0..ec5581dbd90a6 100644 --- a/packages/block-library/src/navigation/block.json +++ b/packages/block-library/src/navigation/block.json @@ -9,7 +9,10 @@ "textdomain": "default", "attributes": { "navigationMenuId": { - "type": "number" + "type": "number" + }, + "slug": { + "type": "string" }, "textColor": { "type": "string" diff --git a/packages/block-library/src/navigation/edit/index.js b/packages/block-library/src/navigation/edit/index.js index 040f8e5761b6c..ebfdc1a8c225a 100644 --- a/packages/block-library/src/navigation/edit/index.js +++ b/packages/block-library/src/navigation/edit/index.js @@ -10,9 +10,10 @@ import { useState, useEffect, useRef, - useCallback, Platform, + useContext, } from '@wordpress/element'; +import { v4 as uuid } from 'uuid'; import { InspectorControls, BlockControls, @@ -28,6 +29,7 @@ import { import { EntityProvider } from '@wordpress/core-data'; import { useDispatch, useSelect } from '@wordpress/data'; import { + Disabled, PanelBody, ToggleControl, __experimentalToggleGroupControl as ToggleGroupControl, @@ -51,6 +53,7 @@ import NavigationMenuNameControl from './navigation-menu-name-control'; import NavigationMenuPublishButton from './navigation-menu-publish-button'; import UnsavedInnerBlocks from './unsaved-inner-blocks'; import NavigationMenuDeleteControl from './navigation-menu-delete-control'; +import useTemplatePartArea from '../use-template-part-area'; function getComputedStyle( node ) { return node.ownerDocument.defaultView.getComputedStyle( node ); @@ -108,13 +111,8 @@ function Navigation( { layout: { justifyContent, orientation = 'horizontal' } = {}, } = attributes; - const navigationMenuId = attributes.navigationMenuId; - const setNavigationMenuId = useCallback( ( postId ) => { - setAttributes( { navigationMenuId: postId } ); - }, [] ); - const [ hasAlreadyRendered, RecursionProvider ] = useNoRecursiveRenders( - `navigationMenu/${ navigationMenuId }` + `navigationMenu/${ attributes.slug }` ); const { innerBlocks, isInnerBlockSelected } = useSelect( @@ -141,10 +139,8 @@ function Navigation( { setHasSavedUnsavedInnerBlocks, ] = useState( false ); - const isWithinUnassignedArea = ! navigationMenuId; - const [ isPlaceholderShown, setIsPlaceholderShown ] = useState( - ! hasExistingNavItems || isWithinUnassignedArea + ! hasExistingNavItems ); const [ isResponsiveMenuOpen, setResponsiveMenuVisibility ] = useState( @@ -158,7 +154,7 @@ function Navigation( { hasResolvedNavigationMenus, navigationMenus, navigationMenu, - } = useNavigationMenu( navigationMenuId ); + } = useNavigationMenu( attributes.slug ); const navRef = useRef(); const isDraftNavigationMenu = navigationMenu?.status === 'draft'; @@ -262,13 +258,33 @@ function Navigation( { setIsPlaceholderShown( ! isEntityAvailable ); }, [ isEntityAvailable ] ); + // Because we can't conditionally call hooks, pass an undefined client id + // arg to bypass the expensive `useTemplateArea` code. The hook will return + // early. + const isDisabled = useContext( Disabled.Context ); + const area = useTemplatePartArea( isDisabled ? undefined : clientId ); + const oldSlug = attributes.slug; + useEffect( () => { + console.log( { oldSlug, areaSlug: area?.area } ); + if ( ! oldSlug ) { + const newSlug = area?.area + ? `wp-${ area.area }-menu` + : `wp-${ uuid() }-menu`; + setAttributes( { slug: newSlug } ); + } + }, [ oldSlug, area?.area ] ); + useEffect( () => { + if ( navigationMenu?.slug ) { + setAttributes( { slug: navigationMenu.slug } ); + } + }, [ navigationMenu?.slug ] ); + // If the block has inner blocks, but no menu id, this was an older // navigation block added before the block used a wp_navigation entity. // Either this block was saved in the content or inserted by a pattern. // Consider this 'unsaved'. Offer an uncontrolled version of inner blocks, // that automatically saves the menu. - const hasUnsavedBlocks = - hasExistingNavItems && ! isEntityAvailable && ! isWithinUnassignedArea; + const hasUnsavedBlocks = hasExistingNavItems && ! isEntityAvailable; if ( hasUnsavedBlocks ) { return ( { setHasSavedUnsavedInnerBlocks( true ); // Switch to using the wp_navigation entity. - setNavigationMenuId( post.id ); + setAttributes( { slug: post.slug } ); } } /> ); @@ -289,7 +305,7 @@ function Navigation( { // Show a warning if the selected menu is no longer available. // TODO - the user should be able to select a new one? - if ( navigationMenuId && isNavigationMenuMissing ) { + if ( attributes.slug && isNavigationMenuMissing ) { return (
@@ -319,7 +335,7 @@ function Navigation( { @@ -332,13 +348,13 @@ function Navigation( { > { ( { onClose } ) => ( { - setNavigationMenuId( id ); + onSelect={ ( { slug } ) => { + setAttributes( { slug } ); onClose(); } } onCreateNew={ () => { setAttributes( { - navigationMenuId: undefined, + slug: undefined, } ); setIsPlaceholderShown( true ); } } @@ -461,7 +477,7 @@ function Navigation( { onDelete={ () => { replaceInnerBlocks( clientId, [] ); setAttributes( { - navigationMenuId: undefined, + slug: undefined, } ); setIsPlaceholderShown( true ); } } @@ -473,7 +489,7 @@ function Navigation( { { setIsPlaceholderShown( false ); - setNavigationMenuId( post.id ); + setAttributes( { slug: post.slug } ); selectBlock( clientId ); } } canSwitchNavigationMenu={ canSwitchNavigationMenu } diff --git a/packages/block-library/src/navigation/use-navigation-menu.js b/packages/block-library/src/navigation/use-navigation-menu.js index cd58adf0684fa..342ca8a815c15 100644 --- a/packages/block-library/src/navigation/use-navigation-menu.js +++ b/packages/block-library/src/navigation/use-navigation-menu.js @@ -2,9 +2,12 @@ * WordPress dependencies */ import { store as coreStore } from '@wordpress/core-data'; -import { useSelect } from '@wordpress/data'; +import { useState } from '@wordpress/element'; +import { useSelect, useDispatch } from '@wordpress/data'; -export default function useNavigationMenu( navigationMenuId ) { +export default function useNavigationMenu( slug ) { + const dispatch = useDispatch(); + const [ created, setCreated ] = useState( false ); // @TODO check with core data return useSelect( ( select ) => { const { @@ -16,12 +19,10 @@ export default function useNavigationMenu( navigationMenuId ) { const navigationMenuSingleArgs = [ 'postType', 'wp_navigation', - navigationMenuId, + slug, ]; - const navigationMenu = navigationMenuId - ? getEditedEntityRecord( ...navigationMenuSingleArgs ) - : null; - const hasResolvedNavigationMenu = navigationMenuId + + const hasResolvedNavigationMenu = slug ? hasFinishedResolution( 'getEditedEntityRecord', navigationMenuSingleArgs @@ -37,15 +38,55 @@ export default function useNavigationMenu( navigationMenuId ) { ...navigationMenuMultipleArgs ); - const canSwitchNavigationMenu = navigationMenuId + let navigationMenu = null; + + console.log( 'TEST' ); + if ( slug ) { + navigationMenu = getEditedEntityRecord( + ...navigationMenuSingleArgs + ); + if ( + ! navigationMenu?.id && + ! created /* && hasResolvedNavigationMenu */ + ) { + console.log( 'WELL IM CREATING A NEW ENTITY! WHY?!' ); + setCreated( true ); + const record = { + slug, + name: slug, + post_name: slug, + status: 'publish', + }; + dispatch( coreStore ).receiveEntityRecords( + 'postType', + 'wp_navigation', + [ { ...record, id: slug } ] + ); + dispatch( coreStore ) + .saveEntityRecord( 'postType', 'wp_navigation', record ) + .then( ( createdRecord ) => { + dispatch( + coreStore + ).receiveEntityRecords( + 'postType', + 'wp_navigation', + [ createdRecord ] + ); + } ); + navigationMenu = getEditedEntityRecord( + ...navigationMenuSingleArgs + ); + } + } + + const canSwitchNavigationMenu = slug ? navigationMenus?.length > 1 : navigationMenus?.length > 0; return { isNavigationMenuResolved: hasResolvedNavigationMenu, isNavigationMenuMissing: - ! navigationMenuId || - ( hasResolvedNavigationMenu && ! navigationMenu ), + ! slug || ( hasResolvedNavigationMenu && ! navigationMenu ), canSwitchNavigationMenu, hasResolvedNavigationMenus: hasFinishedResolution( 'getEntityRecords', @@ -55,6 +96,6 @@ export default function useNavigationMenu( navigationMenuId ) { navigationMenus, }; }, - [ navigationMenuId ] + [ slug, created ] ); } diff --git a/packages/block-library/src/navigation/use-template-part-area.js b/packages/block-library/src/navigation/use-template-part-area.js new file mode 100644 index 0000000000000..405746f585058 --- /dev/null +++ b/packages/block-library/src/navigation/use-template-part-area.js @@ -0,0 +1,71 @@ +/** + * WordPress dependencies + */ +import { store as blockEditorStore } from '@wordpress/block-editor'; +import { store as coreStore } from '@wordpress/core-data'; +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ + +// TODO: this util should perhaps be refactored somewhere like core-data. +import { createTemplatePartId } from '../template-part/edit/utils/create-template-part-id'; + +export default function useTemplatePartArea( clientId ) { + return useSelect( + ( select ) => { + // Use the lack of a clientId as an opportunity to bypass the rest + // of this hook. + if ( ! clientId ) { + return; + } + + const { getBlock, getBlockParentsByBlockName } = select( + blockEditorStore + ); + + const withAscendingResults = true; + const parentTemplatePartClientIds = getBlockParentsByBlockName( + clientId, + 'core/template-part', + withAscendingResults + ); + + if ( ! parentTemplatePartClientIds?.length ) { + return; + } + + // FIXME: @wordpress/block-library should not depend on @wordpress/editor. + // Blocks can be loaded into a *non-post* block editor. + // This code is lifted from this file: + // packages/block-library/src/template-part/edit/advanced-controls.js + // eslint-disable-next-line @wordpress/data-no-store-string-literals + const { getEditedEntityRecord } = select( coreStore ); + + for ( const templatePartClientId of parentTemplatePartClientIds ) { + const templatePartBlock = getBlock( templatePartClientId ); + + // The 'area' usually isn't stored on the block, but instead + // on the entity. + const { theme, slug } = templatePartBlock.attributes; + const templatePartEntityId = createTemplatePartId( + theme, + slug + ); + const templatePartEntity = getEditedEntityRecord( + 'postType', + 'wp_template_part', + templatePartEntityId + ); + + // Look up the `label` for the area in the defined areas so + // that an internationalized label can be used. + if ( templatePartEntity ) { + return templatePartEntity; + } + } + }, + [ clientId ] + ); +} diff --git a/phpunit/class-gutenberg-rest-navigation-controller-test.php b/phpunit/class-gutenberg-rest-navigation-controller-test.php new file mode 100644 index 0000000000000..c2eb9f14b4652 --- /dev/null +++ b/phpunit/class-gutenberg-rest-navigation-controller-test.php @@ -0,0 +1,274 @@ +user->create( + array( + 'role' => 'administrator', + ) + ); + } + + public function test_register_routes() { + $routes = rest_get_server()->get_routes(); + $this->assertArrayHasKey( '/wp/v2/navigation', $routes ); + $this->assertArrayHasKey( '/wp/v2/navigation/(?P[\/\w-]+)', $routes ); + } + + public function test_context_param() { + // TODO: Implement test_context_param() method. + $this->markTestIncomplete(); + } + + public function test_get_items() { + $this->markTestIncomplete(); + +// function find_and_normalize_template_by_id( $templates, $id ) { +// foreach ( $templates as $template ) { +// if ( $template['id'] === $id ) { +// unset( $template['content'] ); +// unset( $template['_links'] ); +// return $template; +// } +// } +// +// return null; +// } +// +// wp_set_current_user( 0 ); +// $request = new WP_REST_Request( 'GET', '/wp/v2/templates' ); +// $response = rest_get_server()->dispatch( $request ); +// $this->assertErrorResponse( 'rest_cannot_manage_templates', $response, 401 ); +// +// wp_set_current_user( self::$admin_id ); +// $request = new WP_REST_Request( 'GET', '/wp/v2/templates' ); +// $response = rest_get_server()->dispatch( $request ); +// $data = $response->get_data(); +// +// $this->assertEquals( +// array( +// 'id' => 'tt1-blocks//index', +// 'theme' => 'tt1-blocks', +// 'slug' => 'index', +// 'title' => array( +// 'raw' => 'Index', +// 'rendered' => 'Index', +// ), +// 'description' => 'The default template used when no other template is available. This is a required template in WordPress.', +// 'status' => 'publish', +// 'source' => 'theme', +// 'type' => 'wp_template', +// 'wp_id' => null, +// 'has_theme_file' => true, +// ), +// find_and_normalize_template_by_id( $data, 'tt1-blocks//index' ) +// ); +// +// // Test template parts. +// $request = new WP_REST_Request( 'GET', '/wp/v2/template-parts' ); +// $response = rest_get_server()->dispatch( $request ); +// $data = $response->get_data(); +// +// $this->assertEquals( +// array( +// 'id' => 'tt1-blocks//header', +// 'theme' => 'tt1-blocks', +// 'slug' => 'header', +// 'title' => array( +// 'raw' => 'header', +// 'rendered' => 'header', +// ), +// 'description' => '', +// 'status' => 'publish', +// 'source' => 'theme', +// 'type' => 'wp_template_part', +// 'wp_id' => null, +// 'area' => WP_TEMPLATE_PART_AREA_HEADER, +// 'has_theme_file' => true, +// ), +// find_and_normalize_template_by_id( $data, 'tt1-blocks//header' ) +// ); + } + + public function test_get_item() { + $this->markTestIncomplete(); +// wp_set_current_user( self::$admin_id ); +// $request = new WP_REST_Request( 'GET', '/wp/v2/templates/tt1-blocks//index' ); +// $response = rest_get_server()->dispatch( $request ); +// $data = $response->get_data(); +// unset( $data['content'] ); +// unset( $data['_links'] ); +// +// $this->assertEquals( +// array( +// 'id' => 'tt1-blocks//index', +// 'theme' => 'tt1-blocks', +// 'slug' => 'index', +// 'title' => array( +// 'raw' => 'Index', +// 'rendered' => 'Index', +// ), +// 'description' => 'The default template used when no other template is available. This is a required template in WordPress.', +// 'status' => 'publish', +// 'source' => 'theme', +// 'type' => 'wp_template', +// 'wp_id' => null, +// 'has_theme_file' => true, +// ), +// $data +// ); +// +// // Test template parts. +// $request = new WP_REST_Request( 'GET', '/wp/v2/template-parts/tt1-blocks//header' ); +// $response = rest_get_server()->dispatch( $request ); +// $data = $response->get_data(); +// unset( $data['content'] ); +// unset( $data['_links'] ); +// $this->assertEquals( +// array( +// 'id' => 'tt1-blocks//header', +// 'theme' => 'tt1-blocks', +// 'slug' => 'header', +// 'title' => array( +// 'raw' => 'header', +// 'rendered' => 'header', +// ), +// 'description' => '', +// 'status' => 'publish', +// 'source' => 'theme', +// 'type' => 'wp_template_part', +// 'wp_id' => null, +// 'area' => WP_TEMPLATE_PART_AREA_HEADER, +// 'has_theme_file' => true, +// ), +// $data +// ); + } + + public function test_create_item() { + $this->markTestIncomplete(); + } + + public function _test_create_item() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/navigation' ); + $request->set_body_params( + array( + 'id' => 'wp-header-menu', + 'name' => 'wp-header-menu', + 'slug' => 'wp-header-menu', + 'title' => 'Header menu', + 'description' => 'Just a description', + 'content' => '', + 'status' => 'publish', + ) + ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + unset( $data['_links'] ); + unset( $data['wp_id'] ); + unset( $data['date'] ); + unset( $data['date_gmt'] ); + unset( $data['guid'] ); + unset( $data['modified'] ); + unset( $data['modified_gmt'] ); + unset( $data['password'] ); + unset( $data['link'] ); + unset( $data['template'] ); + +// $this->assertEquals( +// array( +// 'id' => 'wp-header-menu', +// 'slug' => 'wp-header-menu', +// 'title' => array( +// 'raw' => 'Header menu', +// 'rendered' => 'Header menu', +// ), +// 'status' => 'publish', +// 'type' => 'wp_navigation', +// 'content' => array( +// 'raw' => '', +// 'rendered' => '', +// 'protected' => false, +// 'block_version' => 1, +// ), +// ), +// $data +// ); + } + + public function test_create_update_get() { +// $request = new WP_REST_Request( 'DELETE', '/wp/v2/navigation/wp-header-menu' ); +// $response = rest_get_server()->dispatch( $request ); + + $this->_test_create_item(); + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'PUT', '/wp/v2/navigation/wp-header-menu' ); + $request->set_body_params( + array( + 'id' => 'wp-header-menu', + 'title' => 'My new Index Title', + 'content' => '', + ) + ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( 'My new Index Title', $data['title']['raw'] ); + $this->assertEquals( 'wp-header-menu', $data['slug'] ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/navigation/wp-header-menu' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( 'wp-header-menu', $data['slug'] ); + $this->assertEquals( '', $data['content']['raw'] ); + } + + public function test_update_item() { + $this->markTestIncomplete(); +// $this->test_create_item(); +// wp_set_current_user( self::$admin_id ); +// $request = new WP_REST_Request( 'PUT', '/wp/v2/navigation/wp-header-menu' ); +// $request->set_body_params( +// array( +// 'id' => 'wp-header-menu', +// 'title' => 'My new Index Title', +// 'content' => '', +// ) +// ); + } + + public function test_delete_item() { + $this->markTestIncomplete(); +// wp_set_current_user( self::$admin_id ); +// $request = new WP_REST_Request( 'DELETE', '/wp/v2/templates/justrandom//template' ); +// $response = rest_get_server()->dispatch( $request ); +// $this->assertErrorResponse( 'rest_template_not_found', $response, 404 ); +// +// wp_set_current_user( self::$admin_id ); +// $request = new WP_REST_Request( 'DELETE', '/wp/v2/templates/tt1-blocks//single' ); +// $response = rest_get_server()->dispatch( $request ); +// $this->assertErrorResponse( 'rest_invalid_template', $response, 400 ); + } + + public function test_prepare_item() { + // TODO: Implement test_prepare_item() method. + $this->markTestIncomplete(); + } + + public function test_get_item_schema() { + // TODO: Implement test_get_item_schema() method. + $this->markTestIncomplete(); + } +}