Skip to content

Allow users to be saved without publishing ("Save + Keep Unpublished"), especially for alternate user parents/templates #599

Description

@adrianbj

Short description of the enhancement

Allow user pages to be saved without being published — i.e. give unpublished users the normal "Save + Keep Unpublished" button in the page editor instead of only "Publish". At minimum this should be supported for users under alternate parents/templates ($config->usersPageIDs / $config->userTemplateIDs), where user pages are also frontend content.

This continues processwire-issues#1452, which was closed with a request to re-file here with the specific use case.

Optional: Steps that explain the enhancement

  1. Configure alternate user parent/template support ($config->usersPageIDs, $config->userTemplateIDs) so that user accounts live in a frontend branch of the page tree (e.g. /team/ or /members/), and their profile pages render on the frontend — a staff directory, member profiles, etc.
  2. Create or edit one of these users.
  3. Try to save without publishing: the editor only offers "Publish", because ProcessPageEdit includes User/UserPage in otherCorePageClasses. The only path to an unpublished user is publish first, then check "Unpublished" on the Settings tab and save again — and there's no way to then edit the user again without re-publishing.

Current vs. suggested behavior

Current: New users start unpublished, but the moment you save them from the editor they are published, and an unpublished user cannot be edited/saved without publishing. Publish status and account status are conflated.

Suggested: Unpublished users get the standard "Save + Keep Unpublished" button like any other page. If the default system users under /access/users/ should keep the current behavior for safety, the enhancement could be scoped to users under alternate parents/templates only.

Why would the enhancement be useful to users?

With alternate parent/template users, the user page is content: the same page that holds the account also drives a staff directory entry, a member profile, an author bio page, etc. In that setup, publish status answers "does this profile appear on the site?" while roles answer "what can this account do?" — two orthogonal questions. Real workflows this unlocks:

  • Prepare a new staff member's profile (photo, bio, contact details) before their start date, unpublished, then publish on day one.
  • Unpublish a departed staff member's profile so it disappears from the directory, while retaining the account and its history.
  • Hold newly registered members in a not-yet-active state until payment or verification completes.

What we currently do instead: on a production membership site built on alternate user parents/templates, we work around the missing draft state by overloading the hidden status via hooks — registration hooks (LoginRegister / OAuth userCreated) add hidden to each new account, and a Pages::saveReady hook removes it once a payment webhook or 100% coupon confirms the account. It works, but the costs are real: include=hidden has to be sprinkled through nearly every selector that touches users, hidden can no longer be used for its normal meaning on those pages, and the semantics get confusing (hidden = active-but-unpaid, unpublished = disabled) — all to simulate what "unpublished until ready" would express naturally.

On the security question raised in #1452: the core already defines the key semantics for unpublished users — Session::___allowLogin() refuses login for unpublished users, and unpublished pages are already excluded from frontend find()/render like any other page. So the main missing piece is the editor workflow (the save button), not a new security model: roles would remain the access-control mechanism, and publish status would only govern frontend visibility plus the login block that already exists.

Optional: Screenshots/Links that demonstrate the enhancement

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions