From 9fed188672f7012174a33000a2df8833756f214a Mon Sep 17 00:00:00 2001 From: r00bbert <280805750+r00bbert@users.noreply.github.com> Date: Sun, 17 May 2026 14:13:01 +0200 Subject: [PATCH 01/18] Phase 1: rename plugin metadata and main file - Rename opentrust.php to open-trust-center-by-ettic.php - Plugin Name: OpenTrust to Open Trust Center by Ettic - Plugin URI: /opentrust to /open-trust-center-by-ettic - Text Domain: opentrust to open-trust-center-by-ettic - Version: 1.1.1 to 1.2.0 - readme.txt H1 and Stable tag updated Trademark rename per wp.org review. Folder stays opentrust/ during dev; renamed at submission build. --- opentrust.php => open-trust-center-by-ettic.php | 10 +++++----- readme.txt | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) rename opentrust.php => open-trust-center-by-ettic.php (95%) diff --git a/opentrust.php b/open-trust-center-by-ettic.php similarity index 95% rename from opentrust.php rename to open-trust-center-by-ettic.php index d8d7d03..0f8a8a5 100644 --- a/opentrust.php +++ b/open-trust-center-by-ettic.php @@ -1,16 +1,16 @@ Date: Sun, 17 May 2026 14:15:48 +0200 Subject: [PATCH 02/18] Phase 2: tear down v1-v5 migration chain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop maybe_upgrade() and its init hook, plus the five migration helpers (rename_cpt_slugs_v4, rename_postmeta_keys_v5, backfill_uuids, backfill_model_snapshot). ~180 LOC removed. Reset OPENTRUST_DB_VERSION constant to 1 (fresh-release baseline). Drop legacy ot_* slugs from uninstall.php (their migration path is gone). Keep OpenTrust_CPT::LEGACY_META_MAP intact — still needed by the importer's back-compat remap. Treating 1.2.0 as the first version intended for distribution. Any developer running an older opentrust install migrates via export/import, not in-place schema bumps. --- includes/class-opentrust-cpt.php | 12 +- includes/class-opentrust.php | 181 ------------------------------- open-trust-center-by-ettic.php | 8 +- uninstall.php | 12 -- 4 files changed, 7 insertions(+), 206 deletions(-) diff --git a/includes/class-opentrust-cpt.php b/includes/class-opentrust-cpt.php index 4bc5181..89622d3 100644 --- a/includes/class-opentrust-cpt.php +++ b/includes/class-opentrust-cpt.php @@ -47,14 +47,10 @@ final class OpenTrust_CPT { /** * Legacy postmeta keys from v1.0–v1.1, mapped old `_ot_*` → new - * `_opentrust_*`. The 2-character `_ot_` prefix collided too easily in the - * shared wp_postmeta table; v5 of the schema renames every plugin-owned - * key. Single source of truth for the v4→v5 migration - * (OpenTrust::rename_postmeta_keys_v5()), the import back-compat remap - * (OpenTrust_IO::remap_legacy_meta_keys()), and uninstall cleanup. - * - * @deprecated 1.1.1 Drop in 2.0.0 once v1.x upgrades are no longer - * supported, alongside the `if ($current < 5)` migration branch. + * `_opentrust_*`. Retained for import back-compat: legacy archives + * (exported by v1.0.x/v1.1.x) still carry these keys, and the importer + * remaps them on read via OpenTrust_IO::remap_legacy_meta_keys(). + * Phase 8 extends the chain through `_ettic_otc_*`. */ public const LEGACY_META_MAP = [ // Shared identity. diff --git a/includes/class-opentrust.php b/includes/class-opentrust.php index 25cca3a..9a804c5 100644 --- a/includes/class-opentrust.php +++ b/includes/class-opentrust.php @@ -33,9 +33,6 @@ private function __construct() { // Flush rewrite rules when settings change (transient flag). add_action('init', [$this, 'maybe_flush_rewrites'], 99); - // Check for DB schema upgrades. - add_action('init', [$this, 'maybe_upgrade'], 5); - // Bump the render-cache version on any OpenTrust CPT change. Catches // saves, deletes, trash/untrash, and publish transitions in one wire-up. OpenTrust_CPT::register_invalidator(OpenTrust_CPT::ALL, [$this, 'invalidate_cache']); @@ -170,184 +167,6 @@ public function register_query_vars(array $vars): array { return $vars; } - /** - * Schema upgrade hook. Runs on every init at priority 5. Future schema - * migrations land here, gated on the current value of opentrust_db_version - * vs. the OPENTRUST_DB_VERSION constant. opentrust_db_version is only - * advanced once, at the tail — so an interrupted migration retries cleanly - * on the next init, and every migration step must be idempotent. - */ - public function maybe_upgrade(): void { - $current = (int) get_option('opentrust_db_version', 0); - if ($current === (int) OPENTRUST_DB_VERSION) { - return; - } - - // v3 → v4: rename CPT slugs from `ot_*` to `opentr_*` to satisfy the - // wp.org 4-character-prefix rule. Runs first so any v1→v4 jump renames - // the rows before the v1→v2 UUID back-fill queries them — the back-fill - // resolves OpenTrust_CPT::ALL to the new slugs, so the rows must already - // carry those slugs by the time it runs. Runs at init priority 5, - // before OpenTrust_CPT::register_post_types() at priority 10. - if ($current < 4) { - self::rename_cpt_slugs_v4(); - } - - // v4 → v5: rename postmeta keys from `_ot_*` to `_opentrust_*` so the - // plugin no longer uses a 2-character prefix in the shared postmeta - // table. MUST run before the v1→v2 UUID back-fill below: the back-fill - // stamps `_opentrust_uuid` on any post that lacks it, so a v1 post's - // legacy `_ot_uuid` row has to be renamed to `_opentrust_uuid` first — - // otherwise the back-fill would see no `_opentrust_uuid`, add a second - // one, and the post would end up with two conflicting UUID rows. - if ($current < 5) { - self::rename_postmeta_keys_v5(); - } - - // v1 → v2: back-fill _opentrust_uuid on existing CPT posts. Runs after - // the v4→v5 rename above so it never double-stamps a post that already - // carries a (renamed) legacy UUID. - if ($current < 2) { - self::backfill_uuids(); - } - - // v2 → v3: register the daily AI-model-refresh cron and back-fill - // the active-model snapshot so existing installs don't render a raw - // id before the first cron tick. - if ($current < 3) { - OpenTrust_Admin_AI::schedule_cron(); - self::backfill_model_snapshot(); - } - - update_option('opentrust_db_version', OPENTRUST_DB_VERSION, false); - set_transient('opentrust_flush_rewrite', true); - $this->invalidate_cache(); - - // The chat corpus transient is locale-keyed and not bound to - // opentrust_cache_version, so invalidate_cache() above doesn't bust - // it. After the v4 rename the surviving corpus would be valid but - // mention the old slug context in admin telemetry — drop it so the - // next chat request rebuilds against the renamed rows. - if (class_exists('OpenTrust_Chat_Corpus')) { - OpenTrust_Chat_Corpus::invalidate(); - } - } - - /** - * Seed the active-model snapshot from the existing per-provider transient - * cache. No HTTP — runs at upgrade time only. - */ - private static function backfill_model_snapshot(): void { - $settings = self::get_settings(); - $snap = OpenTrust_Admin_AI::instance()->snapshot_for_provider( - (string) ($settings['ai_provider'] ?? ''), - (string) ($settings['ai_model'] ?? '') - ); - if ($snap === null) { - return; - } - $settings['ai_model_display_name'] = $snap['display_name']; - $settings['ai_model_recommended'] = $snap['recommended']; - OpenTrust_Admin_Settings::instance()->save_settings_raw($settings); - } - - /** - * Rewrite wp_posts.post_type for each renamed CPT. Idempotent: if the - * migration is interrupted, opentrust_db_version stays unadvanced and the - * next init retries. Revisions carry post_type='revision' and are not - * matched. Translation linkage (WPML/Polylang) is keyed by post ID, not - * by slug, so existing translations stay paired. - * - * Collects affected IDs before the UPDATE so each row's WP_Post entry in - * the object cache can be invalidated — otherwise post_type checks that - * read from cache return stale 'ot_*' values until the cache expires. - * - * @deprecated 1.1.0 Drop in 2.0.0 once v1.0.x upgrades are no longer - * supported. Also remove the `if ($current < 4)` branch in - * maybe_upgrade() and the OpenTrust_CPT::LEGACY_* constants. - */ - private static function rename_cpt_slugs_v4(): void { - global $wpdb; - foreach (OpenTrust_CPT::LEGACY_MAP as $old => $new) { - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-shot schema migration; per-row clean_post_cache() runs below + opentrust_cache_version bumped after maybe_upgrade. - $ids = $wpdb->get_col( - $wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE post_type = %s", $old) - ); - if (empty($ids)) { - continue; - } - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-shot schema migration; per-row clean_post_cache() runs below + opentrust_cache_version bumped after maybe_upgrade. - $wpdb->update( - $wpdb->posts, - ['post_type' => $new], - ['post_type' => $old], - ['%s'], - ['%s'] - ); - foreach ($ids as $id) { - clean_post_cache((int) $id); - } - } - } - - /** - * Rewrite wp_postmeta.meta_key for every plugin-owned postmeta key, from - * the legacy `_ot_*` prefix to `_opentrust_*`. Idempotent: a bulk UPDATE - * keyed on the old meta_key matches nothing on a re-run, and - * opentrust_db_version stays unadvanced until the tail of maybe_upgrade() - * so an interrupted migration retries cleanly. - * - * The UPDATE is keyed on meta_key alone, so it catches postmeta on posts, - * revisions, and attachments alike. Affected post IDs are collected first - * so each WP_Post's meta cache can be invalidated — otherwise get_post_meta - * would serve stale `_ot_*` lookups until the cache expires. - * - * @deprecated 1.1.1 Drop in 2.0.0 once v1.0.x upgrades are no longer - * supported. Also remove the `if ($current < 5)` branch in - * maybe_upgrade() and OpenTrust_CPT::LEGACY_META_MAP. - */ - private static function rename_postmeta_keys_v5(): void { - global $wpdb; - foreach (OpenTrust_CPT::LEGACY_META_MAP as $old => $new) { - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-shot schema migration; per-row clean_post_cache() runs below + opentrust_cache_version bumped after maybe_upgrade. - $ids = $wpdb->get_col( - $wpdb->prepare("SELECT DISTINCT post_id FROM {$wpdb->postmeta} WHERE meta_key = %s", $old) - ); - if (empty($ids)) { - continue; - } - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-shot schema migration; per-row clean_post_cache() runs below + opentrust_cache_version bumped after maybe_upgrade. - $wpdb->update( - $wpdb->postmeta, - ['meta_key' => $new], - ['meta_key' => $old], - ['%s'], - ['%s'] - ); - foreach ($ids as $id) { - clean_post_cache((int) $id); - } - } - } - - private static function backfill_uuids(): void { - $posts = get_posts([ - 'post_type' => OpenTrust_CPT::ALL, - 'post_status' => 'any', - 'posts_per_page' => -1, - 'fields' => 'ids', - 'meta_query' => [ - [ - 'key' => '_opentrust_uuid', - 'compare' => 'NOT EXISTS', - ], - ], - ]); - foreach ($posts as $post_id) { - update_post_meta((int) $post_id, '_opentrust_uuid', wp_generate_uuid4()); - } - } - public function maybe_flush_rewrites(): void { if (get_transient('opentrust_flush_rewrite')) { delete_transient('opentrust_flush_rewrite'); diff --git a/open-trust-center-by-ettic.php b/open-trust-center-by-ettic.php index 0f8a8a5..6500d5c 100644 --- a/open-trust-center-by-ettic.php +++ b/open-trust-center-by-ettic.php @@ -22,7 +22,7 @@ define('OPENTRUST_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('OPENTRUST_PLUGIN_URL', plugin_dir_url(__FILE__)); define('OPENTRUST_PLUGIN_FILE', __FILE__); -define('OPENTRUST_DB_VERSION', 5); +define('OPENTRUST_DB_VERSION', 1); require_once OPENTRUST_PLUGIN_DIR . 'includes/class-opentrust.php'; require_once OPENTRUST_PLUGIN_DIR . 'includes/class-opentrust-admin.php'; @@ -70,10 +70,8 @@ // Custom tables. OpenTrust_Chat_Log::create_table(); - // Stamp the schema version on a true first install only. On reactivation - // after an in-place upgrade (deactivate → upload zip → activate) the - // option already exists with the prior version; leaving it untouched lets - // OpenTrust::maybe_upgrade() walk pending migrations on the next init. + // Stamp the schema version on a true first install only. Future schema + // changes (none today) would bump this constant and re-check here. if (false === get_option('opentrust_db_version', false)) { update_option('opentrust_db_version', OPENTRUST_DB_VERSION, false); } diff --git a/uninstall.php b/uninstall.php index 121db72..c8a1f06 100644 --- a/uninstall.php +++ b/uninstall.php @@ -20,24 +20,12 @@ // loading the rest of the plugin, so we cannot reference OpenTrust_CPT::ALL // here. The list below MUST stay in sync with that constant; if a CPT is // added or renamed there, mirror the change here. -// -// Legacy ot_* slugs included as a belt-and-suspenders cleanup for the corner -// case of a v1.0.x install uninstalling before the v3→v4 migration in -// OpenTrust::maybe_upgrade() has had a chance to run. -// -// @deprecated 1.1.0 Drop the ot_* entries in 2.0.0 once v1.0.x upgrades are no -// longer supported. $ot_post_types = [ 'opentr_policy', 'opentr_subprocessor', 'opentr_certification', 'opentr_data_practice', 'opentr_faq', - 'ot_policy', - 'ot_subprocessor', - 'ot_certification', - 'ot_data_practice', - 'ot_faq', ]; foreach ($ot_post_types as $ot_post_type) { From 09e5e689a63468a27fbbd3b800c7f57166ae15d9 Mon Sep 17 00:00:00 2001 From: r00bbert <280805750+r00bbert@users.noreply.github.com> Date: Sun, 17 May 2026 14:21:08 +0200 Subject: [PATCH 03/18] Phase 3: rename PHP class/constant identifiers to ettic_otc Bulk rename across all PHP source: - OpenTrust class to Ettic_OTC (main class) - OpenTrust_* classes to Ettic_OTC_* (25 classes, 420 refs) - OPENTRUST_* constants to ETTIC_OTC_* (5 constants) - class-opentrust-*.php to class-ettic-otc-*.php (25 files) - [OpenTrust] debug-log prefix to [ettic-otc] - .phpstan-bootstrap.php updated to match - 5 user-facing brand strings preserved as Open Trust Center Storage identifiers (option keys, postmeta, CPTs, hooks) keep old names for Phase 4. Text domain unchanged for Phase 6. --- .phpstan-bootstrap.php | 20 ++--- ...in-ai.php => class-ettic-otc-admin-ai.php} | 80 +++++++++--------- ...hp => class-ettic-otc-admin-questions.php} | 24 +++--- ...w.php => class-ettic-otc-admin-review.php} | 14 ++-- ...php => class-ettic-otc-admin-settings.php} | 70 ++++++++-------- ...ls.php => class-ettic-otc-admin-tools.php} | 46 +++++----- ...st-admin.php => class-ettic-otc-admin.php} | 58 ++++++------- ...atalog.php => class-ettic-otc-catalog.php} | 14 ++-- ...et.php => class-ettic-otc-chat-budget.php} | 10 +-- ...us.php => class-ettic-otc-chat-corpus.php} | 34 ++++---- ...t-log.php => class-ettic-otc-chat-log.php} | 10 +-- ...ch.php => class-ettic-otc-chat-search.php} | 2 +- ...s.php => class-ettic-otc-chat-secrets.php} | 2 +- ...class-ettic-otc-chat-stream-collector.php} | 14 ++-- ...hp => class-ettic-otc-chat-summarizer.php} | 28 +++---- ...rust-chat.php => class-ettic-otc-chat.php} | 82 +++++++++--------- ...ntrust-cpt.php => class-ettic-otc-cpt.php} | 22 ++--- ...pentrust-io.php => class-ettic-otc-io.php} | 60 ++++++------- ...-render.php => class-ettic-otc-render.php} | 72 ++++++++-------- ...ory.php => class-ettic-otc-repository.php} | 20 ++--- ...ersion.php => class-ettic-otc-version.php} | 8 +- ...lass-opentrust.php => class-ettic-otc.php} | 32 +++---- includes/data/data-practice-catalog.php | 2 +- ...ass-ettic-otc-chat-provider-anthropic.php} | 4 +- ... class-ettic-otc-chat-provider-openai.php} | 8 +- ...ss-ettic-otc-chat-provider-openrouter.php} | 6 +- ....php => class-ettic-otc-chat-provider.php} | 18 ++-- open-trust-center-by-ettic.php | 84 +++++++++---------- templates/chat.php | 12 +-- templates/partials/certifications.php | 6 +- templates/partials/data-practices.php | 4 +- templates/partials/policies.php | 4 +- templates/partials/policy-single.php | 2 +- templates/partials/subprocessors.php | 2 +- templates/trust-center.php | 32 +++---- uninstall.php | 12 +-- 36 files changed, 459 insertions(+), 459 deletions(-) rename includes/{class-opentrust-admin-ai.php => class-ettic-otc-admin-ai.php} (94%) rename includes/{class-opentrust-admin-questions.php => class-ettic-otc-admin-questions.php} (96%) rename includes/{class-opentrust-admin-review.php => class-ettic-otc-admin-review.php} (95%) rename includes/{class-opentrust-admin-settings.php => class-ettic-otc-admin-settings.php} (94%) rename includes/{class-opentrust-admin-tools.php => class-ettic-otc-admin-tools.php} (94%) rename includes/{class-opentrust-admin.php => class-ettic-otc-admin.php} (85%) rename includes/{class-opentrust-catalog.php => class-ettic-otc-catalog.php} (96%) rename includes/{class-opentrust-chat-budget.php => class-ettic-otc-chat-budget.php} (98%) rename includes/{class-opentrust-chat-corpus.php => class-ettic-otc-chat-corpus.php} (96%) rename includes/{class-opentrust-chat-log.php => class-ettic-otc-chat-log.php} (97%) rename includes/{class-opentrust-chat-search.php => class-ettic-otc-chat-search.php} (99%) rename includes/{class-opentrust-chat-secrets.php => class-ettic-otc-chat-secrets.php} (99%) rename includes/{class-opentrust-chat-stream-collector.php => class-ettic-otc-chat-stream-collector.php} (93%) rename includes/{class-opentrust-chat-summarizer.php => class-ettic-otc-chat-summarizer.php} (93%) rename includes/{class-opentrust-chat.php => class-ettic-otc-chat.php} (94%) rename includes/{class-opentrust-cpt.php => class-ettic-otc-cpt.php} (99%) rename includes/{class-opentrust-io.php => class-ettic-otc-io.php} (94%) rename includes/{class-opentrust-render.php => class-ettic-otc-render.php} (91%) rename includes/{class-opentrust-repository.php => class-ettic-otc-repository.php} (95%) rename includes/{class-opentrust-version.php => class-ettic-otc-version.php} (97%) rename includes/{class-opentrust.php => class-ettic-otc.php} (93%) rename includes/providers/{class-opentrust-chat-provider-anthropic.php => class-ettic-otc-chat-provider-anthropic.php} (99%) rename includes/providers/{class-opentrust-chat-provider-openai.php => class-ettic-otc-chat-provider-openai.php} (98%) rename includes/providers/{class-opentrust-chat-provider-openrouter.php => class-ettic-otc-chat-provider-openrouter.php} (93%) rename includes/providers/{class-opentrust-chat-provider.php => class-ettic-otc-chat-provider.php} (98%) diff --git a/.phpstan-bootstrap.php b/.phpstan-bootstrap.php index c97c929..5672642 100644 --- a/.phpstan-bootstrap.php +++ b/.phpstan-bootstrap.php @@ -7,18 +7,18 @@ declare(strict_types=1); -if (!defined('OPENTRUST_VERSION')) { - define('OPENTRUST_VERSION', '1.0.0'); +if (!defined('ETTIC_OTC_VERSION')) { + define('ETTIC_OTC_VERSION', '1.0.0'); } -if (!defined('OPENTRUST_PLUGIN_DIR')) { - define('OPENTRUST_PLUGIN_DIR', __DIR__ . '/'); +if (!defined('ETTIC_OTC_PLUGIN_DIR')) { + define('ETTIC_OTC_PLUGIN_DIR', __DIR__ . '/'); } -if (!defined('OPENTRUST_PLUGIN_URL')) { - define('OPENTRUST_PLUGIN_URL', 'https://example.com/wp-content/plugins/opentrust/'); +if (!defined('ETTIC_OTC_PLUGIN_URL')) { + define('ETTIC_OTC_PLUGIN_URL', 'https://example.com/wp-content/plugins/open-trust-center-by-ettic/'); } -if (!defined('OPENTRUST_PLUGIN_FILE')) { - define('OPENTRUST_PLUGIN_FILE', __DIR__ . '/opentrust.php'); +if (!defined('ETTIC_OTC_PLUGIN_FILE')) { + define('ETTIC_OTC_PLUGIN_FILE', __DIR__ . '/open-trust-center-by-ettic.php'); } -if (!defined('OPENTRUST_DB_VERSION')) { - define('OPENTRUST_DB_VERSION', 2); +if (!defined('ETTIC_OTC_DB_VERSION')) { + define('ETTIC_OTC_DB_VERSION', 1); } diff --git a/includes/class-opentrust-admin-ai.php b/includes/class-ettic-otc-admin-ai.php similarity index 94% rename from includes/class-opentrust-admin-ai.php rename to includes/class-ettic-otc-admin-ai.php index 9c49c46..b33c718 100644 --- a/includes/class-opentrust-admin-ai.php +++ b/includes/class-ettic-otc-admin-ai.php @@ -2,21 +2,21 @@ /** * AI Chat settings tab and its admin-post handlers. * - * Owns the entire "AI Chat" surface inside the OpenTrust settings page: + * Owns the entire "AI Chat" surface inside the Ettic_OTC settings page: * the provider picker (Anthropic primary, others behind an "advanced" * disclosure), the per-provider key card with validate-and-save flow, * the post-key model picker + budget/limit form, and the four * admin-post.php endpoints that drive key save/forget/refresh and the * summary-backfill sweep. * - * Bootstrapped by OpenTrust_Admin's constructor; subscribes its own + * Bootstrapped by Ettic_OTC_Admin's constructor; subscribes its own * admin_post_* hooks. The settings page (which still lives on - * OpenTrust_Admin as the menu callback) calls render_ai_tab() when the + * Ettic_OTC_Admin as the menu callback) calls render_ai_tab() when the * "ai" tab is active. * * Settings writes that bypass the sanitize_settings filter (key * validation flips ai_enabled / ai_provider / ai_model_list_cached_at) - * route through OpenTrust_Admin_Settings::save_settings_raw(). + * route through Ettic_OTC_Admin_Settings::save_settings_raw(). */ declare(strict_types=1); @@ -25,7 +25,7 @@ exit; } -final class OpenTrust_Admin_AI { +final class Ettic_OTC_Admin_AI { public const CRON_HOOK = 'opentrust_ai_models_refresh'; public const CACHE_TTL = 25 * HOUR_IN_SECONDS; // Slightly > daily cron cadence so the cache never expires between ticks. @@ -53,7 +53,7 @@ private function __construct() { // ────────────────────────────────────────────── public function render_ai_tab(array $settings): void { - $stored_keys = OpenTrust_Chat_Secrets::get_all(); + $stored_keys = Ettic_OTC_Chat_Secrets::get_all(); $active_provider = $settings['ai_provider'] ?? ''; $has_active_key = $active_provider !== '' && isset($stored_keys[$active_provider]); $is_non_anthropic_active = $has_active_key && $active_provider !== 'anthropic'; @@ -91,7 +91,7 @@ public function render_ai_tab(array $settings): void {
Anthropic Claude with the native Citations API to answer visitor questions about your trust center. Every claim the assistant makes is tied to an exact quote from one of your published documents — so no policy text is invented and nothing is paraphrased into something you did not actually publish.', 'opentrust'), + __('Ettic_OTC uses Anthropic Claude with the native Citations API to answer visitor questions about your trust center. Every claim the assistant makes is tied to an exact quote from one of your published documents — so no policy text is invented and nothing is paraphrased into something you did not actually publish.', 'opentrust'), ['strong' => []] ); ?> @@ -141,10 +141,10 @@ public function render_ai_tab(array $settings): void { * up-to-date summary. */ private function render_summary_backfill_banner(array $settings, bool $has_active_key): void { - if (!$has_active_key || empty($settings['ai_auto_summarize']) || !class_exists('OpenTrust_Chat_Summarizer')) { + if (!$has_active_key || empty($settings['ai_auto_summarize']) || !class_exists('Ettic_OTC_Chat_Summarizer')) { return; } - $missing = OpenTrust_Chat_Summarizer::missing_summary_count(); + $missing = Ettic_OTC_Chat_Summarizer::missing_summary_count(); if ($missing < 1) { return; } @@ -179,7 +179,7 @@ private function render_summary_backfill_banner(array $settings, bool $has_activ } private function render_ai_provider_picker(array $settings, array $stored_keys): void { - $providers = OpenTrust_Chat_Provider::available(); + $providers = Ettic_OTC_Chat_Provider::available(); $active_provider = $settings['ai_provider'] ?? ''; // Partition: Anthropic is the primary, everything else is "advanced". @@ -251,7 +251,7 @@ private function render_provider_card(array $provider, array $stored_keys, strin $key_url = (string) $provider['key_url']; $is_active = $slug === $active_provider; $has_key = isset($stored_keys[$slug]); - $masked = $has_key ? OpenTrust_Chat_Secrets::mask($stored_keys[$slug]) : ''; + $masked = $has_key ? Ettic_OTC_Chat_Secrets::mask($stored_keys[$slug]) : ''; $card_classes = ['ot-ai-card', 'ot-ai-card--' . $variant]; if ($is_active) { @@ -409,14 +409,14 @@ private function render_ai_settings_form(array $settings): void {
@@ -281,7 +281,7 @@ private function render_import_panel(): void { private function render_preview_screen(array $preview): void { $action_url = admin_url('admin-post.php'); $manifest_format = (string) ($preview['manifest']['format'] ?? ''); - $is_settings = $manifest_format === OpenTrust_IO::FORMAT_SETTINGS; + $is_settings = $manifest_format === Ettic_OTC_IO::FORMAT_SETTINGS; $totals = ['create' => 0, 'update' => 0, 'skip' => 0]; foreach ($preview['summary'] ?? [] as $row) { foreach ($totals as $k => $_) { @@ -377,7 +377,7 @@ public function handle_export(): void { try { if ($kind === 'settings') { - $manifest = OpenTrust_IO::build_settings_manifest($include_media); + $manifest = Ettic_OTC_IO::build_settings_manifest($include_media); $prefix = 'opentrust-settings'; } else { $selection = $this->parse_selection($_POST); @@ -385,11 +385,11 @@ public function handle_export(): void { $this->bounce_error(__('Pick at least one record to export.', 'opentrust')); return; } - $manifest = OpenTrust_IO::build_content_manifest($selection, $include_media); + $manifest = Ettic_OTC_IO::build_content_manifest($selection, $include_media); $prefix = 'opentrust-content'; } - $zip_path = OpenTrust_IO::write_zip($manifest, $prefix); + $zip_path = Ettic_OTC_IO::write_zip($manifest, $prefix); } catch (\Throwable $e) { $this->bounce_error($e->getMessage()); return; @@ -457,11 +457,11 @@ public function handle_import_preview(): void { $stash_path = (string) $upload['file']; try { - $read = OpenTrust_IO::read_zip($stash_path); + $read = Ettic_OTC_IO::read_zip($stash_path); $manifest = $read['manifest']; - $check = OpenTrust_IO::validate_manifest($manifest); + $check = Ettic_OTC_IO::validate_manifest($manifest); - $strategy = isset($_POST['opentrust_strategy']) ? sanitize_key((string) wp_unslash($_POST['opentrust_strategy'])) : OpenTrust_IO::STRATEGY_SKIP; + $strategy = isset($_POST['opentrust_strategy']) ? sanitize_key((string) wp_unslash($_POST['opentrust_strategy'])) : Ettic_OTC_IO::STRATEGY_SKIP; $preview = [ 'manifest' => $manifest, @@ -473,8 +473,8 @@ public function handle_import_preview(): void { 'records' => [], ]; - if (($manifest['format'] ?? '') === OpenTrust_IO::FORMAT_CONTENT && empty($check['errors'])) { - $diff = OpenTrust_IO::preview_import($manifest, $strategy); + if (($manifest['format'] ?? '') === Ettic_OTC_IO::FORMAT_CONTENT && empty($check['errors'])) { + $diff = Ettic_OTC_IO::preview_import($manifest, $strategy); $preview['summary'] = $diff['summary']; $preview['records'] = $diff['records']; } @@ -522,18 +522,18 @@ public function handle_import_apply(): void { $zip_path = (string) ($preview['zip_path'] ?? ''); $manifest = (array) ($preview['manifest'] ?? []); - $strategy = (string) ($preview['strategy'] ?? OpenTrust_IO::STRATEGY_SKIP); + $strategy = (string) ($preview['strategy'] ?? Ettic_OTC_IO::STRATEGY_SKIP); try { - if (($manifest['format'] ?? '') === OpenTrust_IO::FORMAT_SETTINGS) { - $result = OpenTrust_IO::apply_settings_import($manifest, $zip_path); + if (($manifest['format'] ?? '') === Ettic_OTC_IO::FORMAT_SETTINGS) { + $result = Ettic_OTC_IO::apply_settings_import($manifest, $zip_path); $msg = sprintf( /* translators: %d: count */ _n('%d setting imported.', '%d settings imported.', (int) ($result['updated'] ?? 0), 'opentrust'), (int) ($result['updated'] ?? 0) ); } else { - $result = OpenTrust_IO::apply_content_import($manifest, $zip_path, $strategy); + $result = Ettic_OTC_IO::apply_content_import($manifest, $zip_path, $strategy); $msg = sprintf( /* translators: %1$d: created, %2$d: updated, %3$d: skipped */ __('Imported: %1$d created, %2$d updated, %3$d skipped.', 'opentrust'), @@ -574,7 +574,7 @@ private function parse_selection(array $post): array { $out = []; foreach ($cpts as $cpt => $_) { $cpt = sanitize_key((string) $cpt); - if (!in_array($cpt, OpenTrust_CPT::ALL, true)) continue; + if (!in_array($cpt, Ettic_OTC_CPT::ALL, true)) continue; $picked = isset($ids[$cpt]) && is_array($ids[$cpt]) ? array_values(array_filter(array_map('intval', $ids[$cpt]))) @@ -589,11 +589,11 @@ private function parse_selection(array $post): array { private function cpt_label(string $cpt): string { return match ($cpt) { - OpenTrust_CPT::POLICY => __('Policies', 'opentrust'), - OpenTrust_CPT::CERTIFICATION => __('Certifications', 'opentrust'), - OpenTrust_CPT::SUBPROCESSOR => __('Subprocessors', 'opentrust'), - OpenTrust_CPT::DATA_PRACTICE => __('Data Practices', 'opentrust'), - OpenTrust_CPT::FAQ => __('FAQs', 'opentrust'), + Ettic_OTC_CPT::POLICY => __('Policies', 'opentrust'), + Ettic_OTC_CPT::CERTIFICATION => __('Certifications', 'opentrust'), + Ettic_OTC_CPT::SUBPROCESSOR => __('Subprocessors', 'opentrust'), + Ettic_OTC_CPT::DATA_PRACTICE => __('Data Practices', 'opentrust'), + Ettic_OTC_CPT::FAQ => __('FAQs', 'opentrust'), default => $cpt, }; } diff --git a/includes/class-opentrust-admin.php b/includes/class-ettic-otc-admin.php similarity index 85% rename from includes/class-opentrust-admin.php rename to includes/class-ettic-otc-admin.php index a00c926..dd472a3 100644 --- a/includes/class-opentrust-admin.php +++ b/includes/class-ettic-otc-admin.php @@ -2,13 +2,13 @@ /** * Admin bootstrap. * - * Slim orchestrator that wires up the OpenTrust top-level admin menu, + * Slim orchestrator that wires up the Ettic_OTC top-level admin menu, * the per-screen asset enqueue, the "Plain permalinks" warning, and the * three sub-admin classes that own the actual screens: * - * - OpenTrust_Admin_Settings — Settings API + General/Contact/AI tabs page render - * - OpenTrust_Admin_AI — AI tab body + key save/forget/refresh handlers - * - OpenTrust_Admin_Questions — Questions log screen + export/clear/toggle + * - Ettic_OTC_Admin_Settings — Settings API + General/Contact/AI tabs page render + * - Ettic_OTC_Admin_AI — AI tab body + key save/forget/refresh handlers + * - Ettic_OTC_Admin_Questions — Questions log screen + export/clear/toggle * * Each sub-admin owns its own hook subscriptions; this class only owns * menu/asset/notice scaffolding that is shared across all of them. @@ -20,7 +20,7 @@ exit; } -final class OpenTrust_Admin { +final class Ettic_OTC_Admin { private static ?self $instance = null; @@ -33,16 +33,16 @@ private function __construct() { add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']); add_filter('submenu_file', [$this, 'fix_submenu_highlight']); - // Warn admins on every OpenTrust admin page when the site is on Plain + // Warn admins on every Ettic_OTC admin page when the site is on Plain // permalinks — the plugin's pretty URLs all 404 in that mode. add_action('admin_notices', [$this, 'render_plain_permalinks_notice']); // Sub-admin systems own their own hooks. - OpenTrust_Admin_Settings::instance(); - OpenTrust_Admin_Questions::instance(); - OpenTrust_Admin_AI::instance(); - OpenTrust_Admin_Review::instance(); - OpenTrust_Admin_Tools::instance(); + Ettic_OTC_Admin_Settings::instance(); + Ettic_OTC_Admin_Questions::instance(); + Ettic_OTC_Admin_AI::instance(); + Ettic_OTC_Admin_Review::instance(); + Ettic_OTC_Admin_Tools::instance(); } // ────────────────────────────────────────────── @@ -50,11 +50,11 @@ private function __construct() { // ────────────────────────────────────────────── public function register_menu(): void { - $settings_page = [OpenTrust_Admin_Settings::instance(), 'render_settings_page']; + $settings_page = [Ettic_OTC_Admin_Settings::instance(), 'render_settings_page']; add_menu_page( - __('OpenTrust', 'opentrust'), - __('OpenTrust', 'opentrust'), + __('Open Trust Center', 'opentrust'), + __('Open Trust Center', 'opentrust'), 'manage_options', 'opentrust', $settings_page, @@ -72,7 +72,7 @@ public function register_menu(): void { ); // AI Questions — only visible once AI is enabled. - $settings = OpenTrust::get_settings(); + $settings = Ettic_OTC::get_settings(); if (!empty($settings['ai_enabled'])) { add_submenu_page( 'opentrust', @@ -80,7 +80,7 @@ public function register_menu(): void { __('Questions', 'opentrust'), 'manage_options', 'opentrust-questions', - [OpenTrust_Admin_Questions::instance(), 'render_page'] + [Ettic_OTC_Admin_Questions::instance(), 'render_page'] ); } } @@ -104,7 +104,7 @@ public function fix_submenu_highlight(?string $submenu_file): ?string { return $submenu_file; } - if (in_array($post_type, OpenTrust_CPT::ALL, true)) { + if (in_array($post_type, Ettic_OTC_CPT::ALL, true)) { return "edit.php?post_type={$post_type}"; } @@ -124,7 +124,7 @@ public function enqueue_assets(string $hook): void { $is_opentrust_screen = str_starts_with($screen->id, 'toplevel_page_opentrust') || str_starts_with($screen->id, 'opentrust_page_') - || in_array($screen->post_type, OpenTrust_CPT::CORPUS, true); + || in_array($screen->post_type, Ettic_OTC_CPT::CORPUS, true); if (!$is_opentrust_screen) { return; @@ -135,16 +135,16 @@ public function enqueue_assets(string $hook): void { wp_enqueue_style( 'opentrust-admin', - OPENTRUST_PLUGIN_URL . 'assets/css/admin.css', + ETTIC_OTC_PLUGIN_URL . 'assets/css/admin.css', [], - OPENTRUST_VERSION + ETTIC_OTC_VERSION ); wp_enqueue_script( 'opentrust-admin', - OPENTRUST_PLUGIN_URL . 'assets/js/admin.js', + ETTIC_OTC_PLUGIN_URL . 'assets/js/admin.js', ['wp-color-picker', 'jquery'], - OPENTRUST_VERSION, + ETTIC_OTC_VERSION, true ); @@ -170,10 +170,10 @@ public function enqueue_assets(string $hook): void { // the new-post screen for the two CPTs that support it. Edit screens // are deliberately excluded so we never stomp existing values. $screen = get_current_screen(); - if ($hook === 'post-new.php' && $screen && in_array($screen->post_type, [OpenTrust_CPT::SUBPROCESSOR, OpenTrust_CPT::DATA_PRACTICE, OpenTrust_CPT::CERTIFICATION], true)) { + if ($hook === 'post-new.php' && $screen && in_array($screen->post_type, [Ettic_OTC_CPT::SUBPROCESSOR, Ettic_OTC_CPT::DATA_PRACTICE, Ettic_OTC_CPT::CERTIFICATION], true)) { $payload = [ 'postType' => $screen->post_type, - 'catalog' => OpenTrust_Catalog::for_js($screen->post_type), + 'catalog' => Ettic_OTC_Catalog::for_js($screen->post_type), 'i18n' => [ 'noMatchHint' => __('No match in catalog, just keep typing to add manually.', 'opentrust'), 'helpFact' => __('Auto-filled from catalog, you may want to verify this.', 'opentrust'), @@ -195,7 +195,7 @@ public function enqueue_assets(string $hook): void { // ────────────────────────────────────────────── /** - * Show a persistent warning on every OpenTrust admin screen when the + * Show a persistent warning on every Ettic_OTC admin screen when the * WordPress permalink structure is "Plain" (i.e. empty). In that mode * all of the plugin's pretty URLs (/trust-center/, /trust-center/policy/..., * /trust-center/ask/) return 404. @@ -210,11 +210,11 @@ public function render_plain_permalinks_notice(): void { return; } - // Limit the noise to OpenTrust-owned screens: top-level plugin pages, + // Limit the noise to Ettic_OTC-owned screens: top-level plugin pages, // subpages, and the four content CPTs. Bail on every other admin screen. $is_opentrust_screen = str_contains((string) $screen->id, 'opentrust') || - in_array($screen->post_type, OpenTrust_CPT::CORPUS, true); + in_array($screen->post_type, Ettic_OTC_CPT::CORPUS, true); if (!$is_opentrust_screen) { return; @@ -225,7 +225,7 @@ public function render_plain_permalinks_notice(): void { ?>
- +
- +
%s. Only Anthropic uses a structural Citations API — every other provider relies on prompted citation tags the model can ignore or fabricate. For a published trust center, switch to Anthropic below.', 'opentrust'), ['strong' => []]), + wp_kses(__('You are currently using %s. Only Anthropic uses a structural Citations API — every other provider relies on prompted citation tags the model can ignore or fabricate. For a published trust center, switch to Anthropic below.', 'open-trust-center-by-ettic'), ['strong' => []]), esc_html(ucfirst($active_provider)) ); ?> @@ -91,19 +91,19 @@ public function render_ai_tab(array $settings): void {
Anthropic Claude with the native Citations API to answer visitor questions about your trust center. Every claim the assistant makes is tied to an exact quote from one of your published documents — so no policy text is invented and nothing is paraphrased into something you did not actually publish.', 'opentrust'), + __('Ettic_OTC uses Anthropic Claude with the native Citations API to answer visitor questions about your trust center. Every claim the assistant makes is tied to an exact quote from one of your published documents — so no policy text is invented and nothing is paraphrased into something you did not actually publish.', 'open-trust-center-by-ettic'), ['strong' => []] ); ?>
compliance surface. If the assistant invents a security commitment you never made, that is not a UX papercut — it is a misrepresentation of your security posture, and your customers and auditors will hold you to it.', 'opentrust'), + __('A trust center is a compliance surface. If the assistant invents a security commitment you never made, that is not a UX papercut — it is a misrepresentation of your security posture, and your customers and auditors will hold you to it.', 'open-trust-center-by-ettic'), ['strong' => []] ); ?> @@ -111,13 +111,13 @@ public function render_ai_tab(array $settings): void {
only major provider that exposes a structural Citations API. Documents are sent as typed blocks and the model emits citations as first-class events containing the exact source document and the exact quoted text. The model literally cannot return a citation for text that is not in your source documents.', 'opentrust'), + __('Anthropic is the only major provider that exposes a structural Citations API. Documents are sent as typed blocks and the model emits citations as first-class events containing the exact source document and the exact quoted text. The model literally cannot return a citation for text that is not in your source documents.', 'open-trust-center-by-ettic'), ['strong' => []] ); ?>
- +
- +
- - + +
- +
@@ -276,7 +276,7 @@ private function render_provider_card(array $provider, array $stored_keys, strin ↗ @@ -289,8 +289,8 @@ private function render_provider_card(array $provider, array $stored_keys, strin -