[ $this, 'get_features' ], 'methods' => 'GET', 'permission_callback' => [ $this, 'check_permission' ], ], [ 'args' => $this->get_args(), 'callback' => [ $this, 'update_settings' ], 'methods' => 'PUT', 'permission_callback' => [ $this, 'check_permission' ], ], ] ); } /** * Get args schema. * * @return array */ public function get_args() { $args = []; $features = \ElasticPress\Features::factory()->registered_features; foreach ( $features as $feature ) { $properties = []; $schema = $feature->get_settings_schema(); foreach ( $schema as $schema ) { if ( ! isset( $schema['label'] ) ) { continue; } $type = $schema['type'] ?? ''; $property = [ 'description' => $schema['label'], 'type' => 'string', ]; switch ( $type ) { case 'select': case 'radio': $property['enum'] = array_map( fn( $o ) => $o['value'], $schema['options'] ); break; case 'toggle': $property['type'] = 'boolean'; break; case 'number': $property['type'] = 'number'; break; case 'url': $property['type'] = 'string'; $property['format'] = 'uri'; break; case 'field_group': $property['type'] = 'object'; $property['properties'] = []; } $properties[ $schema['key'] ] = $property; } $args[ $feature->slug ] = [ 'description' => $feature->get_title(), 'properties' => $properties, 'type' => 'object', ]; if ( method_exists( $feature, 'sanitize_settings_callback' ) ) { $args[ $feature->slug ]['sanitize_callback'] = [ $feature, 'sanitize_settings_callback' ]; } } return $args; } /** * Check that the request has permission to save features. * * @return boolean */ public function check_permission() { $capability = Utils\get_capability(); return current_user_can( $capability ); } /** * Update features settings. * * @param \WP_REST_Request $request Full details about the request. * @return array */ public function update_settings( \WP_REST_Request $request ) { if ( Utils\is_indexing() ) { wp_send_json_error( 'is_syncing', 400 ); exit; } $current_settings = FeaturesStore::factory()->get_feature_settings(); $new_settings = $current_settings; $features = \ElasticPress\Features::factory()->registered_features; $settings_that_requires_features = []; foreach ( $features as $slug => $feature ) { $param = $request->get_param( $slug ); if ( ! $param ) { continue; } if ( empty( $current_settings[ $slug ] ) ) { $current_settings[ $slug ] = []; $new_settings[ $slug ] = []; } $schema = $feature->get_settings_schema(); foreach ( $schema as $schema ) { $key = $schema['key']; $type = $schema['type'] ?? ''; if ( isset( $param[ $key ] ) ) { // Handle field group values if ( 'field_group' === $type ) { // Save the nested structure for the settings UI $new_settings[ $slug ][ $key ] = $param[ $key ]; // Flatten the field group values into the main settings array foreach ( $schema['fields'] as $field ) { $field_key = $field['key']; if ( isset( $param[ $key ][ $field_key ] ) ) { $new_settings[ $slug ][ $field_key ] = $param[ $key ][ $field_key ]; // Only apply to current settings if no sync required if ( empty( $schema['requires_sync'] ) ) { $current_settings[ $slug ][ $field_key ] = $param[ $key ][ $field_key ]; } } } } else { $new_settings[ $slug ][ $key ] = $param[ $key ]; } // Only apply to the current settings if does not require a sync or if it is activating it if ( ! empty( $schema['requires_sync'] ) && ! empty( $param[ $key ] ) ) { continue; } /* * If a setting requires another feature, we have to check for it after running through everything, * as it is possible that the feature will be active after this foreach. */ if ( empty( $schema['requires_feature'] ) ) { $current_settings[ $slug ][ $key ] = $param[ $key ]; } else { if ( ! isset( $settings_that_requires_features[ $slug ] ) ) { $settings_that_requires_features[ $slug ] = []; } $settings_that_requires_features[ $slug ][ $key ] = [ 'required_feature' => $schema['requires_feature'], 'value' => $param[ $key ], ]; } } } } foreach ( $settings_that_requires_features as $feature => $fields ) { foreach ( $fields as $field_key => $field_data ) { $required_features = (array) $field_data['required_feature']; $all_required_active = true; foreach ( $required_features as $required_feature_slug ) { if ( empty( $current_settings[ $required_feature_slug ]['active'] ) ) { $all_required_active = false; break; } } if ( $all_required_active ) { $current_settings[ $feature ][ $field_key ] = $field_data['value']; } } } foreach ( $current_settings as $slug => $feature ) { FeaturesStore::factory()->update_feature( $slug, $feature ); } foreach ( $new_settings as $slug => $feature ) { FeaturesStore::factory()->update_feature( $slug, $feature, true, 'draft' ); } return [ 'success' => true, ]; } /** * Return the current features payload along with persisted settings. * * @since 5.3.0 * @return array */ public function get_features() { $store = FeaturesStore::factory(); $settings = $store->get_feature_settings(); $settings_draft = $store->get_feature_settings_draft(); return [ 'features' => $this->get_features_payload(), 'settings' => is_array( $settings ) ? $settings : [], 'settingsDraft' => is_array( $settings_draft ) ? $settings_draft : null, 'success' => true, ]; } /** * Build the serialized features payload. * * @since 5.3.0 * @return array */ protected function get_features_payload() { $features_objects = FeaturesStore::factory()->registered_features; foreach ( $features_objects as $feature ) { $feature->reset_settings_schema(); } $features_data = array_map( fn( $feature ) => $feature->get_json(), $features_objects ); return array_values( $features_data ); } }