Settings - Source Excerpt 03
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);
}
}