settings = $settings; $this->registry = $registry; } /** * Hook the route. */ public function register(): void { add_action( 'rest_api_init', array( $this, 'register_route' ) ); } /** * Register the route. */ public function register_route(): void { register_rest_route( self::NAMESPACE, self::ROUTE, array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get' ), 'permission_callback' => array( $this, 'can_manage' ), ), array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'save' ), 'permission_callback' => array( $this, 'can_manage' ), ), ) ); } /** * Whether the current user may manage the plugin. */ public function can_manage(): bool { return current_user_can( Capabilities::MANAGE ); } /** * Current blocking settings. */ public function get(): WP_REST_Response { return new WP_REST_Response( $this->payload() ); } /** * Save the submitted blocking fields. * * @param WP_REST_Request $request Request whose JSON body holds a partial `blocking` object. * @return WP_REST_Response|WP_Error */ public function save( WP_REST_Request $request ) { $body = $request->get_json_params(); if ( ! is_array( $body ) ) { return new WP_Error( 'consentfold_invalid_body', __( 'Send the blocking settings as a JSON object.', 'consentfold' ), array( 'status' => 400 ) ); } $locked = $this->locked_fields(); $site = $this->settings->get_site(); $blocking = array_merge( is_array( $site['blocking'] ?? null ) ? $site['blocking'] : array(), array_diff_key( $body, array_flip( $locked ) ) ); $document = array_merge( $site, array( 'blocking' => $blocking ) ); unset( $document['schema_version'] ); // The repository carries it over. $errors = $this->settings->rule_category_errors( $document ); // Validate before saving, so a document with any error saves nothing. try { Schema::validate( $document ); } catch ( InvalidSettingsException $e ) { $errors += $e->errors(); } if ( array() === $errors ) { $this->settings->set( $document ); } if ( array() !== $errors ) { return new WP_Error( 'consentfold_invalid_settings', __( 'The blocking settings are not valid.', 'consentfold' ), array( 'status' => 400, 'errors' => $errors, ) ); } return new WP_REST_Response( $this->payload() ); } /** * Effective blocking settings, the fields the network locked, the categories a rule may target and the services no category can grant. * * @return array{blocking: array, locked: list, categories: list, unassigned_services: list} */ private function payload(): array { $settings = $this->settings->get(); $categories = array(); foreach ( $this->settings->rule_categories( $settings ) as $slug => $category ) { $categories[] = array( 'slug' => $slug, 'label' => $category['label'], ); } return array( 'blocking' => (array) $settings['blocking'], 'locked' => $this->locked_fields(), 'categories' => $categories, 'unassigned_services' => $this->registry->unassigned_services(), ); } /** * Blocking fields the network locked, whether it locked the whole branch or one field. * * @return list */ private function locked_fields(): array { $locked = array(); foreach ( $this->settings->locked_keys() as $path ) { foreach ( array_keys( Defaults::all()['blocking'] ) as $field ) { if ( 'blocking' === $path || 0 === strpos( $path, 'blocking.' . $field ) ) { $locked[] = $field; } } } return array_values( array_unique( $locked ) ); } }