Skip to content

Media Library Applet

Overview

The Media Library applet is the file store behind the Customer Portal. You create drives (named containers), put directories and files — images, video, audio, PDF — inside them, and set a share policy per file that decides whether the storefront can show it. It also keeps a small list of categories (labels) for the file store. The CP Commerce Admin applet then points its gallery and slideshow widgets at a drive, offers PDF files as menu-item targets, and can copy post images to and from the library.

It is a master-data applet. Nothing in it posts to stock or the General Ledger, and nothing in it creates a document. Its records live in the platform’s file-management (fm) tables — bl_fm_drive_hdr, bl_fm_file_hdr, bl_fm_drive_login_subject_link, bl_fm_label_hdr — and are served through the core2/tnt/dm/fm/ endpoints. The applet itself is built inside the CP Commerce repository (blg-applets-wavelet-cp-commerce, project media-library-applet) and is registered as media-library-applet.

Where it fits

DirectionApplet / systemHow it connects
DownstreamCP Commerce Admin — Website › Layout Instance › node configurationThe Image Gallery, Video Gallery and Audio Gallery widgets and the slideshow Play List are configured with a drive chosen from this applet’s drives (nodes-edit.component.ts, getDrive() and the selectedDrive / driveGuid controls).
DownstreamCP Commerce Admin — Website › Menu ItemsA menu item of the PDF type lists only files whose mime type contains pdf and whose share policy is ANYONE_WITH_LINK (menu-item-query.component.ts).
DownstreamCP Commerce Admin — Website › Post › Manage Image, Notification › Manage ImagePost images can be created from the library and copied to it (CmsPostImageController: create-from-media-library, create-to-media-library).
DownstreamCP Commerce Admin — Facilities / ActivitiesEach facility and activity keeps its own media links to library files (bl_cms_facilities_media_library_link, bl_cms_activities_media_library_link, both carrying the file’s fm_file_guid and url_key).
DownstreamThe Customer Portal storefront (the wavelet-cp-commerce app)Builds every image URL as core2/tnt/dm/fm/files/file/raw/{url_key} (construct-image-url.pipe.ts). That endpoint is anonymous and serves a file only if its share policy is ANYONE_WITH_LINK (FileController.getFileRawByUrlKey).
UpstreamTenant AdminThe Manage Users tab lists the tenant’s confirmed logins (app_login_principal) as candidates for a drive.
UpstreamTenant permissions (TntFmPermissions)Every drive, file, drive-user and label endpoint checks an API_TNT_DM_FM_* permission unless the login is a tenant owner or admin.
    flowchart LR
  D[Drive] --> F[Directory / File]
  F --> P{Share policy}
  P -- RESTRICTED --> L[login-ep link: only guests in acl_config]
  P -- ANYONE_WITH_LINK --> R[raw link: anonymous]
  R --> W[Storefront widgets, PDF menu items]
  D --> G[CP Commerce Admin gallery and play-list widgets]
  

Screens and menus

The sidebar has two entries, Drive and Categories; the applet opens on Drive (app.routing.ts, empty path redirects to drives). Both work in the usual two-column stack.

Drive

  • Drives Listing — server-side ag-grid: Name, Code, Status, Date Created (sorted newest first). A keyword search (drive name) and an advanced search on Status, Creation Date and Modified Date. The + button opens Create Drive. Each row also triggers a lookup of the creator’s e-mail that is fetched and then discarded (getRowsFactory), so the listing makes one extra call per row for nothing.
  • Create Drive — Drive Code, Drive Name, Status, Description; CREATE.
  • Drive Edit — three tabs. Details (code read-only, name, description, status, created/modified by and date, Delete Drive); Manage Files; Manage Users. SAVE at the top.
  • Manage Files (inside Drive Edit) — a tree grid of the drive’s directories and files (up to 10,000 rows loaded in one call, ordered by sort order), with a quick filter. The + button opens Create File; clicking a row opens Edit File.
  • Create File — File Name, Status, Description, Parent Directory, File Type (DIRECTORY or FILE); for a file, Sort Order and a drop-zone that previews an image, plays a video or renders a PDF before upload. CREATE.
  • Edit File — the same fields (type read-only) with the stored media shown, a replacement drop-zone, Save, Share File and DELETE.
  • Share FileAccess Policy (RESTRICTED / ANYONE_WITH_LINK) and the matching File Link with a Copy Link button. Changing the policy saves immediately; Done only goes back. The “Add people” e-mail and role controls are commented out of the template, so guests cannot be granted from the UI (see Lifecycle).
  • Manage Users (inside Drive Edit) — the drive’s login-subject links; + opens Select Users to add to the drive (a checkbox grid of the tenant’s logins, Add); clicking a row opens Removing user from the drive (Delete).

Categories

  • Categories Listing — Categories Name, Categories Code, Status, Creation Date; keyword search and advanced search (Category Name, Created / Modified Date).
  • Create Category / Category Edit — Category Name, Category Code, Status, Description (edit adds the read-only audit fields and a Delete button).

Settings (gear): System Configuration › Field Settings, Default Selection; Client Side Permissions › Applet Access; Server Side Permissions › Permission Set, User Permission, Team Permission, Role Permission; Integration › Triggers (the shared webhook stub); Developer Tools › Applet Log, Reset Applet State (both route to the 404 page). Personalization: Default Selection and the shared Sidebar screen.

No screenshots are published for this applet; none has been captured on a tenant free of customer data.

Configuration

Before you can use it

  • A confirmed tenant login for anyone who will manage drives, and — unless the login is a tenant owner or admin — the file-management permissions from TntFmPermissions, assigned through Settings › Permission Set / User / Team / Role Permission: API_TNT_DM_FM_DRIVE_CREATE / UPDATE / DELETE / READ (or OWNER / ADMIN) for drives; the same set with FILE for directories and files, plus API_TNT_DM_FM_FILE_UPDATE_ACL_CONFIG_POLICY for the Share File screen; API_TNT_DM_FM_DRIVE_SUBJECT_* for Manage Users; API_TNT_DM_FM_LABEL_* and API_TNT_DM_FM_LABEL_LIST_READ for Categories (DriveController, FileController, DriveSubjectController). Uploading a file’s content needs FILE_UPDATE (or owner / admin), not FILE_CREATE — the multipart create endpoint checks the update permission (FileController.createFile, multipart variant).
  • For the Categories menu only: a label list with code Media_library and label type CP_COM must already exist in bl_fm_label_list_hdr. Create Category looks it up and attaches every new category to it (categories-create.component.ts L90-96); nothing in this applet or in the backend creates that list. If it is missing, the create screen fails in the browser before it can call the server (see Troubleshooting).
  • For the storefront: a website configured in CP Commerce Admin whose widgets or menu items will reference a drive or a file, and files set to ANYONE_WITH_LINK.

Applet settings

Settings are applet-local in layout only. app.routing.ts routes settings/field-settings and settings/default-selection to the applet’s own components under components/settings-container/; the shared FieldConfigurationComponent from blg-shared-utilities is not routed at all. Neither local screen is functional, so the four-proof test (declared, rendered, persisted, consumed) fails for every control:

  • Field Settings is a static template — eight slide toggles (Lines Settings: Unit Discount, SST/VAT/GST, WHT, Blanket Order; Department Settings: Segment, G/L Dimension, Profit Center, Project) with no form binding, a SAVE button with no click handler and an empty component class (field-configuration.component.html, .ts). Rendered; not declared, not persisted, not consumed.
  • Default Selection renders Default Branch and Default Location and writes each change into this.appletContainer.bl_applet_exts under APPLET_SETTINGS. appletContainer is never assigned: the appletSettings$ input is never bound (the routed host is the shared <app-settings> wrapper, whose onSaveDefault is commented out and which renders the child through a bare router-outlet), so changing a value throws in the browser console and SAVE emits to nobody. No code in the applet reads DEFAULT_BRANCH or DEFAULT_LOCATION. Rendered; not persisted, not consumed.
  • Personalization › Default Selection has the same defect (personal-default-settings.component.ts, the subscription that would load the container is commented out at L31-38).

No exposed, working control found (routes and settings components checked at commit 813f007c8, 2026-08-18).

Settings in other applets that control this applet

SettingWhere it is setEffect here
A widget’s Select Drive / Select Drive For Play ListCP Commerce Admin › Website › Layout Instance › node configuration (Image / Video / Audio Gallery, slideshow)Decides which of this applet’s drives a storefront gallery reads. The widget lists drives by code.
A menu item’s PDF targetCP Commerce Admin › Website › Menu ItemsOnly files with mime type pdf and policy ANYONE_WITH_LINK are offered.

Feature visibility / permissions

The applet resolves no client-side permission of its own: app.component.ts only wires the layout, and the Applet Access screen is the shared stub. The registry table bl_applet_client_side_perm_dfn could not be queried for media-library-applet while this page was written (the read-only tunnel was down on 2026-09-15), so the row count is unverified. Access is decided by the server-side API_TNT_DM_FM_* permissions listed under Before you can use it; the API answers not authorised when none applies. The Permission Set screen’s target picker (permission.resolver.ts) knows the CP Commerce target views (Company, Branch, CMS Website, Label, Label List, and so on) but has no entry for drives or files, so a permission scoped to one drive cannot be assigned from this applet.

Fields

Required-ness below is the server’s (DriveDataConsistencyObject, FileDataConsistencyObject, DriveSubjectDataConsistencyObject, FmLabelDataConsistencyObject) unless marked client. The server validators check identity, status, revision, audit columns and referential links only; they do not check that a name or code is present, so those rules are enforced by the form alone.

Create Drive / Drive Edit › Details

FieldMeaningRequiredNotes / validation
Drive Codebl_fm_drive_hdr.codeYes (client)Max 255 characters; read-only after creation. The server does not check uniqueness (DriveDataConsistencyObject has no code rule).
Drive Namebl_fm_drive_hdr.nameYes (client)Max 255
Statusbl_fm_drive_hdr.statusYesACTIVE / INACTIVE; the server rejects a null status (DRIVE_HDR_OBJECT_STATUS_IS_NULL_OR_EMPTY)
Descriptionbl_fm_drive_hdr.descriptionNo
Created By / Date, Modified By / DateAudit columns, shown as the login’s e-mail principalRead-only

Create File / Edit File

FieldMeaningRequiredNotes / validation
File Namebl_fm_file_hdr.file_nameYes (client)Max 255. For an uploaded file the server overwrites it with the uploaded file’s own name (FileController.createFile multipart, uploadFile, replaceFile), so the name typed here survives only for directories.
Statusbl_fm_file_hdr.statusYesACTIVE / INACTIVE
Descriptionbl_fm_file_hdr.descrNo
Parent Directorybl_fm_file_hdr.parent_guidNoOffers NONE plus the drive’s directories. A file’s parent must be a directory: FILE_HDR_OBJECT_PARENT_TYPE_IS_WRONG when the parent is itself a file; FILE_HDR_OBJECT_PARENT_GUID_DOES_NOT_EXIST when it is gone.
File Typebl_fm_file_hdr.typeYesDIRECTORY or FILE; read-only on edit. A directory with no parent must belong to a drive (FILE_HDR_OBJECT_DRIVE_GUID_IS_NULL).
Sort Orderbl_fm_file_hdr.sort_orderNoFiles only; the file tree and the storefront galleries order by it
File (drop-zone)The binary, stored as an app_file_item and linked by app_file_item_guidYes on create for type FILE (client: Please select a file)Accepts audio/*, video/*, image/*, .pdf. On edit, dropping a new file replaces the stored one (replaceFile) after the header is saved; the server records name, extension and mime type from the upload.

Share File

FieldMeaningRequiredNotes
Access Policybl_fm_file_hdr.acl_policy.accessRESTRICTED (default on create) or ANYONE_WITH_LINK. Saved immediately on change through acl-policy/backoffice-ep/{guid}; the change also cascades to the file’s direct children when it is a directory (overwriteChildren defaults to true, one level only).
File LinkComputed…/fm/files/file/raw/{url_key}?tenantCode=… for ANYONE_WITH_LINK; …/fm/files/file/url-key/login-ep/{url_key}?tenantCode=… for RESTRICTED. Copy Link copies it.

Manage Users

FieldMeaningRequiredNotes
Email / Phone (select grid)A tenant login (app_login_principal.principal_id)Checkbox grid; Add writes a bl_fm_drive_login_subject_link with status ACTIVE. The server rejects a link whose drive or subject does not exist and a duplicate drive-subject pair (DRIVE_SUBJECT_OBJECT_COMPOUND_PK_ALREADY_EXISTS).
Email, Status (edit)Shown read-onlyThe e-mail is never resolved: the listing’s Email column displays the subject_guid and the edit screen’s Email box is blank (users-listing.component.ts L92, L117).

Create Category / Category Edit

FieldMeaningRequiredNotes
Category Namebl_fm_label_hdr.nameYes (client)
Category Codebl_fm_label_hdr.codeYes (client)Not checked for uniqueness by the label validator
Statusbl_fm_label_hdr.statusYesACTIVE / INACTIVE
Descriptionbl_fm_label_hdr.descrNo
(hidden) label listbl_fm_label_hdr.label_list_guidSet to the Media_library / CP_COM list found at load time

Lifecycle and effects

Endpoints. The applet uses five blg-akaun-ts-lib services, all under core2/tnt/dm/fm/: DriveServicedrives; FileServicefiles (JSON create for directories, multipart POST files with a data part for uploads, PUT files, PUT files/replace-file/{guid}, PUT files/acl-policy/backoffice-ep/{guid}, GET files/file/{guid} for the preview, DELETE files/{guid}); DriveSubjectServicedrive-subject-links; FileLabelServicelabels/backoffice-ep; FileLabelListServicelabel-lists/backoffice-ep. It does not call the CMS website, menu, post, widget, pricing-scheme or shipping-pricebook endpoints — those services are only injected by the shared permission resolver.

What it writes.

  • Create Drive posts a bl_fm_drive_hdr; SAVE re-reads the drive by guid and puts name, code, description and status back; Delete Drive is a soft delete of the header and its extensions (DriveUow.delete). Files are not touched when their drive is deleted; they stay in bl_fm_file_hdr with a drive_guid that no longer resolves.
  • Create File for a DIRECTORY posts a header only. For a FILE it sends one multipart request; the server stores the binary as an app_file_item, generates a random url_key, and stamps the default share policy: acl_policy.access = RESTRICTED and acl_config.guests = [{ the creator, EDITOR }] (FileService.createFile, getDefaultAclCfgPolicy).
  • Save on Edit File re-reads the header, puts the editable fields, then — if a new file was dropped — calls replace-file. updateFile on the server always restores url_key, acl_config and acl_policy from the database, so an edit can never change a share link or policy by accident; only acl-policy / acl-cfg-policy and reset-url-key can.
  • DELETE on Edit File soft-deletes the header (FileUow.delete). For a directory the applet first checks for children and refuses (Cannot delete this Directory as it has child directories/files); the server has no such rule, and deleting a file does not remove the stored app_file_item.
  • Share File › Access Policy puts { access } to acl-policy/backoffice-ep/{guid} (permission API_TNT_DM_FM_FILE_UPDATE_ACL_CONFIG_POLICY). The aclConfigAddGuest$ and aclConfigDeleteGuest$ effects exist and would call acl-cfg-guest-add / acl-cfg-guest-delete, but no screen dispatches them: the Add people controls are commented out and Done dispatches nothing.
  • Manage Users › Add posts one bl_fm_drive_login_subject_link; Delete removes it. Nothing reads these links: no query in DriveUow or FileUow joins bl_fm_drive_login_subject_link, and no other backend package references the table. Adding a user to a drive therefore grants no access; access is decided by the API_TNT_DM_FM_* permissions and, for reading a file’s content, by its share policy.
  • Categories post, put and soft-delete bl_fm_label_hdr rows. No screen attaches a category to a drive or a file — there is no label-link call anywhere in the applet — so a category is a standalone record until something else uses it.

How the share policy is enforced. GET files/file/raw/{url_key} is an anonymous tenant endpoint that returns the binary only when acl_policy.access == ANYONE_WITH_LINK; otherwise not authorised. GET files/file/url-key/login-ep/{url_key} requires a login and that the login appears in the file’s acl_config.guests with role VIEWER, EDITOR or COMMENTER (FileService.fulfillAclCfgRead). Because the UI can only add the creator as a guest (by default) and cannot add anyone else, a RESTRICTED link works for the uploader alone. The storefront always uses the raw link, so every file the storefront must display has to be ANYONE_WITH_LINK.

Posting proof. Not a document applet: no ServerDocTypes entry, no signums, no JournalPostingTypeHandler handler, no stock processor. It writes the bl_fm_* tables and app_file_item only.

Related applets

  • CP Commerce Admin — the consumer: gallery and play-list widgets read a drive, PDF menu items read ANYONE_WITH_LINK files, post images move to and from the library, facilities and activities link library files. Its own Manage Image screen is a separate image store for posts, not this applet.
  • Tenant Admin — the confirmed logins offered by Manage Users, and where the API_TNT_DM_FM_* permissions a media manager needs are ultimately granted.

Troubleshooting

SymptomCauseFix
An image or PDF referenced by a storefront widget or menu item does not loadThe file’s share policy is RESTRICTED (the default). The storefront requests files/file/raw/{url_key}, which serves only ANYONE_WITH_LINK files; the PDF picker in CP Commerce Admin hides restricted files entirelyEdit File › Share File › set Access Policy to ANYONE_WITH_LINK
The RESTRICTED link works for the uploader and for nobody elseThe login-ep link admits only logins listed in the file’s acl_config.guests; the only guest the UI ever writes is the creator, and the Add people controls are commented outUse ANYONE_WITH_LINK, or add guests through the API (acl-cfg-guest-add/backoffice-ep/{guid})
A user added under Manage Users still cannot open the drive or its filesbl_fm_drive_login_subject_link is written but read by nothing on the server; access comes from the API_TNT_DM_FM_* permissionsGrant the permissions under Settings › Permission Set / User Permission
Create on Create File does nothing, or the toast Please select a fileType FILE was chosen without dropping a fileDrop or browse a file, then Create
The saved file name is not the one typedThe server replaces file_name with the uploaded file’s own name on upload and on replaceRename the file on disk before uploading; the typed name is kept only for directories
Cannot delete this Directory as it has child directories/filesClient-side guard in Edit FileDelete or move the children first (Parent Directory on each child)
Create Category fails in the browser before anything is savedNo label list with code Media_library and type CP_COM exists, so label.data[0] is undefined at load and the create request is never sentCreate that label list (the backend seeds none); then retry
Changing Default Branch / Default Location in Settings throws and saves nothingThe Default Selection screen’s container is never loaded and no code reads those keysNothing to configure; treat both settings screens as inert
Only one of several ticked users is addedSelect Users › Add fills a single link object inside the loop and posts it once, so the last ticked row wins; the duplicate check queries the wrong column (subject_guid = drive guid) and never firesAdd users one at a time
The Manage Users grid shows a GUID under EmailThe e-mail lookup is commented out (users-listing.component.ts L92)Match the GUID against Tenant Admin’s login list
Drive deleted but its files still appear in searchesDrive deletion soft-deletes the header only; files keep their drive_guidDelete the files first, then the drive
not authorised on create, save or deleteMissing API_TNT_DM_FM_DRIVE_* / FILE_* / DRIVE_SUBJECT_* / LABEL_* permission and the login is not a tenant owner or admin. Uploads need FILE_UPDATE, not FILE_CREATEAssign the permission set

No issue in the shared task tracker describes a user-facing failure of this applet; the repository history for the project is the two 2026-07 sub-query clean-ups and the 2024-11 sort-order and thumbnail work.

Related documentation

  • E-Commerce module — where the file store sits between the back office and the storefront.
  • CP Commerce Admin — the widgets, menu items, posts and facilities that reference drives and files.
Last updated on