service = $service; } public function register_routes(): void { register_rest_route( RunController::NAMESPACE, '/runs/(?P[0-9a-fA-F-]{36})/privacy-checklists', array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'view' ), 'permission_callback' => array( $this, 'authorize' ), ) ); register_rest_route( RunController::NAMESPACE, '/runs/(?P[0-9a-fA-F-]{36})/privacy-checklists/(?Pprivacy\.(?:ru_152fz|en_baseline))', array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'save' ), 'permission_callback' => array( $this, 'authorize' ), 'args' => array( 'answers' => array( 'type' => 'array', 'required' => true, 'minItems' => 1, 'maxItems' => 20, 'items' => array( 'type' => 'object', 'additionalProperties' => false, 'required' => array( 'id', 'state', 'comment' ), 'properties' => array( 'id' => array( 'type' => 'string', 'maxLength' => 64, ), 'state' => array( 'type' => 'string', 'enum' => array( 'unreviewed', 'confirmed', 'not_confirmed', 'not_applicable' ), ), 'comment' => array( 'type' => 'string', 'maxLength' => 1000, ), ), ), ), ), ) ); } /** * @param WP_REST_Request> $request Request. * @return WP_REST_Response|WP_Error */ public function view( WP_REST_Request $request ) { try { $result = $this->service->view( (string) $request->get_param( 'uuid' ) ); } catch ( StorageException $exception ) { return $this->error( 'not_found' ); } catch ( InvalidArgumentException $exception ) { return $this->error( 'unavailable' ); } catch ( \Throwable $exception ) { return $this->error( 'failed' ); } return new WP_REST_Response( $result, 200 ); } /** * @param WP_REST_Request> $request Request. * @return WP_REST_Response|WP_Error */ public function save( WP_REST_Request $request ) { $answers = $request->get_param( 'answers' ); if ( ! is_array( $answers ) ) { return $this->error( 'invalid' ); } try { $result = $this->service->save( (string) $request->get_param( 'uuid' ), (string) $request->get_param( 'check_id' ), $answers, get_current_user_id() ); } catch ( StorageException $exception ) { return $this->error( 'not_found' ); } catch ( InvalidArgumentException $exception ) { return $this->error( 'invalid' ); } catch ( \Throwable $exception ) { return $this->error( 'failed' ); } return new WP_REST_Response( $result, 200 ); } /** * @param WP_REST_Request> $request Request. * @return true|WP_Error */ public function authorize( WP_REST_Request $request ) { if ( ! current_user_can( 'manage_options' ) ) { return new WP_Error( 'px320_audit_forbidden', __( 'You are not allowed to manage site audits.', '320px-site-audit' ), array( 'status' => 403 ) ); } $nonce = $request->get_header( 'X-WP-Nonce' ); if ( ! is_string( $nonce ) || false === wp_verify_nonce( $nonce, 'wp_rest' ) ) { return new WP_Error( 'px320_audit_invalid_nonce', __( 'The audit request could not be verified. Refresh the page and try again.', '320px-site-audit' ), array( 'status' => 403 ) ); } return true; } private function error( string $reason ): WP_Error { $status = 'not_found' === $reason ? 404 : ( 'failed' === $reason ? 500 : 400 ); return new WP_Error( 'px320_audit_privacy_checklist_' . $reason, 'failed' === $reason ? __( 'The local checklist could not be saved safely. Nothing was sent outside this site.', '320px-site-audit' ) : __( 'The checklist request is not valid for this completed Full report.', '320px-site-audit' ), array( 'status' => $status ) ); } }