fklavyenet / webblocks-cms
WebBlocks CMS
Requires
- php: ^8.3
- laravel/framework: ^13.0
- laravel/tinker: ^3.0
Requires (Dev)
- fakerphp/faker: ^1.23
- laravel/breeze: ^2.4
- laravel/pail: ^1.2.5
- laravel/pint: ^1.27
- mockery/mockery: ^1.6
- nunomaduro/collision: ^8.6
- phpunit/phpunit: ^12.5.12
This package is auto-updated.
Last update: 2026-06-13 07:41:38 UTC
README
A modern block-based CMS
WebBlocks CMS is a Laravel-based, block-driven CMS for managing sites, pages, media, navigation, and editorial publishing from one admin interface. It includes install-level administration for users, updates, backups, site transfer tools, and system settings.
Feature Summary
- block-based page building with reusable layouts, slots, and blocks
- multisite and locale-aware page management
- fixed WebBlocks CMS product identity in admin chrome, with public site identity managed per site
- admin-only Project Name and Project Tagline settings so multiple installs are easier to distinguish without changing public metadata
- install-level admin listing rows-per-page setting so paginated admin listings can use custom defaults such as
10or12without affecting public pagination - dedicated CMS Profile page for each signed-in admin user to manage their own name, email, and password, while install-level user management remains separate for
super_admin,site_admin, andeditoradministration - password visibility toggles on Profile and Users password fields using the pinned WebBlocks UI Password Toggle pattern
- editorial workflow for pages with review and publishing states
- database-backed public site search scoped by site and locale, with a header-triggered modal UX,
/searchfallback page, Search Form block support, and a Maintenance > Search Rebuild screen - page revisions and in-place restore with actor, source, and event metadata when available
- relational page-scoped CSS and JS assets stored in
page_assets, rendered only on the owning public page, and managed from theEdit Page -> Page Management -> Assetstab through compact rows plus Add/Edit modals - media library and site-scoped navigation management
- contact forms that keep a package-standard
websitehoneypot, quietly discard honeypot hits with the normal success redirect, store real submissions first, intentionally quarantine scored spam for admin review with durable status from conservative commercial/link/repeat-IP signals, then attempt synchronous email notification to a block-level recipient, site default recipient,CONTACT_RECIPIENT_EMAIL, or safeMAIL_FROM_ADDRESSfallback, with admin-visible delivery status and failure details for saved messages; future configurable auto-discard thresholds such asCONTACT_SPAM_AUTO_DISCARD_SCOREshould be considered only after observing production data php artisan contact:mail-diagnosereports the resolved Contact Form mail configuration without printing passwords or tokens, can inspect a specific Contact Form block's recipient fallback chain with--block=ID, and can run a controlled SMTP send check with--send-test=address@example.com- admin list action columns follow the WebBlocks UI table-action standard: use an explicit left-aligned
Actionsheader andtd.wb-table-actions > .wb-action-groupfor row controls, while keeping editorial status and delivery/notification failure signals in separate columns - install-level icon catalog management under
System -> Icons, with WebBlocks UI manifest sync, catalog metadata editing, and filtered navigation-only icon pickers in admin forms - static CMS runtime assets with no Vite, Laravel Vite plugin, Tailwind, npm, or Node build-chain requirement; CMS-owned CSS/JS/brand assets ship under
public/cms, and WebBlocks UI is consumed through pinned published CDN/dist assets - plugin system foundations for manually installed plugin ZIP packages, including compatibility metadata, collision guards, package conventions, safe storage-owned install paths, disabled-by-default installed plugins, explicit enable/disable management, safe manual uninstall from
System -> Plugins, and Plugin Catalog admin views for public WebBlocks CMS-compatible catalog metadata, detail pages, artifact/checksum review, controlled ZIP download links, checksum-verified catalog install into the same disabled-by-default plugin flow, and catalog-backed update actions for installed plugins with newer compatible releases - plugin and package-owned admin views must extend
webblocks-cms::layouts.admin; the maintenance repo no longer provides a rootlayouts.admincompatibility alias that can hide package-consumer view namespace mistakes - site-scoped primary domains and alias domains for one-install multi-domain public routing
- primary
Sitesadmin navigation nearDashboard, with site-domain management grouped underSystem -> Domains - install wizard for first-run setup
- system updates with a simple two-card
Install UpdateandUpdate Detailsoperator flow, release-note/readiness/last-run accordions, automatic retained-run pruning, downloadable support reports for shared hosting, site export/import tools with a direct Sites-list export shortcut and compact import review counts, preserved navigation item icons, and package-based Site Promotion workflows - in-app CMS updates now consume package-rooted release ZIPs and replace the transition
packages/webblocks-cms/runtime plus the active Composer autoload package runtime when a consumer still loads WebBlocks CMS fromvendor/fklavyenet/webblocks-cms/..., keeping the install-owned Laravel shell and root overrides intact; the old1.31.53 -> 1.32.33 bridge -> 1.32.34+ package-rootedpath is retired historical compatibility and is no longer part of routine package-native release validation - site-level Branding and SEO Defaults with public
<head>fallback metadata and favicon support, plus locale-aware page-level SEO overrides on page translations - relational site-scoped
site_variableswith controlled{{ site.variable_key }}public token replacement, tabbedEdit Sitesections, and portability through site clone and site export/import - site-scoped Shared Slots that can render reusable block trees publicly inside existing page slot wrappers, can be managed from the admin, can be assigned per page slot from the Edit Page screen, now have dedicated Shared Slot revision history and restore, and participate in site export/import and site clone workflows
- managed install-level Page Layouts with validated public body classes, relational Page Layout Slots, Edit Page layout-slot comparison, and an explicit Add Missing Layout Slots workflow, while pages still store the selected layout handle on
public_shellfor backward compatibility - a system-owned
Navbarprimitive block for reusable public header wrappers, plus composableNavbar BrandandNavbar Navigationblocks that can be placed anywhere inside a Navbar descendant tree, withClusteras the reusable horizontal distribution primitive for navbar rows and other grouped layouts - super-admin global Blocks index under
Pagesfor cross-CMS block maintenance with compactSearch,Site,Page,Block Type,Status, andLocalefilters - Pages index filters and sort state persist across Edit Page, slot editor, translation editor, and save flows so editors can return to the same filtered list context without rebuilding it manually
- the
Block Typesmodal in the slot editor now keeps all block lists inName A-Zorder, limits filtering to search plus tab state, shows a header count badge for the current picker result set, only showsResetafter active search or tab changes, and keeps the search card visible while long result sets scroll inside the modal - Pages listing card headers now include
Import Page, a first-class admin modal workflow for creating one new draft page from a documented single-page JSON payload without using project-specific commands - Pages now include a Page Converter admin foundation for scoped static HTML analysis; it validates pasted or uploaded
.html/.htminput, shows a structured block suggestion plan with confidence scores and warnings, signs the reviewed plan, and can create a new draft page with supported main-slot and safe structured blocks while skipping unsupported media-backed suggestions
Architecture Note
WebBlocks CMS can run as a standalone CMS, and it can also be installed beside another Laravel host product as an optional website and content management layer. Coexistence installs should keep host-owned login and host admin routes separate from CMS-owned authorization and content administration. See docs/coexistence.md for the current architecture direction.
The plugin system treats CMS core as a generic plugin host for product-specific capabilities such as WebBlocks UI release/CDN management, QuizTem integrations, analytics, SEO pro tools, commerce, and custom block packs. The runtime foundation includes the plugin registry, enabled state, compatibility metadata and required CMS version checks, enabled-only admin route and command registration conventions, plugin-owned permission registration, read-only settings page scaffolding, health/status reporting with incompatible-plugin and setup-required messaging, a super-admin System -> Plugins listing/detail/upload/enable/setup/disable/uninstall surface, typed dashboard and system card extension slots, plugin-owned block declaration hooks, safe public asset contribution hooks, package convention/collision guards, and manual plugin ZIP installation. Uploaded plugins install under storage-owned paths and remain disabled until explicitly enabled; disabled plugins are inert and health is not checked while disabled. CMS super admins can access enabled compatible plugin admin routes through the plugin-owned permissions declared by the active plugin, while non-super-admin roles require explicit plugin permission grants. Enabled compatible plugin admin routes keep the CMS /webadmin web, install, host auth, and CMS admin middleware stack before plugin setup and permission checks, so plugin controllers receive the authenticated CMS user after auth/admin access passes. If an enabled plugin declares migrations and its plugin-owned tables are missing, plugin routes must show controlled setup guidance on the plugin URL instead of raw database errors or dashboard redirects, and super admins can run plugin-owned migrations from the plugin detail screen. System -> Plugins -> Browse Plugin Catalog reads public metadata from the Plugin Catalog service, defaulting to https://plugins.webblocksui.com and configurable with WEBBLOCKS_PLUGIN_CATALOG_BASE_URL; catalog detail pages show safe artifact metadata and can install a compatible release only when the catalog provides a controlled download URL, filename, and SHA-256 checksum, including current latest-compatible responses that return release and artifact metadata as sibling fields. Catalog installs download the ZIP server-side, verify the checksum exactly, then pass the package through the existing manual ZIP validator/installer path. System -> Plugins -> Registered Plugins also shows Update available: {version} and a POST-only Update from Catalog action when an installed plugin has a newer compatible published catalog release with complete artifact metadata. Catalog updates re-read catalog metadata, reuse the same checksum and ZIP validation path, replace the installed plugin package version, preserve enabled or disabled lifecycle state, preserve plugin-owned database tables, do not run plugin migrations automatically, clear optimized runtime caches, and refresh plugin registry/permission/route metadata so the active manifest and route controller path match the updated installed package. Plugin menu visibility and plugin route authorization use the same CMS core permission resolver, including the CMS super-admin bypass for active plugin-owned permissions. Known WebBlocks UI Manager release/settings actions are bridged through CMS core so stale manual artifact route context cannot send enabled compatible plugin admin actions back to /webadmin. Generic plugin dashboard/system contribution cards are not rendered on the plugin management page unless an explicit plugin-management extension slot is added. Manual uninstall is available only for uploaded plugins that have been disabled first, removes the storage-owned package directory, and preserves plugin-owned database tables. WebBlocks UI Manager is now an internal/operator plugin artifact, not a public CMS feature or bundled runtime plugin; ordinary CMS installs should not install it. There is still no marketplace, full remote plugin store, automatic plugin updates, arbitrary Composer package installer, automatic third-party download, external production CDN deployment automation, update-server publishing, or CMS core WebBlocks UI URL change. See docs/plugin-system.md.
Installation
Fresh Laravel Package Install
For a fresh Laravel application that consumes the CMS as a package:
composer require fklavyenet/webblocks-cms php artisan webblocks:install --name="Admin User" --email="admin@example.com" --password="secret-password"
webblocks:install now:
- publishes
config/webblocks-cms.phpwhen needed - removes the untouched fresh Laravel welcome route from
routes/web.phpwhen it is safe, with a timestamped backup first, so CMS public routes can serve/ - safely patches
App\Models\Userwith a package auth trait and creates a backup first - runs the package fresh-install CMS schema for clean consumers
- creates required Laravel support tables for CMS password reset tokens and database-backed session/cache drivers when they are configured, without running the host application's normal migrations
- prepares the
site-transfersfilesystem disk storage used by Export / Import packages - prepares the
backupsfilesystem disk storage used by Backup / Restore - installs package CMS assets into
public/cms - creates
public/storagewhen safe and missing - seeds locales, sites, page layouts, slot types, icons, and core block types idempotently
- records the installed version and install completion marker in
system_settings - creates the first active
super_adminwhen one does not already exist
For the current v1.32.x consumer boundary, App\Models\User remains host-owned and is patched in place with the CMS access trait during install.
After install, open:
- login:
/webadmin/loginfor package-owned CMS auth, or the host/loginwhen the host app owns authentication - admin:
/webadmin - public home:
/
The package-owned /webadmin/login screen renders the WebBlocks UI guest auth shell from package views, loads the pinned WebBlocks UI CSS/JS and /cms/css/guest.css, and keeps CMS product brand assets available from /cms/brand. The CMS brand set includes the canonical product mark, dark-surface and on-accent compatibility marks, and dedicated high-contrast favicon/browser-tab assets (logo-mark.svg, logo-mark-dark.svg, logo-mark-on-accent.svg, favicon.svg, and PNG fallbacks). Auth and admin sidebar product chrome use the package-owned inline SVG brand mark component so marks inherit the active accent or surface color without auth-only light/dark image switching, picture markup, img logos, or CSS masks. See CMS Brand Standard.
WebBlocks CMS has no frontend build step. Do not run or add Vite, Tailwind, npm, Node, @vite, public/build, or hot-file workflows for CMS-owned assets. Product assets are tracked directly in root public/cms and package packages/webblocks-cms/public/cms, while WebBlocks UI continues to be consumed from pinned published assets.
The canonical CMS admin prefix is /webadmin. CMS static assets remain under public/cms, so normal /cms/css, /cms/js, and /cms/brand asset URLs continue to be served as static files without colliding with the Laravel admin route tree. CMS admin prefixes must never reuse a physical public asset directory segment: /cms is reserved for static CMS assets only and must not be reintroduced as an admin route, alias, or redirect.
This split is deliberate. Nginx try_files can resolve /cms/ as the physical public/cms/ directory before Laravel sees a route. The final v1.32.56 behavior avoids that collision with /webadmin instead of relying on a public/cms/index.php front-controller handoff. That handoff file must stay absent from both root public/cms/ and package packages/webblocks-cms/public/cms/ assets.
For a fresh install, first get the source code locally:
git clone https://github.com/fklavyenet/webblocks-cms.git
cd webblocks-cms
git remote set-url --push origin DISABLED
If you already created an empty target directory, use git clone https://github.com/fklavyenet/webblocks-cms.git . and then run git remote set-url --push origin DISABLED before continuing. CMS installations consume upstream releases and updates, but they must not publish or push back to the CMS upstream repository.
Native Quick Start
composer install cp .env.example .env php artisan key:generate
For a fresh install with the browser flow, open /install after the source code, dependencies, and local environment are in place. The install wizard guides database setup, environment creation, core install steps, and first super admin creation.
Typical local URLs:
- installer:
/install - public site:
/ - admin:
/webadmin
Trusted Local Development
For a macOS native PHP/Nginx/MySQL or MariaDB/Redis setup, use the HTTPS-only .test guide in docs/native-local-development.md. The canonical native local CMS URL is https://webblocks-cms.test; run composer native:doctor to check readiness without installing services or changing local files, and composer native:smoke after restores or service restarts.
Manual CLI Install
composer install cp .env.example .env php artisan key:generate php artisan migrate php artisan db:seed php artisan storage:link
Then open:
- installer:
http://127.0.0.1:8000/install - public site:
http://127.0.0.1:8000/ - admin:
http://127.0.0.1:8000/webadmin
See docs/installation.md for the complete install guide.
Quick Start
- Install the CMS with the browser wizard or the CLI flow.
- Sign in to
/webadmin. - Create or edit a site if your install uses more than one site.
- Create a page. New pages start as
draft. - If you already have a compatible single-page JSON payload, use
Admin -> Pages -> Import Pageto import one new page into the selected site as draft. This page-scoped workflow is separate from the site-levelExport / Importtool. - Build page structure with
Section,Container,Cluster, orGrid, then addHeader,Plain Text,Rich Text,Button Link,Card,Stat Card, orBreadcrumbblocks inside that layout tree.Plain Textis for plain body copy only.Rich Textis for safe inline formatting such as bold, italic, inline code, links, paragraphs, and simple lists; keep headings, layouts, buttons, media, tables, and raw HTML composition as dedicated first-class blocks or features.Codeis for multi-line escaped snippets and should not be replaced with Rich Text. Rich Text is edited visually in the admin through a small dependency-free safe HTML editor. Stored Rich Text content is a restricted safe HTML fragment, and public output still renders through the shipped WebBlocks UIwb-rich-textprimitive usingwb-rich-text wb-rich-text-readable. Selected description fields still support safe inline code formatting with backticks. UseBreadcrumbfor header and context bars, and usenavigation-autoonly for actual menus.Page Layoutis the admin-facing name for the page-level outer wrapper concept. The stored compatibility field remainspublic_shell, while managed Page Layout records now own optional public body classes plus relational Page Layout Slots that drive slot wrapper resolution from the page layout and slot name so blocks stay focused on content instead of shell markup.Default Layoutis for regular public pages.Docs Layoutmaps header, sidebar, main, and footer regions into the docs-oriented wrapper structure automatically. Shared Slots now render publicly as reusable site-scoped slot block trees inside those existing slot wrappers when the slot source points at a compatible Shared Slot. Shared Slots are dynamic references, not copied templates. Shared Slots can now be created and managed from the top-level admin navigation, including editing the Shared Slot block tree with the same block editor patterns used for page slots. Shared Slots can optionally be constrained to aPage Layout; the stored compatibility field on the Shared Slot remainspublic_shellfor backward compatibility, empty remains generic, and a selected Page Layout requires exact handle matching. On the Edit Page screen, each slot source can now be set toPage Content,Shared Slot, orDisabled. Switching away from page-owned content does not delete existing page-owned blocks, so editors can safely switch back later. For card actions, preferCard > Cluster > Button Link; the legacy single card action fields remain available as a fallback.Cardcan now be used for editable image cards insideGrid. Selected media is optional, selecting media is enough to render the card image, clearing media removes it, blank or legacynoneimage placement falls back totop, the figure renders inside.wb-card-body, shared image placement and alignment stay canonical, image alt text and caption are locale-aware, existing text and action behavior still works, nestedClusterorButton Linkfooter composition remains supported, the legacy single-action fallback remains available, and existing no-image cards stay valid. - For a docs-style context bar, add
BreadcrumbandHeader Actionsto theHeaderslot.Header Actionsrenders system-owned theme utilities such as color mode, theme preset controls, accent controls, and the public search trigger without requiring raw HTML blocks. Public mode and accent behavior uses the shipped WebBlocks UIdata-wb-*runtime, while CMS keeps only the separate public search modal runtime as CMS-owned JS. For a site header or reusable Shared Slot header, useNavbaras the primitive wrapper block, then add child blocks such asContainer,Cluster,Navbar Brand,Navbar Navigation,Header Actions, orSearch Forminside it.Navbarrenders onlynav.wb-navbarand its child blocks, does not add an automatic container, and keepsPositionas its only built-in setting.Navbar BrandandNavbar Navigationmust be placed somewhere inside the Navbar tree, but they do not need to be direct children of Navbar. Header slots are layout-neutral by default and do not forcewb-stack; useContainer,Cluster, orStackinside the slot when you need layout. For horizontal header composition, set theContainerflow toNoneand useClustersettings to control width, distribution, alignment, wrapping, and gap. - For a docs-style sidebar, add a
Sidebarslot and set the pagePage LayouttoDocs Layout, then addSidebar Brand,Sidebar Navigation, andSidebar Footeras top-level sidebar blocks. InsideSidebar Navigation, addSidebar Nav Itemand optionalSidebar Nav Groupblocks, or point the block at the site-scopedDocsnavigation menu so admin-managedAdd Groupsections render as collapsible docs sidebar groups. - Publish the page as a
site_adminorsuper_admin. - Open the public URL or preview link to confirm the live result.
For common editorial choices, Code, Table, TOC, Quote, Hero, Columns, Link List, and CTA are available as first-class block picker options and remain editable from the slot editor. Use Header as the canonical heading or title block, including optional shared anchor IDs for direct links and TOC targets. TOC renders links from explicit anchored Header blocks on the same public page, including nested headers inside layout wrappers such as Section, Container, Grid, Cluster, or Card. Hero and CTA keep promo copy locale-owned while managed child button URLs stay shared. Columns keeps intro copy locale-owned while child Column Item URLs stay shared. Link List keeps intro copy locale-owned while child Link List Item rows require title and URL, with optional meta and optional description. Code, Table, Quote, TOC, and HTML (Trusted) are content-style blocks, not general-purpose container parents: historical child rows are preserved safely, but new arbitrary child placement is not offered and public rendering ignores those child trees. Do not use the legacy Heading block type.
Feature Grid and Feature Item are also now published source-backed block types, but they remain transitional compatibility aliases over the shared Columns card presentation. testimonial and stats still render through Quote or Columns alias paths and are not treated as standalone published core contracts. Image, Gallery, Download, File, Video, and Audio are also shipped first-class block types. They use the same core catalog sync path, expose product-owned admin forms, and render publicly as media-owning blocks rather than arbitrary child containers.
Gallery is now a media-collection block. Editors manage gallery media as compact Gallery Items rows in the block editor instead of the older large selected-assets grid. Gallery selection order and presentation behavior stay shared, while per-item alt text, caption, overlay title, and overlay text are locale-owned through block_gallery_item_translations. The Add Gallery Items media picker mounts as a plain WebBlocks modal under the shared #wb-overlay-root, so the pinned WebBlocks UI runtime stays responsible for stacked modal interactivity without the older CMS-owned wrapper or z-index stack. Public Gallery variants now differentiate clearly: Grid keeps regular equal cells, Masonry uses a staggered CSS-column layout with natural image heights, and Collage keeps the featured-first composition. Fixed-aspect Gallery tiles preserve the full image for screenshot-style media by using centered contain fitting with intentional letterboxing rather than cropping. The compact picker keeps the list-style row UI, defaults Kind to Image, constrains the Gallery picker dialog itself to the viewport, keeps the normal modal body as the only scroll container, renders the search and filter card in its own non-scrolling region directly between the modal header and body, and renders a real compact empty or error state instead of leaving the picker looking stuck when no rows match or media rows fail to render. Gallery no longer owns a public intro heading or paragraph. The old Gallery Title and Description fields are no longer normal Gallery admin fields, and any legacy stored values may still exist in older content but are ignored by public rendering. When a gallery needs section headings or explanatory copy, use Content Header plus Plain Text or Rich Text before the Gallery block.
HTML (Trusted) is also available in the block picker as an advanced escape hatch for super_admin users only. Use it only for deliberate static trusted markup that cannot be represented cleanly with first-class blocks. For normal editorial work, prefer Rich Text for safe formatted copy and Code for escaped code samples. When trusted HTML includes shipped WebBlocks UI gallery viewer modal markup, the public page shell hoists that overlay content into the single shared page-owned #wb-overlay-root so gallery triggers can keep using the shipped viewer contract without rendering duplicate overlay roots.
On the Edit Page screen, Page Management and slot structure are managed separately. Page Management owns Overview, Settings, Assets, and Layout Slots, while the separate Slots card stays focused on direct page slot/source/block operations. Slot additions are available from a compact Add Slot dropdown, and each slot keeps a compact source summary in the list with Manage Source modal settings for Page Content, Shared Slot, or Disabled. Shared Slot choices are limited to active compatible Shared Slots from the same site. When a slot uses a Shared Slot or is Disabled, the page-owned block tree is preserved and clearly labeled as not currently rendered. The Layout Slots tab shows the Page Layout Slots comparison card that reports which Layout Slots are already present as Page Slots, which are missing, which extra Page Slots are being preserved for safety, plus current Disabled and Shared Slot-backed states. The Add Missing Layout Slots action is explicit and safe: it only creates missing Page Slots from the selected Page Layout and never deletes existing Page Slots, blocks, Shared Slot assignments, disabled states, revisions, or translations.
New page creation now uses the selected Page Layout's active managed Page Layout Slots first, with safe legacy fallback behavior only when managed slot definitions are unavailable. Changing a page's Page Layout on normal save does not silently rewrite slots; editors review the updated comparison card and choose Add Missing Layout Slots only when they want to add the newly missing Layout Slots.
Edit Page -> Page Management now groups Overview, Settings, Assets, and Layout Slots into one calmer top-level admin card. Overview owns page status, published context, and workflow actions. Settings owns the editable page form and the only Save Changes and Cancel footer. Assets is an advanced page-scoped feature for local /site/... CSS and JS files only and keeps its own compact asset card with header-owned Add CSS asset and Add JS asset modal actions. Layout Slots owns the Page Layout Slots comparison card and the Add Missing Layout Slots action so layout alignment is managed separately from direct slot editing. In V1, Page Assets are stored relationally in page_assets, not in pages.settings, only super_admin users can change them, CSS renders in the public <head>, JS renders in the public <head> with defer, and the configured files are loaded only on the owning public page. Public block renderers must not emit inline scripts. CMS core assets now live under public/cms/. Site-level overrides live under public/site/{site_handle}/css/site.css and public/site/{site_handle}/js/site.js, while page-level overrides live under public/site/{site_handle}/pages/{page_slug}/page.{css,js}. Site handles are canonical lowercase hyphenated identifiers such as ui-webblocksui-com. public/storage is the Laravel public storage symlink for storage/app/public and is separate from /site/... override assets. Legacy public named JS rows saved with body_end remain backward compatible, but public named JS output is normalized to <head defer>. When site Export / Import runs with Include media files, referenced page asset files and the canonical site-level css/site.css and js/site.js override files are packaged and restored too. Without that option, the page asset rows still transfer but the target install must already have those public files.
In the admin slot block tree, block deletion now uses a WebBlocks modal instead of a browser confirm dialog. The safe default still deletes only the selected block, which preserves the existing child promotion behavior when a wrapper such as Section or Container is removed. Editors can explicitly opt into Also delete all nested child blocks when they want to remove an entire nested subtree. The modal shows the selected block, whether it has children, direct child count, and total descendant count, and warns that recursive deletion is only recoverable through revision or backup restore flows.
Admin -> Maintenance -> Backups, Admin -> Contact Messages, Admin -> Media, Admin -> Pages, Admin -> Export / Import -> Site Exports, and Admin -> Export / Import -> Site Imports support selected bulk deletion for records visible on the current page. Use the leading row checkboxes or the select visible control to select visible records, then open Delete selected to review the count in a WebBlocks confirmation modal before submitting. These endpoints validate selected IDs again, re-check server-side authorization for every selected record, delete only the records that are still allowed, and report partial failures clearly. Media bulk deletion preserves the existing usage guards and skips media that is still referenced by protected CMS content. Pages and site transfer bulk deletion reuse the same cleanup rules as their single-delete flows, including page related-content cleanup and export/import archive removal. Destructive single-delete actions on these screens use CMS modal confirmation patterns instead of browser confirmation dialogs where the listing exposes a row delete action.
On the Edit Site screen, settings are split into Site, Locales, Branding, SEO Defaults, Contact, and Variables tabs. Manage Domains stays a separate header action instead of a body tab or card. Public-facing Branding and SEO Defaults live on the site itself. Use display_name, tagline, favicon, social image, and site-level SEO defaults there for public metadata fallbacks. The Contact tab owns the site default Contact Form recipient, while individual Contact Form blocks can still override it. Locale-aware page-level SEO overrides now live on each page translation, where editors can override title, description, keywords, and Open Graph fields for one locale without changing the fixed WebBlocks CMS admin product identity.
Site Handle uses the canonical filesystem-safe CMS format: lowercase ASCII-safe text with hyphens as the only separator. Creating a new site auto-suggests the handle from Name, but once an admin edits Handle, later name edits do not overwrite it.
Site Variables are stored relationally in site_variables, not JSON. They are intended for simple reusable public text tokens such as support email addresses, repeated product labels, or legal copy. The only supported token format is {{ site.variable_key }} with optional inner whitespace. Unknown tokens, disabled variables, invalid keys, and non-site tokens remain unchanged. Replacement happens only in shared public rendering and public search indexing; admin forms always keep the raw stored token text.
In the admin slot editor, the Edit Slot Blocks list stays structure-focused as a compact one-row-per-block table with block type, a single primary summary, a dedicated children-count column, status, and actions. The Block Picker now opens with a default Common shortcut tab and additional Layout, Content, Navigation, Advanced, and All tabs so larger catalogs stay navigable without one long mixed list. Advanced only appears when the current user has eligible advanced block types such as HTML (Trusted). Search works across the full eligible picker catalog instead of only the active tab, sort still applies within the currently visible tab or search result set, reset returns the picker to the default Common tab, and the modal keeps the same compact shared admin filter toolbar pattern. On narrow screens the table remains one-row-per-block and scrolls horizontally instead of collapsing labels into vertical letter stacks. Full content should be edited in the block edit modal or block edit page instead of being previewed in the slot list.
When a slot already contains blocks, the slot editor header now also exposes Delete All Blocks. This action is scoped to the current page slot or current Shared Slot only, requires explicit confirmation, shows top-level and nested block counts before submit, and records the change through the normal revision history flow.
Pages index list state is now preserved through the main editorial loop. When editors open a page, slot editor, translation form, or page-assets modal from a filtered Pages list, the admin keeps the same safe Pages return URL so Back to Pages and later save redirects return to the same list context.
The slot editor block picker follows the published block catalog directly. Table, TOC, Quote, and Header appear when their catalog rows are published. The legacy Heading catalog row is removed rather than kept hidden, so it does not appear in normal picker or editor availability.
Admin -> System -> Settings now uses separate focused cards for General, Project Identity, Mail, Privacy, and read-only Runtime Information, with section-specific save actions for editable groups. Project Name and Project Tagline are admin-only context labels used in the topbar so teams can distinguish one install from another. Admin browser tab titles are standardized by the shared admin layout as {Page Title} - WebBlocks CMS. Admin listing rows per page controls the default number of rows shown on paginated core admin listing screens, including Media, defaults to 15, accepts custom numeric values such as 10 or 12, and does not affect public pagination. The Mail card lets CMS-owned password reset and system notification mail use either Laravel environment mail configuration or database-backed CMS custom mail settings, and custom mail fields are shown only when CMS custom mail mode is selected. Admin mail settings do not write to .env, do not overwrite environment variables, and fall back to Laravel MAIL_* configuration when CMS custom mail is disabled. Stored mail secrets are masked and diagnostics only report whether sensitive values are configured. Super admins can send a secret-safe test email from Mail diagnostics through the same CMS mail resolver path used by CMS-owned password reset mail, without affecting host/root auth mail or contact form mail. CMS-owned forgot-password mail always builds a /webadmin/reset-password/{token} reset link through the CMS notification path, remains isolated from host/root password reset callbacks, and does not reveal whether a submitted email belongs to a missing or inactive account. These settings do not change the fixed WebBlocks CMS sidebar brand or version footer, and they do not affect public site metadata, favicon, SEO defaults, search scope, locale-aware page metadata, host/root auth mail, or site contact form routing.
Admin index and listing screens such as Block Types, Blocks, Pages, Media, Contact Messages, Users, Sites, Shared Slots, and Backups should use the shared compact listing filter toolbar partial at resources/views/admin/partials/listing-filters.blade.php whenever the screen has real list-changing filters. The contract is: Search stays first on the far left and grows to fill the remaining horizontal space, Site or other context selectors come immediately after Search, then the remaining compact select or input filters follow, and Apply or Reset actions stay right-aligned on the same toolbar row on wide screens. Page headers should stay focused on title, count, and short description or context, while list-creation actions such as New, Add, Upload, Clone, or Create backup should live in the relevant listing card header instead of the page header. Summary or recommendation cards belong above the filter toolbar, so the listing card or table follows immediately after filters. When a listing uses both page-header and card-header count badges, the page-header badge must show the total record count in the screen's base scope while ignoring active list filters, and the listing-card badge must show the filtered result count for the currently visible list. Admin list pagination should use the shared admin.partials.pagination partial, and dense admin listings should enable its compact mode so the page links and compact summary render together in one row using the from-to/total format instead of a separate verbose summary line. Bulk listing actions should start with page-visible selection only: use a leading checkbox column or visible card checkbox, a select all visible control, a compact selected-count action bar, and a destructive WebBlocks confirmation modal that posts selected IDs to a server-side-validated endpoint. The current bulk action standard does not select all filtered records across pagination; each selected record must still pass server-side authorization and domain-specific safety checks, and mixed success should return partial-success feedback. The super-admin-only Blocks index is linked directly from the main sidebar under Pages and is intended for cross-site block maintenance, especially after imports or other bulk content changes. Its first compact filter set is limited to Search, Site, Page, Block Type, Status, and Locale. On the Pages index, the row-level Page Details action opens the standard admin modal pattern with grouped Page and Status & Audit cards and keeps only Edit Page plus Open Public Page when a public URL exists. Page Details also shows system-managed audit attribution for who created, last edited, published, archived, or submitted the page for review when that metadata exists; older, deleted-user, console, and imported records safely show Not recorded instead of guessing a user.
Admin -> Media now uses Media consistently as both the admin-facing and canonical active internal concept. Legacy Asset names remain only in compatibility wrappers, historical migrations, and legacy payload normalization paths. The Media Library list keeps preview as the eye-icon modal action, but the media title and pencil icon both open Edit Media: {title} with a safe return URL back to the current filtered list. Its compact filter toolbar keeps Search, Kind, and Usage, and now also supports Sort by plus Direction so editors can safely reorder the shared media list by updated date, created date, title, filename, kind, folder, or real usage count without leaving the standard one-row admin listing pattern. On the edit screen, Preview and Usage remain visible as read-only context cards, Media Information owns editable metadata and folder assignment, read-only File Details are available from a modal with the copy-public-URL affordance, and delete remains available from Media list actions.
On Admin -> System -> Block Types, the compact filters now separate metadata from live usage. Support filters capability and content-source metadata such as system-generated vs user-authored behavior plus admin, render, or container support, while Usage filters actual block usage counts so admins can compare used and unused block type rows on an install. The index pencil action now follows the shared query-driven admin modal pattern, so install-specific block types open Edit Block Type: {Name} in a modal and save back to the same filtered or paginated list context. The direct edit route remains available as a no-JavaScript fallback and uses the same named heading. The same index now also exposes a read-only Block Type Contract modal per row so admins can inspect shipped contract details without editing schema, storage, translation ownership, or renderer behavior.
The Admin -> Navigation screen now uses the same query-driven modal pattern as newer admin screens instead of the old drawer. Add Item opens a modal for normal page or custom URL links. Add Group opens the same modal workflow preconfigured for a collapsible parent section. Parent Group only lists existing group items from the same site menu, so editors can clearly build docs-style sections such as Patterns -> Overview / Dashboard Shell / Settings Shell. Site and menu selection now live in the shared compact admin filter bar, while Add Item and Add Group stay with the selected navigation card context. Navigation item icons persist for normal links and groups, including group edits reopened through the edit modal. Public Sidebar Navigation blocks that read a selected navigation menu render those groups with the shipped wb-nav-group contract, keep normal links unchanged, automatically open a parent group when one of its child items is active, and load the CMS sidebar-navigation asset so those groups expand and collapse correctly on click.
Admin -> System -> Icons manages the install-level icon catalog used by admin pickers. The CMS stores labels, active state, sort order, categories, contexts, and keywords, while WebBlocks UI remains the source of shipped icon CSS classes and the manifest. This CMS release pins WebBlocks UI CDN assets to v2.7.12 for stable runtime behavior, using the canonical jsDelivr tag URL format and the standard dist artifacts webblocks-ui.css, webblocks-icons.css, and webblocks-ui.js while minification hardening is deferred. The default icon sync source now matches that pinned release: https://cdn.jsdelivr.net/gh/fklavyenet/webblocks-ui@v2.7.12/packages/webblocks/dist/webblocks-icons.json. Browser-facing CMS CSS and JavaScript must not use the raw.githubusercontent.com fallback because Chrome can block those responses through ORB or MIME handling. Sync icons with php artisan icons:sync-webblocks-ui, or override the source with php artisan icons:sync-webblocks-ui --manifest=/path/or/url/webblocks-icons.json for local development, testing, or a different pinned URL. Navigation item and Sidebar Navigation icon selectors intentionally show only active catalog rows tagged for the navigation context; the full catalog remains on System -> Icons. Custom SVG upload or project-specific icon generation is not part of CMS core.
Admin overlays continue to use the shared #wb-overlay-root. With the pinned WebBlocks UI v2.7.12 runtime, stacked admin modals and pickers follow the shared overlay stack contract: nested overlays render above parent overlays, WebBlocks UI owns backdrop visibility, z-index, pointer lifecycle, Escape or outside-click behavior, top-right toast lifecycle, and topmost interactivity, normal close controls still work through titlebar close or dismiss buttons, dirty admin forms still intercept wb:overlay:close-request only when closing would discard unsaved changes, and those dirty-close flows now use a stacked WebBlocks confirmation modal with Keep editing and Close without saving actions instead of the browser confirm dialog. The CMS no longer hides, reorders, or pointer-manages runtime-owned dialog backdrops.
The Gallery block editor keeps its current stacked overlay flow, but Add Gallery Items now uses a compact media picker result list instead of large cards. Results stay under #wb-overlay-root, open as a normal WebBlocks UI modal without CMS-owned shell sizing or surface overrides, keep search plus folder and kind filters, default Kind to Image, constrain the Gallery picker dialog to the viewport so its header and footer stay visible, keep the modal body as the only scroll container, place the filter card in a dedicated non-scrolling region between the modal header and scrollable body, show a small thumbnail with title, filename, folder, kind, and Select in one row, keep selected rows visibly selected, preserve the existing Add Selected flow back into the Gallery item table, keep natural compact row height even in long result lists, and show a real compact empty or error state instead of lingering placeholder rows.
See docs/getting-started.md for the first-use workflow.
Multisite Domains
- Public host resolution now prefers active
site_domainsrecords, then falls back to the legacysites.domainvalue when needed, and only uses unknown-host fallback behavior wherecms.multisite.unknown_host_fallbackis intentionally enabled for local or compatibility scenarios. - In the admin sidebar,
Sitesis a primary area directly underDashboard, whileSystem -> Domainsowns public host and domain resolution management for sites. - The site Domains screen keeps
Add Domainas a modal action in theAssigned Domainscard header. Assigned domain rows use compact action icons, and domain status, alias redirect behavior, primary-domain changes, and removal are managed through modal workflows instead of inline table forms. System -> Domainsopens the current site's domain screen when the admin already has a site context. If no current site context is available across multiple accessible sites, it shows a compact site list withManage Domainsactions.- In production, an unknown host should not silently render the default site. Point only the domains and subdomains you intend to serve at the CMS install.
- Each site can have one primary domain plus additional active alias domains. Canonical public URLs use the site's primary domain.
- Domain values are normalized lowercase hostnames only. Do not store protocols, paths, or query strings.
- Herne Panel or your server operator must own DNS, SSL, Nginx or virtual-host setup, and inbound server routing before a host reaches the CMS.
- WebBlocks CMS Domains only map an incoming host to a CMS Site, choose the primary canonical host, and optionally redirect alias hosts to that primary domain.
- Site export and import packages include domain metadata for inspection and portability, but imports skip conflicting live domains instead of taking them over automatically.
- Site clone clears copied live domains by default. Provide an explicit
target_domainonly when the clone should claim a new hostname. - Internal domain automation endpoints live under
/admin-api/*and are disabled unlessWEBBLOCKS_CMS_INTERNAL_API_TOKENis configured. Requests must sendX-WebBlocks-Internal-Token: <token>.
Site Promotion
Site Promotion is the controlled one-way workflow for promoting site-owned content from a package into an existing target site.
-
Admin -> Sitesnow includes a per-siteExportrow action that opens a modal, shows the selected site name and handle, and can include media or assets without leaving the Sites list. -
Admin -> Maintenance -> Export / Importnow shows export history and import history together on one operational screen, withRun ExportandRun Importactions in their listing card headers. -
Admin -> Sites -> Promotestays focused on applying an existing export or promotion package into a selected target site with dry run, safety backup, strategy selection, and preserve rules. -
Admin path:
Admin -> Sites -> Promote -
V1 is package-based only and super-admin-only
-
it is not raw database replication
-
it is not a replacement for CMS core updates
-
it can promote safe site identity fields, locale assignments, site variables, pages, page translations, page SEO fields, page slots, Shared Slots, Shared Slot block trees, navigation with optional item icon slugs, page assets, and optional physical media or
/site/...public files -
it preserves install-level and runtime data such as users, sessions, jobs, backups, update history, visitor reports, contact submissions, live domains, environment configuration, internal tokens, and derived search rows
-
dry run is required before apply
-
apply creates a safety backup before content changes
-
apply rebuilds the target site's derived public search index after promotion
Supported strategies:
additive_update: create missing source content and update matching target content without removing extra target contentmirror: create and update matching source content, then archive, deactivate, or remove absent target site-owned content where safe in V1
Typical use cases:
- local to staging delivery
- staging to live site-owned content promotion
- controlled content rollout between installs that must preserve runtime or environment-specific target data
CLI examples:
php artisan site-promotion:inspect storage/app/site-promotions/example.zip php artisan site-promotion:dry-run storage/app/site-promotions/example.zip --target-site=ui-webblocksui-com --strategy=additive_update php artisan site-promotion:apply storage/app/site-promotions/example.zip --target-site=ui-webblocksui-com --strategy=additive_update
Documentation
- See the full documentation in the
docs/directory: AGENTS.mdis the compact AI and project working contract for repository-specific implementation rules.DEVELOPMENT.mddefines the development and release workflow.docs/updates.mdkeeps the retired1.31.53root-managed bridge history for reference, but current update validation focuses on package-rooted release artifacts and package-native updater behavior.php artisan webblocks:package-statusreports the current package-transition resource boundary in a strictly read-only way and does not publish, migrate, clear cache, or mutate install state.php artisan webblocks:package-status --view-checkadditionally renders the internal package diagnostic Blade view through thewebblocks-cms::namespace to verify package view loading without changing runtime ownership. The command now reports package source authority, the deliberately minimal root app shell, root Blade/seeder/runtime asset compatibility paths, root migration/update/install/auth blockers, and package-owned CMS source authority.- Package transition consolidation is complete for all safely movable CMS-owned source in the in-repo package boundary, and the maintenance repo root
app/has been reduced toward the real Composer consumer shape. Package-counterpart root controllers, requests, mailables, models, commands, and support wrappers are intentionally absent; the remaining root app files are host-owned auth/install/project-layer pieces, install redirect middleware,App\Models\User,App\Support\WebBlocks, service providers, and narrow legacy asset transition shims. - Package
public/cms/now also carries admin CSS, JavaScript, and CMS-owned brand source copies that match the current root runtime admin asset set. The active admin layout still links to rootpublic/cms/...URLs, and package install, publish, and System Update flows keep those compatibility paths populated from the package source. - Admin JavaScript must stay page-scoped. The package admin layout loads only pinned WebBlocks UI JavaScript and shared
public/cms/js/admin/core.jsglobally, then renders theadmin-scriptsstack near the shared#wb-overlay-root. Feature behavior such as asset picking, media copy, sortable builders, page-builder modals, slot deletion, slot source modals, page assets, gallery item editing, rich text editing, and password toggles must be pushed by the views or partials that render the matching UI. CMS-owned admin JavaScript remains static underpublic/cms/js/admin/andpackages/webblocks-cms/public/cms/js/admin/; do not introduce Vite, npm, Tailwind,public/build, hot files, or another frontend build chain for CMS admin assets. docs/package-transition-admin-shell-assets-audit.mdmaps the remaining admin asset and brand ownership boundary after the admin layout and shared admin partial package moves..editorconfigandpint.jsondefine the repository formatting standards.- PHP files use 2-space indentation in this repository.
composer format:testchecks Pint formatting and runsscripts/check-php-indentation.php, whilecomposer formatapplies Pint fixes.- Package transition controls live in package
config/webblocks-cms.php: diagnostics, public status routes, admin status routes, and package migration loading stay disabled by default. Package admin and public runtime route loading are enabled so package-owned CMS admin and public route files can be authoritative while their reserved package status routes remain separately guard-disabled. - During the current runtime-authority step, active CMS admin routes now load from package
routes/admin.phpunder/webadmin, including the CMS-owned Profile page. Active CMS public routes now load from packageroutes/public.php, and rootroutes/web.phpis reduced to install, auth, and compatibility loading for package-owned CMS route trees. - The public entry slice for page rendering, search, contact form submission, and privacy-consent sync now uses package-owned controllers, requests, and package namespace entry views without root
App\Http\...wrapper classes. - The public model foundation now includes
Block,ContactMessage,Locale,Page,PageSlot,PageTranslation,PublicSearchIndex,Site,SiteDomain,SystemSetting, andVisitorEventin packagesrc/Models/without rootApp\Models\...wrappers.Userremains app-owned and root-owned. - During the icon catalog package transition, active admin routes,
icons:sync-webblocks-ui, and the icon catalog admin views now use package-owned controller, command, and Blade resources directly. Root icon catalog PHP wrappers are intentionally absent; root Blade view compatibility remains where direct view references still need it. - The remaining safe operational admin route batch now also runs from the package for
Slot TypesandSystem Settings, without root controller or package-counterpart request wrappers. Root Blade compatibility wrappers remain for direct view references. - Package public assets and starter stubs now have real publishable package resources. Use
webblocks-cms-assetsfor package assets andwebblocks-cms-stubsfor starter stubs; CMS package assets publish into the active rootpublic/cmscompatibility path, and System Update refreshes that path from packagepublic/cmsassets while package roots are replaced. - The indentation guard now scans the maintained source, package, route, view, script, and test roots. Pint's indentation-specific fixers are disabled so the project-specific 2-space PHP indentation rule remains authoritative.
- Installation
- Getting Started
- Core Concepts
- Users And Permissions
- Editorial Workflow
- Revisions
- Operations
- Search
- Updates
- Multisite
- Localization
- Public Assets
- Page Layouts
- Page Converter Roadmap
- Package Architecture Transition
- Block Type Contracts
- Renderer Contracts
- Development Workflow
Project Layer
- Project-specific console commands belong under
project/. - Install-specific code should stay outside CMS core and inside
project/. - Release packages exclude
project/so shipped artifacts contain reusable CMS product code only. - Website-specific sync, import, migration, and seed workflows must not be added to CMS core.
- CMS core stays generic; native export/import-style payloads remain the portability mechanism for website content.
- Default local preview host:
webblocks-cms.test. - WebBlocks CMS blocks destructive database reset commands in normal local, development, and production environments. The guard blocks
migrate:fresh,migrate:reset,migrate:refresh, anddb:wipe, including normal console execution andArtisan::call(...)paths where Laravel emits the command start event. - Use
WEBBLOCKS_ALLOW_DESTRUCTIVE_DB_COMMANDS=trueonly when you intentionally need to bypass that safety guard.
Developer Notes
- Repair shipped CMS catalogs on an existing install with
php artisan webblocks:catalog-repair --dry-run --all, thenphp artisan webblocks:catalog-repair --allafter reviewing the report. The command supports--block-types,--slot-types,--page-layouts, and--icons, and preserves install-specific custom catalog rows. The lower-levelphp artisan block-types:sync-corecommand remains available for block type compatibility maintenance. - In-app System Updates apply published release packages, run required update migrations, clear caches, verify the applied WebBlocks CMS code version against the target release, record the update run, prune retained run records, and persist the installed version. The screen defaults to
Install UpdateandUpdate Details;Update Detailskeeps release notes, install readiness, and the last run summary behind accordions, with a safe support report download for shared-hosting operators. They do not automatically run core catalog seeding,block-types:sync-core, icon sync, slot type repair, page layout slot repair, or broad catalog repair. Package-native fresh Composer consumers do not run host Laravel application migrations during System Update; only dedicated package update migrations are considered. - Public CMS installations are update consumers, not upstream publishers. Keep
originfetchable if you want local git visibility, but disable push on installation working copies withgit remote set-url --push origin DISABLED. Create CMS releases only from the real maintenance checkout, not from an installed site working copy. BlockTypeSeederstill uses the shared core block type sync path for fresh installs and keeps the existing legacy compatibility behavior, including drafting non-core rows and removing the legacyheadingcatalog row only when no live published blocks still reference it.docs/block-type-contracts.mddocuments the Phase 1 inventory, Phase 2 read-only admin visibility, and the Phase 3 contract-alignment fixes for currently published core block type contracts, including the shipped media and visual block setimage,gallery,download,file,video, andaudio, plus the Layout + Card groupsection,container,grid,cluster,card, andcontent_header.php artisan block-types:contracts-auditnow exposes the same shipped contract detail in markdown and JSON that the admin contract modal reads from the shared registry.- Gallery item translation rows now participate in page revisions, Shared Slot revisions, site clone, site export/import, site promotion, and safe site deletion. Site export packages also now include
data/block_gallery_item_translations.json. Navbarkeeps the persistedsticky-navbarhandle for compatibility, but it now behaves as a primitive system navigation container. It renders onlynav.wb-navbarand nested child blocks, keeps onlyPosition(static,sticky,fixed) as a built-in setting, and does not add an automaticContainer, brand wrapper, or generated menu markup.Block::ownsPublicRoot()now treats the persistedsticky-navbarslug as root-owning so Navbar output uses its real<nav class="wb-navbar">renderer root without an extra generic public wrapper.Block::ownsPublicRoot()also continues to treatsection,container,grid,cluster,card, andcontent_headeras root-owning so those renderers keepdata-wb-public-block-typeon their real public root instead of receiving a genericwb-public-blockwrapper.Navbar BrandandSidebar Brandnow both use the same conservative shared URL contract: explicit saved brand URL first, otherwise the resolved site public home path when available, then/as the final safe fallback. Both support logo-only rendering when a logo exists, and their accessibility-only label stays shared so logo-only output still has a safe accessible name without forcing visible text.Page Layoutis now a managed install-level CMS concept underAdmin -> System -> Page Layouts. Pages still store the selected layout handle onpublic_shellfor backward compatibility, while the runtime resolves that handle safely through the managed Page Layout catalog. Built-inDefault LayoutandDocs Layoutremain backward compatible with existing pages, imports, exports, Shared Slots, and public rendering. Page Layouts now exposeBody Classplus managedPage Layout Slotsin the admin, while deprecated compatibility fields such asshell_type,slot_schema, andwrapper_schemastay out of the admin UI. Body Class is intended for layout-specific CSS on the public<body>, such aslayout-defaultorlayout-docs, and Page Layout Slots own the public wrapper element, id, and classes for each region. When a default public header slot contains only a Navbar block, CMS promotes the slot wrapper to thenav.wb-navbarroot so shipped WebBlocks UI sticky behavior works without site-specific sticky CSS. Advanced trusted layout HTML is limited to wrapper-adjacent layout markup only and must not be used for scripts. Edit Page compares current Page Slots against the selected Page Layout's managed Layout Slots and offers a safeAdd Missing Layout Slotsaction that creates only missing slots. Custom V1 Page Layouts still reuse the existingdefaultordocsruntime behavior conservatively, and Shared Slot compatibility remains conservative and exact by stored handle.- Header slot wrappers are layout-neutral by default in the public shell. CMS does not force
wb-stackaround header content, so header composition should come from blocks inside the slot such asContainer,Cluster, orStack. Containeris a width primitive first. Legacy containers still render stacked flow by default for compatibility, but editors can now set ContainerFlowtoNonewhen they need layout-neutralwb-containermarkup and compose spacing or alignment with child layout blocks instead.Section,Container,Grid, andClusterremain layout primitives: shared layout settings and child structure stay canonical, while user-facing copy stays out of arbitrary settings JSON.Cardis now a composable shell block: it owns thearticle.wb-cardroot, keeps only shared shell metadata such assettings.layout_name, and only acceptsCard Header,Card Body, andCard Footeras direct children. Those region blocks are addable only insideCard, own their ownwb-card-header,wb-card-body, andwb-card-footerroots, and can contain normal nested editorial blocks without allowing nested card-region recursion. Older saved Card rows still render through a minimal legacy fallback only when no Card region children exist.Content Headerkeeps translated heading, intro, and meta copy, always renders its title as H1, and keeps only alignment shared.- Legacy or transitional slugs such as
tabs,slider,menu,faq-list,showcase-list, andcontact-infoare not part of the published core block contract set unless the shipped core catalog says so. Existing compatibility renderers remain supported conservatively, but new product work should prefer the documented first-class blocks instead of expanding those legacy paths. Navbar BrandandNavbar Navigationare the intended composable blocks for reusable header composition. They must be placed somewhere inside aNavbartree, but they can sit under nested layout wrappers such asContainerorClusterinstead of only as direct Navbar children.Containerowns width,Clusterowns horizontal distribution, andStackowns vertical flow. A recommended navbar pattern isNavbar -> Container (Flow: None) -> Cluster (Width: Full, Justify: Between, Align: Center, Wrap: Nowrap) -> Navbar Brand + Cluster (Justify: End, Align: Center, Wrap: Nowrap) -> Navbar Navigation + Header Actions.Navbar Navigationreads relationalnavigation_itemsrows from the selected CMS Navigation menu, keeps the desktop links visible in-place, and now renders a mobile burger toggle that opens the same menu content through the shipped WebBlocks UI dropdown contract.Navbar Brandrenders logo and optional text using the shipped WebBlocks UI navbar classes, and now supports logo-only usage when a logo is present.Sidebar Navigationkeeps the shipped WebBlocks UIwb-sidebar-linkandwb-nav-group-itemcontracts. ManualSidebar Nav Groupchildren now render through the sameSidebar Nav Itemsemantics for href, target, icon, and active-state resolution instead of maintaining a separate nested-link drift path.- Navbar styling should come from WebBlocks UI classes. CMS intentionally avoids creating a parallel navbar design system; where additional navbar primitives are needed, that work belongs in WebBlocks UI.
- Sticky navbar ownership is intentionally clean now: the page layout and header slot wrapper own page-level structure, while
.wb-navbarowns sticky navbar behavior. CMS no longer emitswb-cms-navbar--stickyon either the header wrapper or the navbar. - Header-to-main spacing belongs to the public shell wrapper, not the Navbar block. Keep Navbar primitive and adjust shell rhythm on the header or main slot boundary instead of adding navbar-specific margins.
- In the admin layout, the mobile or narrow sidebar uses the standard WebBlocks UI sidebar contract, including a shell-local
data-wb-sidebar-backdrop, so outside clicks close the sidebar without inline Blade scripts. - Admin chrome product identity is fixed to
WebBlocks CMS,A modern block-based CMS, andWebBlocks CMS v{VERSION}fromWebBlocks\Cms\Support\WebBlocks. The sidebar footer centers the version label with the WebBlocks UIwb-text-centerutility. System Settings and editable site fields do not change those labels. - Admin form actions belong in the owning card or modal footer, not a separate action-only card. Card footers and modal footers use the same placement: primary action first, cancel or secondary action second, and delete or destructive action last in a separate end-aligned danger group when present. Non-destructive footer actions start on the left, and page headers stay focused on navigation and context actions instead of form submit actions.
- Public rendering ownership is split intentionally: page controls the outer shell (
defaultordocs), slot name controls the public region wrapper semantics, and blocks render content inside those slot wrappers. Headeris the canonical heading block. Its shared anchor is stored explicitly and reused for publicidoutput plus same-pageTOClinks;TOCdoes not parse rendered HTML and does not depend on legacyheadingblocks.- Site-level public metadata now comes from the currently resolved site.
display_name,tagline, favicon, social image, and SEO Defaults provide public<head>fallbacks by site and host context. - Page-level SEO overrides live on
page_translations, stay locale-aware, and affect public<head>metadata plus social metadata only. - Metadata precedence is now: site label plus page translation SEO/title for the public title, page translation SEO override for description and keywords where applicable, site SEO defaults, then safe CMS fallback when no site or page context exists.
- Public page titles now default to
Site Label ยท Page Label, using sitedisplay_name, then siteseo_title, then sitenamefor site context. - Public search modal copy now names the resolved site being searched when a site label is available.
- Page-level SEO does not change CMS admin product identity, does not replace page body content, and is not treated as public search body content by default.
defaultuses standard semantic wrappers such asheader,main,aside, andfooter.docsautomatically maps header, sidebar, and main slots to the docs navbar, sidebar, and main wrappers.- Existing pages remain site-scoped after creation. The normal
Edit Pageform shows the current site as read-only context and does not move pages between sites. - Existing pages can also be copied through a dedicated
Duplicate pageadmin action. Duplicate creates a new page and leaves the source page unchanged. - Cross-site page moves are handled through a dedicated
Move to another siteadmin action on the Edit Page screen. The normalSave Changespath still cannot changesite_iddirectly. super_admincan duplicate pages into any site.site_adminandeditorcan duplicate only when they have access to both the source site and the target site.super_admincan move pages between any sites.site_admincan move pages only when they have access to both the source site and the target site.editorcannot move pages between sites.- Duplicated pages always start as
draft, even when the source page is published or in review. - The duplicate workflow copies the page layout, public shell settings, translations, page slots, page-owned block tree, nested block order, block translations, and existing media references.
- Duplicate does not copy navigation items, revision history, visitor/reporting rows, contact messages, or site transfer history.
- Duplicate can target the same site or another accessible site. Every copied locale must have a unique target path; conflicts block the duplicate instead of auto-renaming slugs.
- The duplicate screen collects the new default-locale title and slug plus explicit title/slug values for every additional copied locale so multilingual duplicates can be validated before writing.
- The duplicate screen now includes a Shared Slot compatibility summary for the currently selected target site whenever the source page uses Shared Slots.
- Shared Slots are site-scoped. Same-site duplicates keep existing
shared_slot_idreferences as-is. - Cross-site duplicate remaps only compatible same-handle Shared Slots from the target site. It never keeps a cross-site
shared_slot_id. - Missing or incompatible target Shared Slots still block cross-site duplication by default.
- The duplicate form now exposes
Disable incompatible Shared Slot-backed slots on the duplicated pagewhen relevant. When selected, only the duplicated page's affected Shared Slot-backed slots are written assource_type = disabledwithshared_slot_id = null. - The duplicate fallback does not create Shared Slots automatically and does not duplicate Shared Slot block trees into the page in this version.
- The page move workflow preserves the page id, workflow state, layout, translations, slots, page-owned blocks, block translations, ordering, and page revisions. It does not duplicate the page or block tree.
- Target-site path conflicts block the move. The first version does not auto-rename slugs or paths to resolve conflicts.
- Shared Slot references must already be compatible on the target site. Matching same-handle Shared Slots are remapped when they satisfy the existing site, shell, active-state, and slot-name compatibility rules. Missing or incompatible target Shared Slots block the move.
- Duplicate keeps same-site Shared Slot references as-is. Cross-site duplicate remaps only to compatible same-handle Shared Slots on the target site; missing or incompatible Shared Slots block the duplicate.
- Page-linked navigation rows keep the moved page link valid by moving those linked items into the target site scope, but broader navigation review remains manual after the move.
- Export / Import and Site Clone remain the site-level portability tools. They preserve page-level
Page Layoutsettings such asDocs Layoutalongside pages, slots, blocks, Shared Slots, and Shared Slot-backed page slot assignments.Move to another sitechanges ownership of one existing page in place, whileDuplicate pagecreates a new page copy inside the same CMS install. - Shared Slots participate only at the slot-content layer. When a page slot source is
shared_slot, the referenced Shared Slot block tree renders inside the resolved page slot wrapper if site scope matches and any optional Shared Slot shell or slot-name constraints are compatible. Invalid or cross-site shared-slot references render no shared content. - The page editor now owns slot source assignment. Editors who can edit the page in its current workflow state can switch a slot between
page,shared_slot, anddisabled. Selecting a Shared Slot requires the same site, active status, and compatibility with the page shell and slot name. - Shared Slots are managed under the site-level admin navigation alongside Pages, Navigation, and Media.
super_admincan manage Shared Slots for all sites,site_admincan manage Shared Slots for assigned sites, and editors can access Shared Slot block editing within assigned sites using the same draft-only content-editing rule used for page content. - Shared Slots now have their own revision history separate from page revisions. Shared Slot revisions capture Shared Slot metadata plus the reusable block tree behind the Shared Slot. Restoring one keeps the same Shared Slot id and existing
page_slots.shared_slot_idreferences, so the restored Shared Slot immediately affects every page that uses it. - Page records now store nullable audit references for
created_by_user_id,updated_by_user_id,published_by_user_id,archived_by_user_id, andreview_requested_by_user_id. These are system-managed audit fields, not editor-facing form inputs. - Page revisions now store nullable actor plus compact
sourceandeventmetadata so revision history can distinguish normal admin edits from console, restore, or project-import style workflows when callers provide that context. - Shared Slot revisions follow the same actor plus
sourceandeventpattern, and Shared Slots themselves now store nullable created/updated audit user references. - Deleting a referenced user does not delete pages, Shared Slots, or revisions. Audit foreign keys use null-on-delete, and admin UI falls back to
Not recordedfor deleted or unknown actors. - Console or project import workflows do not fake authenticated users. They can leave audit user ids null and use workflow-specific sources such as
consoleorproject_importwhere available. - Shared Slot revision viewing follows the same site-scoped access model as page revisions:
super_adminandsite_admincan view and restore within allowed sites, while editors can view but not restore. - Shared Slot deletion is guarded. If any
page_slots.shared_slot_idstill references a Shared Slot, deletion is blocked and references remain intact. - Shared Slots now travel with site portability tools. Site export/import packages include Shared Slot metadata plus the hidden internal source-page block tree, translations, and media references needed to rebuild the reusable Shared Slot in the target site. Page slots that use Shared Slots are exported by Shared Slot handle and remapped to the target-site Shared Slot during import or clone instead of keeping source database IDs.
- Shared Slot revision history does not travel with export/import or site clone. That matches the current page-revision portability boundary and keeps site transfer packages focused on live site content instead of editorial history.
- Hidden Shared Slot source pages remain an internal implementation detail. They are excluded from ordinary page admin listings, ordinary site page totals, normal exported page payloads, and public route resolution even though their block records are still used internally to preserve the existing block editor, translation, and asset flows.
- Search V1 is a core CMS feature backed by the derived
public_search_indextable. It indexes only published public pages, scopes rows by site and locale, includes compatible Shared Slot content in the consuming page record, and excludes hidden Shared Slot source pages from standalone results. - Public search now uses a modal-first enhanced UX from the
Header Actionssearch trigger when JavaScript is available, while/search?q=termand/{locale}/search?q=termremain the fallback and direct-link routes. - Public modal results load from the locale-aware JSON endpoints
/search.json?q=termand/{locale}/search.json?q=term. - The first-class
Search Formblock renders a semantic GET search form that targets the current site's resolved search route and stores translated label, placeholder, and button text in block translation rows. - Super admins can review derived search status and run a non-destructive rebuild from
Admin -> Maintenance -> Search Rebuild. Admin -> System -> Settingsis the compact install-level settings screen for locale, timezone, admin project identity, CMS-owned mail settings, privacy, version, and environment information.Admin -> System -> Visitor Reportsreports anonymous page views across the allowed site scope while keeping richer breakdowns privacy-safe.Maintenanceremains reserved for operational tools such as Search Rebuild, Backups, Export / Import, and Update.- Backup archives are resolved only through the CMS
backupsdisk root, which defaults tostorage/app/backups. Download, detail, and delete actions use the same resolver, block traversal or absolute paths outside that root, and show controlled missing or unreadable archive feedback instead of raw filesystem errors. The runtime web/PHP user must own or be in a group that can read and write that storage root; do not use777permissions. Admin -> System -> Visitor Reportsstores normalized referrer hosts and internal/direct/external type, normalized UTM source/medium/campaign values, device category, and bot flag only; it does not store raw referrer URLs, full query strings, full user-agent strings, or raw IP addresses. Unique visitors, sessions, and average pages per session are shown only when consent-based session identifiers exist; otherwise the report displaysNot trackedinstead of a misleading zero. The screen also includes human/bot page-view totals, All/Human/Bots traffic filtering, referrer counts, device shares, and campaign/source/medium breakdowns when aggregate data is available.- Rebuild the derived search index safely with
php artisan search:rebuild, optionally scoped by--site,--locale, or--page. - If the search table does not exist yet on an install, run
php artisan migratebeforephp artisan search:rebuild. - Search index rows are derived runtime data. They can exist in environment-level backups, but they are not required content payloads for export/import portability because rebuild can recreate them from pages, blocks, translations, and Shared Slots.
- Destructive database reset commands remain guarded. Search rebuild does not require
migrate:fresh,migrate:refresh,migrate:reset, ordb:wipe. - Generic public block wrappers are only for simple non-root-owning content blocks. Layout/root-owning blocks such as
Section,Container,Grid,Cluster,Card,Header, andContent Headerown their real WebBlocks UI root markup and carry their public block type metadata on that root instead of receiving an extra outer wrapper. Codeblocks render as escaped plain<pre><code>output without the old card chrome or a visible language label. Language metadata may still be stored and exposed only as a sanitizeddata-languageattribute on<code>.
Build Artifacts
- CMS release artifacts are prepared locally with
composer release:prepareand published to the update server withcomposer release:publish-update; usecomposer release:publish-update -- --dry-runto validate the retained artifact, checksum, metadata, endpoint, and token configuration without uploading. - Update publishing uses package-owned release defaults for the WebBlocks Publisher API direct publish endpoint
https://publisher.webblocksui.com/api/updates/publish, productwebblocks-cms, and channelstable. Maintainer publishing normally only needsWEBBLOCKS_PUBLISHER_TOKEN. The publisher reports whether that token is configured without printing secrets, and cached-config runs refresh only that token from the project.env. GitHub Actions no longer owns release package creation or update-server publishing, and.githubworkflows are intentionally absent. publisher.webblocksui.comis the canonical update service for both maintainer publishing and installed CMS update checks. Installed CMS sites do not configure Publisher/update server, product, or channel environment keys for normal operation; CMS product code owns the default release server, product key, stable channel, latest path, and publish path throughReleaseDefaults. The formerupdates.webblocksui.combridge is historical only and should not be used as an active configuration path.- Git commits and tags may still be pushed for source history, but installed CMS working copies remain update consumers and must not push to upstream. System Updates consume artifact URLs from update-server metadata, not GitHub release assets; published metadata should return
publisher.webblocksui.comdownload URLs. - The local repo
dist/path is ignored by git and is not part of normal application source.
License
WebBlocks CMS is open-sourced software licensed under the MIT license.
Trademark
"WebBlocks CMS" and related logos are the property of Fklavyenet.
Fklavyenet operates https://fklavye.net.
You may use, modify, and distribute the code under the MIT license. However, you may not use the name "WebBlocks CMS" or its logos for derived products without permission.
If you fork or redistribute this project, you must remove or replace all branding.