Skip to main content
Petanque Life
← Back to all features
18

CMS & Websites

135 features · 19 subsystems

Every tenant (FIPJP, continental, national federation), region, and club gets their own website with a unique URL and full CMS capabilities. The CMS is a core differentiator — replacing the fragmented, outdated websites that federations and clubs manage today.

Multi-Site Platform

F18.01
Planned
How it works
  • F18.01.01 Shipped

    One site per tenant/club/region with unique URL

    ✅ PL-F1801a
  • F18.01.02 Shipped

    Platform subdomain (e.g., norrbotten-bk.petanque-life.com)

    ✅ PL-F1801a
  • F18.01.03 Shipped

    Custom domain support (e.g., www.norrlandbk.se) with auto-SSL

    ✅ PL-F1801a
  • F18.01.04 Shipped

    Site creation wizard (pick template, set branding, go live)

    ✅ PL-F1801a
  • F18.01.05 Shipped

    Site activation/deactivation

    ✅ PL-F1801b
  • F18.01.06 Shipped

    Tenant-scoped site management (federation admin manages federation site + club sites)

    ✅ PL-F1801b
  • F18.01.07 Shipped

    Site directory (find federation/club websites)

    ✅ PL-F1801b
  • F18.01.08 Shipped

    Path-prefix routing — child OrgNode-CmsSites kan mountas under förälderns domän (www.svenskboule.se/<distrikt>/); 1–3 nivåer; reserverar systemslugs; canonical URL pekar på path-formen

    ✅ PL-T233

Page Builder

F18.02
Planned
How it works
  • F18.02.01 Shipped

    Drag-and-drop page builder with content blocks

    ✅ PL-F1802a
  • F18.02.02 Shipped

    Block types: text, image, gallery, video, map, embed, form, columns

    ✅ PL-F1802a
  • F18.02.03 Shipped

    Dynamic data blocks: ranking table, results, calendar, club directory, venue map

    ✅ PL-F1802a
  • F18.02.04 Shipped

    Page templates library (homepage, about, contact, competition, news)

    ✅ PL-F1802a
  • F18.02.05 Shipped

    Custom page creation with SEO-friendly URLs — PL-F1802b

    ✅ PL-F1802b
  • F18.02.06 Shipped

    Page versioning and draft/publish workflow — PL-F1802b

    ✅ PL-F1802b
  • F18.02.07 Shipped

    Page scheduling (publish at future date) — PL-F1802b

    ✅ PL-F1802b
  • F18.02.08 Shipped

    Reusable block components (header, footer, sidebar) — PL-F1802b

    ✅ PL-F1802b

Theme & Branding Engine

F18.03
Planned
How it works
  • F18.03.01 Shipped

    Per-site theming (colors, fonts, logo) — PL-F1803a

    ✅ PL-F1803a
  • F18.03.02 Shipped

    Pre-built theme templates (modern, classic, sport, minimal) — PL-F1803a

    ✅ PL-F1803a
  • F18.03.03 Shipped

    Header/footer layout customization — PL-F1803a

    ✅ PL-F1803a
  • F18.03.04 Shipped

    Custom CSS injection (for advanced users) — PL-F1803a

    ✅ PL-F1803a
  • F18.03.05 Shipped

    Responsive design (mobile-first) — PL-F1803b

    ✅ PL-F1803b
  • F18.03.06 Shipped

    Dark mode support — PL-F1803b

    ✅ PL-F1803b
  • F18.03.07 Shipped

    Federation brand inheritance (club sites can inherit national branding) — PL-F1803b

    ✅ PL-F1803b
  • F18.03.08 Shipped

    Default-tema: bottle-green + ivory, Fraunces display + Inter body, shadcn-parity med www — PL-T109

    ✅ PL-T109

Blog & News

F18.04
Planned
How it works
  • F18.04.01 Shipped

    Article creation with rich text editor — PL-F1804a

    ✅ PL-F1804a
  • F18.04.02 Shipped

    Categories and tags — PL-F1804a

    ✅ PL-F1804a
  • F18.04.03 Shipped

    Featured image and excerpt — PL-F1804a

    ✅ PL-F1804a
  • F18.04.04 Shipped

    Article scheduling (publish at future date) — PL-F1804a

    ✅ PL-F1804a
  • F18.04.05 Shipped

    Multi-author support — PL-F1804b

    ✅ PL-F1804b
  • F18.04.06 Shipped

    News feed aggregation (pull news from parent federation) — PL-F1804b

    ✅ PL-F1804b
  • F18.04.07 Shipped

    RSS feed generation — PL-F1804b

    ✅ PL-F1804b
  • F18.04.08 Shipped

    Comment system (moderated) — PL-F1804b

    ✅ PL-F1804b

Navigation & Structure

F18.05
Planned
How it works
  • F18.05.01 Shipped

    Menu builder (custom navigation) — PL-F1805

    ✅ PL-F1805
  • F18.05.02 Shipped

    Auto-generated pages (rankings, calendar, results — pulled from API) — PL-F1805

    ✅ PL-F1805
  • F18.05.03 Shipped

    Breadcrumb navigation — PL-F1805

    ✅ PL-F1805
  • F18.05.04 Shipped

    Search within site — PL-F1805

    ✅ PL-F1805
  • F18.05.05 Shipped

    Sitemap generation (XML for search engines) — PL-F1805

    ✅ PL-F1805
  • F18.05.06 Shipped

    Footer customization (links, contact info, social media) — PL-F1805

    ✅ PL-F1805

Media Library

F18.06
Planned
How it works
  • F18.06.01 Shipped

    Image upload and management per site — PL-F1806a

    ✅ PL-F1806a
  • F18.06.02 Shipped

    Automatic image optimization (resize, compress, WebP) — PL-F1806a

    ✅ PL-F1806a
  • F18.06.03 Shipped

    Document upload (PDF, Word) — PL-F1806a

    ✅ PL-F1806a
  • F18.06.04 Shipped

    Video embedding (YouTube, Vimeo, direct upload) — PL-F1806a

    ✅ PL-F1806a
  • F18.06.05 Shipped

    Photo gallery creation

    ✅ PL-F1806b
  • F18.06.06 Shipped

    CDN delivery for all media

    ✅ PL-F1806b
  • F18.06.07 Shipped

    Storage quota per site

    ✅ PL-F1806b
  • F18.06.08 Shipped

    Public + lösenordsskyddad media via blob storage (Azure) — PL-T234

    ✅ PL-T234

Forms & Interaction

F18.07
Planned
How it works
  • F18.07.01 Shipped

    Form builder (contact, feedback, registration interest) — PL-F1807

    ✅ PL-F1807
  • F18.07.02 Shipped

    Form submissions management (view, export, notify) — PL-F1807

    ✅ PL-F1807
  • F18.07.03 Shipped

    Email notification on form submission — PL-F1807

    ✅ PL-F1807
  • F18.07.04 Shipped

    Anti-spam protection (captcha) — PL-F1807

    ✅ PL-F1807
  • F18.07.05 Shipped

    Event RSVP / sign-up forms — PL-F1807

    ✅ PL-F1807

SEO & Analytics

F18.08
Planned
How it works
  • F18.08.01 Shipped

    Per-page meta titles and descriptions — PL-F1808

    ✅ PL-F1808
  • F18.08.02 Shipped

    Open Graph images for social sharing — PL-F1808

    ✅ PL-F1808
  • F18.08.03 Shipped

    Structured data (JSON-LD: SportOrganization, Event, etc.) — PL-F1808

    ✅ PL-F1808
  • F18.08.04 Shipped

    Analytics integration (privacy-respecting, e.g., Plausible/Umami) — PL-F1808

    ✅ PL-F1808
  • F18.08.05 Shipped

    Page performance monitoring — PL-F1808

    ✅ PL-F1808
  • F18.08.06 Shipped

    Search engine indexing control (robots.txt, canonical URLs) — PL-F1808

    ✅ PL-F1808

Multi-Language Sites [DONE] `PL-F1809`

F18.09
Planned
How it works
  • F18.09.01 Shipped

    Multi-language page content (parallel versions per language)

    ✅ PL-F1809
  • F18.09.02 Shipped

    Language switcher in navigation

    ✅ PL-F1809
  • F18.09.03 Shipped

    Default language per site

    ✅ PL-F1809
  • F18.09.04 Shipped

    Translation workflow (flag pages needing translation)

    ✅ PL-F1809
  • F18.09.05 Shipped

    hreflang tags for SEO

    ✅ PL-F1809

Role-Based Editing [DONE] `PL-F1810`

F18.10
Planned

**PL-F1810: F18.10 Role-Based Editing** — Implemented:

How it works
  • F18.10.01 Shipped

    Federation editor role (manages federation site) — PL-F1810

    ✅ PL-F1810
  • F18.10.02 Shipped

    Club editor role (manages club site only) — PL-F1810

    ✅ PL-F1810
  • F18.10.03 Shipped

    Region editor role (manages region landing page) — PL-F1810

    ✅ PL-F1810
  • F18.10.04 Shipped

    Content approval workflow (draft > review > publish) for larger federations — PL-F1810

    ✅ PL-F1810
  • F18.10.05 Shipped

    Audit trail on all content changes — PL-F1810

    ✅ PL-F1810
  • F18.10.06 Shipped

    Content scheduling permissions (who can publish immediately vs. schedule) — PL-F1810

    ✅ PL-F1810

Dynamic Data Integration

F18.11

**PL-F1811a: F18.11 Dynamic Data Integration del 1/2** — Implemented:

How it works
  • F18.11.01 Shipped

    Ranking table widget (auto-updated)

    ✅ PL-F1811a
  • F18.11.02 Shipped

    Competition calendar widget

    ✅ PL-F1811a
  • F18.11.03 Shipped

    Recent results widget

    ✅ PL-F1811a
  • F18.11.04 Shipped

    Club directory widget

    ✅ PL-F1811a
  • F18.11.05 Shipped

    Venue map widget

    ✅ PL-F1811a
  • F18.11.06 Shipped

    Live score widget (during competitions) — PL-F1811b

    ✅ PL-F1811b
  • F18.11.07 Shipped

    Player profile widget — PL-F1811b

    ✅ PL-F1811b
  • F18.11.08 Shipped

    Federation statistics widget (member count, club count, etc.) — PL-F1811b

    ✅ PL-F1811b
  • F18.11.09 Shipped

    Upcoming events widget — PL-F1811b

    ✅ PL-F1811b
  • F18.11.10 Shipped

    Sponsor banner widget — PL-F1811b

    ✅ PL-F1811b

SEO & Search Optimization

F18.13
How it works

Theme & Customization

F18.12

**PL-F1812b: F18.12 CMS theme och customization del 2/2** — Implemented:

How it works
  • F18.12.01 Shipped

    Theme gallery with live preview — PL-F1812a

    ✅ PL-F1812a
  • F18.12.02 Shipped

    Per-club color scheme presets — PL-F1812a

    ✅ PL-F1812a
  • F18.12.03 Shipped

    Custom CSS injection enhancements (variables, scoping, validation) — PL-F1812a

    ✅ PL-F1812a
  • F18.12.04 Shipped

    Logo upload with auto-sizing (8 variants) — PL-F1812a

    ✅ PL-F1812a
  • F18.12.05 Shipped

    Header/footer layout selection — PL-F1812b

    ✅ PL-F1812b
  • F18.12.06 Shipped

    Page width and content max-width configuration — PL-F1812b

    ✅ PL-F1812b
  • F18.12.07 Shipped

    Font pair selection (heading + body) — PL-F1812b

    ✅ PL-F1812b
  • F18.13.01 Shipped

    Enhanced per-page meta title/description with configurable title templates, length limits, auto-description, SEO preview and audit endpoints

    ✅ PL-F1813a
  • F18.13.02 Shipped

    Enhanced Open Graph tags with og:locale and og:locale:alternate for multi-language sites plus article-specific OG properties

    ✅ PL-F1813a
  • F18.13.03 Shipped

    Dedicated Twitter Card support with site-level defaults, per-page creator handle, separate twitter:image:alt, and card type fallback

    ✅ PL-F1813a
  • F18.13.04 Shipped

    Schema.org SportsClub structured data with sport="Pétanque", logo, sameAs social profiles, and memberOf federation hierarchy

    ✅ PL-F1813a
  • F18.13.05 Shipped

    Auto-generated sitemap.xml for CMS sites with lastmod, changefreq, priority, and xhtml:link hreflang alternates for multi-language sites

    ✅ PL-F1813b
  • F18.13.06 Shipped

    Enhanced robots.txt per CMS instance with crawl-delay, custom disallow/allow patterns, user-agent rules, and sitemap reference

    ✅ PL-F1813b
  • F18.13.07 Shipped

    Enhanced canonical URLs with language-aware resolution (default uses clean path, non-default includes prefix) and canonical+alternates endpoint

    ✅ PL-F1813b
  • F18.13.08 Shipped

    hreflang link tags for all available language versions plus x-default pointing to default language with partial translation awareness

    ✅ PL-F1813b

Custom Domains via Caddy reverse proxy [DONE] `PL-T232`

F18.14
Planned

**PL-T232 — Caddy reverse proxy ersätter Cloudflare for SaaS** som plattformens primära väg för CMS-custom domains. Den tidigare Cloudflare-implementationen (PL-T046) lämnas som vilande alternativ i `services/cloudflare_saas.py`.

How it works
  • F18.14.01 Shipped

    Custom hostname-registrering via CustomDomain-modell — PL-T232

    ✅ PL-T232
  • F18.14.02 Shipped

    CNAME/TXT-valideringsflöde med dig-baserad DNS-lookup — PL-T232

    ✅ PL-T232
  • F18.14.03 Shipped

    Automatisk TLS-provisionering via Caddy + Let's Encrypt HTTP-01 — PL-T232

    ✅ PL-T232
  • F18.14.04 Shipped

    /public/domains/cert-allowed-gate mot Let's Encrypt-abuse — PL-T232

    ✅ PL-T232
  • F18.14.05 Shipped

    Admin-UI för domänhantering med CNAME-target proxy.web.petanque.life — PL-T232

    ✅ PL-T232
  • F18.14.06 Shipped

    Plattform-subdomän-konvention <slug>.web.petanque.life — PL-T232 (auto-DNS via PL-T235)

    ✅ PL-T232

Premium UX (dual-mode adaptive, SSR-prestanda, editorial) [DONE] `PL-T158`

F18.15
Planned

**PL-T158: F18.15 web/ premium-UX** — Implemented:

How it works
  • F18.15.01 Shipped

    Dual-mode adaptive shell (mobile drawer + bottom-nav / desktop full nav)

    ✅ PL-T158
  • F18.15.02 Shipped

    Editorial display-skala (Fraunces opsz, ss01)

    ✅ PL-T158
  • F18.15.03 Shipped

    PetanqueImage AVIF/WebP/JPEG + focal-point + lazy/priority

    ✅ PL-T158
  • F18.15.04 Shipped

    TenantBrandStyle SSR CSS-var-injektion + auto-accent

    ✅ PL-T158
  • F18.15.05 Shipped

    SSR-prestanda + canonical + hreflang + Lighthouse-budget

    ✅ PL-T158
  • F18.15.06 Shipped

    JSON-LD Organization + WebSite per tenant page

    ✅ PL-T158
  • F18.15.07 Shipped

    Sitemap + robots med tenant-noindex-respekt

    ✅ PL-T158
  • F18.15.08 Shipped

    Skip-to-content + axe-baseline + focus-visible

    ✅ PL-T158
  • F18.15.09 Shipped

    Editorial footer + språkväxlare med session-minne

    ✅ PL-T158
  • F18.15.10 Shipped

    Shared ThemeProvider + HtmlThemeBridge

    ✅ PL-T158

Ministry-report PDF-template-engine [DONE] `PL-T223`

F18.16
Planned

Multi-language PDF-template-resolution för `MinistryImpactReport`. Template

  • F18.16.01 Shipped

    MinistryReportTemplate-config per (tenant_id, funder_type, lang) med pdf_template_ref + required_sections + value_assumptions

    ✅ PL-T223
  • F18.16.02 Shipped

    Template-resolution med fallback-kedja (exact/en/tenant-default/no_template) och audit-warning vid fallback

    ✅ PL-T223
  • F18.16.03 Shipped

    PDF 1.4-wrapper utan extern dependency, deterministisk för testbarhet

    ✅ PL-T223
  • F18.16.04 Shipped

    Multi-lang PDF-export med X-Template-Resolution-header för spårbarhet

    ✅ PL-T223

Club inquiry CMS form-shortcode [DONE] `PL-T222`

F18.17
Planned

Klubbsidor på `web/`-CMS:et exponerar ett intresseanmälnings-formulär som

  • F18.17.01 Shipped

    ClubInquiryFormBlock-komponent på web/, lokaliserade etiketter (en/sv/fr/es), default inquiry_type per shortcode-prop

    ✅ PL-T222
  • F18.17.02 Shipped

    Honeypot-fält (website, off-screen) + Cloudflare-Turnstile-integration via /public/config/turnstile-key

    ✅ PL-T222
  • F18.17.03 Shipped

    Block-registrering i BlockRenderer så CMS-sidor renderar type: "club_inquiry_form"

    ✅ PL-T222
  • F18.17.04 Shipped

    Public POST-endpoint /clubs/{club_id}/inquiries med rate-limit 10/h per IP och 5-min dedup på (email, club_id, subject)

    ✅ PL-T222

Open-Source Continuity Layer [DONE] `PL-T230`

F18.18
Planned

> Spec-källa anger "F18.10 Open-Source Continuity Layer" — F18.10 är dock

  • F18.18.01 Shipped

    Audit-script tools/audit/audit-template-portability.mjs blockerar web/-merges om template/block importerar @/lib/api, @/lib/auth, @/lib/sse, @/lib/booking, next/headers, next/server eller läser process.env.PL_* (whitelist via tools/audit/portability-allowlist.json med obligatorisk reason)

    ✅ PL-T230
  • F18.18.02 Shipped

    Mirror-header-injicering tools/generators/inject-mirror-headers.mjs (idempotent, bevarar "use client"/"use server"-pragmas, två header-typer för portabel resp. allowlistad fil)

    ✅ PL-T230
  • F18.18.03 Shipped

    Sync-script tools/generators/sync-templates-to-oss-repo.mjs med --init (engångs-bootstrap av OSS-repot) + --sync (subtree-push från monorepo); type-only imports till @/lib/api skrivs om till ./types-relativa imports; manifest-snapshot .oss-mirror-manifest.json i OSS-targeten + tools/.state/oss-mirror-state.json i monorepot

    ✅ PL-T230
  • F18.18.04 Shipped

    Sys-endpoint GET /sys/oss-templates/status returnerar manifest-snapshot + GitHub-stats (stars, open issues, latest 5 release-tags) — gate:ad via require_any_sys_role(SYS_SECURITY,SYS_ENGINEER), cache-header private, max-age=300

    ✅ PL-T230
  • F18.18.05 Shipped

    Sys-endpoint POST /sys/oss-templates/sync enqueue:ar mirror-sync som background-job, returnerar job_id (HTTP 202) — gate:ad via require_sys_role(SYS_ENGINEER) + require_fresh_auth(300), sys.oss_templates.sync.enqueue audit-rad

    ✅ PL-T230
  • F18.18.06 Shipped

    Sys-vy views/sys/oss-templates-status (sys/app/(dashboard)/oss-templates-status.tsx) — hero med last-synced-at/commit/by, GitHub-stats-panel, mirrored-files-tabell, skipped-allowlist-lista, "Sync now"-knapp (fresh-auth via lib/api.ts); typed klient sys/src/lib/oss-templates.ts; sidebar-länk under Styrning-gruppen

    ✅ PL-T230
  • F18.18.07 Shipped

    Strategi-doc docs/business/oss-strategy.md (varför MIT, varför separat repo, copyright, risk-bedömning, Continuity Promise-formulering) + arkitektur-doc docs/engineering/architecture/03b-continuity-layer.md (3-lagers-modell, two-mechanism-jämförelse mot PL-T227/T228); pricing-rad i docs/business/business-model.md

    ✅ PL-T230
  • F18.18.08 Shipped

    Allowlist-baserad undantagsmodell tools/audit/portability-allowlist.json (4 type-only @/lib/api-templates + 25 non-portable blocks med per-block reason-fält) som audit-scriptet enforce:ar — saknad reason = audit-fail

    ✅ PL-T230

CMS access control: gated pages per group + grupp-modell [DONE] `PL-T205`

F18.19
Planned

CMS-sidor och enskilda block kan begränsas till tre access-nivåer

  • F18.19.01 Shipped

    CmsAccessGroup-Beanie-dokument tenant-scopad med slug/name/kind/auto_category/auto_entity_id/member_user_ids/member_count/last_synced_at/is_active + uniqueness på (tenant_id, slug)

    ✅ PL-T205
  • F18.19.02 Shipped

    CmsPage utökad med access_level ∈ {public, members, groups}, allowed_group_ids[], preview_for_anonymous: dict[lang, str], block_access per ContentBlock

    ✅ PL-T205
  • F18.19.03 Shipped

    Auto-group-katalog (9 kategorier) seedas via POST /admin/cms-access/seed; per-grupp-sync via POST /admin/cms-access/{id}/sync; bulk-sync via POST /admin/cms-access/sync

    ✅ PL-T205
  • F18.19.04 Shipped

    Custom-grupp-CRUD /tenant/groups med audit-trail (cms.access_group_created/updated/deleted/members_added/member_removed); 409 vid radering av grupp som refereras av page

    ✅ PL-T205
  • F18.19.05 Shipped

    services.cms_access.evaluate_page_access returnerar AccessDecision ∈ {allow, require_login, denied_not_member, denied_not_in_group}; evaluate_block_access är ren (utan I/O) för per-block-gating i SSR

    ✅ PL-T205
  • F18.19.06 Shipped

    Public CMS-endpoints (/public/sites/{id}/pages, /public/sites/{id}/pages/{slug}) filtrerar listor och redacterar block enligt viewer; gated sidor läcker inte i sitemap eller söklistor

    ✅ PL-T205
  • F18.19.07 Shipped

    GET /me/groups med tenant_id/user_id/group_ids[]/is_active_member/hash (sha256 över sorterade group_ids, 32 chars) — SSR cache-key-segment

    ✅ PL-T205
  • F18.19.08 Shipped

    web/-SSR resolveViewerAccess() läser petanque_member_token-cookie + forwardar bearer; getPageBySlug byter till cache: "no-store" när auth-header skickas; AccessGatePlaceholder renderar 3 deny-decisions med i18n-preview

    ✅ PL-T205
  • F18.19.09 Shipped

    Admin-UI: /(dashboard)/cms/access-groups (lista, filter på kind/slug, seed/sync-knappar) + /(dashboard)/cms/access-groups/[id] (detalj — auto read-only, custom edit/add/remove/delete) + /(dashboard)/cms/access-groups/new (skapa custom)

    ✅ PL-T205
  • F18.19.10 Shipped

    42 backend-tester (tests/test_pl_t205_cms_access.py) över modellinvarianter, custom-CRUD, auto-seeding, access-evaluation, user-groups-payload och HTTP-routes

    ✅ PL-T205