LeafCMS GitHub Install
Docs

The in-app guide

This is the same guide as Admin → Help: usage, each admin screen, themes, tags, environment, and install.

How to write Blade inside a theme. Switching, copying, and uploading skins is under Themes. You can also open Edit templates on a copied theme. Pull entries with @cms* — see Tags. Do not query the database from a theme.

Folder layout

resources/views/themes/enterprise/
  theme.json
  preview.png
  index/index.blade.php      home (required for “Use on this site”)
  list/list.blade.php        section list
  list/list_product.blade.php product list (optional)
  show/show.blade.php        entry
  show/show_product.blade.php product (optional)
  category/category.blade.php section cover
  search.blade.php
  tag.blade.php
  sitemap.blade.php
  sitemap-page.blade.php   HTML sitemap for visitors (/sitemap)
  partials/layout.blade.php  chrome
  partials/paginate.blade.php
  member/login.blade.php     plus register, profile, …

If a file is missing, the enterprise theme or resources/views/cms/ is used so the site is not blank.

How a template is chosen

List: section list template → model list_tpl → default list.list. Show: entry show_tpl → section → model → default show.show. Dots map to folders, so list.list_product is list/list_product.blade.php.

Layout prefers partials/layout.blade.php, then a root layout.blade.php.

Page skeleton

@extends('themes.enterprise.partials.layout')

@section('content')
    @cmsBreadcrumb
    <h1>{{ $category->t('title') }}</h1>

    @cmsContent(['page' => true])
        <a href="{{ $item->url }}">{{ $item->t('title') }}</a>
    @endcmsContent

    @cmsPaginate
@endsection

Put @cmsSeo in the head. Nav: @include('cms.partials.header-nav') or @cmsMenu.

Variables

VariableWhenTypical use
$siteAlmost every front pageName, logo, color
$categorySection, list$category->t('title'), frontUrl(), page_size
$entryShow$entry->t('title'), t('content'), url, cover, views
$itemInside a tag loopDefault loop var; nest with 'as' => 'cat'

Use t('title') for locales, not ->title. Extra fields: $item->attr('price'). Images: cms_media($item->cover) (honors CDN).

Make your own

  1. On Themes click “Copy one”, upload a zip with index/index.blade.php, or drop a folder under themes/.
  2. Or: php artisan cms:theme-make brand --from=enterprise --name=Brand.
  3. Edit Blade and CSS. Models and empty sections go in theme.json. “Use on this site” only fills gaps.
  4. Preview: preview.png (or jpg / svg).
{
  "name": "Brand",
  "description": "Company site",
  "i18n": true,
  "responsive": true,
  "models": ["article", "page", "product"],
  "config": {
    "home_notice": {"title": "Home notice", "type": "textarea", "value": ""}
  },
  "categories": [
    {"slug": "news", "title": {"zh_cn": "新闻中心", "en": "News"}, "model": "article", "type": "list", "menu": true}
  ]
}

No executable PHP in the zip. Custom model JSON can live in models/slug.json (same as “Export model”). Object titles are written per language on apply. Keep viewport, the language switcher, and cms_ui() in the layout.

Extend with events, not core edits

Saving entries, comments, forms, switching themes, and writing settings dispatch Laravel events. Listen in app/Providers/AppServiceProvider.php with Event::listen. The same table is in CONTRIBUTING.md.

EventWhen
App\Events\EntrySavedAn entry was saved. $created is a new row; $publishedNow means it became published this save (member drafts do not set that)
App\Events\CommentSubmittedA front comment was posted (including pending)
App\Events\CommentModeratedAdmin approved / rejected / marked spam
App\Events\FormSubmittedA front form POST succeeded
App\Events\ThemeActivatedThe current site’s theme changed ($slug / $previous)
App\Events\SettingSavedSettings keys were written ($keys)
use App\Events\EntrySaved;
use Illuminate\Support\Facades\Event;

Event::listen(EntrySaved::class, function (EntrySaved $e) {
    if ($e->publishedNow) {
        // ping a search engine, write a log, …
    }
});

Bulk status changes on the entry list do not fire EntrySaved, so you do not mail hundreds of times. Do not fork controllers for “when an article is published”.

Group @sanpang