\WP_REST_Server::READABLE, 'callback' => [$this, 'index'], 'permission_callback' => function () { return current_user_can('manage_options'); }, ]); } public function index(): \WP_REST_Response { $types = []; foreach (FieldRegistry::instance()->all() as $type) { $types[] = $this->present($type); } return new \WP_REST_Response([ 'api_level' => FieldRegistry::API_LEVEL, 'types' => $types, 'problems' => FieldRegistry::instance()->problems(), ]); } /** * What the editor needs to know about a type. The server decides what is stored; the editor only gets the names * of named sanitizers, never the rules or callables. */ private function present(FieldType $type): array { $settings = []; foreach ($type->schema() as $setting) { // Fixed and derived values are computed on the server; the editor does not ask for them. if (!isset($setting['control']) || $setting['control'] === null) { continue; } $entry = array_intersect_key($setting, array_flip(['key', 'control', 'label', 'default', 'options'])); // Only the name of a named sanitizer ("toggle_onoff") is shared: it tells the editor how a switch posts. // Callables stay on the server. if (isset($setting['sanitize']) && is_string($setting['sanitize'])) { $entry['sanitize'] = $setting['sanitize']; } $settings[] = $entry; } return [ 'type' => $type::type(), 'label' => $type->label(), 'group' => $type->group(), 'has_option' => $type->hasOption(), 'settings' => $settings, ]; } }