Skip to content
wiki.fftac.org

Restcontroller - Source Excerpt 07

Back to Restcontroller

Summary

This source excerpt preserves a bounded section of Spiralist/wp-content/plugins/spiralist-workspace/includes/Http/RestController.php so readers can inspect the evidence without opening the full source file.

**Source path:** Spiralist/wp-content/plugins/spiralist-workspace/includes/Http/RestController.php

)
        );

        return new WP_REST_Response(array_map(fn(array $prompt): array => $this->augment_prompt_payload($prompt, false), $public));
    }

    /**
     * Returns the canonical prompt category hierarchy.
     */
    public function categories(): WP_REST_Response
    {
        return new WP_REST_Response(
            [
                'domains' => PromptCategoryRegistry::for_api(),
                'subcategory_count' => count(PromptCategoryRegistry::subcategories()),
                'intents' => PromptIntentRegistry::for_api(),
                'intent_count' => count(PromptIntentRegistry::for_api()),
                'facets' => PromptMetaRegistry::for_api(),
            ]
        );
    }

    /**
     * Returns the current participant trust and cooldown state.
     */
    public function get_my_reputation(): WP_REST_Response
    {
        return new WP_REST_Response($this->reputation->get(get_current_user_id()));
    }

    /**
     * Returns the current user's profile style settings.
     */
    public function get_profile_style(): WP_REST_Response
    {
        $user_id = get_current_user_id();
        $user = wp_get_current_user();

        return new WP_REST_Response(
            array_merge(
                ProfileStyle::get($user_id),
                [
                    'scope_class' => ProfileStyle::scope_class($user_id),
                    'profile_url' => $user instanceof \WP_User && $user->user_nicename !== ''
                        ? home_url('/profile/' . rawurlencode($user->user_nicename) . '/')
                        : '',
                ]
            )
        );
    }

    /**
     * Updates the current user's sanitized profile style.
     */
    public function update_profile_style(WP_REST_Request $request): WP_REST_Response
    {
        $user_id = get_current_user_id();
        $style_prompt = (string) $request->get_param('style_prompt');
        $custom_css = (string) $request->get_param('custom_css');
        $result = ProfileStyle::update($user_id, $style_prompt, $custom_css);
        $user = wp_get_current_user();

        $result['scope_class'] = ProfileStyle::scope_class($user_id);
        $result['profile_url'] = $user instanceof \WP_User && $user->user_nicename !== ''
            ? home_url('/profile/' . rawurlencode($user->user_nicename) . '/')
            : '';

        return new WP_REST_Response($result);
    }

    /**
     * Returns the latest scheduled governance-maintenance result.
     */
    public function get_governance_maintenance_status(): WP_REST_Response
    {
        return new WP_REST_Response(
            [
                'scheduled' => (bool) wp_next_scheduled(GovernanceMaintenance::HOOK),
                'next_run_utc' => $this->scheduled_utc(GovernanceMaintenance::HOOK),
                'last_run' => $this->governance_maintenance->status(),
            ]
        );
    }

    /**
     * Runs governance maintenance immediately for admins.
     */
    public function run_governance_maintenance(WP_REST_Request $request): WP_REST_Response
    {
        $limit = (int) ($request->get_param('limit') ?: 250);

        return new WP_REST_Response(
            [
                'ok' => true,
                'scheduled' => (bool) wp_next_scheduled(GovernanceMaintenance::HOOK),
                'result' => $this->governance_maintenance->run($limit),
                'next_run_utc' => $this->scheduled_utc(GovernanceMaintenance::HOOK),
            ]
        );
    }

    /**
     * Returns one immutable prompt version.
     */
    public function get_prompt_version(WP_REST_Request $request)
    {
        $user_id = get_current_user_id();
        $prompt = $this->prompts->get((int) $request['id'], $user_id);

        if (empty($prompt) || !$this->permissions->can_view_prompt($prompt, $user_id)) {
            return new WP_Error('spiralist_workspace_prompt_not_found', 'Prompt not found.', ['status' => 404]);
        }

        $version = $this->versions->get_for_prompt((int) $request['id'], (int) $request['version']);
        if (empty($version)) {
            return new WP_Error('spiralist_workspace_version_not_found', 'Version not found.', ['status' => 404]);
        }

        return new WP_REST_Response($version);
    }

    /**
     * Returns a field-level diff between two prompt versions.
     */
    public function diff_prompt_versions(WP_REST_Request $request)
    {
        $user_id = get_current_user_id();
        $prompt = $this->prompts->get((int) $request['id'], $user_id);

        if (empty($prompt) || !$this->permissions->can_view_prompt($prompt, $user_id)) {
            return new WP_Error('spiralist_workspace_prompt_not_found', 'Prompt not found.', ['status' => 404]);
        }

        $diff = $this->versions->diff((int) $request['id'], (int) $request['left'], (int) $request['right']);

        return empty($diff)
            ? new WP_Error('spiralist_workspace_version_not_found', 'Version diff could not be built.', ['status' => 404])
            : new WP_REST_Response($diff);
    }

    /**
     * Rolls a prompt back by creating a new version from an older snapshot.
     */
    public function rollback_prompt_version(WP_REST_Request $request)
    {
        $user_id = get_current_user_id();
        $prompt = $this->prompts->get((int) $request['id'], $user_id);

        if (empty($prompt) || !$this->permissions->can_edit_prompt($prompt, $user_id)) {
            return new WP_Error('spiralist_workspace_prompt_forbidden', 'Prompt not found.', ['status' => 404]);
        }

        $version = $this->versions->get_for_prompt((int) $request['id'], (int) $request['version']);
        $snapshot = (array) ($version['snapshot'] ?? []);
        if (empty($snapshot)) {
            return new WP_Error('spiralist_workspace_version_not_found', 'Version not found.', ['status' => 404]);
        }

        $snapshot['changelog'] = 'Rolled back to version ' . (int) $request['version'];
        $updated = $this->prompts->update((int) $request['id'], $snapshot, $user_id);
        $this->audit->log($user_id, 'prompt_version_rolled_back', 'prompt', (int) $request['id'], ['version' => (int) $request['version']]);

        return new WP_REST_Response($this->prompts->for_api($updated, true));
    }

    /**
     * Looks up a user by login or email.
     */
    public function find_user(WP_REST_Request $request)
    {
        $query = trim((string) $request->get_param('q'));
        if ($query === '' || strlen($query) < 3 || strpos($query, '@') !== false) {
            return new WP_Error('spiralist_workspace_user_not_found', 'User not found.', ['status' => 404]);
        }

        $target = get_user_by('login', sanitize_user($query, true));
        if (!$target instanceof \WP_User) {
            return new WP_Error('spiralist_workspace_user_not_found', 'User not found.', ['status' => 404]);
        }

        return new WP_REST_Response(
            [
                'id' => (int) $target->ID,
                'display_name' => $target->display_name,
                'user_login' => $target->user_login,
            ]
        );
    }

    /**
     * Permission callback for logged-in actions.
     */
    public function require_logged_in(): bool
    {
        return is_user_logged_in();
    }

    /**
     * Permission callback for prompt creation.
     */
    public function require_prompt_create(): bool
    {
        return is_user_logged_in() && current_user_can('create_prompts');
    }

    /**
     * Permission callback for browser-originated Prompt Cockpit draft saves.
     *
     * WordPress REST cookie authentication normally validates X-WP-Nonce. This
     * endpoint performs an explicit nonce check as well because it creates
     * private prompt records for the signed-in user and should not be callable
     * by a cross-site form post.
     */
    public function require_prompt_draft_save(WP_REST_Request $request): bool
    {
        if (!is_user_logged_in() || !current_user_can('create_prompts')) {
            return false;
        }

        $nonce = (string) $request->get_header('X-WP-Nonce');
        if ($nonce === '') {
            $nonce = (string) $request->get_param('_wpnonce');
        }

        return $nonce !== '' && wp_verify_nonce($nonce, 'wp_rest') !== false;
    }

    /**
     * Permission callback for admin actions.
     */
    public function require_workspace_admin(): bool
    {
        return is_user_logged_in() && ($this->permissions->is_admin(get_current_user_id()));
    }

    /**
     * Permission callback for share-target lookups.
     */
    public function require_share_lookup(): bool
    {
        $user_id = get_current_user_id();