Content System: Pages, Posts, Menus and Widgets

Pages, posts, taxonomy, menus, widgets, advertisements and comments — what each does, how it is stored, and how it is rendered.

In the user guide: the Content chapter (pages, blog posts, menus, widgets, advertisements, support tickets).

Pages

A Page (app/Models/Page.php) is a translatable, sluggable, media-aware record. There is no separate "homepage" or "contact page" model: the homepage, blog index, contact page, privacy and terms pages are simply Page rows that an admin selects in Settings → Page setup (see Page types and public URLs).

AspectDetail
Translated attributestitle, content, styles, scripts, excerpt, meta_title, meta_description, og_title, og_description (table page_translations)
Plain columnsauthor_id, published (bool), settings (JSON object), soft deletes
settingsPage-level overrides. PageSlugRenderer calls override_theme_options($page->settings), so a page can pin a primary menu, header preset, floating/sticky header, breadcrumb mode, etc. for itself only
MediaCollection featured-image (JPEG, PNG, GIF, WebP)
SlugHasSlug — legacy slug column plus the global slugs table; renderer PageSlugRenderer
Scopespublished(), home(), blog(), contact(), status(), byAuthor(), slug()
Other traitsSearchableTrait, InteractsWithViews (view counts), Sitemapable

Editing:Admin\PageController has two editing surfaces. edit/update manage metadata and per-locale titles; editContent/updateContent open the GrapesJS page builder for one locale (?locale=en) and save content + styles through clean_page_builder().

Rendering:PageSlugRenderer registers the assets of every shortcode that appears in the content (ShortcodeRegistry::registerContentAssets()), compiles the saved content with Blade::render() (so <x-shortcode-*> tags execute), applies page-level theme overrides, sets SEO meta and breadcrumbs, then renders pages.show.

Posts, categories and tags

The blog is one fixed design

The public blog uses App\Services\Blog\Publication to prepare the lead story, supporting features, numbered archive rows and related stories; templates live in resources/themes/echo/blog and posts. There is intentionally no layout registry or settings selector — see docs/blog-publication.md. Search is ?q= on the blog route; the blog index shows the authored Page::blog() content unless a search is active or the page is empty.

The blog and blog-cards shortcodes use the same publication layer as the blog index. The posts widget and the post binding source query published posts directly.

Navigation trees are Menu + MenuItem records managed in Appearance → Menus (Admin\MenuController). An item can be a custom URL, a named route with parameters, or a resource (morphTo) pointing at any linkable model (a page, a post, a booking service…).

Widgets and widget areas

Widget areas are named regions in the theme (sidebar, categories-sidebar, tags-sidebar, pages-sidebar, post-sidebar, and footer-1…footer-6; the footer count follows the theme option footer_widget_columns). Each area holds ordered widget instances (Widget model: name, title, settings, order, web/mobile visibility, async flag).

Built-in widgets: tru-menu, tru-text, tru-html, tru-advertisement, tru-posts, tru-posts-categories, tru-posts-tags. Modules add more (booking-services, trucommerce-cart-mini, …). Widgets are Arrilot widget classes; instances are loaded from the database for the current device class (web or mobile) and cached for one hour per device. See Widgets and widget areas.

Advertisements

Advertisement records have a type (1 = text, 2 = image, 3 = code), an options JSON blob, a status flag and a click counter. They render through the tru-advertisement widget or the adslot shortcode, and an AdvertisementObserver clears the related caches.

Comments

Threaded comments on posts are provided by a comments package (App\Models\Comment extends its entity). They are permission-aware, show a sign-in prompt to guests and are styled in resources/themes/echo/vendor/comments. CommentsPresenter loads the conversation and commenter media once and builds the reply tree in memory. A comment change flushes the response cache.

FAQs

A Faqs model (translatable) and a FAQ block exist in the page builder, but the core has no admin CRUD route for FAQ records. If you need managed FAQs, build them in a plugin and expose them as a shortcode.

Extending content