Use uri consistently for permalinks and fix Micropub Location header for likes/bookmarks #148

Closed
opened 2026-10-29 20:17:49 +01:00 by jonny · 0 comments
Owner

Problem

The Micropub Location header is wrong for likes and bookmarks (app/Services/Micropub/Handlers/EntryHandler.php):

  • Likes return ->url, which is the liked post's URL, not our /likes/{id} page.
  • Bookmarks return ->uri, but Bookmark has no uri attribute (only local_uri), so the Location header is empty.

The root cause is inconsistent naming for "this post's permalink on jonnybarnes.uk".

url is already a database column meaning the external thing on several models, so it can't also mean our page:

Model url currently means Our permalink today
Like column: the post you liked none (template hard-codes /likes/{id})
Bookmark column: the page you bookmarked local_uri
Article column: the link-post target uri (absolute), plus link (relative)
Note — uri
Place — uri
Media accessor: the file's URL n/a (not a post)

Convention: uri = the absolute permalink on our site for every postable model, and url stays free for "what it points at". uri is already the majority, and ProcessWebMention, the syndication jobs, SendWebMentions and the Micropub handlers all use it.

Tasks

  • Add uri() accessor to Like (config('app.url').'/likes/'.$this->id)
  • Rename Bookmark::localUri() to uri() and update the test that uses local_uri
  • EntryHandler: use ->uri for likes and bookmarks
  • Tests asserting the Location header value for a like and a bookmark created via Micropub
  • Optional: resources/views/templates/like.blade.php uses the attribute instead of the hard-coded /likes/{id} (note/article templates use relative paths deliberately; leave them)

Not planned: a shared HasPermalink interface. It's more ceremony than this site needs.

Optional follow-up: newbase60 IDs for likes and bookmarks

If we want them to match notes, switch likes and bookmarks to nb60 IDs. Do this after the uri work above, because then uri() is the only place that builds the permalink.

The catch: digits are valid nb60 characters (0123456789ABC…), so an existing /likes/123 would decode to 3723 instead of 123. That would 404 or show the wrong post. Like notes did (/note/{decId} → /notes/{nb60}), the nb60 pages need a new path, with 301 redirects from the old numeric ones.

  • nb60id accessor and nb60() scope on Like and Bookmark, copied from Note, including the check for IDs too large for int4
  • uri() uses nb60id
  • New routes (e.g. /like/{nb60}, /bookmark/{nb60}). Controllers take string $urlId and look it up through the scope, instead of automatic route model binding.
  • Old /likes/{id} and /bookmarks/{id} become 301 redirects (keep whereNumber)
  • Update templates; add tests for the new pages, the redirects and the decoding

Low priority. The short domain is gone, so the gain is mostly cosmetic (123 → 24). Neither type sends webmentions or gets syndicated, so few outside links exist.

Separate follow-up: drop notes.shorturl

The short domain has been dropped and nothing in app, config, views or tests references shorturl any more. The only leftover is a dead notes.shorturl column (varchar 20). It has no non-null values locally, but check production before dropping it. This is a schema change, so do it on its own branch.

🤖 Generated with Claude Code

## Problem The Micropub `Location` header is wrong for likes and bookmarks (`app/Services/Micropub/Handlers/EntryHandler.php`): - **Likes** return `->url`, which is the *liked* post's URL, not our `/likes/{id}` page. - **Bookmarks** return `->uri`, but `Bookmark` has no `uri` attribute (only `local_uri`), so the `Location` header is empty. The root cause is inconsistent naming for "this post's permalink on jonnybarnes.uk". ## Naming: use `uri` for our own permalinks `url` is already a **database column meaning the external thing** on several models, so it can't also mean our page: | Model | `url` currently means | Our permalink today | |---|---|---| | Like | column: the post you liked | **none** (template hard-codes `/likes/{id}`) | | Bookmark | column: the page you bookmarked | `local_uri` | | Article | column: the link-post target | `uri` (absolute), plus `link` (relative) | | Note | — | `uri` | | Place | — | `uri` | | Media | accessor: the file's URL | n/a (not a post) | Convention: **`uri` = the absolute permalink on our site** for every postable model, and `url` stays free for "what it points at". `uri` is already the majority, and `ProcessWebMention`, the syndication jobs, `SendWebMentions` and the Micropub handlers all use it. ## Tasks - [ ] Add `uri()` accessor to `Like` (`config('app.url').'/likes/'.$this->id`) - [ ] Rename `Bookmark::localUri()` to `uri()` and update the test that uses `local_uri` - [ ] `EntryHandler`: use `->uri` for likes and bookmarks - [ ] Tests asserting the `Location` header value for a like and a bookmark created via Micropub - [ ] Optional: `resources/views/templates/like.blade.php` uses the attribute instead of the hard-coded `/likes/{id}` (note/article templates use relative paths deliberately; leave them) Not planned: a shared `HasPermalink` interface. It's more ceremony than this site needs. ## Optional follow-up: newbase60 IDs for likes and bookmarks If we want them to match notes, switch likes and bookmarks to nb60 IDs. Do this after the `uri` work above, because then `uri()` is the only place that builds the permalink. **The catch:** digits are valid nb60 characters (`0123456789ABC…`), so an existing `/likes/123` would decode to 3723 instead of 123. That would 404 or show the wrong post. Like notes did (`/note/{decId}` → `/notes/{nb60}`), the nb60 pages need **a new path**, with 301 redirects from the old numeric ones. - [ ] `nb60id` accessor and `nb60()` scope on `Like` and `Bookmark`, copied from `Note`, including the check for IDs too large for int4 - [ ] `uri()` uses `nb60id` - [ ] New routes (e.g. `/like/{nb60}`, `/bookmark/{nb60}`). Controllers take `string $urlId` and look it up through the scope, instead of automatic route model binding. - [ ] Old `/likes/{id}` and `/bookmarks/{id}` become 301 redirects (keep `whereNumber`) - [ ] Update templates; add tests for the new pages, the redirects and the decoding Low priority. The short domain is gone, so the gain is mostly cosmetic (123 → `24`). Neither type sends webmentions or gets syndicated, so few outside links exist. ## Separate follow-up: drop `notes.shorturl` The short domain has been dropped and nothing in `app`, `config`, views or tests references shorturl any more. The only leftover is a dead `notes.shorturl` column (varchar 20). It has no non-null values locally, but check production before dropping it. This is a schema change, so do it on its own branch. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jonny closed this issue 2026-11-01 16:49:49 +01:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jonny/jonnybarnes.uk#148
No description provided.