What engine-dmo adds
The modules the DMO bundle compiles on top of the core content platform, what each one is for, how they depend on each other and which configuration files belong to them.
engine-dmo
The examples in this section write the locale code en-GB. Replace it with your own site’s code in every label, slug and seed value. engine-dmo init writes en, and an example pasted under another code fails check --strict or, for a landing’s slugs, only warns.
engine-dmo is the core content platform (everything engine-cms does) plus the modules a destination marketing organisation runs on: a directory of places, the businesses behind them, their memberships, a portal where those businesses keep their own listings, and the commercial features built on that relationship. The modules are compiled into the binary; there is nothing to install or switch on. A module’s features appear as soon as you configure them, and most of them do nothing until you do.
Check which bundle and modules a binary carries with version:
engine-dmo version meridian v0.8.0 (pure-go) go1.27.3
bundle dmo: modules ai directory forms crm affiliates memberships newsletter portal claims memberreport promotions paidlistings translation engine-cms version lists ai forms newsletter translation only. The modules shared by both bundles (AI assistance, forms, the newsletter and translation) are documented under Features.
The modules
| Module | What it adds | Requires | Page |
|---|---|---|---|
directory | The core listing and area types, landing pages (a place crossed with a category at a clean URL), entry.landings | nothing | Directory, Landing pages |
crm | Organisations and people, their custom fields, partner roles, smart lists of organisations and people, broadcasts, the CRM import, merge and erasure | forms | CRM |
memberships | Membership tiers, each organisation’s membership on each site, renewals and reminders, memberTier() | crm | Partners and the portal |
portal | The member portal (sign in, screens, invitations), the member API, the requireMember tag | crm, memberships | Partners and the portal, Portal screens and actions |
claims | A business claiming its entry: the public claim form, verification, scoring and the staff queue | crm, memberships, portal | Claims |
promotions | Promotion slots, partners’ requests from the portal, the staff queue, promoted() and promotionLabel() | crm, memberships, portal | Promotions |
memberreport | The member report (portal, member API, PDF, monthly digest) and scheduled organisation reports | crm, memberships, portal | Member reports |
affiliates | Affiliate programmes, tracked links under /go/, conversion imports, the signed postback | crm | Affiliates |
paidlistings | Prices for tiers and promotions, orders, Stripe Checkout or staff invoices, fulfilment | crm, memberships, portal, promotions | Paid listings |
The arrows point from a module to the modules it requires; a requirement reached through another module is drawn once:
flowchart BT forms["forms (core)"] directory crm --> forms memberships --> crm portal --> crm portal --> memberships claims --> portal promotions --> portal memberreport --> portal affiliates --> crm paidlistings --> promotions paidlistings --> portal
How they fit together
The directory is content: listings and areas are documents like any other, edited in the admin, routed, rendered and searched by the core engine. The CRM is records: an organisation is a business or body, a person is someone you deal with, and a person is linked to an organisation in a partner role. The two meet at one point: a document can be linked to an organisation, which is then its owner. In the admin the link is the Organisation panel of the document editor; in templates it is entry.organisation.
This is the partner side’s data model, in outline:
erDiagram
NETWORK ||--|{ SITE : "groups"
NETWORK ||--o{ ORGANISATION : "holds"
NETWORK ||--o{ PERSON : "holds"
PERSON ||--o{ CONTACT : "is"
ORGANISATION ||--o{ CONTACT : "has"
PARTNER_ROLE ||--o{ CONTACT : "grants"
ORGANISATION ||--o{ MEMBERSHIP : "holds"
SITE ||--o{ MEMBERSHIP : "sells"
TIER ||--o{ MEMBERSHIP : "ranks"
ORGANISATION |o--o{ DOCUMENT : "owns"A contact is a person’s link to an organisation in a partner role; a person may be linked to several organisations. A membership is one organisation’s on one site at one tier. Documents are shared across the install; their organisation must be in the network of the document’s owner site.
Everything else hangs off that link:
- An organisation holds a membership on a site, at a tier (
config/memberships.yaml). The tier decides what its people may change on its listings (member_editableon the type), which promotion slots and portal screens it may use, and which report digest it receives. - The organisation’s people sign in to the portal and edit their listings within those rules. Their changes become revisions that wait for review or publish at once, as the tier says.
- Visitors’ interactions with the listing (impressions, views, website clicks, phone reveals, enquiries) are counted per organisation and returned to it as the member report.
- A business with no portal account yet can claim its organisation from the public site.
- Members can ask for promotions in the slots a site declares, and paid listings can take payment for tiers and promotions.
- Sites in the same network share organisations, people and portal accounts; sites in different networks share none of them. See Multisite and networks.
Configuration files the modules read
All of these are optional. A project that declares none of them still runs on engine-dmo; it simply has no tiers, no slots and the portal’s defaults.
| File | Module | Page |
|---|---|---|
config/memberships.yaml | memberships | Partners and the portal |
config/networks.yaml | core (used by the CRM and portal) | Multisite and networks |
config/crm/organisation.yaml, config/crm/person.yaml | crm | CRM |
config/portal/portal.yaml | portal | Partners and the portal |
config/portal/roles.yaml | crm, portal | Partners and the portal |
config/portal/enquiries.yaml | portal | Partners and the portal |
config/sites/<site>/portal.yaml | portal | Portal screens and actions |
config/sites/<site>/promotions.yaml | promotions | Promotions |
config/analytics/reports.yaml | memberreport | Member reports |
config/paidlistings.yaml | paidlistings | Paid listings |
landings: on a route in routes.yaml | directory | Landing pages |
member_editable: in a type’s schema file | memberships, portal | Partners and the portal |
memberships.tiers in site.yaml | memberships | Partners and the portal |
claims: and membership: in engine.yaml | claims, memberships | Claims, Partners and the portal |
engine-dmo config schema --file <name> prints the JSON Schema of any of them (memberships, networks, crm, portal, site-portal, partner-roles, enquiries, promotions, reports), and engine-dmo check validates every one, reporting each problem at its file, line and column.
Staff permissions the modules add
The core staff permissions include the CRM’s (crm.organisation.*, crm.person.*, crm.membership.view, crm.membership.edit, crm.tag.manage, crm.smartlist.manage, crm.claim.review). The modules register these as well:
| Permission | Module | Granted to built-in roles |
|---|---|---|
broadcast.draft, broadcast.send | crm | administrator, partnership-manager |
crm.import | crm | administrator, partnership-manager |
promotions.decide | promotions | administrator, partnership-manager |
affiliates.view | affiliates | administrator, partnership-manager, analyst |
affiliates.manage | affiliates | administrator, partnership-manager |
paidlistings.manage | paidlistings | administrator, partnership-manager |
The built-in owner role holds every permission. Grant the others through config/access/roles.yaml and config/access/grants.yaml as for any staff permission (see Users, roles and permissions).
Partners (the people signing in to the portal) never hold staff permissions. Their permissions come from partner roles, described in Partners and the portal.
Commands the modules add
| Command | What it does | Page |
|---|---|---|
engine-dmo crm ... | Organisations, memberships, invitations, import, merge, erase, duplicate candidates, claims | CRM |
engine-dmo broadcast ... | Draft, test, schedule, send and report broadcasts | CRM |
engine-dmo lists ... | Check, count and run smart lists | CRM |
engine-dmo portal ... | Check, scaffold and inspect the portal | Portal screens and actions |
engine-dmo api member ... | The member API’s OpenAPI document, routes and form descriptions | Portal screens and actions |
engine-dmo affiliates ... | Networks, imports, connections, postback keys | Affiliates |
engine-dmo analytics report, digest, report-run | Member reports and organisation reports | Member reports |
engine-dmo dev tier, dev role, dev slot, dev portal, dev network | Declare tiers, partner roles, slots, portal screens and actions, and networks, with their ids | the page for each |
Run engine-dmo <command> --help for every flag, or engine-dmo help --json for the whole command tree.
Where to start
Turning off what a site does not use
Most modules do nothing until configured, but two answer from the start on every engine-dmo site: the member portal at /members (with the member API) and, through it, the claim form under /members/claim/. engine-dmo describe project shows the portal as portal /members, both, ... even on a site with no partners. To switch it off for the whole install:
portal:
mode: off /members then answers 404, and describe project shows portal /members, off. A single site can do the same in config/sites/<site>/portal.yaml with mode: off. Setting auth.member.registration: invite_only in engine.yaml closes only the claim form.
A site with no listings simply declares no route for the listing and area types; their tables stay empty.
Example projects
The example projects (dmo-minimal with listings, tiers, affiliates and enquiries; dmo-destination with two sites, landing pages, promotions and custom portal screens and actions; dmo-multisite with two sites in two networks) live in the Meridian source repository, under examples/. They are not inside the binary or the container image, and engine-dmo init with no flag writes only the bare starter. engine-dmo init <dir> --from <path> copies a project directory you already have on disk, so it works only with a copy of the repository’s examples/ folder. Without one, build up from the starter with the pages of this section and the dev commands; see Your first project.
Accessibility
What the engine does for accessibility on public pages and what a template author must still do, the accessibility checks in engine-dmo check and engine-dmo a11y audit, the alt text policy, and the accessibility statement, issues register and conformance report the engine generates.
Directory
The listing and area types engine-dmo adds, how to extend them, route and facet them, link a listing to the business behind it, and show places, maps, nearby results and enquiries in templates.