Skip to content

Plugins

A plugin is newmeta's core unit of extension. Every admin module, shop feature, Pagebuilder widget, and API endpoint lives in a plugin. The CMS ships with 42 plugins — the same plugin API is available for your own modules.

What is a plugin?

Every plugin is a subdirectory under _public/extensions/core/backend/{plugin-name}/ and consists of:

  • a bootstrap.php that registers the plugin,
  • optional admin layouts (layout/index.vue, layout/index_detail.vue),
  • optional PHP action scripts (script/),
  • optional API models (api/),
  • optional Pagebuilder widgets (widgets/),
  • optional SQL migrations (migrations/).

Each plugin decides for itself which of these parts it needs. Small plugins only ship a bootstrap.php plus a table; large plugins like shop or elearning come with full API directories, their own widgets, and dozens of migrations.

Why build a plugin?

Use casePlugin pattern
CRUD for a new data modelcontent_construct=table + content_table=my_table → automatic backend with list + edit modal + API
Custom UI without a DB tablecontent_construct=template + your own layout/index.vue
New REST endpoint$this->apiEndpoints in bootstrap.php, model in api/
Integration with an external servicePagebuilder widget + webhook event + scheduled task
Custom Pagebuilder elementWidget subdirectory under widgets/ (see widget docs)

Plugins are the only right place to add functionality — you never edit the core framework directly.

Plugin lifecycle

A plugin goes through three phases. install() itself only sets properties — the actual DB inserts happen in the core installers and don't need to be idempotent (a re-install typically removes the previous one via uninstall() first). uninstall(), in turn, must clean up properly so re-installs don't run into stale state.

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  install()  │ --> │  update()   │ --> │ uninstall() │
└─────────────┘     └─────────────┘     └─────────────┘
      │                   │                   │
      ▼                   ▼                   ▼
 Create                Schema migration    Remove
 plugin_backend        via MigrationRunner plugin_backend
 entry                                     entries

install() — runs once on first activation. Sets all properties (content_construct, content_table, apiEndpoints, scheduledTasks, webhookEvents, …). The core then automatically calls these installers:

installBackend()         → plugin_backend entries (one per content_construct)
installWidgets()         → Pagebuilder widgets in pagebuilder_widgets + page_widgets
installDatabase()        → Run plugin-local migrations
installInjections()      → Register script injections
installSources()         → CSS/JS assets for the frontend bundle
installAPI()             → apiEndpoints in the pluginAPI table
installScheduledTasks()  → scheduled_tasks entries
installWebhookEvents()   → webhook_events entries (manual + auto from content_table)

update() — runs on a system update. Typically empty — schema changes go through migrations, not through update(). Only needed when existing plugin_backend entries must be rewritten.

uninstall() — runs on deactivation. Removes entries from plugin_backend, pluginAPI, scheduled_tasks, and webhook_events. Tables are not dropped — data is retained in case the plugin is reactivated later.

Required methods

Every plugin class must implement install(), uninstall(), update(), getVersion(), and getName(). Details: Plugin Anatomy.

The plugin_backend table

The heart of backend integration. For every content_construct of a plugin, one entry is created:

sql
CREATE TABLE plugin_backend (
  id                INT AUTO_INCREMENT PRIMARY KEY,
  plugin_id         INT NOT NULL,              -- FK → plugins.id
  content_construct ENUM('table','template','formonly'),
  content_table     VARCHAR(255),              -- DB table when construct=table
  content_titles    VARCHAR(255),              -- UI label ("Articles", "Courses", …)
  content_columns   TEXT,                       -- Visible columns (JSON/CSV)
  content_button    TEXT,                       -- Row buttons (JSON, urlencoded)
  action_button     TEXT,                       -- Header action button (JSON)
  content_replace   TEXT,                       -- Column renames
  header_buttons    TEXT,                       -- Additional header buttons
  url_rewrite       VARCHAR(255),               -- URL slug → /admin/{url_rewrite}
  modal_edits       TEXT                        -- Modal field config
);

content_construct drives the entire UI logic of a plugin. The three variants (table, template, formonly) are explained in Content Constructs.

Other important tables

TableContents
pluginsPlugin registry (name, version, root_folder, type, location)
plugin_backendAdmin UI entry points per plugin
pluginAPIRegistered REST endpoints (endpoint, modelPath, plugin_id)
scheduled_tasksCron jobs per plugin
webhook_eventsRegistered webhook events per plugin
pagebuilder_widgetsPagebuilder widget registry (one row per widget type)
page_widgetsWidget instances per page (one row per placed widget)
_migrationsExecuted SQL migrations (scope + migration + checksum)

Install & update

You activate plugins via the Plugin Manager under /admin/plugin-manager, or through the core installer during a system update. Both paths call installPlugin() on the install_controller and run the steps listed above.

Just drop new plugins into _public/extensions/core/backend/, with migrations either in the plugin's own subdirectory or centrally under _migrations/plugins/{name}/. On the next admin login, the Plugin Manager sees the plugin and offers installation.

Details on the update process: Plugins › Migrations.

See also