Promptrepository - Source Excerpt 01
Summary
This source excerpt preserves a bounded section of Spiralist/wp-content/plugins/spiralist-workspace/includes/Domain/PromptRepository.php so readers can inspect the evidence without opening the full source file.
**Source path:** Spiralist/wp-content/plugins/spiralist-workspace/includes/Domain/PromptRepository.php
<?php
namespace SpiralistWorkspace\Domain;
use WP_Post;
use SpiralistWorkspace\Config\Settings;
use SpiralistWorkspace\Support\PromptCategoryRegistry;
use SpiralistWorkspace\Support\PromptIntentRegistry;
use SpiralistWorkspace\Support\PromptMetaRegistry;
if (!defined('ABSPATH')) {
exit;
}
/**
* Stores prompt entities on a dedicated custom post type.
*/
class PromptRepository
{
public const POST_TYPE = 'spiralist_prompt';
public const LEGACY_POST_TYPE = 'spiralist_workspace_prompt';
public const TAG_TAXONOMY = 'spiralist_prompt_tag';
public const LEGACY_TAG_TAXONOMY = 'spiralist_workspace_prompt_tag';
public const TYPE_TAXONOMY = 'spiralist_prompt_type';
public const LEGACY_TYPE_TAXONOMY = 'spiralist_workspace_prompt_type';
public const CATEGORY_TAXONOMY = 'spiralist_prompt_cat';
public const LEGACY_CATEGORY_TAXONOMY = 'spiralist_workspace_prompt_category';
private VersionRepository $versions;
private PermissionRepository $shares;
private FavoriteRepository $favorites;
private ?ReputationRepository $reputation;
public function __construct(
VersionRepository $versions,
PermissionRepository $shares,
FavoriteRepository $favorites,
?ReputationRepository $reputation = null
)
{
$this->versions = $versions;
$this->shares = $shares;
$this->favorites = $favorites;
$this->reputation = $reputation;
}
/**
* Registers post type and taxonomies.
*/
public function register_content_types(): void
{
self::register_post_type();
self::register_taxonomies();
PromptCategoryRegistry::ensure_terms();
}
/**
* Registers the internal prompt post type.
*/
public static function register_post_type(): void
{
register_post_type(
self::POST_TYPE,
[
'labels' => [
'name' => 'Workspace Prompts',
'singular_name' => 'Workspace Prompt',
'menu_name' => 'Workspace Prompts',
'add_new_item' => 'Add Workspace Prompt',
'edit_item' => 'Edit Workspace Prompt',
'view_item' => 'View Workspace Prompt',
],
'public' => false,
'show_ui' => true,
'show_in_menu' => 'spiralist-workspace',
'show_in_rest' => false,
'supports' => ['title', 'editor', 'excerpt', 'author', 'thumbnail'],
'menu_icon' => 'dashicons-format-chat',
'rewrite' => false,
'taxonomies' => [self::TAG_TAXONOMY, self::TYPE_TAXONOMY, self::CATEGORY_TAXONOMY],
]
);
}
/**
* Registers prompt taxonomies.
*/
public static function register_taxonomies(): void
{
register_taxonomy(
self::TAG_TAXONOMY,
[self::POST_TYPE],
[
'label' => 'Prompt Tags',
'public' => false,
'show_ui' => true,
'show_in_rest' => false,
'hierarchical' => false,
]
);
register_taxonomy(
self::TYPE_TAXONOMY,
[self::POST_TYPE],
[
'label' => 'Prompt Types',
'public' => false,
'show_ui' => true,
'show_in_rest' => false,
'hierarchical' => false,
]
);
register_taxonomy(
self::CATEGORY_TAXONOMY,
[self::POST_TYPE],
[
'label' => 'Prompt Categories',
'public' => false,
'show_ui' => true,
'show_in_rest' => false,
'hierarchical' => true,
]
);
}
/**
* Migrates legacy post type and taxonomy slugs to WordPress-valid lengths.
*/
public static function migrate_content_types(): void
{
global $wpdb;
$wpdb->update(
$wpdb->posts,
['post_type' => self::POST_TYPE],
['post_type' => self::LEGACY_POST_TYPE],
['%s'],
['%s']
);
foreach (self::legacy_taxonomy_map() as $legacy => $current) {
$wpdb->update(
$wpdb->term_taxonomy,
['taxonomy' => $current],
['taxonomy' => $legacy],
['%s'],
['%s']
);
}
}
/**
* Returns the legacy-to-current taxonomy mapping.
*
* @return array<string, string>
*/
private static function legacy_taxonomy_map(): array
{
return [
self::LEGACY_TAG_TAXONOMY => self::TAG_TAXONOMY,
self::LEGACY_TYPE_TAXONOMY => self::TYPE_TAXONOMY,
self::LEGACY_CATEGORY_TAXONOMY => self::CATEGORY_TAXONOMY,
];
}
/**
* Registers the admin meta box.
*/
public function register_meta_boxes(): void
{
add_meta_box(
'spiralist-workspace-prompt',
'Prompt Workspace Fields',
[$this, 'render_meta_box'],
self::POST_TYPE,
'normal',
'high'
);
}
/**
* Saves meta box fields.
*/
public function handle_post_save(int $post_id, WP_Post $post, bool $update): void
{
if ($post->post_type !== self::POST_TYPE) {
return;
}
if (
!isset($_POST['spiralist_workspace_prompt_nonce']) ||
!wp_verify_nonce(
sanitize_text_field(wp_unslash($_POST['spiralist_workspace_prompt_nonce'])),
'spiralist_workspace_prompt'
)
) {
return;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (!current_user_can('edit_post', $post_id)) {
return;
}
$existing = $this->get($post_id);
$payload = $this->normalize_payload($_POST, $existing);
$this->persist_meta($post_id, $payload);
$this->set_taxonomies($post_id, $payload);
$version = max(1, (int) ($payload['version_number'] ?? 1));
$this->versions->create($post_id, $version, $this->get($post_id), get_current_user_id(), (string) ($payload['changelog'] ?? ''));
}
/**
* Renders the workspace prompt meta box.
*/
public function render_meta_box(WP_Post $post): void
{
$prompt = $this->get($post->ID);
wp_nonce_field('spiralist_workspace_prompt', 'spiralist_workspace_prompt_nonce');
?>
<div class="spiralist-workspace-admin-grid">
<?php if (($prompt['prompt_origin'] ?? '') === 'system') : ?>
<div style="grid-column:1 / -1;padding:1rem;border:1px solid #dcdcde;background:#fff8e5;">
<strong>Managed system prompt.</strong>
<p style="margin:.5rem 0 0;">This prompt is synced from a versioned file at <code><?php echo esc_html((string) ($prompt['source_path'] ?? '')); ?></code>. Source version: <code><?php echo esc_html((string) ($prompt['source_version'] ?? '')); ?></code>. Manual edits may be overwritten by the next deploy sync.</p>
</div>
<?php endif; ?>
<p><label><strong>Summary</strong><br /><textarea class="widefat" name="spiralist_workspace_summary" rows="3"><?php echo esc_textarea((string) ($prompt['summary'] ?? '')); ?></textarea></label></p>
<p><label><strong>Author Label</strong><br /><input class="widefat" type="text" name="spiralist_workspace_author_label" value="<?php echo esc_attr((string) ($prompt['author_label'] ?? '')); ?>" /></label></p>
<div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1rem;">
<p><label><strong>Category</strong><br />
<select class="widefat" name="spiralist_workspace_category">
<?php foreach (PromptCategoryRegistry::categories() as $category) : ?>
<option value="<?php echo esc_attr($category); ?>" <?php selected((string) ($prompt['category'] ?? ''), $category); ?>><?php echo esc_html($category); ?></option>
<?php endforeach; ?>
</select>
</label></p>
<p><label><strong>Subcategory</strong><br />
<select class="widefat" name="spiralist_workspace_subcategory">
<?php foreach (PromptCategoryRegistry::hierarchy() as $category => $subcategories) : ?>
<optgroup label="<?php echo esc_attr($category); ?>">