Repository navigation
Releases: CrawlCove/wordpress-seo-connector
Release list
Crawl Cove Connector 0.10.8
- Fix: pushing a title or description no longer strips embeds from the
post. After every write the plugin re-saved the whole post to refresh
caches, and WordPress re-filtered the body as the pushing user. For a user
without theunfiltered_htmlcapability (any Author, and every site
administrator on multisite) that deleted iframes, scripts and forms an
admin had put in the content. The plugin now writes SEO meta only and
purges caches directly (clean_post_cache(), W3 Total Cache, LiteSpeed
Cache, WP Fastest Cache). Yoast still rebuilds its indexable from its own
meta watcher. The post's modified date no longer changes on a push. New
ccc_after_post_writeaction. - Fix: Revert no longer refuses a change that cleared a value. 0.10.7's
check compared the field with the pushed text, but a cleared value can read
back as a fallback (an empty Polylang French homepage title reads as the
English one), so Revert reported a newer value that did not exist. It now
compares with what the SEO plugin served right after the push, recorded
asafterin the change log entry when it differs fromto. - Docs: the wordpress.org copy installs from the directory.
bin/build-zip.sh --wporgnow also rewrites readme.txt Installation step 1 to "Plugins → Add
Plugin, search for Crawl Cove Connector", so the listing never sends a
directory user to the GitHub zip. The GitHub copy keeps the download step,
and both readmes now use the current "Add Plugin" menu name.
Crawl Cove Connector 0.10.7
- Fix: reverting an older change no longer wipes a newer one (review
ticket #248). Revert wrote the old value back without looking, so undoing
an earlier push after a re-push, or after a hand edit in the SEO plugin,
silently overwrote the newer value./revertnow answers 409
ccc_value_changedwith the current value and writes nothing, unless sent
force: true. The Tools page marks overwritten entries "replaced by a
later change" and offers "Revert anyway" when a revert is refused. - Fix: pushed titles and descriptions keep their SEO template variables
(review ticket #233). The value was run through core
sanitize_text_field(), which deletes%plus two hex digits, so
%category%was stored astegory%and%%date%%as%te%%, then
printed literally in the page title. Tags are still stripped. Verified on
real Rank Math, Yoast, SEOPress and AIOSEO.
Crawl Cove Connector 0.10.6
- Security:
/resolveand/changesno longer disclose what the user
cannot read (review ticket #227). A Contributor or Author could resolve
?p=Nfor any id and read the title and SEO values of other users'
drafts and private posts, and of privacy requests (whose title is the
requester's email). Those now answerccc_unresolvable, like a missing
post./changeslists only entries on targets the user may edit;
administrators still see the whole log. The app resolves only crawled
public URLs, so nothing changes for it. - The Application Passwords hint (Tools page, readme FAQ) now tells users of
other security plugins to look for a setting with a similar name, instead
of asserting they all have one; matches the crawlcove.com FAQ (seo #222).
Crawl Cove Connector 0.10.5
- When a plugin has switched Application Passwords off on an HTTPS site
(Wordfence's default), the Tools page and the API 401 now say so and name
Wordfence's setting, instead of blaming HTTPS and offering the proxy fix
(review ticket #220). Sameccc_app_passwords_unavailablecode; new FAQ. - readme.txt and README install step 4 say the desktop app can push one fix
at a time or every open title and description finding at once (app 1.3.8
bulk push, ticket #217). Docs only. - wordpress.org screenshot-1 regenerated with the Application Passwords row
that 0.10.4 added to the Tools page; caption updated to match.
Crawl Cove Connector 0.10.4
-
The plugin now says when the site cannot accept Application Passwords at
all (review ticket #211). WordPress core only enables them over HTTPS or
withWP_ENVIRONMENT_TYPE=local, so on a plain-http staging site, or an
HTTPS site behind a proxy that hides the scheme, core silently ignored the
desktop app's login and the app could only report "wrong credentials".
Every route now answers such an anonymous request with its own error,
ccc_app_passwords_unavailable(401), whose message names the cause; the
Tools page Status table gains an "Application Passwords" row (On, or Off
with the proxy fix and the last-resort filter); readme.txt gains a "Does my
site need HTTPS?" FAQ; README documents the code for the app. A logged-in
admin's own cookie session is never blocked. NewCCC_App_Passwords
class, 5 unit tests, andtests/integration/app-passwords-checks.sh(12
checks, wired into run.sh) which lifts the harness's own
wp_is_application_passwords_availablefilter and proves both surfaces
against real core auth. -
Update URIheader (review ticket #212). Installed from GitHub, the
plugin had no Update URI, so core asked api.wordpress.org for updates
against a slug nobody has claimed yet; whoever one day owns
crawl-cove-connectorthere would have been offered to every GitHub
install as an upgrade. The header now points at
https://crawlcove.com/wordpress-plugin, which makes core skip the
directory for this plugin.bin/build-zip.sh --wporgstrips the line for
the wordpress.org SVN copy (the directory serves updates only to plugins
without one, and Plugin Check flags it);plugin-check.shchecks that
copy. No update notices for GitHub installs yet: that needs an external
request both readmes promise not to make. -
README documents how to find the REST root. WordPress serves the API
at/wp-json/,/index.php/wp-json/or/index.php?rest_route=/
depending on the permalink setting, and a client that hard-codes
/wp-json/cannot reach a plain-permalink site (the desktop app did
exactly that, review ticket #200). The contract now says: read the
Link rel="https://api.w.org/"header on the site URL, fall back to the
?rest_route=form, and treat a 2xx non-JSON body as "not the API". New
harness scripttests/integration/rest-root-checks.sh(13 checks, wired
into run.sh) pins the header, the advertised root and the route under it
against a real WordPress on both the plain (?rest_route=) and the pretty
(/wp-json/) permalink setting, and the taxonomy script now also resolves
a post and the static front page by their pretty URLs (what the crawler
really sends), not only by?p=N. No plugin code change. -
No em dashes in what the plugin itself says. Six strings reworded:
the plugin header description (the Plugins screen), the Tools page's
"None" SEO-plugin line, and four REST error messages the desktop app
shows (no SEO plugin, URL not on this site, static front page, Rank Math
homepage title clear). The Polylang homepage label is now
"Homepage (latest posts, fr)". Error codes are unchanged, so the app
needs nothing. POT regenerated. A unit test tokenises the plugin's PHP
and fails on an em dash in any string literal or inline HTML (comments
exempt, the lone empty-cell placeholder allowed). -
Readmes rewritten without em dashes, and a short wordpress.org
changelog. readme.txt and README.md (including the repo's H1) now follow
the house style, with the sentences reworded rather than the dashes
swapped for hyphens. The readme.txt changelog is a few plain lines for
0.10.x plus one summary of the GitHub-only versions, linking here for the
full history (167 words, was 1,324). Unit tests keep both readmes free of
em dashes outside code blocks and that changelog under 250 words.
CHANGELOG.md got the same rewrite (61 em dashes, version headings now
read## 0.10.3 (2026-10-04); the build and release scripts still find
them) and the same test, and so did SECURITY-NOTES.md (69). -
Both readmes now say the desktop app is paid. The plugin is free, but
the Crawl Cove crawler it works with is a paid app with a free trial, and
neither readme said so: a reader saw "free, GPL" and found out at
/pricing. One sentence in the Description (readme.txt) and "Why this
plugin exists" (README.md), linking crawlcove.com/pricing. Plus an FAQ:
deleting the plugin removes its change log, so revert history goes with
it, while the applied titles and descriptions stay. A unit test pins
both, and that the Stable tag matches the plugin version. No code change.
Crawl Cove Connector 0.10.3
- Tools page shows whether the desktop app has connected. The readmes
promised a "connection status" but the Status table only listed the
detected SEO plugin and the endpoint, so a wrong password or URL typo
looked like nothing at all. There is now a "Desktop app" row: "Not
connected yet" until an authenticated user calls acrawlcove/v1route,
then "Last connected by ". Recorded in the permission
callback, so rejected and anonymous requests never count; stored as one
non-autoloaded option (ccc_last_connection), written at most once a
minute per user, removed on uninstall. Inbound only: no external request.
No REST contract change.
Crawl Cove Connector 0.10.2
- Change log keeps 1,000 entries, and evicts reverted ones first. The
log was capped at 200 entries and silently dropped the oldest when full.
A title and a description on one post are two entries, so one push over
100 posts overflowed it: the earliest changes of that same push were
already gone — and unrevertable — when it finished, while both readmes
promised "any change can be reverted". The cap is now 1,000 (worst case
at the input limits is about 2MB in a non-autoloaded option that only
/apply, /revert and the Tools page read), already-reverted entries are
dropped before any live one, the cap is documented in both readmes, and
the Tools page says when the log is full. No REST contract change: the
/changesshape is unchanged, it can just return more entries. The
Tools page paginates the log (100 rows a page, revert keeps your page)
and loads each page's posts in one query, so the bigger cap costs the
admin nothing.
Crawl Cove Connector 0.10.0
-
Fix: TranslatePress compatibility. On a site running TranslatePress
(the other major free multilingual plugin besides Polylang), every URL
under a non-default language's prefix —/fr/some-post/,/fr/category/ news/, the/fr/homepage, all of it — wasccc_unresolvable. This
wasn't a narrow edge case like Polylang's homepage gap; it was every
translated URL on the whole site. Root cause, verified against
TranslatePress's own source rather than assumed: it manages its
language-prefixed URLs (includes/class-url-converter.php) with plain
string manipulation, not a WordPress rewrite rule — there is no
add_rewrite_rule()call anywhere in the plugin.url_to_postid()and
CCC_Term_Resolver's own rewrite-rule matching both work by matching
against WordPress's compiled rewrite rules, so a prefix that isn't one
of those rules is invisible to them, full stop. -
Architecturally, TranslatePress turned out to be the OPPOSITE problem
shape from Polylang: Polylang creates genuinely separate content (a real
post_id per translation) plus a real (if non-public) taxonomy whose
rewrite rule could collide with CCC's own resolution — see 0.8.1.
TranslatePress has no separate content or SEO storage per language at
all; it translates the SAME post's SAME Yoast/Rank Math postmeta at
render time via its own gettext-style string dictionary, keyed on the
literal original string text. That makes the fix simpler than Polylang's:
CCC_Service::strip_translatepress_language_prefix()recognises and
strips a configured non-default language's URL slug (read from
TranslatePress's owntrp_settingsoption — it ships no public@api
equivalent of Polylang'spll_home_url()) before any resolution runs, so
every existing post/term/homepage path handles the stripped URL exactly
like its default-language counterpart. No REST contract change:lang
stays empty throughout, correctly — there is nothing per-language here
for it to mean. -
Verified end-to-end against a real WordPress + Yoast + TranslatePress
install, not source-reading alone (tests/integration/ translatepress-checks.sh, 9/9): a post URL and its/fr/-prefixed
counterpart resolve to the same post_id; the homepage and its/fr/
counterpart both resolve toHOME_ID; a CCC title write is read back
identically from either URL. Confirmed the harness genuinely catches the
regression by reverting the fix locally first (5/9 failed, as predicted). -
One consequence documented, not fixed, because it isn't a CCC bug: since
TranslatePress's translation lookup is keyed on the literal original
string, ANY edit to that string — from CCC or from a site owner's own
hand-edit in Yoast's metabox, no difference — orphans whatever
translation already existed until a human re-translates it in
TranslatePress's own editor. Noted in SECURITY-NOTES.md so it isn't
rediscovered as "a bug" later. -
Security: the
langfield shipped in 0.9.0 was never added to
security-checks.sh's type/injection/unicode/null-byte/oversized-string
fuzzing matrix every other field gets. Added 12 checks to
polylang-yoast-home-checks.sh(the one harness with Polylang+Yoast
live) — no bug found,is_string()+ a strictin_array()whitelist
already rejected everything; now a permanent regression check. -
Packaging (30 Sept, before this version was tagged):
bin/build-zip.sh
builds the installablecrawl-cove-connector.zipfrom the same
production file settests/integration/plugin-check.shchecks (one
exclude list now, shared), verifies the archive unpacks under
crawl-cove-connector/with no tests/vendor inside, and refuses to build
if the plugin header,CCC_VERSION, readmeStable tagand this
changelog disagree. A tag push (v*) now creates the GitHub Release with
that zip attached (.github/workflows/release.yml). Installation step 1
in readme.txt/README.md names the download; the "a few hundred lines"
size claim is gone (the plugin is about two thousand lines of PHP now).