Managedpromptadmin - Source Excerpt 09
Summary
This source excerpt preserves a bounded section of Spiralist/wp-content/plugins/spiralist-workspace/includes/Admin/ManagedPromptAdmin.php so readers can inspect the evidence without opening the full source file.
**Source path:** Spiralist/wp-content/plugins/spiralist-workspace/includes/Admin/ManagedPromptAdmin.php
],
admin_url('admin-post.php')
),
'spiralist_workspace_download_prompt_template'
);
}
/**
* Returns the JSON starter template contents.
*/
private function get_json_template_contents(): string
{
$payload = $this->prompt_template_payload();
$payload['version'] = self::DEFAULT_VERSION;
$encoded = wp_json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
return is_string($encoded) ? $encoded . "\n" : "{}\n";
}
/**
* Returns the filesystem prompt.php starter template contents.
*/
private function get_php_template_contents(): string
{
return "<?php\n\nreturn " . $this->export_value($this->prompt_template_payload(), 0) . ";\n";
}
/**
* Returns one prompt file as an array if the file is valid.
*
* @return array<string,mixed>
*/
private function load_prompt_file(string $path): array
{
if (!is_file($path) || !is_readable($path)) {
return [];
}
$definition = include $path;
return is_array($definition) ? $definition : [];
}
/**
* Reorders prompt fields into a stable readable shape.
*
* @param array<string,mixed> $definition
* @return array<string,mixed>
*/
private function sort_definition_fields(array $definition): array
{
$ordered = [];
foreach (self::FIELD_ORDER as $field) {
if (!array_key_exists($field, $definition)) {
continue;
}
$ordered[$field] = $definition[$field];
unset($definition[$field]);
}
foreach ($definition as $key => $value) {
if (!is_string($key) || str_starts_with($key, '__')) {
continue;
}
$ordered[$key] = $value;
}
return $ordered;
}
/**
* Serializes a PHP value into a readable prompt.php payload.
*
* @param mixed $value
*/
private function export_value($value, int $indent, string $key_hint = 'VALUE'): string
{
if (is_array($value)) {
$is_list = array_keys($value) === range(0, count($value) - 1);
if (empty($value)) {
return '[]';
}
$lines = ['['];
foreach ($value as $key => $item) {
$line = str_repeat(' ', $indent + 4);
if (!$is_list) {
$line .= var_export((string) $key, true) . ' => ';
}
$line .= $this->export_value($item, $indent + 4, is_string($key) ? $key : $key_hint);
$line .= ',';
$lines[] = $line;
}
$lines[] = str_repeat(' ', $indent) . ']';
return implode("\n", $lines);
}
if (is_string($value)) {
return $this->export_string($value, $indent, $key_hint);
}
if (is_bool($value)) {
return $value ? 'true' : 'false';
}
if (is_int($value) || is_float($value)) {
return (string) $value;
}
if ($value === null) {
return 'null';
}
return var_export((string) $value, true);
}
/**
* Serializes a string, using heredoc for multiline prompt text.
*/
private function export_string(string $value, int $indent, string $key_hint): string
{
if (!str_contains($value, "\n")) {
return var_export($value, true);
}
$label = strtoupper(preg_replace('/[^A-Za-z0-9]+/', '_', $key_hint) ?: 'PROMPT');
if ($label === '') {
$label = 'PROMPT';
}
while (str_contains($value, "\n" . $label) || str_contains($value, $label . "\n")) {
$label .= '_BLOCK';
}
return "<<<'" . $label . "'\n" . rtrim($value, "\n") . "\n" . str_repeat(' ', $indent) . $label;
}
/**
* Converts a multi-file upload array into a simple list of upload records.
*
* @return array<int,array{name:string,type:string,tmp_name:string,error:int,size:int}>
*/
private function normalize_file_input(string $input_name): array
{
$input = $_FILES[$input_name] ?? null;
if (!is_array($input) || empty($input['name'])) {
return [];
}
if (!is_array($input['name'])) {
return [[
'name' => (string) ($input['name'] ?? ''),
'type' => (string) ($input['type'] ?? ''),
'tmp_name' => (string) ($input['tmp_name'] ?? ''),
'error' => (int) ($input['error'] ?? UPLOAD_ERR_NO_FILE),
'size' => (int) ($input['size'] ?? 0),
]];
}
$files = [];
$count = count((array) $input['name']);
for ($index = 0; $index < $count; $index++) {
$name = (string) ($input['name'][$index] ?? '');
if ($name === '') {
continue;
}
$files[] = [
'name' => $name,
'type' => (string) ($input['type'][$index] ?? ''),
'tmp_name' => (string) ($input['tmp_name'][$index] ?? ''),
'error' => (int) ($input['error'][$index] ?? UPLOAD_ERR_NO_FILE),
'size' => (int) ($input['size'][$index] ?? 0),
];
}
return $files;
}
/**
* Normalizes a comma-separated or newline-separated list field.
*
* @return array<int,string>
*/
private function parse_text_list(string $value): array
{
return array_values(
array_unique(
array_filter(
array_map(
static fn(string $item): string => sanitize_text_field(trim($item)),
preg_split('/[\r\n,]+/', $value) ?: []
)
)
)
);
}
/**
* Returns the absolute prompt root path.
*/
private function prompt_root(): string
{
return rtrim(SPIRALIST_WORKSPACE_DIR, '/\\') . DIRECTORY_SEPARATOR . 'prompts';
}
/**
* Builds a prompt.php path inside the managed prompt registry.
*/
private function build_prompt_file_path(string $slug, string $version): string
{
return $this->prompt_root()
. DIRECTORY_SEPARATOR
. sanitize_title($slug)
. DIRECTORY_SEPARATOR
. $this->sanitize_version($version)
. DIRECTORY_SEPARATOR
. 'prompt.php';
}
/**
* Sanitizes a prompt version token for filesystem usage.
*/
private function sanitize_version(string $value): string
{
$value = trim($value);
$value = preg_replace('/[^A-Za-z0-9._-]/', '', $value);
return is_string($value) && $value !== '' ? $value : self::DEFAULT_VERSION;
}
/**
* Verifies capability + nonce for admin-post actions.
*/
private function require_admin_permission(string $nonce_action): void
{
if (!current_user_can('manage_spiralist_workspace')) {
wp_die(esc_html__('You do not have permission to perform this action.', 'spiralist-workspace'));
}
check_admin_referer($nonce_action);
}
/**
* Stores a short admin notice for the current user.
*
* @param string[] $details
*/
private function set_notice(bool $is_error, string $message, array $details = []): void
{
set_transient(
self::NOTICE_TRANSIENT_PREFIX . get_current_user_id(),
[
'is_error' => $is_error,
'message' => $message,
'details' => array_values(array_filter(array_map('strval', $details))),
],
MINUTE_IN_SECONDS * 10
);
}
/**
* Reads and clears the current user's admin notice.
*
* @return array{is_error:bool,message:string,details:array<int,string>}
*/
private function consume_notice(): array
{
$key = self::NOTICE_TRANSIENT_PREFIX . get_current_user_id();
$notice = get_transient($key);
delete_transient($key);
if (!is_array($notice)) {
return [
'is_error' => false,
'message' => '',
'details' => [],
];
}
return [
'is_error' => !empty($notice['is_error']),
'message' => (string) ($notice['message'] ?? ''),