PHPackages                             fklavyenet/webblocks-cms - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. fklavyenet/webblocks-cms

ActiveLibrary[Framework](/categories/framework)

fklavyenet/webblocks-cms
========================

WebBlocks CMS

v1.32.94(1w ago)0299↓45.5%MITPHPPHP ^8.3

Since Apr 17Pushed yesterdayCompare

[ Source](https://github.com/fklavyenet/webblocks-cms)[ Packagist](https://packagist.org/packages/fklavyenet/webblocks-cms)[ RSS](/packages/fklavyenet-webblocks-cms/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (10)Dependencies (18)Versions (267)Used By (0)

WebBlocks CMS
=============

[](#webblocks-cms)

> 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
---------------

[](#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 `10` or `12` without 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`, and `editor` administration
- 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, `/search` fallback page, Search Form block support, and a Maintenance &gt; 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 the `Edit Page -> Page Management -> Assets` tab through compact rows plus Add/Edit modals
- media library and site-scoped navigation management
- contact forms that keep a package-standard `website` honeypot, 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 safe `MAIL_FROM_ADDRESS` fallback, with admin-visible delivery status and failure details for saved messages; future configurable auto-discard thresholds such as `CONTACT_SPAM_AUTO_DISCARD_SCORE` should be considered only after observing production data
- `php artisan contact:mail-diagnose` reports 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 `Actions` header and `td.wb-table-actions > .wb-action-group` for 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 root `layouts.admin` compatibility alias that can hide package-consumer view namespace mistakes
- site-scoped primary domains and alias domains for one-install multi-domain public routing
- primary `Sites` admin navigation near `Dashboard`, with site-domain management grouped under `System -> Domains`
- install wizard for first-run setup
- system updates with a simple two-card `Install Update` and `Update Details` operator 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, 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 from `vendor/fklavyenet/webblocks-cms/...`, keeping the install-owned Laravel shell and root overrides intact; the old `1.31.53 -> 1.32.33 bridge -> 1.32.34+ package-rooted` path is retired historical compatibility and is no longer part of routine package-native release validation
- site-level Branding and SEO Defaults with public `` fallback metadata and favicon support, plus locale-aware page-level SEO overrides on page translations
- relational site-scoped `site_variables` with controlled `{{ site.variable_key }}` public token replacement, tabbed `Edit Site` sections, 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_shell` for backward compatibility
- a system-owned `Navbar` primitive block for reusable public header wrappers, plus composable `Navbar Brand` and `Navbar Navigation` blocks that can be placed anywhere inside a Navbar descendant tree, with `Cluster` as the reusable horizontal distribution primitive for navbar rows and other grouped layouts
- super-admin global Blocks index under `Pages` for cross-CMS block maintenance with compact `Search`, `Site`, `Page`, `Block Type`, `Status`, and `Locale` filters
- 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 Types` modal in the slot editor now keeps all block lists in `Name A-Z` order, limits filtering to search plus tab state, shows a header count badge for the current picker result set, only shows `Reset` after 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

Architecture Note
-----------------

[](#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
------------

[](#installation)

### Fresh Laravel Package Install

[](#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.php` when needed
- removes the untouched fresh Laravel welcome route from `routes/web.php` when it is safe, with a timestamped backup first, so CMS public routes can serve `/`
- safely patches `App\Models\User` with 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-transfers` filesystem disk storage used by Export / Import packages
- prepares the `backups` filesystem disk storage used by Backup / Restore
- installs package CMS assets into `public/cms`
- creates `public/storage` when 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_admin` when 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/login` for package-owned CMS auth, or the host `/login` when 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](docs/brand-standard.md).

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

[](#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

[](#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](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

[](#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
-----------

[](#quick-start)

1. Install the CMS with the browser wizard or the CLI flow.
2. Sign in to `/webadmin`.
3. Create or edit a site if your install uses more than one site.
4. Create a page. New pages start as `draft`.
5. If you already have a compatible single-page JSON payload, use `Admin -> Pages -> Import Page` to import one new page into the selected site as draft. This page-scoped workflow is separate from the site-level `Export / Import` tool.
6. Build page structure with `Section`, `Container`, `Cluster`, or `Grid`, then add `Header`, `Plain Text`, `Rich Text`, `Button Link`, `Card`, `Stat Card`, or `Breadcrumb` blocks inside that layout tree. `Plain Text` is for plain body copy only. `Rich Text` is 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. `Code` is 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 UI `wb-rich-text` primitive using `wb-rich-text wb-rich-text-readable`. Selected description fields still support safe inline code formatting with backticks. Use `Breadcrumb` for header and context bars, and use `navigation-auto` only for actual menus. `Page Layout` is the admin-facing name for the page-level outer wrapper concept. The stored compatibility field remains `public_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 Layout` is for regular public pages. `Docs Layout` maps 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 a `Page Layout`; the stored compatibility field on the Shared Slot remains `public_shell` for 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 to `Page Content`, `Shared Slot`, or `Disabled`. Switching away from page-owned content does not delete existing page-owned blocks, so editors can safely switch back later. For card actions, prefer `Card > Cluster > Button Link`; the legacy single card action fields remain available as a fallback. `Card` can now be used for editable image cards inside `Grid`. Selected media is optional, selecting media is enough to render the card image, clearing media removes it, blank or legacy `none` image placement falls back to `top`, 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, nested `Cluster` or `Button Link` footer composition remains supported, the legacy single-action fallback remains available, and existing no-image cards stay valid.
7. For a docs-style context bar, add `Breadcrumb` and `Header Actions` to the `Header` slot. `Header Actions` renders 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 UI `data-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, use `Navbar` as the primitive wrapper block, then add child blocks such as `Container`, `Cluster`, `Navbar Brand`, `Navbar Navigation`, `Header Actions`, or `Search Form` inside it. `Navbar` renders only `nav.wb-navbar` and its child blocks, does not add an automatic container, and keeps `Position` as its only built-in setting. `Navbar Brand` and `Navbar Navigation` must 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 force `wb-stack`; use `Container`, `Cluster`, or `Stack` inside the slot when you need layout. For horizontal header composition, set the `Container` flow to `None` and use `Cluster` settings to control width, distribution, alignment, wrapping, and gap.
8. For a docs-style sidebar, add a `Sidebar` slot and set the page `Page Layout` to `Docs Layout`, then add `Sidebar Brand`, `Sidebar Navigation`, and `Sidebar Footer` as top-level sidebar blocks. Inside `Sidebar Navigation`, add `Sidebar Nav Item` and optional `Sidebar Nav Group` blocks, or point the block at the site-scoped `Docs` navigation menu so admin-managed `Add Group` sections render as collapsible docs sidebar groups.
9. Publish the page as a `site_admin` or `super_admin`.
10. 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 ``, JS renders in the public `` 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 ``. 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
-----------------

[](#multisite-domains)

- Public host resolution now prefers active `site_domains` records, then falls back to the legacy `sites.domain` value when needed, and only uses unknown-host fallback behavior where `cms.multisite.unknown_host_fallback` is intentionally enabled for local or compatibility scenarios.
- In the admin sidebar, `Sites` is a primary area directly under `Dashboard`, while `System -> Domains` owns public host and domain resolution management for sites.
- The site Domains screen keeps `Add Domain` as a modal action in the `Assigned Domains` card 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 -> Domains` opens 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 with `Manage Domains` actions.
- 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_domain` only when the clone should claim a new hostname.
- Internal domain automation endpoints live under `/admin-api/*` and are disabled unless `WEBBLOCKS_CMS_INTERNAL_API_TOKEN` is configured. Requests must send `X-WebBlocks-Internal-Token: `.

Site Promotion
--------------

[](#site-promotion)

Site Promotion is the controlled one-way workflow for promoting site-owned content from a package into an existing target site.

- `Admin -> Sites` now includes a per-site `Export` row 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 / Import` now shows export history and import history together on one operational screen, with `Run Export` and `Run Import` actions in their listing card headers.
- `Admin -> Sites -> Promote` stays 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 content
- `mirror`: 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
-------------

[](#documentation)

- See the full documentation in the `docs/` directory:
- `AGENTS.md` is the compact AI and project working contract for repository-specific implementation rules.
- `DEVELOPMENT.md` defines the development and release workflow.
- `docs/updates.md` keeps the retired `1.31.53` root-managed bridge history for reference, but current update validation focuses on package-rooted release artifacts and package-native updater behavior.
- `php artisan webblocks:package-status` reports 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-check` additionally renders the internal package diagnostic Blade view through the `webblocks-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 root `public/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.js` globally, then renders the `admin-scripts` stack 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 under `public/cms/js/admin/` and `packages/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.md` maps the remaining admin asset and brand ownership boundary after the admin layout and shared admin partial package moves.
- `.editorconfig` and `pint.json` define the repository formatting standards.
- PHP files use 2-space indentation in this repository.
- `composer format:test` checks Pint formatting and runs `scripts/check-php-indentation.php`, while `composer format` applies 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.php` under `/webadmin`, including the CMS-owned Profile page. Active CMS public routes now load from package `routes/public.php`, and root `routes/web.php` is 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`, and `VisitorEvent` in package `src/Models/` without root `App\Models\...` wrappers. `User` remains 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 Types` and `System 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-assets` for package assets and `webblocks-cms-stubs` for starter stubs; CMS package assets publish into the active root `public/cms` compatibility path, and System Update refreshes that path from package `public/cms` assets 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](docs/installation.md)
- [Getting Started](docs/getting-started.md)
- [Core Concepts](docs/core-concepts.md)
- [Users And Permissions](docs/users-and-permissions.md)
- [Editorial Workflow](docs/editorial-workflow.md)
- [Revisions](docs/revisions.md)
- [Operations](docs/operations.md)
- [Search](docs/search.md)
- [Updates](docs/updates.md)
- [Multisite](docs/multisite.md)
- [Localization](docs/localization.md)
- [Public Assets](docs/public-assets.md)
- [Page Layouts](docs/page-layouts.md)
- [Package Architecture Transition](docs/package-architecture.md)
- [Block Type Contracts](docs/block-type-contracts.md)
- [Renderer Contracts](docs/block-ui-renderer-contract.md)
- [Development Workflow](DEVELOPMENT.md)

Project Layer
-------------

[](#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`, and `db:wipe`, including normal console execution and `Artisan::call(...)` paths where Laravel emits the command start event.
- Use `WEBBLOCKS_ALLOW_DESTRUCTIVE_DB_COMMANDS=true` only when you intentionally need to bypass that safety guard.

Developer Notes
---------------

[](#developer-notes)

- Repair shipped CMS catalogs on an existing install with `php artisan webblocks:catalog-repair --dry-run --all`, then `php artisan webblocks:catalog-repair --all` after reviewing the report. The command supports `--block-types`, `--slot-types`, `--page-layouts`, and `--icons`, and preserves install-specific custom catalog rows. The lower-level `php artisan block-types:sync-core` command 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 Update` and `Update Details`; `Update Details` keeps 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 `origin` fetchable if you want local git visibility, but disable push on installation working copies with `git remote set-url --push origin DISABLED`. Create CMS releases only from the real maintenance checkout, not from an installed site working copy.
- `BlockTypeSeeder` still 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 legacy `heading` catalog row only when no live published blocks still reference it.
- `docs/block-type-contracts.md` documents 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 set `image`, `gallery`, `download`, `file`, `video`, and `audio`, plus the Layout + Card group `section`, `container`, `grid`, `cluster`, `card`, and `content_header`. `php artisan block-types:contracts-audit` now 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`.
- `Navbar` keeps the persisted `sticky-navbar` handle for compatibility, but it now behaves as a primitive system navigation container. It renders only `nav.wb-navbar` and nested child blocks, keeps only `Position` (`static`, `sticky`, `fixed`) as a built-in setting, and does not add an automatic `Container`, brand wrapper, or generated menu markup.
- `Block::ownsPublicRoot()` now treats the persisted `sticky-navbar` slug as root-owning so Navbar output uses its real `` renderer root without an extra generic public wrapper.
- `Block::ownsPublicRoot()` also continues to treat `section`, `container`, `grid`, `cluster`, `card`, and `content_header` as root-owning so those renderers keep `data-wb-public-block-type` on their real public root instead of receiving a generic `wb-public-block` wrapper.
- `Navbar Brand` and `Sidebar Brand` now 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 Layout` is now a managed install-level CMS concept under `Admin -> System -> Page Layouts`. Pages still store the selected layout handle on `public_shell` for backward compatibility, while the runtime resolves that handle safely through the managed Page Layout catalog. Built-in `Default Layout` and `Docs Layout` remain backward compatible with existing pages, imports, exports, Shared Slots, and public rendering. Page Layouts now expose `Body Class` plus managed `Page Layout Slots` in the admin, while deprecated compatibility fields such as `shell_type`, `slot_schema`, and `wrapper_schema` stay out of the admin UI. Body Class is intended for layout-specific CSS on the public ``, such as `layout-default` or `layout-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 the `nav.wb-navbar` root 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 safe `Add Missing Layout Slots` action that creates only missing slots. Custom V1 Page Layouts still reuse the existing `default` or `docs` runtime 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-stack` around header content, so header composition should come from blocks inside the slot such as `Container`, `Cluster`, or `Stack`.
- `Container` is a width primitive first. Legacy containers still render stacked flow by default for compatibility, but editors can now set Container `Flow` to `None` when they need layout-neutral `wb-container` markup and compose spacing or alignment with child layout blocks instead.
- `Section`, `Container`, `Grid`, and `Cluster` remain layout primitives: shared layout settings and child structure stay canonical, while user-facing copy stays out of arbitrary settings JSON. `Card` is now a composable shell block: it owns the `article.wb-card` root, keeps only shared shell metadata such as `settings.layout_name`, and only accepts `Card Header`, `Card Body`, and `Card Footer` as direct children. Those region blocks are addable only inside `Card`, own their own `wb-card-header`, `wb-card-body`, and `wb-card-footer` roots, 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 Header` keeps 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`, and `contact-info` are 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 Brand` and `Navbar Navigation` are the intended composable blocks for reusable header composition. They must be placed somewhere inside a `Navbar` tree, but they can sit under nested layout wrappers such as `Container` or `Cluster` instead of only as direct Navbar children. `Container` owns width, `Cluster` owns horizontal distribution, and `Stack` owns vertical flow. A recommended navbar pattern is `Navbar -> 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 Navigation` reads relational `navigation_items` rows 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 Brand` renders logo and optional text using the shipped WebBlocks UI navbar classes, and now supports logo-only usage when a logo is present.
- `Sidebar Navigation` keeps the shipped WebBlocks UI `wb-sidebar-link` and `wb-nav-group-item` contracts. Manual `Sidebar Nav Group` children now render through the same `Sidebar Nav Item` semantics 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-navbar` owns sticky navbar behavior. CMS no longer emits `wb-cms-navbar--sticky` on 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`, and `WebBlocks CMS v{VERSION}` from `WebBlocks\Cms\Support\WebBlocks`. The sidebar footer centers the version label with the WebBlocks UI `wb-text-center` utility. 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 (`default` or `docs`), slot name controls the public region wrapper semantics, and blocks render content inside those slot wrappers.
- `Header` is the canonical heading block. Its shared anchor is stored explicitly and reused for public `id` output plus same-page `TOC` links; `TOC` does not parse rendered HTML and does not depend on legacy `heading` blocks.
- Site-level public metadata now comes from the currently resolved site. `display_name`, `tagline`, favicon, social image, and SEO Defaults provide public `` fallbacks by site and host context.
- Page-level SEO overrides live on `page_translations`, stay locale-aware, and affect public `` 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 site `display_name`, then site `seo_title`, then site `name` for 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.
- `default` uses standard semantic wrappers such as `header`, `main`, `aside`, and `footer`. `docs` automatically maps header, sidebar, and main slots to the docs navbar, sidebar, and main wrappers.
- Existing pages remain site-scoped after creation. The normal `Edit Page` form 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 page` admin action. Duplicate creates a new page and leaves the source page unchanged.
- Cross-site page moves are handled through a dedicated `Move to another site` admin action on the Edit Page screen. The normal `Save Changes` path still cannot change `site_id` directly.
- `super_admin` can duplicate pages into any site. `site_admin` and `editor` can duplicate only when they have access to both the source site and the target site.
- `super_admin` can move pages between any sites. `site_admin` can move pages only when they have access to both the source site and the target site. `editor` cannot 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_id` references 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 page` when relevant. When selected, only the duplicated page's affected Shared Slot-backed slots are written as `source_type = disabled` with `shared_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 Layout` settings such as `Docs Layout` alongside pages, slots, blocks, Shared Slots, and Shared Slot-backed page slot assignments. `Move to another site` changes ownership of one existing page in place, while `Duplicate page` creates 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`, and `disabled`. 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_admin` can manage Shared Slots for all sites, `site_admin` can 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_id` references, 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`, and `review_requested_by_user_id`. These are system-managed audit fields, not editor-facing form inputs.
- Page revisions now store nullable actor plus compact `source` and `event` metadata 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 `source` and `event` pattern, 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 recorded` for 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 `console` or `project_import` where available.
- Shared Slot revision viewing follows the same site-scoped access model as page revisions: `super_admin` and `site_admin` can view and restore within allowed sites, while editors can view but not restore.
- Shared Slot deletion is guarded. If any `page_slots.shared_slot_id` still 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_index` table. 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 Actions` search trigger when JavaScript is available, while `/search?q=term` and `/{locale}/search?q=term` remain the fallback and direct-link routes.
- Public modal results load from the locale-aware JSON endpoints `/search.json?q=term` and `/{locale}/search.json?q=term`.
- The first-class `Search Form` block 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 -> Settings` is the compact install-level settings screen for locale, timezone, admin project identity, CMS-owned mail settings, privacy, version, and environment information. `Admin -> System -> Visitor Reports` reports anonymous page views across the allowed site scope while keeping richer breakdowns privacy-safe. `Maintenance` remains reserved for operational tools such as Search Rebuild, Backups, Export / Import, and Update.
- Backup archives are resolved only through the CMS `backups` disk root, which defaults to `storage/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 use `777` permissions.
- `Admin -> System -> Visitor Reports` stores 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 displays `Not tracked` instead 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 migrate` before `php 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`, or `db: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`, and `Content Header` own their real WebBlocks UI root markup and carry their public block type metadata on that root instead of receiving an extra outer wrapper.
- `Code` blocks render as escaped plain `` output without the old card chrome or a visible language label. Language metadata may still be stored and exposed only as a sanitized `data-language` attribute on ``.

Build Artifacts
---------------

[](#build-artifacts)

- CMS release artifacts are prepared locally with `composer release:prepare` and published to the update server with `composer release:publish-update`; use `composer release:publish-update -- --dry-run` to validate the retained artifact, checksum, metadata, endpoint, and token configuration without uploading.
- Update publishing uses the WebBlocks Publisher API direct publish endpoint `https://publisher.webblocksui.com/api/updates/publish` through `WEBBLOCKS_PUBLISHER_URL`, with `WEBBLOCKS_PUBLISHER_TOKEN`, product `webblocks-cms`, and channel `stable` by default. The publisher reports whether each canonical `WEBBLOCKS_PUBLISHER_*` key is configured without printing secrets, and cached-config runs refresh those publisher keys from the project `.env`. Legacy publisher environment aliases are not supported. GitHub Actions no longer owns release package creation or update-server publishing, and `.github` workflows are intentionally absent.
- `publisher.webblocksui.com` is the canonical update service for both maintainer publishing and installed CMS update checks. Installed CMS sites use `WEBBLOCKS_UPDATES_SERVER_URL`, defaulting to `https://publisher.webblocksui.com`, and read latest metadata from `https://publisher.webblocksui.com/api/updates/latest`.
- 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.com` download URLs.
- The local repo `dist/` path is ignored by git and is not part of normal application source.

License
-------

[](#license)

WebBlocks CMS is open-sourced software licensed under the MIT license.

Trademark
---------

[](#trademark)

"WebBlocks CMS" and related logos are the property of Fklavyenet.

Fklavyenet operates .

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.

###  Health Score

51

—

FairBetter than 95% of packages

Maintenance99

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity68

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

244

Last Release

10d ago

Major Versions

v0.4.0 → v1.0.02026-04-25

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1913076?v=4)[Osman Bozdag](/maintainers/obozdag)[@obozdag](https://github.com/obozdag)

---

Top Contributors

[![obozdag](https://avatars.githubusercontent.com/u/1913076?v=4)](https://github.com/obozdag "obozdag (9 commits)")

---

Tags

laravelcmsblockswebblocks

###  Code Quality

TestsPHPUnit

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/fklavyenet-webblocks-cms/health.svg)

```
[![Health](https://phpackages.com/badges/fklavyenet-webblocks-cms/health.svg)](https://phpackages.com/packages/fklavyenet-webblocks-cms)
```

###  Alternatives

[laravel/laravel

The skeleton application for the Laravel framework.

84.4k62.4M1.0k](/packages/laravel-laravel)[nasirkhan/laravel-starter

A CMS like modular Laravel starter project.

1.4k2.7k](/packages/nasirkhan-laravel-starter)[unopim/unopim

UnoPim Laravel PIM

10.1k2.2k](/packages/unopim-unopim)[statamic/statamic

Statamic

831176.7k](/packages/statamic-statamic)[typicms/base

A modular multilingual CMS built with Laravel, enabling developers to manage structured content like pages, news, events, and more.

1.6k20.4k](/packages/typicms-base)[codewithdennis/larament

Larament is a time-saving starter kit to quickly launch Laravel 13.x projects. It includes FilamentPHP 5.x pre-installed and configured, along with additional tools and features to streamline your development workflow.

3861.7k](/packages/codewithdennis-larament)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
