Skip to content
wiki.fftac.org

Settings - Source Excerpt 03

Back to Settings

Summary

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

**Source path:** Spiralist/wp-content/plugins/spiralist-workspace/includes/Config/Settings.php

<input class="small-text" id="spiralist_workspace_conversation_memory_max_output_tokens" name="<?php echo esc_attr(self::OPTION); ?>[conversation_memory_max_output_tokens]" type="number" min="300" max="4000" value="<?php echo esc_attr((string) $settings['conversation_memory_max_output_tokens']); ?>" />
                            <p class="description">Stored for compatibility with older memory UI. Local prompt packets do not spend output tokens.</p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><label for="spiralist_workspace_default_visibility">Default Visibility</label></th>
                        <td>
                            <select id="spiralist_workspace_default_visibility" name="<?php echo esc_attr(self::OPTION); ?>[default_visibility]">
                                <?php foreach (['private', 'members', 'public', 'specific_users'] as $visibility) : ?>
                                    <option value="<?php echo esc_attr($visibility); ?>" <?php selected((string) $settings['default_visibility'], $visibility); ?>>
                                        <?php echo esc_html($visibility); ?>
                                    </option>
                                <?php endforeach; ?>
                            </select>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><label for="spiralist_workspace_default_moderation_status">Default Moderation Status</label></th>
                        <td>
                            <select id="spiralist_workspace_default_moderation_status" name="<?php echo esc_attr(self::OPTION); ?>[default_moderation_status]">
                                <?php foreach (PromptMetaRegistry::moderation_statuses() as $status => $label) : ?>
                                    <option value="<?php echo esc_attr($status); ?>" <?php selected((string) $settings['default_moderation_status'], $status); ?>>
                                        <?php echo esc_html($label); ?>
                                    </option>
                                <?php endforeach; ?>
                            </select>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><label for="spiralist_workspace_logging_mode">Logging Mode</label></th>
                        <td>
                            <select id="spiralist_workspace_logging_mode" name="<?php echo esc_attr(self::OPTION); ?>[logging_mode]">
                                <?php foreach (['redacted', 'minimal', 'disabled'] as $mode) : ?>
                                    <option value="<?php echo esc_attr($mode); ?>" <?php selected((string) $settings['logging_mode'], $mode); ?>>
                                        <?php echo esc_html($mode); ?>
                                    </option>
                                <?php endforeach; ?>
                            </select>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><label for="spiralist_workspace_retention_days">Retention Days</label></th>
                        <td>
                            <input class="small-text" id="spiralist_workspace_retention_days" name="<?php echo esc_attr(self::OPTION); ?>[retention_days]" type="number" min="1" value="<?php echo esc_attr((string) $settings['retention_days']); ?>" />
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><label for="spiralist_workspace_featured_prompt_ids">Featured Prompt IDs</label></th>
                        <td>
                            <textarea class="large-text code" id="spiralist_workspace_featured_prompt_ids" name="<?php echo esc_attr(self::OPTION); ?>[featured_prompt_ids]" rows="3"><?php echo esc_textarea((string) $settings['featured_prompt_ids']); ?></textarea>
                            <p class="description">Comma-separated prompt post IDs featured on the public and member libraries.</p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">Toggles</th>
                        <td>
                            <label><input name="<?php echo esc_attr(self::OPTION); ?>[public_execution_enabled]" type="checkbox" value="1" <?php checked((string) $settings['public_execution_enabled'], '1'); ?> /> Enable anonymous prompt-packet rendering for public prompts</label><br />
                            <label><input name="<?php echo esc_attr(self::OPTION); ?>[conversation_sharing_enabled]" type="checkbox" value="1" <?php checked((string) $settings['conversation_sharing_enabled'], '1'); ?> /> Enable conversation sharing architecture</label><br />
                            <label><input name="<?php echo esc_attr(self::OPTION); ?>[member_approval_required]" type="checkbox" value="1" <?php checked((string) $settings['member_approval_required'], '1'); ?> /> Require member approval before prompt access</label>
                        </td>
                    </tr>
                </table>
                <?php submit_button('Save Workspace Settings'); ?>
            </form>
        </div>
        <?php
    }

    /**
     * Moves untouched installations off the old closed anonymous-execution defaults.
     *
     * @param array<string,mixed> $settings
     * @param array<string,mixed> $stored
     * @return array<string,mixed>
     */
    private function upgrade_legacy_defaults(array $settings, array $stored): array
    {
        $legacy_model_allowlist = (string) ($stored['model_allowlist'] ?? '');
        $uses_legacy_model_default = $this->normalize_multiline_default($legacy_model_allowlist) === self::LEGACY_MODEL_ALLOWLIST;
        $uses_legacy_daily_limits = $this->daily_limits_match((string) ($stored['daily_limits_json'] ?? ''), self::LEGACY_DAILY_LIMITS);

        if ($uses_legacy_model_default) {
            $settings['model_allowlist'] = self::DEFAULT_MODEL_ALLOWLIST;
        }

        if ($uses_legacy_model_default && $uses_legacy_daily_limits) {
            $settings['daily_limits_json'] = (string) wp_json_encode(self::DEFAULT_DAILY_LIMITS, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        }

        if (
            array_key_exists('public_execution_enabled', $stored)
            && (string) $stored['public_execution_enabled'] === '0'
            && $uses_legacy_model_default
            && $uses_legacy_daily_limits
        ) {
            $settings['public_execution_enabled'] = '1';
        }

        return $settings;
    }

    /**
     * @param array<string,int> $expected
     */
    private function daily_limits_match(string $json, array $expected): bool
    {
        $decoded = json_decode($json, true);
        if (!is_array($decoded)) {
            return false;
        }

        $normalized = [];
        foreach ($decoded as $key => $value) {
            $normalized[(string) $key] = (int) $value;
        }

        $expected_normalized = [];
        foreach ($expected as $key => $value) {
            $expected_normalized[(string) $key] = (int) $value;
        }

        ksort($normalized);
        ksort($expected_normalized);

        return $normalized === $expected_normalized;
    }

    private function normalize_multiline_default(string $value): string
    {
        $lines = preg_split('/[\r\n]+/', trim($value)) ?: [];

        return implode("\n", array_map('trim', $lines));
    }

    /**
     * Default plugin settings.
     */
    public static function defaults(): array
    {
        return [
            'openai_base_url' => '',
            'openai_api_key' => '',
            'model_allowlist' => self::DEFAULT_MODEL_ALLOWLIST,
            'daily_limits_json' => wp_json_encode(
                self::DEFAULT_DAILY_LIMITS,
                JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
            ),
            'default_visibility' => 'private',
            'default_moderation_status' => 'draft',
            'public_execution_enabled' => '1',
            'logging_mode' => 'redacted',
            'retention_days' => '90',
            'featured_prompt_ids' => '',
            'conversation_memory_token_threshold' => '5000',
            'conversation_memory_max_output_tokens' => '1200',
            'conversation_sharing_enabled' => '0',
            'member_approval_required' => '0',
        ];
    }

    /**
     * Returns a fixed masked secret preview for admin settings screens.
     */
    private function masked_secret_value(string $secret): string
    {
        return trim($secret) === '' ? '' : 'Stored key on file ' . str_repeat('*', 12);
    }
}