Makes a PUBLISHED portal issue mentionable — as its public URL.
Why this is not just another type on ResourceLinks
A portal issue IS an assignment, so it shares its uuid with the
project_task type. The handler callback receives a flat uuid list with
no type attached, so one module answering for both would resolve the same
uuid twice and the merge would decide which link won — silently, and in
favour of whichever branch ran last. Splitting the type onto its own
module is what keeps "the admin page for staff" and "the public page for
everyone" from collapsing into each other.
The link has to be the public one
project_task resolves to /admin/projects/list/<project>, which an
anonymous reader cannot open. Linking a portal issue to that page would
render every # on a public board as a locked chip pointing at an admin
URL. This resolves to /portal/<slug>/i/<uuid> — the page the reader is
already on the same board as.
Visibility is the board's, not the project's
ResourceLinks.visible_resource_uuids/2 answers "may this viewer open
the project", which is the wrong question here: portal readers are
usually anonymous and belong to nothing. The right question is "is this
issue published on a board this viewer may reach", and that is exactly
what Portal.resolve/2 plus the board's own scoping already decide — so
this defers to them rather than reimplementing the rule.
Summary
Functions
Titles and RAW public paths for published portal issues.
Not globally searchable, on purpose.
The single type this module owns, for the resource_links/0 callback.
Which of these issues this viewer may actually read.
Functions
Titles and RAW public paths for published portal issues.
Raw because ResourceLinks applies the url prefix once at render.
A uuid that is not a published issue on a live portal simply isn't in the
result — the caller renders that as missing, which is the correct answer
for an issue that was unpublished after someone linked it.
Not globally searchable, on purpose.
Core asks every registered type for # results, and a portal issue
offered from an ADMIN composer would be the wrong record to link: staff
writing internally want the task, which project_task already provides,
and a public URL pasted into an internal note invites exactly the mixup
this module exists to prevent. The portal composer builds its own list,
scoped to the one board being read — see Portal.mention_candidates/4.
The single type this module owns, for the resource_links/0 callback.
Which of these issues this viewer may actually read.
Deliberately re-runs the board check per issue rather than trusting that
a published row is universally readable: link and members boards
publish too, and their audiences are not the open web. Portal.resolve/2
is the same doorway the page itself uses, so a board that goes private,
rotates its slug or loses the extension takes its mentions with it.