Mohamed AjabSoftware / security / dataLet’s talk
Back to the project mapPR / 25 / CASE STUDY
Working application · public visitor demo4 min read

Pressroom

Write, review and publish content without letting unfinished edits leak onto the live site.

Pressroom is a team content-management app. Authors draft pages, editors review them, and approved revisions become a public website and API. A live demo gives each visitor a separate temporary workspace.

Read the case study
01 / CONTEXT

What this project is about

Pressroom is a Django publishing application for teams that need to draft, review and publish content without exposing unfinished edits. It combines a private editorial workspace, a public site and a REST API. The central question is simple: what should readers see while an author is still changing a page?

I built it as a portfolio application with workspace separation, author and editor permissions, revision history and scheduled publishing. A live demo gives each visitor a temporary workspace to try the actual workflow. It is a demonstration, not an established commercial service.

02 / IMPLEMENTATION

How it works

The workflow, at a glance
  1. Write a draft
  2. Review an approved revision
  3. Publish to site + API

Separate drafts from approved content

Authors edit drafts and submit them for review. Editors approve a particular revision for publication. The public page and API read that snapshot, so later edits do not silently change the published content. Restoring an older revision creates a new draft rather than rewriting history.

Handle permissions and competing edits

Owners, editors, authors and viewers have distinct responsibilities within a workspace. Private pages, revisions, membership and audit records are checked against that workspace. Writes include an expected version; outdated API saves return a conflict instead of overwriting another person's work. PostgreSQL row locks protect concurrent changes.

Share rules across the website and API

HTML views and REST endpoints use the same publishing service layer. Page state, revisions and audit events are committed together. A scheduler publishes approved content when due; repeated runs do not repeat the transition. Editing scheduled content cancels approval and scheduling. Markdown is sanitised before rendering.

03 / REASONING

The important design choices

Publish a snapshot

Keep the approved revision separate from ongoing edits.

The trade-offThere is additional revision state to manage, but readers see precisely the content an editor approved.

Keep the interface server-rendered

Use Django templates and CSS alongside a REST API.

The trade-offLess client-side state makes the workflow simpler to follow. Richer editing would require more interface work. SQLite supports quick local setup, while PostgreSQL is needed to test row-locking behaviour.

04 / WALKTHROUGH

Edit without changing the live page

ILLUSTRATIVE EXAMPLE

An author needs to revise a page that an editor has already published.

  1. Change the draft while readers continue seeing the existing approved revision.
  2. Submit the new revision and have an editor review it before publication.
  3. If someone else edited the draft first, resolve the version conflict instead of overwriting their work.

What this showsApproval belongs to specific content, while competing writes remain visible.

Illustrative walkthrough of the implemented workflow, not a production incident.

05 / EVIDENCE

How it was tested—and what it shows

The checks

The repository records 76 PostgreSQL tests, CI across Python 3.12 and 3.13, and browser verification of the publishing workflow. Concurrency checks exercise competing edits and scheduler behaviour; SQLite skips database-specific cases rather than pretending to test row locks.

These are inspectable implementation checks, not proof of production reliability under arbitrary load or an independent security assessment. Verification and architecture notes explain what was exercised.

What to take from the results

The result is a complete draft, review, publish and restore workflow. Public content remains on its approved snapshot while editorial work continues, and stale writes are surfaced rather than silently lost.

The project does not include enterprise SSO, billing, media uploads or email invitations. Audit records are append-only through application behaviour, not tamper-proof storage. Demo rate limits are local to each worker, not a distributed abuse-control system.

06 / BOUNDARIES

What to keep in mind

  • A portfolio application, not a replacement for an enterprise CMS or evidence of commercial adoption.
  • Media uploads, SSO, billing and email invitations are outside the current release.
  • Rate limits use per-worker memory; audit events are not a tamper-proof compliance ledger.

Where the work could go next

Possible next steps—not features or experiments already completed.

  1. Add media handling and richer editing alongside equivalent permission, sanitisation and revision tests.
  2. Before shared production use, add operational monitoring, distributed rate limits and deployment-specific security review.
07 / REFERENCE

Technical notes & source

Built with

Python · Django · Django REST Framework · PostgreSQL · Docker

Terms used in this project

Approved snapshot
The exact reviewed revision served publicly, separate from later draft edits.
Optimistic version check
Reject an edit when the record has changed since the editor loaded it.