components-web-app / components-web-app/api-components-bundle

#[Publishable] on AbstractPage / AbstractPageData — page-level draft/live toggle

Open
#186 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
PHP
Stars
32
Forks
8
PR merge metrics
No merged PRs in 30d

Description

Problem

#[Publishable] (with publishedAt) exists for components: a component can be in draft state even inside a published page. There is no equivalent for pages themselves.

Currently, the only "draft" signal for a page is the absence of a Route. This works before publication, but once a page is live (route exists), there is no way to take it offline without deleting the route — losing URL history and any redirects pointed at it.

Desired behaviour

  • AbstractPage (base of Page and AbstractPageData) gets a publishedAt: ?\DateTimeInterface column via #[Publishable] (or a new #[PublishablePage] attribute if there are conflicts with the existing component-level implementation)
  • Unpublished pages (publishedAt: null) are invisible to unauthenticated users via the existing voter infrastructure (AbstractRoutableVoter, RouteVoter)
  • Admins can still access and edit unpublished pages via the entity IRI (same as draft components today)
  • RouteVoter gates public route resolution by the target page's publishedAt state
  • The resource_manifest endpoint respects the same gate

Things to consider

  • #[Publishable] today lives on components; its voter logic (PublishableVoter?) would need to be checked / extended for page-level entities
  • Page.isTemplate pages should probably always be accessible to admins regardless of publishedAt (template pages are never public-facing)
  • Draft page data entities with an existing Route — the route should resolve to 404/403 for public traffic, not 500
  • Interaction with cascadeChildPaths and the children endpoint: should unpublished children be hidden from the public children list?
  • Migration: existing pages have no publishedAt — default to null (draft) or to now() (treat existing pages as already published)? Likely now() for backwards compatibility

Acceptance criteria

  • AbstractPage and/or AbstractPageData gain a publishedAt column
  • Unauthenticated GET /_/routes/{path} to a page with publishedAt: null returns 403/404
  • Admin GET /_/resource_manifest/{uuid} for an unpublished page works for ROLE_ADMIN
  • Behat scenarios cover: public access denied, admin access allowed, publish via PATCH
  • Backwards-compatible migration (existing pages treated as published)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with AbstractPage and AbstractPageData, then trace the existing component-level #[Publishable] implementation and voter logic, including AbstractRoutableVoter and RouteVoter. Check route resolution and the resource_manifest endpoint, plus interactions with templates and child paths. Add Behat coverage for public denial, admin access, publishing via PATCH, and a backwards-compatible migration for existing pages.

Written by the indexing model from the issue text.

Assessment

Tech stack
php, symfony
Domain
api, authorization, backend, database, testing
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.