// Bail early if this isn't a sitemap or stylesheet route. if ( ! ( $sitemap || $stylesheet_type ) ) { return; } if ( ! $this->sitemaps_enabled() ) { $wp_query->set_404(); status_header( 404 ); return; } // Render stylesheet if this is stylesheet route. if ( $stylesheet_type ) { $stylesheet = new WP_Sitemaps_Stylesheet(); $stylesheet->render_stylesheet( $stylesheet_type ); exit; } // Render the index. if ( 'index' === $sitemap ) { $sitemap_list = $this->index->get_sitemap_list(); $this->renderer->render_index( $sitemap_list ); exit; } $provider = $this->registry->get_provider( $sitemap ); if ( ! $provider ) { return; } if ( empty( $paged ) ) { $paged = 1; } $url_list = $provider->get_url_list( $paged, $object_subtype ); // Force a 404 and bail early if no URLs are present. if ( empty( $url_list ) ) { $wp_query->set_404(); status_header( 404 ); return; } $this->renderer->render_sitemap( $url_list ); exit; } /** * Redirects a URL to the wp-sitemap.xml * * @since 5.5.0 * @deprecated 6.7.0 Deprecated in favor of {@see WP_Rewrite::rewrite_rules()} * * @param bool $bypass Pass-through of the pre_handle_404 filter value. * @param WP_Query $query The WP_Query object. * @return bool Bypass value. */ public function redirect_sitemapxml( $bypass, $query ) { _deprecated_function( __FUNCTION__, '6.7.0' ); // If a plugin has already utilized the pre_handle_404 function, return without action to avoid conflicts. if ( $bypass ) { return $bypass; } // 'pagename' is for most permalink types, name is for when the %postname% is used as a top-level field. if ( 'sitemap-xml' === $query->get( 'pagename' ) || 'sitemap-xml' === $query->get( 'name' ) ) { wp_safe_redirect( $this->index->get_index_url() ); exit(); } return $bypass; } /** * Adds the sitemap index to robots.txt. * * @since 5.5.0 * * @param string $output robots.txt output. * @param bool $is_public Whether the site is public. * @return string The robots.txt output. */ public function add_robots( $output, $is_public ) { if ( $is_public ) { $output .= "\nSitemap: " . esc_url( $this->index->get_index_url() ) . "\n"; } return $output; } } ue if the request has permission; WP_Error object otherwise. */ public function create_item_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable if ( ! $this->dev_tools_user_access->is_user_enabled() ) { return new WP_Error( 'amp_rest_no_dev_tools', __( 'Sorry, you do not have access to dev tools for the AMP plugin for WordPress.', 'amp' ), [ 'status' => rest_authorization_required_code() ] ); } return true; } /** * Validate preview nonce. * * @see _show_post_preview() * * @param string $preview_nonce Preview nonce. * @param int $post_id Post ID. * @return bool Whether the preview nonce is valid. */ public function is_valid_preview_nonce( $preview_nonce, $post_id ) { return false !== wp_verify_nonce( $preview_nonce, 'post_preview_' . $post_id ); } /** * Returns validation information about a URL, validating the URL along the way. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function validate_post_url( $request ) { $post_id = (int) $request['id']; $preview_nonce = $request['preview_nonce']; $url = amp_get_permalink( $post_id ); if ( ! empty( $preview_nonce ) ) { // Verify that the preview nonce is valid. Note this is not done in a `validate_callback` because // at that point there won't be a validated `id` parameter. if ( ! $this->is_valid_preview_nonce( $preview_nonce, $post_id ) ) { return new WP_Error( 'amp_post_preview_denied', __( 'Sorry, you are not allowed to validate this post preview.', 'amp' ), [ 'status' => 403 ] ); } $url = add_query_arg( [ 'preview' => 1, 'preview_id' => $post_id, 'preview_nonce' => $preview_nonce, ], $url ); } $validity = $this->url_validation_provider->get_url_validation( $url, get_post_type( $post_id ) ); if ( is_wp_error( $validity ) ) { return $validity; } $data = [ 'results' => [], 'review_link' => get_edit_post_link( $validity['post_id'], 'raw' ), 'support_link' => 'https://wordpress.org/support/plugin/amp/#new-topic-0', ]; foreach ( AMP_Validated_URL_Post_Type::get_invalid_url_validation_errors( $validity['post_id'] ) as $result ) { // Handle case where a validationError's `sources` are an object (with numeric keys). // Note: this will no longer be an issue after https://github.com/ampproject/amp-wp/commit/bbb0e495a817a56b37554dfd721170712c92d7b8 // but is still required for validation errors stored in the database prior to that commit. if ( isset( $result['data']['sources'] ) ) { $result['data']['sources'] = array_values( $result['data']['sources'] ); } else { // Make sure sources are always defined. $result['data']['sources'] = []; } $data['results'][] = [ 'error' => $result['data'], 'status' => $result['status'], 'term_id' => $result['term']->term_id, 'title' => AMP_Validation_Error_Taxonomy::get_error_title_from_code( $result['data'] ), ]; } return rest_ensure_response( $this->filter_response_by_context( $data, $request['context'] ) ); } /** * Retrieves the schema for plugin options provided by the endpoint. * * @return array Item schema data. */ public function get_item_schema() { if ( $this->schema ) { return $this->schema; } $sources_type = [ 'items' => [ 'type' => 'object', ], 'type' => 'array', ]; $sources_type['items']['properties']['sources'] = $sources_type; $this->schema = [ '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'amp-wp-url-validation', 'type' => 'object', 'properties' => [ 'results' => [ 'description' => __( 'Validation errors for the post.', 'amp' ), 'readonly' => true, 'type' => 'array', 'items' => [ 'type' => 'object', 'properties' => [ 'error' => [ 'properties' => [ 'code' => [ 'context' => [], 'type' => 'string', ], 'node_attributes' => [ 'context' => [], 'type' => 'object', ], 'node_name' => [ 'context' => [], 'type' => 'string', ], 'node_type' => [ 'context' => [], 'type' => 'integer', ], 'parent_name' => [ 'context' => [], 'type' => 'string', ], 'sources' => $sources_type, 'type' => [ 'type' => 'string', ], ], 'type' => 'object', ], 'status' => [ 'type' => 'integer', ], 'term_id' => [ 'type' => 'integer', ], 'title' => [ 'type' => 'string', ], ], ], ], 'review_link' => [ 'description' => __( 'The URL where validation errors can be reviewed.', 'amp' ), 'readonly' => true, 'type' => 'string', ], 'support_link' => [ 'description' => __( 'The URL for AMP support.', 'amp' ), 'readonly' => true, 'type' => 'string', ], ], ]; return $this->schema; } } ue if the request has permission; WP_Error object otherwise. */ public function create_item_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable if ( ! $this->dev_tools_user_access->is_user_enabled() ) { return new WP_Error( 'amp_rest_no_dev_tools', __( 'Sorry, you do not have access to dev tools for the AMP plugin for WordPress.', 'amp' ), [ 'status' => rest_authorization_required_code() ] ); } return true; } /** * Validate preview nonce. * * @see _show_post_preview() * * @param string $preview_nonce Preview nonce. * @param int $post_id Post ID. * @return bool Whether the preview nonce is valid. */ public function is_valid_preview_nonce( $preview_nonce, $post_id ) { return false !== wp_verify_nonce( $preview_nonce, 'post_preview_' . $post_id ); } /** * Returns validation information about a URL, validating the URL along the way. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function validate_post_url( $request ) { $post_id = (int) $request['id']; $preview_nonce = $request['preview_nonce']; $url = amp_get_permalink( $post_id ); if ( ! empty( $preview_nonce ) ) { // Verify that the preview nonce is valid. Note this is not done in a `validate_callback` because // at that point there won't be a validated `id` parameter. if ( ! $this->is_valid_preview_nonce( $preview_nonce, $post_id ) ) { return new WP_Error( 'amp_post_preview_denied', __( 'Sorry, you are not allowed to validate this post preview.', 'amp' ), [ 'status' => 403 ] ); } $url = add_query_arg( [ 'preview' => 1, 'preview_id' => $post_id, 'preview_nonce' => $preview_nonce, ], $url ); } $validity = $this->url_validation_provider->get_url_validation( $url, get_post_type( $post_id ) ); if ( is_wp_error( $validity ) ) { return $validity; } $data = [ 'results' => [], 'review_link' => get_edit_post_link( $validity['post_id'], 'raw' ), 'support_link' => 'https://wordpress.org/support/plugin/amp/#new-topic-0', ]; foreach ( AMP_Validated_URL_Post_Type::get_invalid_url_validation_errors( $validity['post_id'] ) as $result ) { // Handle case where a validationError's `sources` are an object (with numeric keys). // Note: this will no longer be an issue after https://github.com/ampproject/amp-wp/commit/bbb0e495a817a56b37554dfd721170712c92d7b8 // but is still required for validation errors stored in the database prior to that commit. if ( isset( $result['data']['sources'] ) ) { $result['data']['sources'] = array_values( $result['data']['sources'] ); } else { // Make sure sources are always defined. $result['data']['sources'] = []; } $data['results'][] = [ 'error' => $result['data'], 'status' => $result['status'], 'term_id' => $result['term']->term_id, 'title' => AMP_Validation_Error_Taxonomy::get_error_title_from_code( $result['data'] ), ]; } return rest_ensure_response( $this->filter_response_by_context( $data, $request['context'] ) ); } /** * Retrieves the schema for plugin options provided by the endpoint. * * @return array Item schema data. */ public function get_item_schema() { if ( $this->schema ) { return $this->schema; } $sources_type = [ 'items' => [ 'type' => 'object', ], 'type' => 'array', ]; $sources_type['items']['properties']['sources'] = $sources_type; $this->schema = [ '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'amp-wp-url-validation', 'type' => 'object', 'properties' => [ 'results' => [ 'description' => __( 'Validation errors for the post.', 'amp' ), 'readonly' => true, 'type' => 'array', 'items' => [ 'type' => 'object', 'properties' => [ 'error' => [ 'properties' => [ 'code' => [ 'context' => [], 'type' => 'string', ], 'node_attributes' => [ 'context' => [], 'type' => 'object', ], 'node_name' => [ 'context' => [], 'type' => 'string', ], 'node_type' => [ 'context' => [], 'type' => 'integer', ], 'parent_name' => [ 'context' => [], 'type' => 'string', ], 'sources' => $sources_type, 'type' => [ 'type' => 'string', ], ], 'type' => 'object', ], 'status' => [ 'type' => 'integer', ], 'term_id' => [ 'type' => 'integer', ], 'title' => [ 'type' => 'string', ], ], ], ], 'review_link' => [ 'description' => __( 'The URL where validation errors can be reviewed.', 'amp' ), 'readonly' => true, 'type' => 'string', ], 'support_link' => [ 'description' => __( 'The URL for AMP support.', 'amp' ), 'readonly' => true, 'type' => 'string', ], ], ]; return $this->schema; } } ue if the request has permission; WP_Error object otherwise. */ public function create_item_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable if ( ! $this->dev_tools_user_access->is_user_enabled() ) { return new WP_Error( 'amp_rest_no_dev_tools', __( 'Sorry, you do not have access to dev tools for the AMP plugin for WordPress.', 'amp' ), [ 'status' => rest_authorization_required_code() ] ); } return true; } /** * Validate preview nonce. * * @see _show_post_preview() * * @param string $preview_nonce Preview nonce. * @param int $post_id Post ID. * @return bool Whether the preview nonce is valid. */ public function is_valid_preview_nonce( $preview_nonce, $post_id ) { return false !== wp_verify_nonce( $preview_nonce, 'post_preview_' . $post_id ); } /** * Returns validation information about a URL, validating the URL along the way. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function validate_post_url( $request ) { $post_id = (int) $request['id']; $preview_nonce = $request['preview_nonce']; $url = amp_get_permalink( $post_id ); if ( ! empty( $preview_nonce ) ) { // Verify that the preview nonce is valid. Note this is not done in a `validate_callback` because // at that point there won't be a validated `id` parameter. if ( ! $this->is_valid_preview_nonce( $preview_nonce, $post_id ) ) { return new WP_Error( 'amp_post_preview_denied', __( 'Sorry, you are not allowed to validate this post preview.', 'amp' ), [ 'status' => 403 ] ); } $url = add_query_arg( [ 'preview' => 1, 'preview_id' => $post_id, 'preview_nonce' => $preview_nonce, ], $url ); } $validity = $this->url_validation_provider->get_url_validation( $url, get_post_type( $post_id ) ); if ( is_wp_error( $validity ) ) { return $validity; } $data = [ 'results' => [], 'review_link' => get_edit_post_link( $validity['post_id'], 'raw' ), 'support_link' => 'https://wordpress.org/support/plugin/amp/#new-topic-0', ]; foreach ( AMP_Validated_URL_Post_Type::get_invalid_url_validation_errors( $validity['post_id'] ) as $result ) { // Handle case where a validationError's `sources` are an object (with numeric keys). // Note: this will no longer be an issue after https://github.com/ampproject/amp-wp/commit/bbb0e495a817a56b37554dfd721170712c92d7b8 // but is still required for validation errors stored in the database prior to that commit. if ( isset( $result['data']['sources'] ) ) { $result['data']['sources'] = array_values( $result['data']['sources'] ); } else { // Make sure sources are always defined. $result['data']['sources'] = []; } $data['results'][] = [ 'error' => $result['data'], 'status' => $result['status'], 'term_id' => $result['term']->term_id, 'title' => AMP_Validation_Error_Taxonomy::get_error_title_from_code( $result['data'] ), ]; } return rest_ensure_response( $this->filter_response_by_context( $data, $request['context'] ) ); } /** * Retrieves the schema for plugin options provided by the endpoint. * * @return array Item schema data. */ public function get_item_schema() { if ( $this->schema ) { return $this->schema; } $sources_type = [ 'items' => [ 'type' => 'object', ], 'type' => 'array', ]; $sources_type['items']['properties']['sources'] = $sources_type; $this->schema = [ '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'amp-wp-url-validation', 'type' => 'object', 'properties' => [ 'results' => [ 'description' => __( 'Validation errors for the post.', 'amp' ), 'readonly' => true, 'type' => 'array', 'items' => [ 'type' => 'object', 'properties' => [ 'error' => [ 'properties' => [ 'code' => [ 'context' => [], 'type' => 'string', ], 'node_attributes' => [ 'context' => [], 'type' => 'object', ], 'node_name' => [ 'context' => [], 'type' => 'string', ], 'node_type' => [ 'context' => [], 'type' => 'integer', ], 'parent_name' => [ 'context' => [], 'type' => 'string', ], 'sources' => $sources_type, 'type' => [ 'type' => 'string', ], ], 'type' => 'object', ], 'status' => [ 'type' => 'integer', ], 'term_id' => [ 'type' => 'integer', ], 'title' => [ 'type' => 'string', ], ], ], ], 'review_link' => [ 'description' => __( 'The URL where validation errors can be reviewed.', 'amp' ), 'readonly' => true, 'type' => 'string', ], 'support_link' => [ 'description' => __( 'The URL for AMP support.', 'amp' ), 'readonly' => true, 'type' => 'string', ], ], ]; return $this->schema; } } ue if the request has permission; WP_Error object otherwise. */ public function create_item_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable if ( ! $this->dev_tools_user_access->is_user_enabled() ) { return new WP_Error( 'amp_rest_no_dev_tools', __( 'Sorry, you do not have access to dev tools for the AMP plugin for WordPress.', 'amp' ), [ 'status' => rest_authorization_required_code() ] ); } return true; } /** * Validate preview nonce. * * @see _show_post_preview() * * @param string $preview_nonce Preview nonce. * @param int $post_id Post ID. * @return bool Whether the preview nonce is valid. */ public function is_valid_preview_nonce( $preview_nonce, $post_id ) { return false !== wp_verify_nonce( $preview_nonce, 'post_preview_' . $post_id ); } /** * Returns validation information about a URL, validating the URL along the way. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function validate_post_url( $request ) { $post_id = (int) $request['id']; $preview_nonce = $request['preview_nonce']; $url = amp_get_permalink( $post_id ); if ( ! empty( $preview_nonce ) ) { // Verify that the preview nonce is valid. Note this is not done in a `validate_callback` because // at that point there won't be a validated `id` parameter. if ( ! $this->is_valid_preview_nonce( $preview_nonce, $post_id ) ) { return new WP_Error( 'amp_post_preview_denied', __( 'Sorry, you are not allowed to validate this post preview.', 'amp' ), [ 'status' => 403 ] ); } $url = add_query_arg( [ 'preview' => 1, 'preview_id' => $post_id, 'preview_nonce' => $preview_nonce, ], $url ); } $validity = $this->url_validation_provider->get_url_validation( $url, get_post_type( $post_id ) ); if ( is_wp_error( $validity ) ) { return $validity; } $data = [ 'results' => [], 'review_link' => get_edit_post_link( $validity['post_id'], 'raw' ), 'support_link' => 'https://wordpress.org/support/plugin/amp/#new-topic-0', ]; foreach ( AMP_Validated_URL_Post_Type::get_invalid_url_validation_errors( $validity['post_id'] ) as $result ) { // Handle case where a validationError's `sources` are an object (with numeric keys). // Note: this will no longer be an issue after https://github.com/ampproject/amp-wp/commit/bbb0e495a817a56b37554dfd721170712c92d7b8 // but is still required for validation errors stored in the database prior to that commit. if ( isset( $result['data']['sources'] ) ) { $result['data']['sources'] = array_values( $result['data']['sources'] ); } else { // Make sure sources are always defined. $result['data']['sources'] = []; } $data['results'][] = [ 'error' => $result['data'], 'status' => $result['status'], 'term_id' => $result['term']->term_id, 'title' => AMP_Validation_Error_Taxonomy::get_error_title_from_code( $result['data'] ), ]; } return rest_ensure_response( $this->filter_response_by_context( $data, $request['context'] ) ); } /** * Retrieves the schema for plugin options provided by the endpoint. * * @return array Item schema data. */ public function get_item_schema() { if ( $this->schema ) { return $this->schema; } $sources_type = [ 'items' => [ 'type' => 'object', ], 'type' => 'array', ]; $sources_type['items']['properties']['sources'] = $sources_type; $this->schema = [ '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'amp-wp-url-validation', 'type' => 'object', 'properties' => [ 'results' => [ 'description' => __( 'Validation errors for the post.', 'amp' ), 'readonly' => true, 'type' => 'array', 'items' => [ 'type' => 'object', 'properties' => [ 'error' => [ 'properties' => [ 'code' => [ 'context' => [], 'type' => 'string', ], 'node_attributes' => [ 'context' => [], 'type' => 'object', ], 'node_name' => [ 'context' => [], 'type' => 'string', ], 'node_type' => [ 'context' => [], 'type' => 'integer', ], 'parent_name' => [ 'context' => [], 'type' => 'string', ], 'sources' => $sources_type, 'type' => [ 'type' => 'string', ], ], 'type' => 'object', ], 'status' => [ 'type' => 'integer', ], 'term_id' => [ 'type' => 'integer', ], 'title' => [ 'type' => 'string', ], ], ], ], 'review_link' => [ 'description' => __( 'The URL where validation errors can be reviewed.', 'amp' ), 'readonly' => true, 'type' => 'string', ], 'support_link' => [ 'description' => __( 'The URL for AMP support.', 'amp' ), 'readonly' => true, 'type' => 'string', ], ], ]; return $this->schema; } }