Skip to main content

Documentation

No results found.
Features

Shared Rows — Page Titles, CTAs & Your Own Reusable Rows

Every WebProCMS shared row is a named slot with one active design: edit the design and every page that uses the row updates; swap in a different design and every page that follows the row switches at once. The system ships two slots — Page...

Every WebProCMS shared row is a named slot with one active design: edit the design and every page that uses the row updates; swap in a different design and every page that follows the row switches at once. The system ships two slots — Page Title and Call to Action — and you can create your own ("Testimonial Band," "Holiday Banner") by sharing any row from the page editor. One concept, one flat list, no tabs.

A slot always resolves to one concrete instance (a real, editable row), but pages reference the slot, not the instance — so the design can change underneath them without breaking anything. It's the same idea as the default Header and Footer (a page says "use the site header," not "use header #3"), extended to any band you want reused across pages.


What it gives you

  • A site-wide default page title and CTA. Generated content-type pages (services, projects, etc.), listing pages, and detail pages all lead with your chosen page-title band and — where appropriate — close with your chosen CTA, without you placing them by hand.
  • Your own reusable rows. Style a band once, share it, and insert it anywhere. Editing it updates every page that uses it.
  • One-click design swaps that reach every page. Each shared row can hold several designs; make a different one active and every page following the row switches to it. The seasonal-banner swap (holiday design in December, normal design in January) is one click each way.
  • User-authored designs for the system slots. Style a page-title band on any page, then "Add to existing → Page Title" in the share modal — your design joins the Page Title library beside the shipped ones, swappable like any other.
  • Per-page freedom when you want it. A page can pin a specific design instead of following the active one, or customize the shared row for that page only. Following the active design is the convenient norm, not a cage.

Late binding: the heart of the feature

When a page uses a shared row, it doesn't bake in a copy — it stores a reference to the slot. The actual band is resolved when the page renders. So:

  • Changing the active design updates every page that follows it — instantly, with no rebuild and no per-page edits.
  • It can't break a page. If a slot ever can't be resolved, the row simply renders empty rather than erroring — pages stay up no matter what.
  • It's free at render time. Resolving the slot is an in-memory settings lookup the page already pays for; it adds no database queries, and fully-cached pages are unaffected.

Where you manage it

Dashboard → Design → Shared Rows is one flat list: the system Page Title and Call to Action slots first (marked System), then your own shared rows. Each entry shows a live preview of its active design, how many pages use it, and:

  • Edit — opens the active design in the row editor.
  • Designs — the slot's detail view: installed designs with an Active badge, Make Active on the others, plus Edit, Refresh (re-pull structure from the design library), and Delete (with a usage warning; the active design can't be deleted — make another active first). System slots also get a Library pill listing shipped designs to Install & Use or Install only.
  • Delete (user rows only) — removes the shared row everywhere, with a list of affected pages first. System slots can't be deleted.

New shared rows are created from the page editor, not from this page — see below.

Sharing a row from the page editor

Click the share icon on any row and the Share this row modal offers two paths:

  • New shared row — name it, and on save it becomes a new slot with this row as its active design. The page follows the slot from then on.
  • Add to existing — pick a shared row (Page Title, CTA, or one of yours), name the design, and choose:
    • Make it the active design off (default): the design joins that row's library and this page is pinned to it. No other page changes.
    • Make it the active design on: it becomes the row's active design — the modal shows how many pages will update before you commit.

Nothing is written until you save the page; undo (or leaving without saving) cancels the share cleanly.

In the editor's Add Section drawer, the Shared Rows tab lists every slot with its active design — inserting one drops a late-bound reference, not a frozen copy, so the page follows the row from then on.

Moving pages between sites

  • Cloning a page (same install) keeps references — the clone follows the same shared rows.
  • Exporting a page bundle (the .zip you can import into another WebProCMS site) carries the system Page Title / CTA as a reference: import it and the page adopts that site's page title and CTA. A user-created shared row (or a pinned design) is carried as a portable copy instead — the destination doesn't have your slot registry, so the row arrives as an ordinary local row with its content intact.

How it works (technical)

  • Slot registry — App\Support\SharedRowRoles. The two system slots (page_title, cta) live in the ROLES const with their label, template-name families, library criteria, and theme-shipped default. User slots live in the shared_rows.custom_slots Setting ({key: {label}}), created via createSlot() (key derived from the label, e.g. holiday_banner) and removed via forgetSlot(). roles() merges both; isSystemRole() distinguishes them.
  • Instances are shared rows with explicit membership. A slot resolves to a concrete shared-rows/{template}-{id}.blade.php + a SharedRow record. The shared_rows.role column stamps which slot an instance belongs to (shared-rows:sync-roles backfills legacy records; familyInstances() keeps a template-prefix fallback for unstamped system rows). The install's active design per slot is a Setting (shared_rows.role.{key}).
  • Dashboard operations — App\Support\SharedRowRoleService: install / setDefault / refresh / usage / uninstall, plus the v2 additions adoptInstance() (stamp a shared page row into a slot, optionally swapping the active design site-wide), createSlotFromInstance() (the share modal's "new" path), roleUsage() (pages following a slot late-bound), and deleteSlot() (user slots: strips the row from every page, removes instances + registry).
  • The share modal — the row card's share button opens a modal (on EditorModals) that stages {mode, name, role, make_default} via the editor's stageRowShare(). Configs are held server-side in $pendingShareConfigs keyed by row slug (the row's pending_shared flag is boolean-coerced through the client round-trip, so the config can't live on the row). saveFile's materialization loop writes the shared file, then creates/joins the slot and rewrites the page row as a late-bound reference (new slot or make-default) or a pinned concrete embed (add-without-default).
  • Late binding is a Blade directive. Pages store a row marked {{-- ROW:start:role:{key}:shared=1 --}} whose body is @sharedRole('{key}'). The directive resolves the slot to the install's active instance at render and @includes it, sharing the page's data ($pageName, breadcrumb context). Resolution is read-only (no DB writes on the render path) and null-safe (renders empty if unresolved — including for a deleted custom slot). The page-data compiler indexes the resolved instance's content into each page's sidecar, so the band's copy renders with no extra queries. All of this is generic over the slot key — user slots ride the same markers, directive, compiler scan, and fan-out recompile as the system slots.
  • Edit-in-place. When the editor loads a page, a role:* marker is resolved to its concrete instance so the editor treats it like a normal shared row (content overrides, drill-in, preview, per-page override mode). On save the role marker is restored — so opening and saving a page never silently converts it to a hardcoded include.
  • Migration. php artisan pages:migrate-shared-roles converts pre-role pages' hardcoded page-title/CTA includes to role markers (only where the row matches the slot's current instance; idempotent). It runs automatically during CMS updates, after shared-rows:sync-roles (which also stamps the role column).

Adding a new system slot

Add an entry to SharedRowRoles::ROLES with its label, families, library criteria, and default instance. The dashboard list, the editor's Shared Rows drawer tab, the share modal's slot picker, late-binding resolution, the compiler scan, and the migration command all derive from the registry — no per-slot wiring elsewhere.