400] ); if (!in_array($context_type, self::$context_types, true)) { return $invalid; } if ($context_type !== 'site' && (!$context_id || $context_id <= 0)) { return $invalid; } if ($context_id && !get_post($context_id)) { return $invalid; } // SECURITY: everything above establishes that the context *exists*, not // that this caller may see it. `edit_post` is a meta capability, so // map_meta_cap() resolves authorship, published state and // edit_others_posts for this specific post — the same check // Schema_Input_Validator::validate_context_ownership() and // Settings_Management_Endpoint::authorize_settings_context() make on // their write paths. // // Site context is deliberately left to the route's capability gate. // The write paths demand manage_options for it, but applying that here // would stop a delegated Schema Manager reading site-level schema at // all, which is the point of delegating the section. if ($context_type !== 'site' && !current_user_can('edit_post', $context_id)) { return new WP_Error( 'rest_forbidden', 'You are not allowed to access this content.', ['status' => 403] ); } return true; } /** * Read the context pair off a request and authorise it in one step. * * Handlers that only need "is this allowed, and what are the resolved * values" use this rather than repeating the read/cast/validate dance. * * @since 2.0.1 * * @param \WP_REST_Request $request Request object. * @return array{0: string, 1: int|null}|WP_Error The resolved * [$context_type, $context_id], or the validation error. */ protected function resolve_request_context(\WP_REST_Request $request) { $context_type = $request->get_param('context_type') ?? 'site'; $context_id = $request->get_param('context_id'); $context_id = (null === $context_id || '' === $context_id) ? null : (int) $context_id; $validation = $this->validate_context((string) $context_type, $context_id); if (is_wp_error($validation)) { return $validation; } return [(string) $context_type, $context_id]; } /** * REST args declaring the context pair, so the values arrive typed. * * Named apart from Social_Media_Endpoint's own private get_context_args(): * a class method silently wins over a trait method of the same name, and * that one marks both parameters required — correct for the * /social-media/meta/{context} routes it was written for, and a 400 on * every site-context read if it captured these registrations. * * @since 2.0.1 * * @return array> Argument definitions. */ protected function get_context_route_args(): array { return [ 'context_type' => [ 'required' => false, 'type' => 'string', 'enum' => self::$context_types, 'default' => 'site', 'description' => 'Context type', ], 'context_id' => [ 'required' => false, 'type' => 'integer', 'minimum' => 1, 'description' => 'Context ID (required for non-site contexts)', ], ]; } }