PhoenixKitProjects.Schemas.Portal (PhoenixKitProjects v0.21.1)

Copy Markdown View Source

One row per project with the portal extension enabled (chain V10).

Access mode (V12)

access_mode answers who may resolve the page at all, and it is one enum rather than a pair of booleans because the quadrants aren't orthogonal — "secret slug that search engines index" and "human-readable slug where possession is the authorization" are both nonsense, and booleans let an admin build them.

  • link (default, and what every portal was before this existed) — the slug IS the grant: 16 CSPRNG bytes, possession is authorization, rotatable to revoke, never indexed.
  • members — any signed-in site user. The slug keeps its CSPRNG shape because unguessability is free and there is nothing to gain by throwing it away; it is simply no longer the thing being checked.
  • public — anyone, indexable, human-chosen slug.

Participation

submit_access and comment_access are separate because the risks are different in kind: a submission is invisible until staff act on it, while a comment is live surface area the moment it lands. Collapsing them into one control forces a choice between "anonymous comments go live" and "anonymous submission is as gated as commenting", and both are wrong.

Summary

Functions

The three access modes, most restrictive first.

Changeset — slug/project are server-generated, never user input.

A fresh access slug: 16 CSPRNG bytes, url-base64 (~22 chars) — the entropy floor from the portal security panel (find #9).

The participation values a write policy may take.

Slugs a public board may not take, because they collide with paths a host is likely to mount beside it or with things a reader would misread as system pages.

A starting public slug derived from a project name — a suggestion for the admin to accept or edit, never applied silently.

Types

t()

@type t() :: %PhoenixKitProjects.Schemas.Portal{
  __meta__: term(),
  access_mode: term(),
  comment_access: term(),
  inserted_at: term(),
  project: term(),
  project_uuid: term(),
  settings: term(),
  slug: term(),
  submit_access: term(),
  updated_at: term(),
  uuid: term()
}

Functions

access_modes()

@spec access_modes() :: [String.t()]

The three access modes, most restrictive first.

changeset(portal, attrs)

Changeset — slug/project are server-generated, never user input.

generate_slug()

@spec generate_slug() :: String.t()

A fresh access slug: 16 CSPRNG bytes, url-base64 (~22 chars) — the entropy floor from the portal security panel (find #9).

participation_levels()

@spec participation_levels() :: [String.t()]

The participation values a write policy may take.

reserved_slugs()

@spec reserved_slugs() :: [String.t()]

Slugs a public board may not take, because they collide with paths a host is likely to mount beside it or with things a reader would misread as system pages.

suggest_public_slug(name)

@spec suggest_public_slug(String.t() | nil) :: String.t()

A starting public slug derived from a project name — a suggestion for the admin to accept or edit, never applied silently.