Skip to content

Public 'Submit an Event' frontend form with moderation queue #326

@Salem874

Description

@Salem874

ELI5

Right now only admins can add calendar events — there's no way for a small-group leader or youth coordinator to propose one without admin keys. This adds a public /calendar/submit form (logged-in OR anonymous with captcha) that drops submissions into a pending queue an admin approves, mirroring the pattern we already shipped for Prayer Requests in PR #129.

Detailed proposal

Schema (additive, no breaking changes):

  • Add submissionStatus ENUM('pending','approved','rejected') NULL to tblEvents — NULL = legacy/admin-created (treat as approved), non-NULL = went through moderation
  • Add submittedBy INT NULL (FK tblUsers.userID, nullable for anonymous), submitterName VARCHAR(120) NULL, submitterEmail VARCHAR(255) NULL, submittedAt DATETIME NULL, moderatedBy INT NULL, moderatedAt DATETIME NULL, moderationNote TEXT NULL
  • New tblSettings keys: calendar.publicSubmit.enabled (bool), calendar.publicSubmit.allowAnonymous (bool), calendar.publicSubmit.requireCaptcha (bool, default true for anon), calendar.publicSubmit.notifySubmitter (bool), calendar.publicSubmit.allowedCategoryIDs (CSV — restrict which tblEventCategories rows are selectable)

Routes (register via AppRegistry, follow Prayer Requests precedent):

  • GET /calendar/submit — public form (brand-aware via Site::branding(), honours Site::productName() for copy)
  • POST /calendar/submit — CSRF + captcha (reuse multi-provider stack from PR feat(captcha): multi-provider support (Turnstile / reCAPTCHA v2+v3 / hCaptcha) with drag-and-drop priority #130) → insert with status='draft', isPublic=0, submissionStatus='pending'
  • GET /admin/calendar/moderation — pending queue (uses portal-data-list)
  • POST /admin/calendar/moderation/{eventID}/{approve|reject|edit} — on approve flip status='published', isPublic=1, submissionStatus='approved'; optionally email submitter

UI: form supports single events only in v1 (series/recurrence via tblEventSeries/tblEventRecurrence deferred — admins can promote on approval). RSVP (tblEventRsvps) and event-type (tblEventTypes) restricted to safe defaults; category picker filtered by allowedCategoryIDs.

Pros / Cons

Pros: unblocks ministry-level event entry without granting admin rights; reuses Prayer Requests moderation pattern, captcha stack, and AppRegistry plumbing already in production; high-leverage for church vertical (matches TEC Community Events parity); brand-aware out of the box; additive schema = zero migration risk.

Cons: new public attack surface (mitigated by captcha + moderation queue); adds admin workload unless allowedCategoryIDs is scoped tight; series/recurrence gap may frustrate power users in v1; needs clear UX to distinguish "submitted" vs "published" state in admin lists.

How necessary?

Medium-high for church installs (small-group/ministry leaders, youth coordinators); low for single-admin organisations.

Acceptance criteria

Estimated effort

6-8 hours focused work (form + moderation page + settings UI + tests); pattern is well-trodden from Prayer Requests.


Filed during The Events Calendar competitive analysis on 2026-06-16; decision pending.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions