diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4cf1e44..dcc69e8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -28,6 +28,23 @@ jobs:
- name: Lint every PHP file
run: find . -name '*.php' -not -path './.git/*' -print0 | xargs -0 -n1 -P4 php -l
+ phpcs:
+ name: WordPress coding standards
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.3'
+ coverage: none
+ tools: composer:v2
+ - run: composer install --no-interaction --no-progress
+ # phpcs.xml.dist: WordPress-Extra + PHPCompatibilityWP (7.4-) on the files the archive ships. Every
+ # phpcs:ignore in the code carries its reason on the same line; the gate honours them, and the second run
+ # lists what they cover so a review can read it in the log without trusting the annotations.
+ - run: composer lint
+ - run: composer lint:all -- --report=source || true
+
metadata:
name: Plugin metadata is consistent
runs-on: ubuntu-latest
diff --git a/README.md b/README.md
index 8d0e59f..cea887f 100644
--- a/README.md
+++ b/README.md
@@ -135,10 +135,15 @@ SHA-256 is stored.
GET. The first accepted call of any route pairs the key with it — an access
check that reads a non-existent export (`404 bridge_not_found`) included;
another pairing is refused.
-- It acts as the administrator who created it, while they still have `export`
- + `manage_options`, and opens these routes only. It sees only the exports it
- started, and runs one at a time: another scope while one runs is
- `409 bridge_export_live`; once that one is `ready` or `failed`, a rerun starts.
+- It runs with **no WordPress user** (`get_current_user_id()` is 0 for the whole
+ request) and opens these routes only; it is valid while the administrator who
+ created it still has `export` + `manage_options` (checked with `user_can()` on
+ their id, never by signing them in). It sees only the exports it started —
+ indexed in the plugin's own option, never in a user's meta — and runs one at a
+ time: another scope while one runs is `409 bridge_export_live`; once that one
+ is `ready` or `failed`, a rerun starts. An export the administrator started
+ with an application password is never reused, bound or replaced by the key.
+ Revoking the key removes its exports.
- Unpaired, it stops working an hour after it was created. Paired, it has no
idle limit (reruns can be days apart) and works until 14 days after it was
created, or until it is revoked (the Bridge screen, or `DELETE /key`) or
diff --git a/RELEASING.md b/RELEASING.md
index 92e21e1..51229a1 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -15,6 +15,7 @@ listed at the end, and they are not paperwork.
```bash
npm ci --ignore-scripts
+composer install && composer lint # PHP_CodeSniffer: WordPress-Extra + PHPCompatibilityWP (phpcs.xml.dist)
npm run test:wordpress # container acceptance suite
npm run test:plugin # official Plugin Check
npm run test:store # contentrain validate + canonical byte parity
@@ -35,10 +36,11 @@ nothing to check. Start from `docker compose -f tests/compose.yml down -v` — a
reused stack keeps the content of previous runs, which inflates the check and
entry counts and makes a local number look unlike CI's.
-Last full clean run (0.7.0): 394 acceptance checks · Plugin Check 0 errors / 4
-warnings (the four WPML hook names, below) · `contentrain validate` 14 models,
-62 entries · canonical byte parity 55 files · 5 documents carry frontmatter ·
-handoff, reader and uninstall pass · archive 33 runtime files.
+Last full clean run (0.7.1): 399 acceptance checks · Plugin Check 0 errors / 0
+warnings (the four WPML hook names, below, now carry a reasoned `phpcs:ignore`)
+· `contentrain validate` 14 models, 62 entries · canonical byte parity 55 files
+· 5 documents carry frontmatter · handoff, reader and uninstall pass · archive
+33 runtime files · `composer lint` 0 errors / 0 warnings on 27 packaged files.
## The reader gate
@@ -158,7 +160,8 @@ another green CI run.
Plugin Check 2.1.0 (all categories, including plugin_repo and security) on the 0.6.5 release zip: zero errors,
26 warnings, in three classes. Since 0.7.0 each direct query carries a `phpcs:ignore` with its reason on its own line,
-so the run shows only the first class. Reviewers accept all three with a stated reason; do not silence them by
+and since 0.7.1 the WPML hook names do too, so the run shows none of them; `composer lint:all -- --report=source`
+in CI lists every ignored sniff. Reviewers accept all three with a stated reason; do not silence them by
weakening the check.
- **Third-party hook names (4):** `wpml_default_language`, `wpml_post_language_details`, `wpml_element_trid` and
diff --git a/assets/admin.js b/assets/admin.js
index feb3001..4b6f6f3 100644
--- a/assets/admin.js
+++ b/assets/admin.js
@@ -14,8 +14,23 @@
const STEP_TIMEOUT = 120000;
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
function retryable(message) { return Object.assign(new Error(message), { retryable: true }); }
+ /**
+ * The request as plain form fields, one per value, the way PHP reads them: a list as `name[]`, an object as
+ * `name[key]`, a flag as 1 or 0. The server reads each field once with the function for its type; no JSON blob.
+ */
+ function fields(payload, body = new URLSearchParams(), prefix = '') {
+ for (const [name, value] of Object.entries(payload)) {
+ const key = prefix ? `${prefix}[${name}]` : name;
+ if (value === null || value === undefined) continue;
+ if (Array.isArray(value)) value.forEach((item) => body.append(`${key}[]`, String(item)));
+ else if (typeof value === 'object') fields(value, body, key);
+ else if (typeof value === 'boolean') body.append(key, value ? '1' : '0');
+ else body.append(key, String(value));
+ }
+ return body;
+ }
async function api(payload, timeout = 0) {
- const body = new URLSearchParams({ action: 'contentrain_bridge', nonce: ContentrainBridge.nonce, payload: JSON.stringify(payload) });
+ const body = fields({ action: 'contentrain_bridge', nonce: ContentrainBridge.nonce, ...payload });
const controller = new AbortController();
const timer = timeout ? setTimeout(() => controller.abort(), timeout) : 0;
let response;
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..0d8fe69
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,24 @@
+{
+ "name": "contentrain/wordpress-bridge",
+ "description": "Development dependencies for Contentrain Bridge: coding-standards checks. Not part of the installed plugin.",
+ "type": "wordpress-plugin",
+ "license": "GPL-2.0-or-later",
+ "require": {
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+ "wp-coding-standards/wpcs": "^3.1",
+ "phpcompatibility/phpcompatibility-wp": "^2.1"
+ },
+ "scripts": {
+ "lint": "phpcs --standard=phpcs.xml.dist",
+ "lint:all": "phpcs --standard=phpcs.xml.dist --ignore-annotations"
+ },
+ "config": {
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true
+ },
+ "sort-packages": true
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..2f3a423
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,650 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "7cfdcfca035b0dddb959018898dd48d8",
+ "packages": [],
+ "packages-dev": [
+ {
+ "name": "dealerdirect/phpcodesniffer-composer-installer",
+ "version": "v1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/composer-installer.git",
+ "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/963f0c67bffde0eac41b56be71ac0e8ba132f0bd",
+ "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^2.2",
+ "php": ">=5.4",
+ "squizlabs/php_codesniffer": "^3.1.0 || ^4.0"
+ },
+ "require-dev": {
+ "composer/composer": "^2.2",
+ "ext-json": "*",
+ "ext-zip": "*",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpcompatibility/php-compatibility": "^9.0 || ^10.0.0@dev",
+ "yoast/phpunit-polyfills": "^1.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Franck Nijhof",
+ "email": "opensource@frenck.dev",
+ "homepage": "https://frenck.dev",
+ "role": "Open source developer"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
+ "keywords": [
+ "PHPCodeSniffer",
+ "PHP_CodeSniffer",
+ "code quality",
+ "codesniffer",
+ "composer",
+ "installer",
+ "phpcbf",
+ "phpcs",
+ "plugin",
+ "qa",
+ "quality",
+ "standard",
+ "standards",
+ "style guide",
+ "stylecheck",
+ "tests"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/composer-installer/issues",
+ "security": "https://github.com/PHPCSStandards/composer-installer/security/policy",
+ "source": "https://github.com/PHPCSStandards/composer-installer"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcsstandards",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2026-05-06T08:26:05+00:00"
+ },
+ {
+ "name": "phpcompatibility/php-compatibility",
+ "version": "9.3.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCompatibility/PHPCompatibility.git",
+ "reference": "9fb324479acf6f39452e0655d2429cc0d3914243"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243",
+ "reference": "9fb324479acf6f39452e0655d2429cc0d3914243",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3",
+ "squizlabs/php_codesniffer": "^2.3 || ^3.0.2"
+ },
+ "conflict": {
+ "squizlabs/php_codesniffer": "2.6.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0"
+ },
+ "suggest": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.",
+ "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
+ },
+ "type": "phpcodesniffer-standard",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Wim Godden",
+ "homepage": "https://github.com/wimg",
+ "role": "lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "homepage": "https://github.com/jrfnl",
+ "role": "lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors"
+ }
+ ],
+ "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.",
+ "homepage": "http://techblog.wimgodden.be/tag/codesniffer/",
+ "keywords": [
+ "compatibility",
+ "phpcs",
+ "standards"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues",
+ "source": "https://github.com/PHPCompatibility/PHPCompatibility"
+ },
+ "time": "2019-12-27T09:44:58+00:00"
+ },
+ {
+ "name": "phpcompatibility/phpcompatibility-paragonie",
+ "version": "1.3.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git",
+ "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/244d7b04fc4bc2117c15f5abe23eb933b5f02bbf",
+ "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf",
+ "shasum": ""
+ },
+ "require": {
+ "phpcompatibility/php-compatibility": "^9.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+ "paragonie/random_compat": "dev-master",
+ "paragonie/sodium_compat": "dev-master"
+ },
+ "suggest": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
+ "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
+ },
+ "type": "phpcodesniffer-standard",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Wim Godden",
+ "role": "lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "role": "lead"
+ }
+ ],
+ "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.",
+ "homepage": "http://phpcompatibility.com/",
+ "keywords": [
+ "compatibility",
+ "paragonie",
+ "phpcs",
+ "polyfill",
+ "standards",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues",
+ "security": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/security/policy",
+ "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCompatibility",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcompatibility",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2025-09-19T17:43:28+00:00"
+ },
+ {
+ "name": "phpcompatibility/phpcompatibility-wp",
+ "version": "2.1.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git",
+ "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/7c8d18b4d90dac9e86b0869a608fa09158e168fa",
+ "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa",
+ "shasum": ""
+ },
+ "require": {
+ "phpcompatibility/php-compatibility": "^9.0",
+ "phpcompatibility/phpcompatibility-paragonie": "^1.0",
+ "squizlabs/php_codesniffer": "^3.3"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0"
+ },
+ "suggest": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
+ "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
+ },
+ "type": "phpcodesniffer-standard",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Wim Godden",
+ "role": "lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "role": "lead"
+ }
+ ],
+ "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.",
+ "homepage": "http://phpcompatibility.com/",
+ "keywords": [
+ "compatibility",
+ "phpcs",
+ "standards",
+ "static analysis",
+ "wordpress"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues",
+ "security": "https://github.com/PHPCompatibility/PHPCompatibilityWP/security/policy",
+ "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCompatibility",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcompatibility",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2025-10-18T00:05:59+00:00"
+ },
+ {
+ "name": "phpcsstandards/phpcsextra",
+ "version": "1.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHPCSExtra.git",
+ "reference": "39467533fdb742446d68c1d10ac33d625ee0311c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/39467533fdb742446d68c1d10ac33d625ee0311c",
+ "reference": "39467533fdb742446d68c1d10ac33d625ee0311c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4",
+ "phpcsstandards/phpcsutils": "^1.2.3",
+ "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1"
+ },
+ "require-dev": {
+ "php-parallel-lint/php-console-highlighter": "^1.0",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpcsstandards/phpcsdevcs": "^1.2.0",
+ "phpcsstandards/phpcsdevtools": "^1.2.1",
+ "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
+ },
+ "type": "phpcodesniffer-standard",
+ "extra": {
+ "branch-alias": {
+ "dev-stable": "1.x-dev",
+ "dev-develop": "1.x-dev"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Juliette Reinders Folmer",
+ "homepage": "https://github.com/jrfnl",
+ "role": "lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors"
+ }
+ ],
+ "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.",
+ "keywords": [
+ "PHP_CodeSniffer",
+ "phpcbf",
+ "phpcodesniffer-standard",
+ "phpcs",
+ "standards",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues",
+ "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy",
+ "source": "https://github.com/PHPCSStandards/PHPCSExtra"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcsstandards",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2026-07-27T11:13:17+00:00"
+ },
+ {
+ "name": "phpcsstandards/phpcsutils",
+ "version": "1.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHPCSUtils.git",
+ "reference": "5f35d9408c54d7b529501f3c688b6eae562aea1f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/5f35d9408c54d7b529501f3c688b6eae562aea1f",
+ "reference": "5f35d9408c54d7b529501f3c688b6eae562aea1f",
+ "shasum": ""
+ },
+ "require": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0",
+ "php": ">=5.4",
+ "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1"
+ },
+ "require-dev": {
+ "ext-filter": "*",
+ "php-parallel-lint/php-console-highlighter": "^1.0",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpcsstandards/phpcsdevcs": "^1.2.0",
+ "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0 || ^3.0.0"
+ },
+ "type": "phpcodesniffer-standard",
+ "extra": {
+ "branch-alias": {
+ "dev-stable": "1.x-dev",
+ "dev-develop": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "PHPCSUtils/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Juliette Reinders Folmer",
+ "homepage": "https://github.com/jrfnl",
+ "role": "lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors"
+ }
+ ],
+ "description": "A suite of utility functions for use with PHP_CodeSniffer",
+ "homepage": "https://phpcsutils.com/",
+ "keywords": [
+ "PHP_CodeSniffer",
+ "phpcbf",
+ "phpcodesniffer-standard",
+ "phpcs",
+ "phpcs3",
+ "phpcs4",
+ "standards",
+ "static analysis",
+ "tokens",
+ "utility"
+ ],
+ "support": {
+ "docs": "https://phpcsutils.com/",
+ "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues",
+ "security": "https://github.com/PHPCSStandards/PHPCSUtils/security/policy",
+ "source": "https://github.com/PHPCSStandards/PHPCSUtils"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcsstandards",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2026-07-27T10:28:41+00:00"
+ },
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.13.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
+ "reference": "4c378e1a528ea066890fc2397cbdd2f94eb2fc91"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/4c378e1a528ea066890fc2397cbdd2f94eb2fc91",
+ "reference": "4c378e1a528ea066890fc2397cbdd2f94eb2fc91",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
+ },
+ "bin": [
+ "bin/phpcbf",
+ "bin/phpcs"
+ ],
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "Former lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "role": "Current lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "keywords": [
+ "phpcs",
+ "standards",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
+ "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
+ "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcsstandards",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2026-08-06T00:17:32+00:00"
+ },
+ {
+ "name": "wp-coding-standards/wpcs",
+ "version": "3.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
+ "reference": "ec2ff942335f33683a5957a85d138753876a05cf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/ec2ff942335f33683a5957a85d138753876a05cf",
+ "reference": "ec2ff942335f33683a5957a85d138753876a05cf",
+ "shasum": ""
+ },
+ "require": {
+ "ext-filter": "*",
+ "ext-libxml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlreader": "*",
+ "php": ">=7.2",
+ "phpcsstandards/phpcsextra": "^1.5.1",
+ "phpcsstandards/phpcsutils": "^1.2.3",
+ "squizlabs/php_codesniffer": "^3.13.5"
+ },
+ "require-dev": {
+ "php-parallel-lint/php-console-highlighter": "^1.0.0",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpcompatibility/php-compatibility": "^10.0.0@dev",
+ "phpcsstandards/phpcsdevtools": "^1.2.0",
+ "phpunit/phpunit": "^8.0 || ^9.0"
+ },
+ "suggest": {
+ "ext-iconv": "For improved results",
+ "ext-mbstring": "For improved results"
+ },
+ "type": "phpcodesniffer-standard",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
+ "keywords": [
+ "phpcs",
+ "standards",
+ "static analysis",
+ "wordpress"
+ ],
+ "support": {
+ "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues",
+ "source": "https://github.com/WordPress/WordPress-Coding-Standards",
+ "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "custom"
+ }
+ ],
+ "time": "2026-07-27T11:53:23+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {},
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=7.4"
+ },
+ "platform-dev": {},
+ "plugin-api-version": "2.9.0"
+}
diff --git a/contentrain-bridge.php b/contentrain-bridge.php
index d3732b0..46b6b2d 100644
--- a/contentrain-bridge.php
+++ b/contentrain-bridge.php
@@ -3,7 +3,7 @@
* Plugin Name: Contentrain Bridge
* Plugin URI: https://github.com/Contentrain/wordpress-bridge
* Description: Model and export WordPress content and interface text as Contentrain JSON/Markdown, with optional GitHub delivery.
- * Version: 0.7.0
+ * Version: 0.7.1
* Requires at least: 6.2
* Requires PHP: 7.4
* Author: Contentrain
@@ -16,7 +16,7 @@
*/
defined( 'ABSPATH' ) || exit;
-define( 'CONTENTRAIN_BRIDGE_VERSION', '0.7.0' );
+define( 'CONTENTRAIN_BRIDGE_VERSION', '0.7.1' );
define( 'CONTENTRAIN_BRIDGE_FILE', __FILE__ );
foreach ( array( 'policy', 'files', 'exporter', 'menus', 'source', 'inventory', 'delta', 'seo-render', 'seo', 'redirects', 'routing', 'scanner', 'text', 'coverage', 'integrations', 'acf', 'models', 'validator', 'rawir', 'jobs', 'github', 'key', 'remote', 'robots', 'admin' ) as $contentrain_bridge_class ) {
@@ -25,6 +25,7 @@
unset( $contentrain_bridge_class );
\Contentrain\Bridge\Admin::register();
+\Contentrain\Bridge\Key::register();
\Contentrain\Bridge\Robots::register();
foreach ( array( 'save_post', 'deleted_post', 'added_post_meta', 'updated_post_meta', 'deleted_post_meta', 'created_term', 'edited_term', 'delete_term', 'added_term_meta', 'updated_term_meta', 'deleted_term_meta', 'comment_post', 'edit_comment', 'deleted_comment', 'transition_comment_status', 'profile_update', 'switch_theme' ) as $contentrain_bridge_hook ) {
add_action( $contentrain_bridge_hook, array( '\Contentrain\Bridge\Source', 'changed' ), 10, 3 );
diff --git a/includes/class-contentrain-bridge-acf.php b/includes/class-contentrain-bridge-acf.php
index bfa4abb..30fa026 100644
--- a/includes/class-contentrain-bridge-acf.php
+++ b/includes/class-contentrain-bridge-acf.php
@@ -155,7 +155,7 @@ private static function page_address( $post_id ) {
return null;
}
}
- return get_permalink( $post ) ?: null;
+ return Source::permalink( $post ) ?: null;
}
/**
diff --git a/includes/class-contentrain-bridge-admin.php b/includes/class-contentrain-bridge-admin.php
index 4134cda..d294458 100644
--- a/includes/class-contentrain-bridge-admin.php
+++ b/includes/class-contentrain-bridge-admin.php
@@ -63,8 +63,10 @@ public static function page() {
-
-
+
+
+
+
@@ -211,45 +213,97 @@ private static function robots_card() {
] =