# WordPress.org Plugin Directory listing

This connector ships through two surfaces, with **wordpress.org owning
updates** once the Directory listing is live (Plugin Check and the
[detailed plugin guidelines](https://developer.wordpress.org/plugins/wordpress-org/detailed-plugin-guidelines/)
forbid a custom update channel / `Update URI` pointing off .org):

1. **WordPress.org Plugin Directory** at
   `https://wordpress.org/plugins/seekmodo/` — discoverability, first
   install via **Plugins → Add New**, and **all subsequent WP-admin
   updates** after approval (SVN `tags/<Stable tag>/`).

2. **seekmodo.com** at `https://seekmodo.com/plugins/wordpress` —
   marketing download + signed release zips for operators who install
   outside the Directory. After listing approval, keep SVN in lockstep
   with each seekmodo.com release so Directory installs and marketing
   zips stay on the same version.

Text Domain / i18n slug: **`seekmodo`** (matches the Directory slug).

This document covers the one-time submission ritual and the per-
release SVN sync once we're approved.

---

## Initial submission (one-time)

### Prerequisites

- A wordpress.org account with **Plugin Directory submission**
  rights (a fresh account is fine; you don't need any prior
  contribution history). Sign in at
  <https://wordpress.org/plugins/developers/>.
- A built submission zip from the `v0.11.7` (or newer) tag of this
  repo. Build it with the standard ritual:
  ```bash
  git tag -a v0.11.7 -m "v0.11.7 - WP.org Plugin Check clean"
  git push origin v0.11.7
  python tools/build_release.py
  # → dist/seekmodo-wordpress-v0.11.7.zip
  ```
  The submission zip MUST be sourced from a real release — w.org's
  reviewer will dig into the headers, `readme.txt`, and Plugin Check
  findings. Confirm there is **no** `Update URI` header and no custom
  update client in the artefact.
- The plugin assets (icon + banners) already rendered at
  `services/marketing-site/public/plugins/assets/seekmodo/` in the
  seekmodo monorepo (run
  `node tools/render_wp_plugin_assets.mjs` from that repo's root to
  regenerate from the brand SVGs).

### Step 1 — Pre-submission self-review

Walk through each box. The wordpress.org Plugin Review Team checks
all of these and will hold the submission until they're satisfied.

- [x] Slug `seekmodo` is still available
  ([api.wordpress.org/plugins/info/1.0/seekmodo.json](https://api.wordpress.org/plugins/info/1.0/seekmodo.json)
  returns `{"error":"Plugin not found."}`).
- [x] Plugin header matches `readme.txt` for `Stable tag`, `Requires
  at least`, `Tested up to`, `Requires PHP`, `License`, and
  `License URI`.
- [x] `License: GPL-2.0-or-later` (or `GPLv2 or later` in
  `readme.txt`) AND the license actually covers the whole tree
  (`LICENSE` file at the repo root + GPL-compatible third-party deps;
  see GPL Audit below).
- [x] `readme.txt` short description (line under the headers, before
  `== Description ==`) is no longer than ~150 characters (card-preview
  blurb on the directory listing).
- [x] **External service dependency is disclosed in the
  description** before the user is asked to install. We follow the
  Mailchimp / Algolia / Akismet pattern: a paragraph that names the
  service (Seekmodo at mcp.seekmodo.com / admin.seekmodo.com), notes
  the 14-day free trial, and discloses that paid tiers start at
  $29/month. Reviewer pushback on this is the most common reason for
  a hold.
- [x] **What-happens-if-you-stop-paying is explicit**. We say so:
  "the plugin does not break your site; every search entry point
  falls back to native WordPress / WooCommerce search inside the
  same request". This sidesteps the trialware concern.
- [x] No bundled minified code without source. Suggest/ask
  `.global.js` and the full web-components ESM ship with source maps
  containing `sourcesContent`. Composer `vendor/` is not shipped in
  the release zip (the build does not run `composer install` into the
  artefact; only `vendor/bin` is listed in `EXCLUDE_DIRS` as a
  safety net if someone vendors locally). Verify by unzipping a
  built artefact and inspecting.
- [x] No phone-home telemetry without informed consent.
  Gateway-bound traffic IS phone-home, but the user opted in by
  pairing with their tenant — explicit consent. Privacy Policy Guide
  text discloses indexed content, search/click telemetry, and request
  metadata (IP / user agent / referer). After listing, plugin updates
  come from wordpress.org (no seekmodo.com update-check phone-home).
- [x] No GPL-incompatible CDN-loaded assets at runtime by default.
  Gutenberg / PDP web-components load from
  `assets/web-components/index.js` (vendored ESM). Operators may
  override via `seekmodo_web_components_src` (not the default).
- [x] No "premium" features locked behind a check_license call. The
  plugin code is fully functional GPL.
- [x] No security holes. Run a `grep -RE 'eval\(|extract\(|create_function'`
  pass and a `grep -RE 'unfiltered_html|file_get_contents.*\$_GET'`
  pass; both should turn up nothing in production PHP (tests may use
  `eval` for stubs).
- [x] All admin pages capability-check (`current_user_can(...)`).
- [x] All inputs sanitised, all outputs escaped (search the codebase
  for raw `$_GET` / `$_POST` reads and confirm each one goes through
  `sanitize_text_field` / `wp_unslash`).

If any box is unchecked, fix before submission.

### GPL Audit

| Path | License | Notes |
| --- | --- | --- |
| Plugin PHP / readable JS | GPLv2 or later | Repo root `LICENSE` + `license.txt` |
| `assets/suggest/*.global.js` | MIT (upstream `@seekmodo/web-components` / SDK) | Source maps with `sourcesContent` |
| `assets/ask/*.global.js` | MIT (same) | Source maps with `sourcesContent` |
| `assets/web-components/index.js` | MIT (same) | Full ESM pin; see `assets/web-components/LICENSE-*.txt` |
| `assets/typeahead/*`, `assets/recommendations/*` | GPLv2 or later | First-party readable sources |

MIT is GPL-compatible. Do not add proprietary or AGPL-only runtime deps
without a reviewer-facing disclosure.

### Step 2 — Submit the zip

1. Go to <https://wordpress.org/plugins/developers/add/>.
2. Fill in:
   - **Plugin Name**: `Seekmodo Search`
   - **Plugin Description**: paste the short blurb (first paragraph
     of `readme.txt`'s description; ≤150 chars).
   - **Plugin Zip**: upload `dist/seekmodo-wordpress-v0.11.6.zip`.
3. Submit.
4. You'll get a confirmation email; the reviewer team picks up the
   submission within 1–12 weeks (historically faster — usually
   1–3 weeks). They'll reply on the same email thread; respond
   inline rather than re-submitting.

### Step 3 — Reviewer back-and-forth

The reviewer almost always asks 1–3 questions. Typical asks:

- **"Why does the plugin require an external service?"** — paste a
  link to the description's External Service Dependency paragraph
  and the link to <https://seekmodo.com/legal/privacy>.
- **"How does the user know what data is sent where?"** — point at
  the readme's Privacy & Data Handling paragraph + the same legal
  page.
- **"Why is the slug `seekmodo` and not `seekmodo-search`?"** —
  brand identity; we operate the platform at seekmodo.com and the
  connector is one of many (Zen Cart, BigCommerce, etc.). The slug
  is the canonical product identifier.

If they ask for code changes, treat it like a normal PR review:
make the fix on `main`, cut a new patch version (e.g. v0.1.5),
re-build with `tools/build_release.py`, and reply with the new zip
attached. Do NOT increment the major or minor version for
review-driven changes.

### Step 4 — Initial SVN check-in

Once approved, you'll get SVN credentials (username = your
wordpress.org username, password = your wordpress.org password) and
an SVN URL: `https://plugins.svn.wordpress.org/seekmodo/`.

The repository layout is:

```
seekmodo/
├── trunk/      ← current dev / mirrors `numinix/seekmodo-wordpress-connector` main
├── tags/
│   └── 0.1.4/  ← the immutable v0.1.4 release tree
└── assets/     ← icons + banners (NOT in trunk; served by w.org for the listing)
```

First checkout + push:

```bash
# Local SVN working copy at ~/svn/seekmodo (or wherever).
svn co https://plugins.svn.wordpress.org/seekmodo svn-seekmodo
cd svn-seekmodo

# Populate trunk/ from the unzipped v0.1.4 plugin zip.
unzip -d /tmp ~/Repositories/Clients/seekmodo.com/seekmodo-wordpress-connector/dist/seekmodo-wordpress-v0.1.4.zip
rsync -a --delete /tmp/seekmodo-wordpress/ trunk/

# Populate assets/ from the marketing-site PNGs.
mkdir -p assets
cp ~/Repositories/Clients/seekmodo.com/seekmodo/services/marketing-site/public/plugins/assets/seekmodo/icon-128x128.png   assets/icon-128x128.png
cp ~/Repositories/Clients/seekmodo.com/seekmodo/services/marketing-site/public/plugins/assets/seekmodo/icon-256x256.png   assets/icon-256x256.png
cp ~/Repositories/Clients/seekmodo.com/seekmodo/services/marketing-site/public/plugins/assets/seekmodo/banner-772x250.png assets/banner-772x250.png
cp ~/Repositories/Clients/seekmodo.com/seekmodo/services/marketing-site/public/plugins/assets/seekmodo/banner-1544x500.png assets/banner-1544x500.png

# Tag the release.
svn cp trunk tags/0.1.4

# Mark new paths.
svn add --force trunk assets tags/0.1.4

# Commit. wordpress.org propagates to the directory listing within
# ~15 minutes; the actual zip the WP-admin "Install Now" button
# downloads is mirrored to api.wordpress.org within ~1 hour.
svn ci -m "release: seekmodo v0.1.4 initial submission"
```

Once `https://wordpress.org/plugins/seekmodo/` resolves and the
directory listing shows our banner + icon, the listing is live.
Search for "seekmodo" in WP-admin → Plugins → Add New on any test
site to confirm the install flow.

---

## Per-release SVN sync (going forward)

Each time we ship a new release on seekmodo.com (via the standard
`tools/build_release.py --auto-pr` ritual; see `RELEASING.md`), we
ALSO want the wordpress.org listing to stay in sync. Why:

- The directory page (`wordpress.org/plugins/seekmodo/`) shows the
  current `Tested up to`, `Last updated`, description, screenshots,
  changelog. Stale listings hurt discoverability.
- The "compatibility" filter on the WP-admin "Plugins → Add New"
  page filters by `Tested up to`; we want to stay compatible with the
  latest WordPress.
- The directory's "Active installs" graph, "Requires WordPress
  version" indicator, and the changelog all rebuild from the trunk
  on each SVN commit.

Note: After Directory approval, WP-admin's "Update Now" button pulls
from wordpress.org SVN. Keep `tags/<Stable tag>/` current on every
release so Directory installs are not stuck behind seekmodo.com.

Ritual (operator-local; same workstation as the seekmodo.com release):

```bash
# (1) Build a fresh zip from the connector repo as usual.
cd ~/Repositories/Clients/seekmodo.com/seekmodo-wordpress-connector
git tag -a vX.Y.Z -m "vX.Y.Z - <summary>"
git push origin vX.Y.Z
python tools/build_release.py --auto-pr   # publishes to seekmodo.com

# (2) Sync to SVN.
cd ~/svn/seekmodo
svn up
unzip -d /tmp ~/Repositories/Clients/seekmodo.com/seekmodo-wordpress-connector/dist/seekmodo-wordpress-vX.Y.Z.zip
rsync -a --delete /tmp/seekmodo-wordpress/ trunk/

# (3) If the brand assets have changed, refresh them too (rare).
cp ~/Repositories/Clients/seekmodo.com/seekmodo/services/marketing-site/public/plugins/assets/seekmodo/*.png assets/

# (4) Tag the release.
svn cp trunk tags/X.Y.Z

# (5) Commit. SVN credentials are your wordpress.org login.
svn add --force trunk assets tags/X.Y.Z
svn ci -m "release: seekmodo vX.Y.Z"
```

If you don't tag in SVN, the directory page stays stale even after
the trunk update — w.org reads version off `tags/<stable_tag>/`
where `<stable_tag>` is whatever `Stable tag` line your `readme.txt`
declares. **The `Stable tag` in `readme.txt` MUST exist as a
matching `tags/<value>/` directory in SVN** or w.org silently keeps
serving the old version.

### Automating the SVN push

The operator workflow above is short but easy to forget. A future
iteration of `tools/build_release.py` can add a `--push-svn` flag
that runs the rsync + `svn cp` + `svn ci` automatically. Out of
scope for the initial submission; tracked separately. For now the
operator runs the SVN sync manually after each seekmodo.com
release.

---

## When seekmodo.com zips and SVN drift

- **SVN `tags/<Stable tag>/` is the source of truth for Directory
  installs and WP-admin updates.**
- **seekmodo.com zips** remain useful for marketing / manual install;
  keep them at the same version as SVN so support docs stay simple.

If SVN lags a seekmodo.com hot-fix, Directory installs will not see
the fix until the operator runs the SVN sync ritual above.

---

## Removing the listing (if we ever need to)

If we decide to retire the wordpress.org listing — say we're going
fully private-listed — the steps are:

1. Email <plugins@wordpress.org> from the registered Plugin Author
   address asking for the listing to be closed.
2. Set the next release's `Stable tag` in `readme.txt` to a value
   that does NOT exist as a `tags/<value>/` directory in SVN. This
   short-circuits the w.org auto-update path immediately (defence in
   depth alongside step 1).
3. Continue publishing signed zips on seekmodo.com for manual
   upgrades; Directory installs would need a one-time manual zip
   replace after the listing closes.
