Skip to content

feat: hub templates#3598

Merged
isTravis merged 10 commits intotr/hubsfrom
tr/templates
Apr 21, 2026
Merged

feat: hub templates#3598
isTravis merged 10 commits intotr/hubsfrom
tr/templates

Conversation

@isTravis
Copy link
Copy Markdown
Member

Adds a full community template system to PubPub. Templates are reusable blueprints that capture an entire community configuration: pages, collections, navigation, appearance, starter pubs, default members, and facet overrides. When a user creates a new community through a hub, a template can be applied to scaffold the community with pre-configured content and settings.

What's New

Data Model

CommunityTemplate — A new Sequelize model storing template configuration as structured JSONB fields:

  • communityOverrides — partial community appearance settings (hero, colors, header, footer branding, social links)
  • pages — array of page blueprints with title, slug, description, visibility, and optional layout blocks
  • collections — array of collection blueprints with kind, visibility, and optional layout
  • navigation — slug-based navigation entries (resolved to real IDs at application time)
  • footerLinks — simple title + URL pairs
  • starterPubs — pub blueprints with optional content (ProseMirror DocJSON) and collection assignment
  • defaultMembers — users automatically added to communities with specified permission levels
  • facetOverrides — license, citation style, and other facet settings
  • Metadata: slug, title, description, avatar, isActive, sourceCommunityId, createdById, hubId

Each template has an optional hubId FK establishing a simple one-to-many relationship: a hub owns zero or more templates, and each template belongs to at most one hub. Hub managers have full edit access to their hub's templates.

API Routes

Template CRUD

  • GET/POST /api/communityTemplates — list all / create blank template (superadmin)
  • POST /api/communityTemplates/from-community — clone a template from an existing community by ID or URL
  • GET/PUT/DELETE /api/communityTemplates/:id — read / update / delete a template

Hub Integration

  • GET /api/hubs/:hubId/templates — list templates owned by a hub
  • POST /api/hubs/:hubId/templates — create a template owned by a hub
  • POST /api/hubs/:hubId/templates/from-community — clone from community, set hub ownership
  • GET /api/hubs/:hubId/activeTemplates — public endpoint for active templates (used by community creation flow)

Utility

  • POST /api/communityTemplates/fetch-pub-content — fetch a pub's latest release DocJSON by URL for starter pub content authoring

Template Application Engine

applyTemplate(template, communityId, actorId) runs after the base community is created and applies the blueprint in order:

  1. Community overrides — merges appearance settings onto the community
  2. Pages — creates pages; a page with empty slug replaces the existing default home page
  3. Collections — creates collections with slug deconfliction
  4. Navigation — resolves slug-based entries to real entity IDs, ensures home page is in nav
  5. Footer links — applied directly
  6. Default members — adds members; upgrades permissions if user already exists (e.g. the community creator)
  7. Facet overrides — delegates to the facets system
  8. Starter pubs — creates pubs, writes DocJSON content to Postgres via DraftCheckpoint, links to collections by slug

Template application is non-fatal — errors are caught and logged but the community is always created.

Cloning from Existing Communities

createTemplateFromCommunity loads a community and extracts:

  • ~30 appearance fields into communityOverrides
  • Up to 10 pages and 10 collections (with truncation warnings)
  • ID-based navigation converted to slug-based references
  • Footer links

This enables quickly bootstrapping a template from a well-configured community.

Community Creation Integration

The community create endpoint now accepts an optional templateId:

  • Hub context: validates the template belongs to the hub and is active
  • No hub context: just checks isActive
  • Template is applied after community creation; errors don't block community creation
  • Hub-created communities skip the spam-review email workflow (skipEmail: true) and are auto-approved

Editor UI

TemplateEditorDialog — a large (90vw) Blueprint dialog with 9 tabs:

Tab Description
General Title, slug, description, avatar, active toggle
Appearance Dense field rows for colors, hero, header, images, social
Pages Two-column layout: left sidebar list + right detail panel with inline layout editor
Collections Two-column layout with collection settings and layout editor
Navigation Compact inline rows for page refs, collection refs, external links, and dropdowns
Footer Links Simple title + URL rows
Starter Pubs Two-column layout with pub settings and inline MinimalEditor for content
Default Members User autocomplete with permission level picker
Import / Export JSON export/import for transferring template configs between templates

Features:

  • Dirty state detection with unsaved changes warning on close
  • Blueprint Popover confirmations for destructive actions (no browser alerts)
  • Header links to the owning hub and source community
  • Note above the MinimalEditor about limitations (citations, math, footnotes not available)
  • Math button removed from the pub content formatting bar

SuperAdmin Dashboard (/dash/super-admin) — table view of all templates with filtering, create/clone actions, and click-to-edit.

Hub Manager Dashboard — card-based view of hub templates with create/clone, active toggle, and edit capabilities.

Design Decisions

  1. Circular FK avoidance: sourceCommunityId is a plain UUID column (not a Sequelize FK) because Community.templateId already points at CommunityTemplate. A helper function manually fetches the source community data.

  2. Slug-based navigation: Templates store nav entries as slug references rather than IDs. At application time, slugs are resolved to newly-created entity IDs. Unresolvable entries are silently dropped.

  3. Simple hub ownership: Each template belongs to at most one hub via a hubId FK. No join table — hub managers have direct edit access to all their hub's templates.

  4. Lazy imports: Starter pub creation uses dynamic await import() to avoid circular dependencies with the pub and Firebase modules.

  5. Permission upgrade: When a default member is already a community member (e.g. the creator), permissions are only upgraded, never downgraded.

  6. Content resilience: DraftCheckpoint writes for starter pub content are individually try/caught — a failure writing one pub's content doesn't affect others or the overall template application.

Files Changed

New Files

  • server/communityTemplate/model.ts — Sequelize model
  • server/communityTemplate/api.ts — Express routes
  • server/communityTemplate/queries.ts — Database queries and helpers
  • server/communityTemplate/applyTemplate.ts — Template application engine
  • client/components/TemplateEditor/TemplateEditorDialog.tsx — Main editor dialog
  • client/components/TemplateEditor/TemplateEditorParts.tsx — Editor sub-components
  • client/components/TemplateEditor/templateEditor.scss — Styles
  • client/containers/SuperAdminDashboard/CommunityTemplates/CommunityTemplates.tsx — SuperAdmin page
  • client/containers/HubData/HubTemplatesTab.tsx — Hub manager tab

Modified Files

  • server/community/api.ts — Template application during community creation, hub auto-approval
  • server/community/model.ts — Added templateId FK to CommunityTemplate
  • server/spamTag/communityQueries.ts — Added skipEmail option
  • server/models.ts — Registered new models
  • server/apiRoutes.ts — Registered template routes

@isTravis isTravis merged commit c0e075b into tr/hubs Apr 21, 2026
@isTravis isTravis deleted the tr/templates branch April 21, 2026 23:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant