namespace = self::API_NAMESPACE; } /** * Checks a plugin permission and builds the matching error. * * A logged out request gets 401 so a client knows to authenticate, while a * logged in one gets 403 because authenticating again will not help. * * @param string $permission One of the {@see Capabilities} constants. * * @return true|WP_Error True when allowed, an error otherwise. */ protected function authorize( string $permission ): bool|WP_Error { if ( Capabilities::current_user_can( $permission ) ) { return true; } /* * 401 for a caller we do not recognise, 403 for one we do and who is not * allowed — and the code says which. The ternary was redundant, because * `rest_authorization_required_code()` already answers 401 when nobody is * logged in and 403 when somebody is; what it did not do was stop * `formpilot_rest_forbidden` being returned alongside a 401, which tells a client * that authenticating will not help when in fact it is the only thing that * will. */ $status = rest_authorization_required_code(); return new WP_Error( 401 === $status ? 'formpilot_rest_unauthorized' : 'formpilot_rest_forbidden', 401 === $status ? __( 'You must be signed in to perform this request.', 'devplugins-custom-form-builder' ) : __( 'You are not allowed to perform this request.', 'devplugins-custom-form-builder' ), array( 'status' => $status ) ); } /** * Builds a collection response carrying the pagination headers. * * @param array $items Prepared items. * @param int $total Total number of matching records. * @param int $per_page Page size used for the query. */ protected function collection( array $items, int $total, int $per_page ): WP_REST_Response { $response = rest_ensure_response( $items ); $pages = $per_page > 0 ? (int) ceil( $total / $per_page ) : 0; $response->header( 'X-WP-Total', (string) $total ); $response->header( 'X-WP-TotalPages', (string) $pages ); return $response; } /** * Returns the page size a collection request asked for, within the documented range. * * The schema says 1 to 100, but a sanitize callback replaces the core check that * would enforce it, so 100000 reached the repository, which clamps to its own, * larger ceiling - and `X-WP-TotalPages` was then worked out from the 100000, * telling a client there was one page while it had received the first 500 rows. * * @param WP_REST_Request $request Request being served. */ protected function per_page( WP_REST_Request $request ): int { return min( 100, max( 1, (int) $request->get_param( 'per_page' ) ) ); } /** * Returns the arguments every collection endpoint accepts. * * @return array> Argument definitions. */ protected function pagination_args(): array { return array( 'page' => array( 'description' => __( 'Current page of the collection.', 'devplugins-custom-form-builder' ), 'type' => 'integer', 'default' => 1, 'minimum' => 1, 'sanitize_callback' => 'absint', ), 'per_page' => array( 'description' => __( 'Maximum number of items returned per page.', 'devplugins-custom-form-builder' ), 'type' => 'integer', 'default' => 20, 'minimum' => 1, 'maximum' => 100, 'sanitize_callback' => 'absint', ), 'search' => array( 'description' => __( 'Limit results to those matching a string.', 'devplugins-custom-form-builder' ), 'type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field', ), 'order' => array( 'description' => __( 'Sort direction.', 'devplugins-custom-form-builder' ), 'type' => 'string', 'default' => 'desc', 'enum' => array( 'asc', 'desc' ), ), ); } /** * Returns the argument definition of a record ID. * * @param string $description Text shown in the schema. * * @return array> Argument definitions. */ protected function id_arg( string $description ): array { return array( 'id' => array( 'description' => $description, 'type' => 'integer', 'required' => true, 'minimum' => 1, 'sanitize_callback' => 'absint', ), ); } /** * Builds the error returned when a record does not exist. * * @param string $message Message shown to the client. */ protected function not_found( string $message ): WP_Error { return new WP_Error( 'formpilot_rest_not_found', $message, array( 'status' => 404 ) ); } }