Content Management System
Five flat editors over the storefront content the Customer Portal reads — themes, content categories, widgets, menus and pages — with no website selector, no preview and no publishing step: a row is live as soon as its status is active, and it touches neither stock nor the ledger. It is the older, generic view of the content CP Commerce Admin now edits website by website. Because this applet cannot say which website a page or theme belongs to, a theme saved here never appears on your website and a page saved here is missing from the website’s Posts tab — create anything website-specific in CP Commerce Admin.
Overview
The Content Management System applet is a set of five flat editors over the same storefront content that Customer Portal (CP Commerce) storefronts read: Theme (colour and font presets), Content Category (a hierarchy of content labels), Widgets (named instances of the platform’s widget catalogue), Menu (navigation items inside a menu list) and Pages (rich-text posts). Each editor is a listing with create, edit and delete; there is no website selector, no preview and no publishing step — a row is live for the storefront as soon as its status is ACTIVE.
It is the older, generic view of content that the CP Commerce Admin applet now edits per website, tab by tab. Both applets edit the same content (see Lifecycle and effects), so a menu item or content category saved here appears in CP Commerce Admin and vice versa — with one important difference: this applet cannot say which website a post or theme belongs to (see Troubleshooting). It writes nothing to stock or the General Ledger.
Where it fits
| Position | Applet / system | Why |
|---|---|---|
| Module | E-Commerce, CRM & Digital | Storefront content |
| Same content, richer screens | CP Commerce Admin | Its Website › Menu List, Posts, Label List and Content Category tabs write the same menus, posts, label lists and content categories, scoped to a website. Its Layout Instance tab is what places widgets on a storefront page, and it picks each widget from a fixed list built into that applet — not from the widget instances named here |
| Reads what is saved here | Customer Portal storefront (web and mobile app) | Menus, posts, content categories and themes are rendered by the storefront’s widgets |
| Images | Media Library | Post images uploaded from the Pages editor go to the post’s own image store, not to a Media Library drive |
| Orders | Shopping Cart (Internal) | Not connected: nothing here touches carts or documents; listed because readers arriving from the E-Commerce module expect it |
Screens and menus
Sidebar: a CMS separator, then Theme, Content Category, Widgets, Menu, Pages, then Settings and Personalization entries (added 2026-10-05: before that the entry was Setting, matched no route, and landed you on the Theme listing). Opening the applet lands on Theme.
Every editor has the same shape: an ag-grid listing on the left with a + (create) action, a create form, and an edit form that opens from a row. Since the September 2026 build the listings page through the server with the shared advanced-search component, and the Content Category screens use the two-column layout of the newer applets.
| Menu | Listing columns | Create / edit form | What it reads and writes |
|---|---|---|---|
| Theme | Title, Template, Status, Creation Date, Updated Date | Title, Template (one of the platform’s theme templates), Description, Status, then fifteen colour and font-size controls; edit adds an image grid with Add Image | Theme templates (read only) and theme instances |
| Content Category | Name, Category Level, Status, Creation Date, Updated Date | Name, URL Key, Description, Status, optional Parent Category (nesting toggle) | Label lists (read, first list only) and content categories |
| Widgets | Title, Type, Status, Creation Date, Updated Date | Title, Widget Type (from the widget catalogue), Status; edit adds an image grid with Add Image | The widget catalogue (read only) and widget instances |
| Menu | Title, Menu Level, Menu List, Sort Number, Link Type, Status, Creation Date, Updated Date | Title, Menu List, Link Type (Component, Page, Content Category, External Link), Link or External URL, Sort Number, Status, optional Parent Menu (nesting toggle); the menu icon is set for you, not chosen (see Fields); Redirect Type is present in the template but commented out | Menu lists (read), menu items, and pages / content categories to fill the Link picker |
| Pages | Title, Author Name, Status, Creation Date, Updated Date | Title, URL Key, Status, Content Category, rich-text Content (Froala editor with inline image upload); Layout Type and Widget are present in the template but commented out; edit adds an image grid with Add Image | Content categories (read) and pages |
Advanced search per listing: Pages by Page Title, Author Name, Status; the other four by their title/name and status.
Configuration
Before you can use it
- Theme templates — at least one theme template on the tenant. The Theme form’s Template picker is filled from it and a theme cannot be saved without one. Templates are platform data; no screen in this applet or in CP Commerce Admin creates them.
- A label list — the Content Category form silently uses the first label list the tenant has. If the tenant has none, every category save fails because no label list is set. Label lists are created per website on the CP Commerce Admin Label List tab.
- A menu list — a menu item cannot be saved without one. Menu lists are created on the CP Commerce Admin website Menu List tab.
- Widget catalogue — the platform’s widget catalogue fills the Widget Type picker; nothing in this applet adds to it.
- Server-side permissions on the CMS endpoints:
API_TNT_DM_ERP_CMS_THEME_INSTANCES_*,API_TNT_DM_ERP_CMS_MENUS_*,API_TNT_DM_CMS_LABEL_*,TNT_API_CMS_POST_*andTNT_API_CMS_WIDGET_INSTANCE_*(CREATE/READ/UPDATE/DELETE, plusADMIN/OWNER). This applet has no permission screens of its own; assign them from an applet that has (CP Commerce Admin › Settings, or Tenant Admin).
Applet settings
Two controls, both added on 2026-10-05 with the applet’s internationalisation work. Settings → Default Selection saves DEFAULT_LANGUAGE_CODE, DEFAULT_BRANCH and DEFAULT_LOCATION as applet settings, and Personalization saves the same three per user; the applet reads only the language code, to switch its own labels. The rest of the Settings screen is the shared module: Field Settings shows eight toggles (Unit Discount, SST/VAT/GST, WHT, Blanket Order, Segment, G/L Dimension, Profit Center, Project) that are bound to nothing and save nothing, and Webhook, Feature Visibility and the four permission screens are the shared ones. No CMS screen reads a branch or location setting.
Document behaviour settings
Not applicable — the applet creates no documents.
Settings in other applets that control this applet
| Setting | Where it is set | Effect here |
|---|---|---|
| Website › Content Category label list | CP Commerce Admin › Website › Details | Decides which label list the storefront reads categories from. This applet always writes categories into the tenant’s first label list, which is not necessarily the one a website points at |
| Website › Menu List | CP Commerce Admin › Website › Menu List tab | Creates the menu lists offered by the Menu form’s Menu List picker |
| Website › Layout Instance | CP Commerce Admin › Website › Layout Instance | The place widgets are put on a storefront page. A layout node chooses its widget from a list built into CP Commerce Admin and carries its own settings; it never points at a widget instance saved here |
Feature visibility / permissions
No client-side permission is read anywhere in the applet, and it registers none. Access is decided entirely by the server-side permissions listed above; a login without …_READ on an endpoint sees an empty listing and no error.
Fields
Required-ness below is what the backend validators enforce; the form’s own required markers are listed separately where they differ.
Two things hold on every editor. Status starts empty on every create form, and Save stays greyed out until you pick ACTIVE or INACTIVE. INACTIVE keeps the row in the listing and off the storefront, while Delete takes it out of every listing for good — so to take a menu item or page down for a season and bring it back later, set it INACTIVE rather than deleting it. And no create screen here shows the server’s refusal: none of the five create forms handles an error, so a refused save leaves the form open with no message. If Save appears to do nothing, go back to the listing; no new row means the save was refused, and Troubleshooting names the usual causes.
Theme
The form insists on Title, Description, Status and Template before Save lights up, although the server needs only Template and Status. Title is what the Theme listing shows; Description stays on the theme and appears nowhere else in this applet.
| Field | Required (backend) | Notes |
|---|---|---|
| Template | Yes — …THEME_HDR_GUID_IS_NULL_OR_EMPTY, …DOES_NOT_EXIST | Picker filled from the platform’s theme templates; the picked template’s title is also stored as the theme’s type |
| Status | Yes — …STATUS_IS_NULL_OR_EMPTY | ACTIVE / INACTIVE |
| Primary, Secondary, Background, Card Background, Header Background, Header Text, Title Font, Subtitle Font, Normal Text Font, Login/Register Background, Login/Register Text colours; Header, Title, Subtitle, Normal Text font sizes | No | Colour pickers and text inputs, stored together as one theme configuration; edit rewrites the matching entries in place |
| Website | Yes — …WEBSITE_HDR_GUID_IS_NULL_OR_EMPTY, …DOES_NOT_EXIST | Not on the form. Create writes a constant website identifier built into the applet; see Troubleshooting |
Content Category
The form insists on Name, URL Key, Description and Status; the server insists on Name and Status.
| Field | Required (backend) | Notes |
|---|---|---|
| Name | Yes — the save is refused without it | Every category is saved with the same fixed code, so the name is the only thing that tells two categories apart, and nothing refuses a second category with a name already in use. Pages record their category by name (see Pages), so renaming a category does not follow through to pages already saved — whoever renames it reopens each page under the old name and picks the category again. |
| URL Key | No (form: yes) | Nothing checks that another category does not already use it. A menu item that links to this category keeps its own copy of the URL Key as it was when the link was chosen, and that copy changes only when someone picks the link again (read from the code, not tried). After changing a URL Key, open each menu item that links here and choose the link again. |
| Parent Category | No | Only when the nesting toggle is on. The picker offers top-level categories from every label list, not only the one this category is written into. The category level becomes 2 (1 when no parent). Editing a nested category and saving makes it top-level again: the edit form never fills its Parent Category list, and a save with no parent picked in that session writes level 1 and no parent (read from the code, not tried). Test it on one nested category — open it, Save, and read Category Level in the listing. Until it is fixed, whoever maintains the storefront’s categories checks that column after every edit and nests the category again on CP Commerce Admin’s Content Category tab. |
| — | Yes (code, label list) | The form fixes a code, a category type and the label list (always the tenant’s first); none is editable |
Widgets
The form insists on Title, Widget Type and Status; the server insists on Status only. Nothing in the screens read points at a widget instance, by title or otherwise: the only link a page could carry to one is the Pages Widget box, which is commented out, and CP Commerce Admin’s layout nodes pick their widget from a list built into that applet. Before building widget instances here, ask whoever runs the storefront what on it reads them; if nobody can say, they change nothing a shopper sees.
| Field | Required (backend) | Notes |
|---|---|---|
| Widget Type | No (form: yes) | Picker from the widget catalogue; the catalogue title is stored as the instance’s type |
| Thumbnail | Checked by the backend (…PIX_THUMBNAIL_GUID_IS_NULL_OR_EMPTY) | Not on the form; the Add Image screen uploads it after creation |
Menu
The form insists on Title, Menu List, Link Type, Sort Number and Status; the server insists on Title, Menu List, Link Type, Status and the level.
| Field | Required (backend) | Notes |
|---|---|---|
| Menu List | Yes — refused when empty or when the list does not exist | Picker from the tenant’s menu lists |
| Link Type | Yes — …MENU_LINK_TYPE_IS_NULL_OR_EMPTY | COMPONENT, PAGE, CONTENT CATEGORY, EXTERNAL LINK (Product and Product Category are not offered) |
| Link / External URL | No | A Page or Content Category target, or an external URL; Page picker from pages, category picker from content categories. For a Page or Content Category the item stores the target and a copy of its URL Key as it was at that moment; the copy is refreshed only when the link is picked again (read from the code, not tried) |
| Sort Number | No (form: yes) | The box accepts any text, but the server stores a whole number, so type digits only; anything else will not save, and — like every create here — the screen shows no message (read from the code, not tried) |
| Parent Menu | Level yes — …MENU_LEVEL_IS_NULL_OR_EMPTY; parent must exist | Nesting toggle; the menu level becomes 2 (1 without). The picker offers top-level items from every menu list, not only the list chosen above, so check the parent belongs to the same list. Editing a nested item and saving makes it top-level again unless you pick the parent again in that session (read from the code, not tried): after any edit of a nested item, read Menu Level in the listing |
| Menu icon | Not on the form | Set for you from the link: a Component link takes that component’s built-in icon, a Page or Content Category link clears it, and an External Link sets none. The listing does not show it. To change an icon, change what the item links to |
Pages
| Field | Required (backend) | Notes |
|---|---|---|
| Title | No (form: yes) | The backend checks identifiers, status and references only — not the title |
| URL Key | No (form: yes) | Nothing checks that another page does not already use it — the server compares identifiers, status and references, not URL Keys. A menu item linking to this page keeps its own copy of the URL Key (see Menu › Link), so after changing it, pick the link again on those items |
| Content Category | No (form: yes) | The page stores the category’s name as text, not a link to the category, so the server’s category check never applies to pages saved here and a later rename of the category does not reach them. The picker lists categories from every label list by name, so two categories with one name look identical here |
| Layout Type, Widget | — | Not on the form — both controls are commented out of the create and edit templates. A page’s layout is assigned in CP Commerce Admin › Posts › Layout Instance |
| Content | No | Froala rich text; inline images are uploaded to the post’s own image store (JPEG/PNG) |
| Author | No | Name and identity from the signed-in user’s profile; edit records who modified it |
| — | — | The form fixes the post type (page) and a code; not editable |
| Website | Only validated when present | Never set by this applet — see Troubleshooting |
| Images (edit › Add Image) | — | The post’s own image store, with an Image Type of Main Image or Additional Image; JPEG/PNG only |
Lifecycle and effects
Saving. Save writes the whole record back, not only the fields you changed; the listings and pickers page through the server like every other applet’s.
Statuses. There is no draft/final lifecycle. Each row carries ACTIVE or INACTIVE as chosen on the form; the storefront treats INACTIVE as hidden. Delete is immediate — the edit forms call the service on the first click with no confirmation dialog. Deleted rows disappear from every listing.
What it writes. Theme instances and their configuration (Theme), content categories (Content Category), widget instances and their images (Widgets), menu items (Menu), posts and post images (Pages). It reads — and never creates — theme templates, the widget catalogue, label lists and menu lists.
Shared with CP Commerce Admin. That applet’s Menu List tab creates menu lists, and its menu items are the same menu items edited here (link types there include Layout Instance); its Posts tab writes posts tagged with the open website; its Label List and Content Category tabs write label lists and content categories. CP Commerce Admin does not edit themes or widget instances, so Theme and Widgets are edited only here.
Posting proof block. Not a document applet: no document type, no journal posting, no stock processor, no documents. Amount and quantity signums do not apply; nothing to void.
Related applets
- CP Commerce Admin — the per-website editor for the same menus, posts and labels, and the only place that creates menu lists, label lists and page layouts; prefer it for anything website-specific.
- Media Library — drives and categories for storefront media; this applet’s image uploads bypass it.
- Shopping Cart (Internal) — the document a storefront checkout produces; unrelated to content but the next step in the E-Commerce module.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| A theme saved here never appears on your website | The applet writes a constant website identifier, built into it, on every theme instance; the form has no Website field | Until the applet is fixed, themes cannot be assigned to a tenant’s website from this screen |
| Theme save fails because the website does not exist | The hard-coded website GUID is not a website on this tenant | Same as above |
| A page created here is not listed on the CP Commerce Admin Posts tab | Posts here are saved with no website; the Posts tab filters by the open website | Create website posts from CP Commerce Admin › Website › Posts |
| Content category save fails because no label list is set, or the category is missing on the storefront | The form uses the tenant’s first label list; a tenant with none cannot save, and a website that points at another list will not see the category | Create a label list on the website first; create website-scoped categories from CP Commerce Admin › Website › Content Category |
| Menu save fails because no menu list is set | Menu List left empty | Pick a menu list (created on the CP Commerce Admin website Menu List tab) |
| There is no Layout Type or Widget box on a page | Both controls are commented out of the form | Assign a layout to a post from CP Commerce Admin › Posts › Layout Instance |
| Save does nothing and no message appears | The server refused the save; no create form in this applet shows the refusal | Check the causes above — theme website, label list, menu list, a non-numeric Sort Number |
| A nested category or menu item shows level 1 after an edit | The edit form saves no parent unless one is picked again in that session | For a menu item, pick its parent again on the edit form and save. A category cannot be re-nested here: the edit form’s parent list is never filled, and saving it writes level 1 with no parent, so an edit here un-nests the category. Nest it again on CP Commerce Admin’s Content Category tab, which edits the same categories and does fill the parent list. Read the level column after every edit of a nested row |
| Theme › Add Image saves nothing | The upload step is switched off in the applet; the screen only navigates back | No image can be attached to a theme from this applet |
| Toast Please Select Valid Image format (JPEG or png)! | Post or widget image upload with any other type | Convert to JPEG or PNG |
| Clicking Setting in the sidebar opens the Theme listing | A build from before 2026-10-05: that entry matched no route, and the applet falls back to the Theme listing | Nothing to fix on the tenant; the current build has a Settings screen (above) |
| A row vanished after one click on Delete | Delete is immediate; no confirmation dialog is shown | Re-create the row; the storefront reflects the change at once |
| A listing is empty but the CP Commerce Admin tab shows rows | The login lacks the …_READ permission for that content; the applet shows no error | Grant the permission set from an applet that has permission screens |
Related documentation
- E-Commerce module — where storefront content sits next to the cart and marketplace connectors.
- CP Commerce Admin — the website-scoped editor for the same content.
- Media Library — storefront media drives and categories.