Skip to content
wiki.fftac.org

Frontendcontroller - Source Excerpt 01

Back to Frontendcontroller

Summary

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

**Source path:** Spiralist/wp-content/plugins/spiralist-workspace/includes/Web/FrontendController.php

<?php

namespace SpiralistWorkspace\Web;

use SpiralistWorkspace\Auth\Permissions;
use SpiralistWorkspace\Config\Settings;
use SpiralistWorkspace\Domain\AppealRepository;
use SpiralistWorkspace\Domain\AuditRepository;
use SpiralistWorkspace\Domain\ConversationRepository;
use SpiralistWorkspace\Domain\FavoriteRepository;
use SpiralistWorkspace\Domain\PermissionRepository;
use SpiralistWorkspace\Domain\PromptRepository;
use SpiralistWorkspace\Domain\ReputationRepository;
use SpiralistWorkspace\Domain\ReviewRepository;
use SpiralistWorkspace\Domain\RunRepository;
use SpiralistWorkspace\Domain\TestRepository;
use SpiralistWorkspace\Domain\VersionRepository;
use SpiralistWorkspace\Support\PromptCategoryRegistry;
use SpiralistWorkspace\Support\PromptGovernanceSummary;
use SpiralistWorkspace\Support\PromptIntentRegistry;
use SpiralistWorkspace\Support\PromptMetaRegistry;
use SpiralistWorkspace\Support\ProfileStyle;

if (!defined('ABSPATH')) {
    exit;
}

/**
 * Handles front-end routing and template rendering.
 */
class FrontendController
{
    private PromptRepository $prompts;
    private Permissions $permissions;
    private PermissionRepository $shares;
    private VersionRepository $versions;
    private RunRepository $runs;
    private TestRepository $tests;
    private ConversationRepository $conversations;
    private FavoriteRepository $favorites;
    private ReviewRepository $reviews;
    private AppealRepository $appeals;
    private ReputationRepository $reputation;
    private AuditRepository $audit;
    private Settings $settings;
    private array $public_prompt_cache = [];
    private int $public_prompt_cache_limit = 0;
    private bool $public_prompt_sync_attempted = false;

    public function __construct(
        PromptRepository $prompts,
        Permissions $permissions,
        PermissionRepository $shares,
        VersionRepository $versions,
        RunRepository $runs,
        TestRepository $tests,
        ConversationRepository $conversations,
        FavoriteRepository $favorites,
        ReviewRepository $reviews,
        AppealRepository $appeals,
        ReputationRepository $reputation,
        AuditRepository $audit,
        Settings $settings
    ) {
        $this->prompts = $prompts;
        $this->permissions = $permissions;
        $this->shares = $shares;
        $this->versions = $versions;
        $this->runs = $runs;
        $this->tests = $tests;
        $this->conversations = $conversations;
        $this->favorites = $favorites;
        $this->reviews = $reviews;
        $this->appeals = $appeals;
        $this->reputation = $reputation;
        $this->audit = $audit;
        $this->settings = $settings;
    }

    /**
     * Instance hook wrapper.
     */
    public function register_routes(): void
    {
        self::register_rewrites();
    }

    /**
     * Returns the front-end route map version.
     */
    public static function route_version(): string
    {
        return '3';
    }

    /**
     * Flushes rewrite rules when the public route map changes.
     */
    public function maybe_flush_rewrite_rules(): void
    {
        $stored_version = (string) get_option('spiralist_workspace_route_version', '');
        $current_version = self::route_version();

        if ($stored_version === $current_version) {
            return;
        }

        self::register_rewrites();
        flush_rewrite_rules(false);
        update_option('spiralist_workspace_route_version', $current_version, false);
    }

    /**
     * Registers custom rewrite rules.
     */
    public static function register_rewrites(): void
    {
        add_rewrite_tag('%spiralist_workspace_route%', '([^&]+)');
        add_rewrite_tag('%spiralist_workspace_prompt_slug%', '([^&]+)');
        add_rewrite_tag('%spiralist_workspace_run_id%', '([0-9]+)');
        add_rewrite_tag('%spiralist_workspace_conversation_id%', '([0-9]+)');
        add_rewrite_tag('%spiralist_workspace_profile_slug%', '([^&]+)');
        add_rewrite_tag('%spiralist_workspace_api%', '([^&]+)');
        add_rewrite_tag('%spiralist_workspace_filter_key%', '([^&]+)');
        add_rewrite_tag('%spiralist_workspace_filter_value%', '([^&]+)');

        $rules = [
            '^prompts/archive/?$' => 'index.php?spiralist_workspace_route=library&spiralist_workspace_filter_key=lane&spiralist_workspace_filter_value=prompt-ecology-archive',
            '^prompts/official-canon/?$' => 'index.php?spiralist_workspace_route=library&spiralist_workspace_filter_key=lane&spiralist_workspace_filter_value=official-canon',
            '^prompts/community-library/?$' => 'index.php?spiralist_workspace_route=library&spiralist_workspace_filter_key=lane&spiralist_workspace_filter_value=community-library',
            '^prompts/search/([^/]+)/?$' => 'index.php?spiralist_workspace_route=library&spiralist_workspace_filter_key=search&spiralist_workspace_filter_value=$matches[1]',
            '^prompts/(lane|category|provenance|moderation|risk|function|level|intent)/([^/]+)/?$' => 'index.php?spiralist_workspace_route=library&spiralist_workspace_filter_key=$matches[1]&spiralist_workspace_filter_value=$matches[2]',
            '^prompts/?$' => 'index.php?spiralist_workspace_route=library',
            '^workspace/?$' => 'index.php?spiralist_workspace_route=workspace',
            '^members/?$' => 'index.php?spiralist_workspace_route=members',
            '^prompt/([^/]+)/?$' => 'index.php?spiralist_workspace_route=prompt&spiralist_workspace_prompt_slug=$matches[1]',
            '^profile/([^/]+)/?$' => 'index.php?spiralist_workspace_route=profile&spiralist_workspace_profile_slug=$matches[1]',
            '^run/([0-9]+)/?$' => 'index.php?spiralist_workspace_route=run&spiralist_workspace_run_id=$matches[1]',
            '^conversation/([0-9]+)/?$' => 'index.php?spiralist_workspace_route=conversation&spiralist_workspace_conversation_id=$matches[1]',
            '^api/(prompts|runs|conversations)/?$' => 'index.php?spiralist_workspace_route=api&spiralist_workspace_api=$matches[1]',
        ];

        foreach ($rules as $pattern => $query) {
            add_rewrite_rule($pattern, $query, 'top');
        }
    }

    /**
     * Registers custom query vars.
     */
    public function register_query_vars(array $vars): array
    {
        $vars[] = 'spiralist_workspace_route';
        $vars[] = 'spiralist_workspace_prompt_slug';
        $vars[] = 'spiralist_workspace_run_id';
        $vars[] = 'spiralist_workspace_conversation_id';
        $vars[] = 'spiralist_workspace_profile_slug';
        $vars[] = 'spiralist_workspace_api';
        $vars[] = 'spiralist_workspace_filter_key';
        $vars[] = 'spiralist_workspace_filter_value';

        return $vars;
    }

    /**
     * Enqueues front-end assets for workspace pages.
     */
    public function enqueue_assets(): void
    {
        $needs_front_page_assets = is_front_page() && is_user_logged_in();

        if (!$this->is_workspace_request() && !is_page('ai-access') && !$needs_front_page_assets) {
            return;
        }

        $current_locale = function_exists('spiralist_get_current_locale_tag')
            ? spiralist_get_current_locale_tag()
            : '';
        $localize_url = static function (string $path) use ($current_locale): string {
            $url = home_url('/' . trim($path, '/') . '/');

            return $current_locale !== '' && function_exists('spiralist_get_language_prefixed_url')
                ? spiralist_get_language_prefixed_url($url, $current_locale)
                : $url;
        };
        $ui_text = static function (string $key, string $fallback): string {
            return function_exists('spiralist_get_ui_text')
                ? spiralist_get_ui_text($key, $fallback)
                : $fallback;
        };

        wp_enqueue_style(
            'spiralist-workspace',
            SPIRALIST_WORKSPACE_URL . 'assets/css/workspace.css',
            [],
            SPIRALIST_WORKSPACE_VERSION
        );

        wp_enqueue_script(
            'spiralist-workspace',
            SPIRALIST_WORKSPACE_URL . 'assets/js/workspace.js',
            ['spiralist-core'],
            SPIRALIST_WORKSPACE_VERSION,
            true
        );

        wp_localize_script(
            'spiralist-workspace',
            'SpiralistWorkspace',
            [
                'apiRoot' => esc_url_raw(rest_url('spiralist-workspace/v1/')),
                'spiralScriptApiRoot' => esc_url_raw(rest_url('spiral-script/v1/')),
                'nonce' => wp_create_nonce('wp_rest'),
                'loginUrl' => function_exists('spiralist_get_login_url') ? spiralist_get_login_url() : wp_login_url(),
                'signupUrl' => function_exists('spiralist_get_signup_url') ? spiralist_get_signup_url() : wp_registration_url(),