The save was authorised by the record id alone — ids are small
sequential integers, publicly visible on every directory card, so
a PATCH carrying only an id let one visitor overwrite another's
listing. The save now:
- uses PATCH (C.saveMethod), matching n8n
- carries the hash in the body under the same "has" key the
delete already sends, so save and delete share one credential
path, alongside the record id
- refuses to fire at all when the page holds no hash, and says why
in the status line
Everything else is unchanged: the 15-point gate, receipt only on
2xx, the honest failure line that keeps the edits, the save.disabled
handling and the DELETE shape.
The edit form's legend read "Skill areas" and carried no directive, while the
application form read "Skill profile — 15 points to distribute" and said what to
do. Same rule, worded in two places — and the edit form lost the sentence.
- New static/focus-rule.js exposes window.O4PFocus: LEGEND, DIRECTIVE, TOTAL, MAX
and message(sum). Both forms render the legend, the directive and the
wrong-total message from it, so the two cannot drift apart again.
- Edit form: legend from the constant, the application form's directive under it,
and the blocked-save message now names the direction.
- Application form: legend set from the constant, live warn and sum gate use the
same message(); the all-zero gate keeps the directive.
- Message wording: over -> "remove N", under -> "add N", zero -> "place all 15
points"; the total, aria-live, data-bad, the blocked save and the
fieldset/legend structure are unchanged.
The rule text now appears once: "You have 17 of 15 — remove 2."
Card (odoo-community-developers/):
- "Ask question anonymously" moves into its own row ABOVE the links row, in the
runtime renderer and in build-directory.py (the build-time template).
- A cog icon button sits next to it: a real <button> with an accessible name
(aria-label="Edit or delete your profile"), same quiet weight as its neighbour.
- Pressing the cog opens a modal that explains the flow and asks for the email
address, then POSTs {"email":"..."} to odoo_experts_edit (body shape in one
constant, EDIT_EMAIL_KEY). The reply is never read: one message on any 2xx, so
the box cannot tell a visitor whether an address is on file. A non-2xx fails
honestly and shows no receipt. Both controls ship dark (added only while their
route constant is set), so neither is ever a dead control.
New private page (freelancer-edit/, noindex+nofollow, unlinked, absent from
sitemap.xml and llms.txt):
- Emailed link is https://odoo4projects.com/freelancer-edit/?has=<hash> — the
same shape as /freelancer-answer/; reads `has` first, falls back to `hash`.
- GETs the edit webhook with the hash and pre-fills the form: Name, Focus (<=70),
description (<=230), the five axis numbers (must total 15), the three language
numbers (0-5), Country, OCA_link. The GET reply is read through one alias map;
a shape it does not recognise names the fields it received instead of guessing.
404 "is not registered" is a CONFIGURATION error, never "your link is invalid";
404/500 "No item to return was found" is an invalid/expired link; a network
failure keeps the hash for a retry.
- Save = POST with the record id (one constant, SAVE_METHOD). Delete is a
deliberate second action (a confirm tick gates the button) and never reports
success on a non-2xx.
- The image is read-only here, stated in the UI: replacing a photo is a different
problem from editing text.
- The hash never reaches analytics: the query is stripped before the Plausible
snippet loads. referrer policy no-referrer; JS off says so plainly.