Content Management System
Overview
The Content Management System applet is a set of five flat editors over the CMS tables 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 write the same tables (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; the applet’s route prefix is crm/cms |
| Same tables, richer screens | CP Commerce Admin | Its Website › Menu List, Posts, Label List and Content Category tabs write bl_cms_menu_hdr, bl_cms_post_hdr, bl_cms_label_list_hdr and bl_cms_label_hdr scoped to a website. Its Layout Instance tab is what actually places widgets on a storefront page; this applet only names widget instances |
| 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 (cms/posts/add-files), 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 (from shared/menu-items.ts; everything else in that file is commented out): a CMS separator, then Theme, Content Category, Widgets, Menu, Pages, and a Setting entry whose route (setting) does not exist — clicking it falls through to the wildcard redirect and lands on the Theme listing. The applet’s route prefix is applets/tnt/wavelet/crm/cms; opening the prefix with no sub-path redirects to member-listing, which also does not exist, so the practical landing screen is always 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 2026-09-01 build (PR #1, “[FE][Sub Query Remove]”) the listings no longer run raw sub-queries against the entity table; they page through the CMS endpoints with the shared advanced-search component, and the Content Category screens were rebuilt on the two-column container used by the newer applets.
| Menu | Listing columns | Create / edit form | 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 | themes (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 labels |
| Widgets | Title, Type, Status, Creation Date, Updated Date | Title, Widget Type (from the widget catalogue), Status; edit adds an image grid with Add Image | widgets (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), menu icon; Redirect Type is present in the template but commented out | menu-lists (read), menus, and posts / labels to fill the Link picker |
| Pages | Title, Author Name, Status, Creation Date, Updated Date | Title, URL Key, Status, Content Category, Layout Type, Widget, rich-text Content (Froala editor with inline image upload); edit adds an image grid with Add Image | labels (categories) and posts |
Advanced search per listing (models under models/advanced-search-models/): Pages by Page Title, Author Name, Status; the other four by their title/name and status.
Also in the bundle but reachable from no route: an About Us general-configuration editor, a CSV customer-profile upload dialog, and a marketing landing page inherited from the platform shell. They are declared in app.module.ts only.
Configuration
Before you can use it
- Theme templates — at least one row in
bl_cms_theme_hdr(GET cms/themes). The Theme form’s Template picker is filled from it and a theme cannot be saved without one (CMS_THEME_INSTANCE_HDR_OBJECT_THEME_HDR_GUID_IS_NULL_OR_EMPTY). 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 (
getLabelList()takesresp.data[0]). If the tenant has none, every category save fails withCMS_LABEL_HDR_OBJECT_LABEL_LIST_GUID_IS_NULL_OR_EMPTY. Label lists are created per website on the CP Commerce Admin Label List tab. - A menu list — the Menu form’s Menu List picker reads
cms/menu-lists; a menu item needs one (CMS_MENU_HDR_OBJECT_MENU_LIST_GUID_IS_NULL_OR_EMPTY). Menu lists are created on the CP Commerce Admin website Menu List tab. - Widget catalogue —
bl_cms_widget_hdrrows (GET cms/widgets) fill the Widget Type picker. - 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), all inTntCmsPermissions. 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
No exposed control found (routes and module checked at commit 3b4e90c4). The applet has no settings container, no field-configuration, default-selection, printables, webhook, feature-visibility or permission routes, and no personalization screen. The Setting sidebar entry points at a route that does not exist. Nothing is read from bl_applet_ext.APPLET_SETTINGS.
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 (bl_cms_website_hdr.content_category_label_list_guid) | 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 only place widget instances are placed on a page; instances named here do nothing until a layout node references them |
Feature visibility / permissions
No client-side permission is read anywhere in the applet’s code (no permission reference in components/), and it ships no bl_applet_client_side_perm_dfn rows that the code would consult. Access is decided entirely by the server-side TntCmsPermissions constants 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.
Theme
| Field | Column | Required (backend) | Notes |
|---|---|---|---|
| Title | bl_cms_theme_instance_hdr.title | No (form: yes) | Listing column |
| Template | theme_hdr_guid (from bl_cms_theme_hdr); the picked title is also stored in type | Yes — …THEME_HDR_GUID_IS_NULL_OR_EMPTY, …DOES_NOT_EXIST | Picker filled from cms/themes |
| Description | descrption (sic, column name) | No | |
| Status | 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 | one bl_cms_theme_instance_ext row, param_code = THEME_CONFIG, param_type = JSON, a themeConfig array of {title, value, type} | No | Colour pickers and text inputs; edit rewrites the matching array entries in place |
| Website | website_hdr_guid | Yes — …WEBSITE_HDR_GUID_IS_NULL_OR_EMPTY, …DOES_NOT_EXIST | Not on the form. Create writes a constant GUID hard-coded in theme-config-create.component.ts; see Troubleshooting |
Content Category
| Field | Column | Required (backend) | Notes |
|---|---|---|---|
| Name | bl_cms_label_hdr.name | Yes — CMS_LABEL_HDR_OBJECT_NAME_IS_NULL_OR_EMPTY | Form: required |
| URL Key | url_key | No (form: yes) | |
| Description | descr | No | |
| Status | status | Yes | ACTIVE / INACTIVE |
| Parent Category | parent_guid; sets level_value 2 (1 when no parent) | No | Only when the nesting toggle is on |
| — | code = CMS_CODE, txn_type = CMS_CATEGORY, label_list_guid = the tenant’s first label list | Yes (code, label list) | Fixed by the form; not editable |
Widgets
| Field | Column | Required (backend) | Notes |
|---|---|---|---|
| Title | bl_cms_widget_instance_hdr.title | No (form: yes) | |
| Widget Type | widget_hdr_guid and type (the catalogue title) | No | Picker from cms/widgets |
| Status | status | Yes | |
| Thumbnail | pix_thumbnail_guid | Checked by CmsWidgetInstanceDataConsistencyObject (…PIX_THUMBNAIL_GUID_IS_NULL_OR_EMPTY) | Not on the form; the Add Image screen uploads through widget-instances after creation |
Menu
| Field | Column | Required (backend) | Notes |
|---|---|---|---|
| Title | bl_cms_menu_hdr.title | Yes — CMS_MENU_HDR_OBJECT_TITLE_IS_NULL_OR_EMPTY | |
| Menu List | menu_list_guid | Yes — …MENU_LIST_GUID_IS_NULL_OR_EMPTY, …DOES_NOT_EXIST | Picker from cms/menu-lists |
| Link Type | menu_link_type | Yes — …MENU_LINK_TYPE_IS_NULL_OR_EMPTY | COMPONENT, PAGE, CONTENT CATEGORY, EXTERNAL LINK (Product and Product Category are commented out) |
| Link / External URL | target_guid + target_url (Page or Content Category), or target_url only (External Link) | No | Page picker from cms/posts, category picker from cms/labels |
| Sort Number | sort_code | No (form: yes) | Integer |
| Status | status | Yes | |
| Parent Menu | parent_guid; sets menu_level 2 (1 without) | Level yes — …MENU_LEVEL_IS_NULL_OR_EMPTY; parent must exist | Nesting toggle |
| Menu icon | property_json.menu_icon | No | Chosen on the form; the listing does not show it |
Pages
| Field | Column | Required (backend) | Notes |
|---|---|---|---|
| Title | bl_cms_post_hdr.title | No (form: yes) | The header validator checks GUID, revision, status and referenced GUIDs only |
| URL Key | url_key | No | |
| Status | status | Yes — CMS_POST_HDR_OBJECT_STATUS_IS_NULL_OR_EMPTY | |
| Content Category | post_category (the label’s GUID as text) | No | Picker from cms/labels |
| Layout Type, Widget | form controls layout, widget | No | Not saved — neither is copied into the container on create or edit |
| Content | content_body | No | Froala rich text; inline images are PUT to core2/tnt/dm/cms/posts/add-files/{postGuid} (JPEG/PNG) |
| Author | author_name, author_guid, created_by_guid (create), modified_by_guid (edit) | No | From the signed-in user’s profile |
| — | post_type = PAGE, code = CMS_GENERAL_CONFIGURATION, a random pix_thumbnail_guid | — | Fixed by the form |
| Website | website_hdr_guid | Only validated when present | Never set by this applet — see Troubleshooting |
| Images (edit › Add Image) | post image store via posts/add-files with an Image Type of Main Image or Additional Image | — | JPEG/PNG only |
Lifecycle and effects
Endpoints. All through blg-akaun-ts-lib services under core2/tnt/dm/cms/: theme-instances (and themes, read), labels (and label-lists, read), widget-instances (and widgets, read), menus (and menu-lists, read), posts. Create is POST, Save is PUT on the whole container (service.edit), Delete is DELETE /{guid}. Every listing and picker sends the standard paged getByCriteria query.
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 (the confirmation-dialog references in menu-config-edit and the theme listing are declared but never opened). Deleted rows disappear from every listing (hdr.status != 'DELETED' filters in CmsMenuUow, CmsLabelUow, CmsPostUow, CmsWidgetInstanceUow).
What it writes. bl_cms_theme_instance_hdr + bl_cms_theme_instance_ext (Theme), bl_cms_label_hdr (Content Category), bl_cms_widget_instance_hdr (+ its image extension on upload) (Widgets), bl_cms_menu_hdr (Menu), bl_cms_post_hdr and post images (Pages). It reads bl_cms_theme_hdr, bl_cms_widget_hdr, bl_cms_label_list_hdr, bl_cms_menu_list_hdr.
Shared with CP Commerce Admin. That applet’s Menu List tab creates bl_cms_menu_list_hdr and its menu items write bl_cms_menu_hdr through the same MenuService (link types there include Layout Instance); its Posts tab writes bl_cms_post_hdr with website_hdr_guid set to the open website; its Label List and Content Category tabs write bl_cms_label_list_hdr and bl_cms_label_hdr. CP Commerce Admin does not use theme-instances or widget-instances outside two unused temp services, so Theme and Widgets are edited only here.
Posting proof block. Not a document applet: no ServerDocTypes entry, no JournalPostingTypeHandler handler, no stock processor, no bl_fi_generic_doc_* rows. 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 | theme-config-create.component.ts writes a constant website_hdr_guid (a GUID hard-coded in the source) 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 with CMS_THEME_INSTANCE_HDR_OBJECT_WEBSITE_HDR_GUID_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_hdr_guid; the Posts tab filters by the open website | Create website posts from CP Commerce Admin › Website › Posts |
Content category save fails with CMS_LABEL_HDR_OBJECT_LABEL_LIST_GUID_IS_NULL_OR_EMPTY, 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 with CMS_MENU_HDR_OBJECT_MENU_LIST_GUID_IS_NULL_OR_EMPTY | Menu List left empty | Pick a menu list (created on the CP Commerce Admin website Menu List tab) |
| Layout Type and Widget on a page are lost after Save | Neither control is copied into the post container on create or edit | Assign a layout to a post from CP Commerce Admin › Posts › Layout Instance |
| Theme › Add Image saves nothing | The upload call in theme-config-edit-image-create.component.ts is commented out; 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 | The setting route does not exist; the wildcard redirects to theme-listing | There are no settings; nothing to fix on the tenant |
| 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 endpoint’s …_READ permission in TntCmsPermissions; the applet shows no error | Grant the permission set from an applet that has permission screens |
The applet repository has one issue, the sub-query removal that produced the 2026-09-01 build (gh:bigledger/blg-applet-akaun-platform-cms-applet#1); no user-reported failures are on record.
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.