Skip to content

Publish to WordPress - #76

Merged
mbuckaway merged 6 commits into
masterfrom
topic/wordpress-publish
Sep 23, 2026
Merged

mbuckaway merged 6 commits into
masterfrom
topic/wordpress-publish

Conversation

@mbuckaway

Copy link
Copy Markdown
Owner

What

Adds Results ▸ Publish to WordPress…, which creates or updates a page on a WordPress site
through its REST API, authenticated with an Application Password over HTTPS.

The dialog collects the site URL, username, Application Password, page title and slug, an optional
parent "section" page, and the publish status. The connection and page defaults persist in
settings.json (first cut; planned to move onto the ride later). The page is looked up by slug and
updated if it exists, created if it does not.

Why

Operators currently export the results page and upload it by hand. This publishes a finished ride's
results straight to the club site.

How it is built

  • rivercrossing.wordpress — a wx-free REST client on the stdlib only (discover,
    find_page_by_slug, publish_page), HTTPS-only for every request, with typed errors. No new
    dependency.
  • htmlexport.render_wordpress — emits only an inner body fragment wrapped in .rc-results
    (no <!DOCTYPE>/<html>/<head>/<body>, no race-data script). A selector-scoped
    compiled_css_wp artifact scopes the app's CSS under .rc-results, so publishing cannot restyle
    the site theme
    ; the cascade layers are flattened so the fragment's own layout wins on specificity
    against a theme's unlayered CSS.
  • Page, not Post — Pages are hierarchical (a parent page is the "section") and reach the site
    menu; categories and tags are Post-only.
  • New frozen names (publish_wordpress_dlg, its wp_* controls, mi_publish_wordpress) are
    registered in design/docs-md/xrc-windows.md and spec.md §15/§15b.

The site owner still places the page on a menu in wp-admin — WordPress core REST has no menus
endpoint.

Verification

  • lint · typecheck · importlint · ids_drift · css_drift · assets_drift · unit all green —
    6847 passed, coverage 99.86% (wordpress.py 100% branch).
  • The open/quit smoke (tests/functional/test_app_menu_quit.py) passes.
  • End-to-end against a live WordPress (Podman: WordPress + MariaDB + Caddy/TLS): discover reports
    wp/v2 and application-passwords; publish_page created a page and then updated the same page;
    the published page keeps its inline <style> through kses and renders in the app's own layout.

Notes

  • The Application Password is stored in the settings file in plain text for now (the planned move
    is to the ride, and eventually the OS keychain).
  • The inline <style> survives for an account with unfiltered_html (single-site Editor or
    Administrator); a lower role would have it stripped.
  • Follow-ups (not in this PR): a user-guide chapter, and a live test against gorba.ca.

Add Results > Publish to WordPress..., which creates or updates a page on a WordPress site through its REST API, authenticated with an Application Password over HTTPS.

- rivercrossing.wordpress: wx-free REST client (discover, find_page_by_slug, publish_page) built on the stdlib only, HTTPS-only, with typed errors.

- htmlexport.render_wordpress plus a selector-scoped compiled_css_wp artifact: it emits only an inner body fragment wrapped in .rc-results, so publishing cannot restyle the site theme. Cascade layers are flattened so the fragment's rules win on specificity against a theme's unlayered CSS, keeping the app's own layout.

- publish_wordpress_dlg and mi_publish_wordpress, the pure presenter and the wx view, and the off-loop publish wiring in the app (errors surfaced via the status bar or a native dialog).

- Five wp_* settings persisted in the settings file (the Application Password is stored there for now).

- Unit tests cover the client (100% branch), presenter, renderer and settings; the frozen-name registry and CHANGELOG are updated.

Verified against a live local WordPress (Podman): discover, create-then-update, and the rendered page.
@gitguardian

gitguardian Bot commented Sep 23, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 5 secrets following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secrets in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
37542209 Triggered Username Password da00f1f tests/unit/test_wordpress.py View secret
37542208 Triggered Username Password da00f1f tests/unit/ui/test_settings_contract.py View secret
37542206 Triggered Username Password da00f1f tests/unit/presenters/test_publish_wordpress.py View secret
37542207 Triggered Base64 Basic Authentication da00f1f tests/unit/test_wordpress.py View secret
37542208 Triggered Username Password da00f1f tests/unit/presenters/test_settings.py View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secrets safely. Learn here the best practices.
  3. Revoke and rotate these secrets.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

compiled_css_wp was missing from .gitattributes, so Windows autocrlf rewrote it to CRLF on checkout while the generator emits LF, failing css_drift and the test_gen_css byte compare on windows-latest. Add the same eol=lf contract the sibling compiled_css/fonts_css artifacts carry.
GitGuardian flagged three 'Username Password' literals and one 'Base64 Basic Authentication' literal on the branch. All four were test fixtures, not real credentials: a fabricated application password ('hunter2') in the settings tests, and a precomputed base64 Basic header for a fabricated user:password in the client tests.

Replace them with clearly synthetic placeholders (example-user / example-value) and compute the expected Basic header from the fixture with stdlib base64 instead of embedding the encoded credential. No behaviour or test intent changes; the production docstring's illustration is updated too.
…ords

Wordfence disables WordPress Application Passwords by default and the option is not obvious to find, so the publish flow fails until it is re-enabled. Document the prerequisite in the Publishing results section and add a troubleshooting entry: the setting is on Wordfence - Firewall - All Firewall Options, in the Brute Force Protection section under 'Additional Options' (the inverted 'Disable WordPress application passwords' checkbox), and the fastest way to reach it is the search box at the top of that page.

docs/user-guide.html regenerated with tools/gen_userguide.py (userguide_drift green).
The Application Password used to test Results > Publish to WordPress against gorca.ca lives in .wp-password.txt at the repo root. Ignore it so it can never be committed.
Union-resolve the conflicts with the zero-lap/DNS work (5b80858): keep both features' settings fields (wp_* + show_dns_riders), Results menu items (mi_publish_wordpress + mi_show_dns_riders), spec/user-guide sections and test pins, and recompute the merged menu/id counts.

Regenerated ids.py (gen_ids) and user-guide.html (gen_userguide). render_wordpress now forwards the unique-rider count so a published page keeps master's header tally.
@mbuckaway
mbuckaway merged commit 82f75b4 into master Sep 23, 2026
27 checks passed
@mbuckaway
mbuckaway deleted the topic/wordpress-publish branch September 23, 2026 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant