diff --git a/.cursor/Dockerfile b/.cursor/Dockerfile
index 5898f3f..f8d431d 100644
--- a/.cursor/Dockerfile
+++ b/.cursor/Dockerfile
@@ -24,7 +24,7 @@ RUN printf '%s\n' \
'export WP_TESTS_DB_USER=root' \
'export WP_TESTS_DB_PASSWORD=root' \
'export WP_TESTS_DB_HOST=127.0.0.1' \
- > /etc/profile.d/fqj-wp-tests-db.sh
+ > /etc/profile.d/rfsd-wp-tests-db.sh
RUN echo 'ubuntu ALL=(ALL) NOPASSWD: /usr/sbin/service mariadb *' > /etc/sudoers.d/mariadb \
&& chmod 440 /etc/sudoers.d/mariadb
diff --git a/.cursor/cloud-install.sh b/.cursor/cloud-install.sh
index 4deb9c2..603a325 100755
--- a/.cursor/cloud-install.sh
+++ b/.cursor/cloud-install.sh
@@ -4,8 +4,8 @@ set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
-if [[ ! -f /etc/profile.d/fqj-wp-tests-db.sh ]]; then
- sudo tee /etc/profile.d/fqj-wp-tests-db.sh >/dev/null <<'EOF'
+if [[ ! -f /etc/profile.d/rfsd-wp-tests-db.sh ]]; then
+ sudo tee /etc/profile.d/rfsd-wp-tests-db.sh >/dev/null <<'EOF'
export WP_TESTS_DB_NAME=wordpress_test
export WP_TESTS_DB_USER=root
export WP_TESTS_DB_PASSWORD=root
diff --git a/.cursor/environment.json b/.cursor/environment.json
index 66ac950..3480ea8 100644
--- a/.cursor/environment.json
+++ b/.cursor/environment.json
@@ -1,5 +1,5 @@
{
- "name": "FAQ JSON-LD WordPress Plugin",
+ "name": "Renderbit FAQ Structured Data WordPress Plugin",
"build": {
"dockerfile": "Dockerfile",
"context": "."
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 3f5d898..3ce993e 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -1,5 +1,5 @@
name: Bug Report
-description: Report a bug or issue with the FAQ JSON-LD Manager plugin
+description: Report a bug or issue with the Renderbit FAQ Structured Data plugin
title: "[Bug]: "
labels: ["bug"]
body:
@@ -12,7 +12,7 @@ body:
id: version
attributes:
label: Plugin Version
- description: What version of FAQ JSON-LD Manager are you using?
+ description: What version of Renderbit FAQ Structured Data are you using?
placeholder: e.g., 3.0.0
validations:
required: true
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index 9776822..9ac1f67 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -1,5 +1,5 @@
name: Feature Request
-description: Suggest a new feature or improvement for the FAQ JSON-LD Manager plugin
+description: Suggest a new feature or improvement for the Renderbit FAQ Structured Data plugin
title: "[Feature]: "
labels: ["enhancement"]
body:
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index 37c71da..6f70709 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -1,4 +1,4 @@
-# Copilot Instructions for FAQ JSON-LD WordPress Plugin
+# Copilot Instructions for Renderbit FAQ Structured Data WordPress Plugin
## Overview
@@ -6,20 +6,20 @@ This plugin manages FAQ items as a custom post type (CPT) and injects FAQ JSON-L
## Architecture & Key Components
-- **Custom Post Type:** `faq_item` is registered for FAQ entries.
-- **Mapping Table:** MySQL table (`fqj_mappings`) links FAQ items to posts, post types, terms, URLs, or global context. See `includes/indexer.php`.
+- **Custom Post Type:** `rfsd_faq` is registered for FAQ entries.
+- **Mapping Table:** MySQL table (`rfsd_mappings`) links FAQ items to posts, post types, terms, URLs, or global context. See `includes/indexer.php`.
- **JSON-LD Output:** Injected on the frontend using `includes/frontend.php`, with per-post transient caching for performance.
- **Cache Invalidation:** Uses a background queue (`includes/queue.php`) processed by WP-Cron or WP-CLI. Queue state and logs are visible in the Health admin page.
-- **Admin UI:** Custom meta boxes and settings in `includes/admin.php` and `includes/settings.php`. Select2 (bundled in `assets/vendor/select2`) is used for post selectors (see `assets/js/fqj-admin.js`).
+- **Admin UI:** Custom meta boxes and settings in `includes/admin.php` and `includes/settings.php`. Select2 (bundled in `assets/vendor/select2`) is used for post selectors (see `assets/js/rfsd-admin.js`).
- **Health & Diagnostics:** Admin page (`includes/health.php`) shows queue status, logs, and provides manual actions.
-- **WP-CLI Integration:** `includes/class-fqjcli.php` provides commands for purging transients, processing the invalidation queue and showing its length.
+- **WP-CLI Integration:** `includes/class-rfsd-cli.php` provides commands for purging transients, processing the invalidation queue and showing its length.
## Developer Workflows
- **Activate/Upgrade:** On plugin activation, the mapping table is created/updated automatically.
- **Settings:** Accessible under the FAQ Items menu. Options include cache TTL, batch size, and output type.
- **Cache Management:**
- - Manual: Use Health admin page or WP-CLI (`wp fqj purge-transients`, `wp fqj process-queue [--limit=N]`, `wp fqj queue-info`).
+ - Manual: Use Health admin page or WP-CLI (`wp rfsd purge-transients`, `wp rfsd process-queue [--limit=N]`, `wp rfsd queue-info`).
- Automatic: Invalidation queue is processed every 5 minutes by WP-Cron.
- **Debugging:**
- Check the Health admin page for queue/logs.
@@ -28,8 +28,8 @@ This plugin manages FAQ items as a custom post type (CPT) and injects FAQ JSON-L
## Project-Specific Patterns
- **Mapping Structure:** All associations (post, post_type, term, url, global) are stored in a single table for fast lookups.
-- **Transient Keys:** Format is `fqj_faq_json_{generation}_{post_id}`; always build it with `fqj_cache_key( $post_id )`. The mapping table name comes from `fqj_table_name()`.
-- **Admin JS:** Uses Select2 for post selectors, AJAX for search (`fqj_search_posts` action).
+- **Transient Keys:** Format is `rfsd_faq_json_{generation}_{post_id}`; always build it with `rfsd_cache_key( $post_id )`. The mapping table name comes from `rfsd_table_name()`.
+- **Admin JS:** Uses Select2 for post selectors, AJAX for search (`rfsd_search_posts` action).
- **Settings/Health Pages:** Registered as submenus under the FAQ Items CPT.
## Integration Points
@@ -40,15 +40,15 @@ This plugin manages FAQ items as a custom post type (CPT) and injects FAQ JSON-L
## Key Files
-- `faq-jsonld.php`: Main plugin loader and CPT registration.
+- `renderbit-faq-structured-data.php`: Main plugin loader and CPT registration.
- `includes/indexer.php`: Mapping table logic.
- `includes/queue.php`: Invalidation queue logic.
- `includes/frontend.php`: JSON-LD output and caching.
- `includes/admin.php`: Admin UI/meta boxes.
- `includes/settings.php`: Settings page.
- `includes/health.php`: Health/diagnostics UI.
-- `includes/class-fqjcli.php`: WP-CLI commands.
-- `assets/js/fqj-admin.js`, `assets/js/fqj-health.js`: Admin JS (Select2, AJAX).
+- `includes/class-rfsd-cli.php`: WP-CLI commands.
+- `assets/js/rfsd-admin.js`, `assets/js/rfsd-health.js`: Admin JS (Select2, AJAX).
## Example: Adding a New Mapping Type
diff --git a/.github/workflows/deploy-wporg.yml b/.github/workflows/deploy-wporg.yml
index 7a4606c..eddb6f0 100644
--- a/.github/workflows/deploy-wporg.yml
+++ b/.github/workflows/deploy-wporg.yml
@@ -22,10 +22,10 @@ jobs:
run: |
tag="${GITHUB_REF#refs/tags/}"
tag="${tag#v}"
- header="$(sed -n 's/^ \* Version: *//p' faq-jsonld.php)"
- constant="$(sed -n "s/^define( 'FQJ_VERSION', '\(.*\)' );$/\1/p" faq-jsonld.php)"
+ header="$(sed -n 's/^ \* Version: *//p' renderbit-faq-structured-data.php)"
+ constant="$(sed -n "s/^define( 'RFSD_VERSION', '\(.*\)' );$/\1/p" renderbit-faq-structured-data.php)"
stable="$(sed -n 's/^Stable tag: *//p' readme.txt)"
- echo "tag=$tag header=$header FQJ_VERSION=$constant stable=$stable"
+ echo "tag=$tag header=$header RFSD_VERSION=$constant stable=$stable"
[ "$tag" = "$header" ] && [ "$tag" = "$constant" ] && [ "$tag" = "$stable" ]
- name: Deploy to WordPress.org SVN
@@ -33,4 +33,4 @@ jobs:
env:
SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
- SLUG: faq-jsonld
+ SLUG: renderbit-faq-structured-data
diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml
index 7006952..bf6ff74 100644
--- a/.github/workflows/draft-release.yml
+++ b/.github/workflows/draft-release.yml
@@ -40,6 +40,6 @@ jobs:
name: Release ${{ env.TAG_NAME }}
draft: true
generate_release_notes: true
- files: faq-jsonld.zip
+ files: renderbit-faq-structured-data.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 5eee2dd..ee3dd46 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -32,7 +32,7 @@ jobs:
tools: none
- name: Lint plugin files
- run: find faq-jsonld.php uninstall.php includes -name '*.php' -print0 | xargs -0 -n1 php -l
+ run: find renderbit-faq-structured-data.php uninstall.php includes -name '*.php' -print0 | xargs -0 -n1 php -l
phpcs:
name: Coding standards
@@ -59,12 +59,12 @@ jobs:
# Check what ships (the git archive, as in the release zip and WordPress.org SVN), not the dev files.
- name: Export plugin files
- run: mkdir -p build/faq-jsonld && git archive HEAD | tar -x -C build/faq-jsonld
+ run: mkdir -p build/renderbit-faq-structured-data && git archive HEAD | tar -x -C build/renderbit-faq-structured-data
- uses: WordPress/plugin-check-action@10857da14b6c2246d15402b3e69f777edcf8c12e # v1.1.9
with:
- build-dir: build/faq-jsonld
- slug: faq-jsonld
+ build-dir: build/renderbit-faq-structured-data
+ slug: renderbit-faq-structured-data
strict: true
phpunit:
diff --git a/AGENTS.md b/AGENTS.md
index 2f922f6..9822920 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,8 +1,8 @@
# AGENTS.md
-WordPress plugin `faq-jsonld`: FAQs as a `faq_item` CPT, schema.org `FAQPage` JSON-LD printed on singular posts/pages. Procedural PHP, every function prefixed `fqj_`, no build step, no runtime Composer deps.
+WordPress plugin `renderbit-faq-structured-data`: FAQs as a `rfsd_faq` CPT (`RFSD_POST_TYPE`; FAQs from the old `faq-jsonld` plugin are imported on activation), schema.org `FAQPage` JSON-LD printed on singular posts/pages. Procedural PHP, every function prefixed `rfsd_`, no build step, no runtime Composer deps.
-**PHP ceiling is 7.2 for shipped code** — `composer.json` pins platform 7.4, so syntax up to 7.4 passes locally while CI's `php -l` matrix (7.2–8.4) fails. Write 7.2-compatible syntax in `faq-jsonld.php`, `uninstall.php`, `includes/`.
+**PHP ceiling is 7.2 for shipped code** — `composer.json` pins platform 7.4, so syntax up to 7.4 passes locally while CI's `php -l` matrix (7.2–8.4) fails. Write 7.2-compatible syntax in `renderbit-faq-structured-data.php`, `uninstall.php`, `includes/`.
Full architecture, data flow (save → index → render → invalidate), and versioning rules live in `CLAUDE.md` — read its Architecture section before touching `includes/indexer.php`, `includes/queue.php`, `includes/frontend.php`, or cache keys. Keep `CLAUDE.md` in sync if you change behavior it describes.
@@ -11,13 +11,13 @@ Full architecture, data flow (save → index → render → invalidate), and ver
```bash
composer install && npm install # setup; .env.testing must exist (copy .env.testing.example)
-composer lint # phpcs, WordPress Coding Standards, text domain faq-jsonld
+composer lint # phpcs, WordPress Coding Standards, text domain renderbit-faq-structured-data
composer test # PHPUnit, single-site
composer test:multisite # same suite as a multisite network
vendor/bin/phpunit --filter test_name # one test
vendor/bin/phpunit tests/php/test-queue.php # one file
npm test # Jest (jsdom) for assets/js
-npx jest tests/js/fqj-admin.test.js -t "name" # one JS test
+npx jest tests/js/rfsd-admin.test.js -t "name" # one JS test
```
CI (`.github/workflows/tests.yml`) runs all of the above plus `WordPress/plugin-check` on every PR. Verify with `composer lint && composer test && npm test` before claiming done.
@@ -29,6 +29,6 @@ CI (`.github/workflows/tests.yml`) runs all of the above plus `WordPress/plugin-
- Test groups: network-only tests are tagged `@group ms-required`, single-site-only `@group ms-excluded`; each phpunit config excludes the other group, so a test in neither group runs twice and one in both runs never.
- `composer build-zip` runs `git archive HEAD` — **uncommitted changes are not in the zip**. `composer check-zip` verifies contents against `.gitattributes` `export-ignore`, which also decides what ships to WordPress.org SVN. Add new dev-only files there.
- Raw `$wpdb` queries use `%i` for table identifiers and carry targeted `phpcs:ignore`/`phpcs:disable … phpcs:enable` comments naming the specific sniffs — keep this pattern so `composer lint` stays clean.
-- Two independent version constants: bump `FQJ_DB_VERSION` only when `CREATE TABLE` SQL in `fqj_create_table()` changes (dbDelta rules: no `IF NOT EXISTS`, two spaces after `PRIMARY KEY`, `KEY` not `INDEX`, no inline comments); bump `FQJ_DATA_VERSION` only to force a one-time reindex + cache purge.
-- Transient keys are always built with `fqj_cache_key( $post_id )` (embeds `fqj_cache_gen`) — never hand-build `fqj_faq_json_*` keys, or purge-all breaks with a persistent object cache.
-- Releasing: `composer bump-patch|minor|major` keeps four version strings in sync (`composer.json`, plugin header, `FQJ_VERSION`, `readme.txt` Stable tag); add the `readme.txt` changelog entry by hand. Pushing a `vX.Y.Z` tag to `main` creates a draft release; publishing it deploys to WordPress.org SVN.
+- Two independent version constants: bump `RFSD_DB_VERSION` only when `CREATE TABLE` SQL in `rfsd_create_table()` changes (dbDelta rules: no `IF NOT EXISTS`, two spaces after `PRIMARY KEY`, `KEY` not `INDEX`, no inline comments); bump `RFSD_DATA_VERSION` only to force a one-time reindex + cache purge.
+- Transient keys are always built with `rfsd_cache_key( $post_id )` (embeds `rfsd_cache_gen`) — never hand-build `rfsd_faq_json_*` keys, or purge-all breaks with a persistent object cache.
+- Releasing: `composer bump-patch|minor|major` keeps four version strings in sync (`composer.json`, plugin header, `RFSD_VERSION`, `readme.txt` Stable tag); add the `readme.txt` changelog entry by hand. Pushing a `vX.Y.Z` tag to `main` creates a draft release; publishing it deploys to WordPress.org SVN.
diff --git a/CLAUDE.md b/CLAUDE.md
index 054ea11..3fcf28c 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -2,61 +2,61 @@
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
-WordPress plugin (slug `faq-jsonld`, published on WordPress.org) that manages FAQs as a `faq_item` custom post type and prints schema.org `FAQPage` JSON-LD in `wp_head` on the singular posts/pages each FAQ is mapped to. Procedural PHP, every function prefixed `fqj_`; no build step and no runtime Composer dependencies. Supports WordPress 6.5+ and PHP 7.2+, so don't use syntax newer than PHP 7.2 in shipped code.
+WordPress plugin (slug `renderbit-faq-structured-data`, published on WordPress.org) that manages FAQs as a `rfsd_faq` custom post type (`RFSD_POST_TYPE`) and prints schema.org `FAQPage` JSON-LD in `wp_head` on the singular posts/pages each FAQ is mapped to. Procedural PHP, every function prefixed `rfsd_`; no build step and no runtime Composer dependencies. Supports WordPress 6.5+ and PHP 7.2+, so don't use syntax newer than PHP 7.2 in shipped code.
## Commands
```bash
composer install && npm install
-composer lint # phpcs with WordPress Coding Standards (phpcs.xml), text domain faq-jsonld
+composer lint # phpcs with WordPress Coding Standards (phpcs.xml), text domain renderbit-faq-structured-data
composer test # PHPUnit integration suite, single-site
composer test:multisite # same suite as a multisite network
vendor/bin/phpunit --filter test_name_here # one test
vendor/bin/phpunit tests/php/test-queue.php # one file
npm test # Jest (jsdom) tests for assets/js
-npx jest tests/js/fqj-admin.test.js -t "name" # one JS test
+npx jest tests/js/rfsd-admin.test.js -t "name" # one JS test
composer build-zip && composer check-zip # release zip from `git archive HEAD`, then verify contents
composer bump-patch # or bump-minor / bump-major
```
-PHPUnit runs against a real MySQL/MariaDB database that the suite **wipes on every run**. Settings come from `.env.testing` (copy `.env.testing.example`); shell env vars win. Tests are loaded by `tests/php/bootstrap.php`, which loads the plugin on `muplugins_loaded` and calls `fqj_activate()` on `init`. Extend `FQJ_TestCase` (`tests/php/class-fqj-testcase.php`, has `create_faq()` helpers) or `FQJ_Multisite_TestCase`. Tag network-only tests `@group ms-required` and single-site-only tests `@group ms-excluded`; each config excludes the other group. PHPUnit converts notices, warnings and deprecations to failures. WP-CLI is stubbed in `tests/php/stubs/class-wp-cli.php`.
+PHPUnit runs against a real MySQL/MariaDB database that the suite **wipes on every run**. Settings come from `.env.testing` (copy `.env.testing.example`); shell env vars win. Tests are loaded by `tests/php/bootstrap.php`, which loads the plugin on `muplugins_loaded` and calls `rfsd_activate()` on `init`. Extend `RFSD_TestCase` (`tests/php/class-rfsd-testcase.php`, has `create_faq()` helpers) or `RFSD_Multisite_TestCase`. Tag network-only tests `@group ms-required` and single-site-only tests `@group ms-excluded`; each config excludes the other group. PHPUnit converts notices, warnings and deprecations to failures. WP-CLI is stubbed in `tests/php/stubs/class-wp-cli.php`.
## Architecture
-`faq-jsonld.php` defines constants, requires every file in `includes/`, registers the CPT, and handles activation/deactivation (looping over all sites via `fqj_for_each_site()` on network activation and setting up new sites on `wp_initialize_site`).
+`renderbit-faq-structured-data.php` defines constants, requires every file in `includes/`, registers the CPT, imports a site's data from the old FAQ JSON-LD Manager plugin (`faq-jsonld`, `fqj_` names listed in `rfsd_legacy_storage_names()`) once per site in `rfsd_import_legacy_data()` (on `plugins_loaded` priority 5 and on activation, before the schema upgrade, the data migration and activation's has-FAQs check), and handles activation/deactivation (looping over all sites via `rfsd_for_each_site()` on network activation and setting up new sites on `wp_initialize_site`).
Data flow:
-1. **Save (`includes/admin.php`)**: the meta box stores the FAQ's associations as JSON in post meta `fqj_assoc_data_json` (keys `urls`, `posts`, `post_types`, `terms`, `global`), then the index is rebuilt.
-2. **Index (`includes/indexer.php`)**: `fqj_rebuild_index_for_faq()` replaces the FAQ's rows in the `{prefix}fqj_mappings` table (`faq_id`, `mapping_type` ∈ post/post_type/term/url/global, `mapping_value`). URLs go through `fqj_normalize_url()`, which stores URLs on this site relative to the site root (`/about`, `/`, `/?p=12`). A URL association covers whichever post's permalink equals that URL now, so it never gets a `post` row (ADR-0001). Old and new mappings are both passed to `fqj_enqueue_invalidation_for_mappings()`. Hooks on status transitions, post updates, deletes, term changes and permalink changes keep the index and cache in sync.
-3. **Render (`includes/frontend.php`)**: on `is_singular()` only, builds candidate (type, value) pairs for the current post (its ID, post type, terms in all its taxonomies, normalized permalink, global; the legacy absolute and trailing-slash URL shapes only while data version is below 3), runs one query against the mapping table, and caches the `' );
+ set_transient( rfsd_cache_key( $post ), '' );
$html = $this->render_head_for( $post );
$this->assertStringContainsString( '(cached)', $html );
@@ -97,21 +97,21 @@ public function test_cached_output_is_served_from_transient() {
public function test_blank_cached_value_reports_unknown_state() {
$post = self::factory()->post->create();
- set_transient( fqj_cache_key( $post ), ' ' );
+ set_transient( rfsd_cache_key( $post ), ' ' );
$this->assertStringContainsString( 'cached, unknown state', $this->render_head_for( $post ) );
}
public function test_generated_output_is_cached_with_ttl() {
- update_option( FQJ_OPTION_KEY, array( 'cache_ttl' => 600 ) );
+ update_option( RFSD_OPTION_KEY, array( 'cache_ttl' => 600 ) );
$post = self::factory()->post->create();
$this->create_faq( 'Q', 'A', array( 'posts' => array( $post ) ) );
$html = $this->render_head_for( $post );
- $cached = get_transient( fqj_cache_key( $post ) );
+ $cached = get_transient( rfsd_cache_key( $post ) );
$this->assertStringStartsWith( '