Content model
How a story, its translations and its pictures fit together.
A story is a group of versions#
A story isn't one record with translated fields. Each language is its own row — its own headline, summary, body, slug, status and publish time — and a translation group ties them together, remembering which language the story was written in.
- One row per language version, unique on site, language and slug.
- At most one version per language in a group.
- Pictures, category, byline and the breaking and featured flags are kept the same across the group.
- Deleting one version leaves the others alone.
The body#
Bodies are stored as a structured document, not HTML, and turned into sanitised HTML when read. Pictures are stored as media ids rather than addresses, so moving storage or changing sizes never rewrites a story.
Revisions#
Each save that changes a story keeps the previous version; the last 20 are kept, and edits close together are merged into one.
Every row belongs to a site#
Every content table carries a site id, and the database enforces it with row-level security instead of trusting each query to filter. A query that forgets the site returns nothing, never another site's data.