Skip to content

Repository files navigation

Total CMS Extension Starter

A template for building Total CMS extensions. Clone this repo, rename it, and start building.

What's Included

This starter demonstrates every extension point:

FeatureFileDescription
Twig functionExtension.phpstarter_greet() function
Twig filterExtension.phpreverse_words filter
Twig globalExtension.phpExample in register() (commented out)
CLI commandsrc/Command/GreetCommand.phptcms acme:greet
API routesrc/Action/ApiHelloAction.phpProtected API at /ext/acme/starter/api/hello
Public routesrc/Action/PublicStatusAction.phpUnauthenticated at /ext/acme/starter/status
Admin pagesrc/Action/DashboardAction.phpAdmin page at /admin/ext/acme/starter/dashboard
Admin navExtension.phpSidebar link with custom SVG icon
Dashboard widgettemplates/widgets/starter.twigWidget on admin home
Event listenerExtension.phpLogs object create/update events
AutomationExtension.phpprune-drafts scheduled job (+ commented event-driven example)
Custom field typesrc/Field/ColorPickerField.phpNative HTML color picker field
MCP toolExtension.phpacme_search_inventory — query the inventory from an AI agent
MCP tool with progressExtension.phpBulk-reindex example with SSE progress notifications (commented)
MCP resourceExtension.phpacme://message/of-the-day — addressable content for AI agents
MCP resource templateExtension.phpURI template acme://inventory/{sku} for unbounded resource sets (commented)
Admin CSS assetassets/colorpicker.cssStyles for the color picker field
Admin JS assetExtension.phpExample in register() (commented out)
Frontend CSS/JS assetsExtension.phpExamples in register() (commented out)
Container definitionExtension.phpExample in register() (commented out)
Page middlewareExtension.phpExample in register() (commented out)
Read-only schemaschemas/starter-items.jsonExample collection schema (Pro+)
Installable schemaExtension.phpExample in boot() (commented out, Pro+)
Settingssettings-schema.jsonConfigurable greeting message
Iconicon.svgDisplayed on the extension card in the admin UI
Manifest linksextension.json"Documentation" and "Dashboard" links on the admin card
Review noteextension.jsonPlain-language note shown on the pre-enable review screen

Getting Started

  1. Clone this repo into your T3 extensions directory:
cd tcms-data/extensions/
mkdir your-vendor
cd your-vendor
git clone https://github.com/totalcms/extension-starter.git your-extension
cd your-extension
  1. Update extension.json with your extension's ID, name, and details:
{
"id": "your-vendor/your-extension",
"name": "Your Extension"
}
  1. Update the PHP namespace in composer.json and all PHP files:
"autoload": {
"psr-4": {
"YourVendor\\YourExtension\\": "src/"
}
}
  1. Install dependencies and generate the autoloader:
composer install
  1. Enable the extension:
tcms extension:enable your-vendor/your-extension
  1. Delete the parts you don't need and start building.

Directory Structure

your-extension/
extension.json # Manifest (required)
Extension.php # Entry point (required)
icon.svg # Extension icon (optional)
composer.json # Dependencies and autoloading
settings-schema.json # Settings form definition
src/
Action/ # HTTP action handlers
Command/ # CLI commands
schemas/ # Read-only schemas (Pro+)
templates/ # Twig templates

Manifest Links

Add a links array to extension.json to surface buttons on the admin Extensions card:

"links": [
{"label": "Documentation", "url": "https://docs.totalcms.co/extensions/"},
{"label": "Dashboard", "url": "/admin/ext/acme/starter/dashboard"}
]

Each entry needs a label and a url.

  • URLs starting with http:// or https:// are treated as external and open in a new tab.
  • Relative URLs (admin pages your extension registers) only show when the extension is enabled — they wouldn't resolve otherwise. External links are always shown so users can read your docs before enabling.

Review Note

When a user enables an extension that touches sensitive capabilities — public (unauthenticated) routes, listening to all content changes, registering services, or exposing tools/resources to AI agents over MCP — Total CMS shows a short pre-enable review screen before the extension turns on. (Extensions that use none of those just enable in one click.) The screen also lists any high-risk patterns a static source scan finds in your code (shell_exec, raw network calls, etc.).

The reviewNote field in extension.json is your message at the top of that screen — your chance to explain, in plain language, what your extension does and why it needs the access it asks for:

"reviewNote": "Receives Stripe webhooks at a public endpoint to confirm payments, and watches new orders to send notifications. No data leaves your site except the Stripe calls you configure."

Guidelines:

  • Write for the site owner, not a developer. Say what the extension does and why it needs each sensitive capability.
  • Be specific about public endpoints and data access — those are what users worry about.
  • Keep it short — a sentence or two. It's a reassurance, not a manual.
  • You'll see this exact screen when you enable your own extension during testing, so you can preview how it reads.

Two behaviors worth knowing as you build:

  • Capabilities you add in a later version default to off for users who already had your extension enabled. Total CMS surfaces the new capability in the extension's settings as a toggle, so the user opts in deliberately — adding a capability in an update won't silently start using it.
  • An update that introduces high-risk code patterns (caught by the source scan) disables the extension until the user reviews and re-enables it. Clean updates install without interruption. So keep risky calls intentional and explainable.

reviewNote is optional — but on an extension that uses any sensitive capability, a good note is the difference between a confident "Enable" and a nervous one.

Schemas

Two ways to ship schemas with an extension. Both require the Pro edition.

ApproachWhere it livesEditable by user?Use when
Read-onlyschemas/yourname.json in the extensionNo — owned by the extensionThe schema is core to the extension's behavior and shouldn't be modified
Installable$context->installSchema([...]) in boot()Yes — copied once into tcms-data/.schemas/The schema is a starting point the user is expected to customize

Read-only schemas are auto-discovered. They show up in the admin schemas view alongside built-in schemas, and can be referenced by collections. Disabling the extension's Schemas capability hides them.

installSchema() is a one-shot copy: it skips silently if a schema with the same id already exists, so subsequent boots don't overwrite the user's edits.

Version Requirements

The requires block in extension.json is enforced before the extension can be enabled:

"requires": {
"totalcms": ">=3.3.0",
"php": ">=8.2"
}

If the running Total CMS or PHP version doesn't satisfy these constraints, the extension is still listed on the Extensions admin page but the Enable button is disabled and a yellow warning panel explains why. This means users can see your extension exists and read its docs, but they can't enable it on an unsupported runtime.

Documentation

License

MIT

Releases

Packages

Contributors

Languages