CMS Adapter Reference

This document covers the mapping layer between the CMS document model and the WI rendering stack. The CMS uses string-based blockCode and containerTypeCode identifiers; the WI renderer uses ContentType and WiContainerType enums. The adapter in projects/cms/master/contentviewer/cms-content-adapters.ts performs this translation. Content always flows one direction: CMS → WI renderer.

grid_view Block Type Codes CmsBlock.blockCode

info
blockCode is a string constant stored in the CMS database on each CmsBlock row. The adapter maps it to ContentType enum values used by ContentBlockRendererComponent.
blockCode ContentType Enum Value contentRef format Notes
RICHTEXTRichText0HTML stringpropsJson.html
IMAGEImage1URL stringpropsJson.url + optional caption
VIDEOVideo2URL stringpropsJson.url + loop + duration
PDFPDF3URL stringpropsJson.url
CHECKLISTChecklist4JSON arraypropsJson.items[]
WARNINGWarning5Level integer stringpropsJson.level 1–3
DECISIONDecisionBranch6JSON arraypropsJson.branches[]
FILEFile7URL stringpropsJson.url + label
QRCODEQRCode8Data stringpropsJson.data to encode
TABLEDataTable9JSON objectpropsJson.headers + rows
BANNERBanner10JSON objectpropsJson.title, subtitle, bgColor

view_quilt Container Type Codes CmsContainer.containerTypeCode

containerTypeCode WiContainerType Enum Value Zones Zone name pattern
SINGLESingle01 zonemain
TWOCOLTwoColumn12 zonesleft, right
BANNERBanner21 zonebanner
ACCORDIONAccordion3N zonessection-1, section-2, …
TABTab4N zonestab-1, tab-2, …
Single
SINGLE
WiContainerType = 0
main
Two Column
TWOCOL
WiContainerType = 1
left
right
Banner
BANNER
WiContainerType = 2
banner
Accordion
ACCORDION
WiContainerType = 3
chevron_right section-1
chevron_right section-2
chevron_right section-3
section-N (dynamic)
Tab
TAB
WiContainerType = 4
TAB-1
TAB-2
+
active zone content
tab-N (dynamic)

data_object propsJson Schemas per Block Type

Each CmsBlock.propsJson is a JSON string. The adapter calls JSON.parse(block.propsJson) and maps fields to WiStepContent.

text_fields
RichText
blockCode: RICHTEXT
ContentType = 0
propsJson schema
{
  "html": "<p>Content…</p>",
  "plainText": "Content…"
}
Mapped to
contentRef ← propsJson.html
Write From Library Upload Embed URL
image
Image
blockCode: IMAGE
ContentType = 1
propsJson schema
{
  "url": "https://cdn…/img.jpg",
  "caption": "Alt text",
  "zoomRegion": {
    "x": 0, "y": 0,
    "w": 100, "h": 100
  }
}
Mapped to
contentRef ← propsJson.url
Write From Library Upload Embed URL
videocam
Video
blockCode: VIDEO
ContentType = 2
propsJson schema
{
  "url": "https://cdn…/vid.mp4",
  "caption": "Video caption",
  "loopMedia": 0,
  "displayDurationSecs": 0
}
Mapped to
contentRef ← propsJson.url · loopMedia and displayDurationSecs hoisted to block fields
Write From Library Upload Embed URL
picture_as_pdf
PDF
blockCode: PDF
ContentType = 3
propsJson schema
{
  "url": "https://cdn…/doc.pdf",
  "caption": "Download PDF"
}
Mapped to
contentRef ← propsJson.url
Write From Library Upload Embed URL
checklist
Checklist
blockCode: CHECKLIST
ContentType = 4
propsJson schema
{
  "items": [
    {
      "id": "c1",
      "label": "Verify PPE",
      "checked": false,
      "isOptional": false
    }
  ]
}
Write From Library Upload Embed URL
warning
Warning
blockCode: WARNING
ContentType = 5
propsJson schema
{
  "level": 2,
  // 0=None 1=Caution 2=Warning 3=Danger
  "message": "Optional extra message"
}
WarningLevel
0 = None 1 = Caution 2 = Warning 3 = Danger
Write From Library Upload Embed URL
call_split
DecisionBranch
blockCode: DECISION
ContentType = 6
propsJson schema
{
  "branches": [
    {
      "id": "b1",
      "label": "Yes → proceed",
      "description": "If condition met",
      "targetStepId": 5
    }
  ]
}
Write From Library Upload Embed URL
attach_file
File
blockCode: FILE
ContentType = 7
propsJson schema
{
  "url": "https://cdn…/file.xlsx",
  "label": "Download Report",
  "description": "Monthly summary"
}
Write From Library Upload Embed URL
qr_code
QRCode
blockCode: QRCODE
ContentType = 8
propsJson schema
{
  "data": "https://forms.example.com/audit",
  "size": "medium"
  // size: "small" | "medium" | "large"
}
Mapped to
contentRef ← propsJson.data
Write From Library Upload Embed URL
table_chart
DataTable
blockCode: TABLE
ContentType = 9
propsJson schema
{
  "headers": ["Part", "Qty", "Unit"],
  "rows": [
    ["Electrode", "5", "pcs"]
  ]
}
Write From Library Upload Embed URL
view_day
Banner
blockCode: BANNER
ContentType = 10
propsJson schema
{
  "title": "Section Title",
  "subtitle": "Supporting text",
  "bgColor": "#0f172a",
  "bgImageUrl": ""
}
Write From Library Upload Embed URL

code Adapter Code — cms-content-adapters.ts

folder_open
File location: projects/cms/master/contentviewer/cms-content-adapters.ts
One-way dependency: CMS project → WI library (not the reverse).

BLOCK_TYPE_MAP and CONTAINER_TYPE_MAP

TypeScript
import { ContentType, WiContainerType } from 'projects/wi/src/lib/models/wi.models';

export const BLOCK_TYPE_MAP: Record<string, ContentType> = {
  RICHTEXT: ContentType.RichText,    // 0
  IMAGE:    ContentType.Image,       // 1
  VIDEO:    ContentType.Video,       // 2
  PDF:      ContentType.PDF,         // 3
  CHECKLIST:ContentType.Checklist,   // 4
  WARNING:  ContentType.Warning,     // 5
  DECISION: ContentType.DecisionBranch, // 6
  FILE:     ContentType.File,        // 7
  QRCODE:   ContentType.QRCode,      // 8
  TABLE:    ContentType.DataTable,   // 9
  BANNER:   ContentType.Banner,      // 10
};

export const CONTAINER_TYPE_MAP: Record<string, WiContainerType> = {
  SINGLE:    WiContainerType.Single,    // 0
  TWOCOL:    WiContainerType.TwoColumn, // 1
  BANNER:    WiContainerType.Banner,    // 2
  ACCORDION: WiContainerType.Accordion, // 3
  TAB:       WiContainerType.Tab,       // 4
};

cmsToContainers() function

TypeScript
export function cmsToContainers(
  cmsContainers: CmsContainer[]
): WiContainer[] {
  return cmsContainers.map((c, idx) => ({
    wiContainerId:     -(idx + 1),          // negative = CMS-origin (read-only in WI)
    containerType:     CONTAINER_TYPE_MAP[c.containerTypeCode]
                         ?? WiContainerType.Single,
    containerLabel:    c.label,
    slNo:              idx + 1,
    blocks:            c.blocks.map((b, bi) => mapBlock(b, bi)),
  }));
}

function mapBlock(b: CmsBlock, idx: number): WiStepContent {
  const props = JSON.parse(b.propsJson ?? '{}');
  const contentType = BLOCK_TYPE_MAP[b.blockCode] ?? ContentType.RichText;
  const contentRef  = resolveContentRef(contentType, props);

  return {
    wiStepContentId:      -(idx + 1),
    wiStepId:             0,
    slNo:                 idx + 1,
    contentType,
    contentSource:        ContentSource.CMS,    // always CMS-sourced
    contentRef,
    mediaCaption:         props.caption ?? '',
    displayDurationSecs:  props.displayDurationSecs ?? 0,
    loopMedia:            props.loopMedia ?? 0,
    zoneName:             b.zoneName,
    blockInContainerPos:  idx + 1,
    isActive:             1,
  };
}

function resolveContentRef(type: ContentType, props: Record<string, unknown>): string {
  switch (type) {
    case ContentType.RichText:  return (props.html as string) ?? '';
    case ContentType.Warning:   return String(props.level ?? 0);
    case ContentType.Checklist:
    case ContentType.DecisionBranch:
    case ContentType.DataTable:
    case ContentType.Banner:    return JSON.stringify(props);
    case ContentType.QRCode:    return (props.data as string) ?? '';
    default:                    return (props.url as string) ?? '';
  }
}

schema CmsBlock & CmsContainer Interfaces

TypeScript
interface CmsBlock {
  cmsBlockId:   number;
  blockCode:    string;       // RICHTEXT | IMAGE | VIDEO | …
  zoneName:     string;       // main | left | right | section-N | tab-N
  slNo:         number;       // sort order within zone
  propsJson:    string;       // JSON string; shape depends on blockCode
  sourceCode:   string;       // WRITE | LIBRARY | UPLOAD | URL
  cmsRefId:     number | null; // FK to referenced CMS document (source=LIBRARY)
  isActive:     0 | 1;
}

interface CmsContainer {
  cmsContainerId:    number;
  containerTypeCode: string;  // SINGLE | TWOCOL | BANNER | ACCORDION | TAB
  label:             string;  // section heading
  slNo:              number;
  blocks:            CmsBlock[];
  isActive:          0 | 1;
}

interface CmsDocument {
  cmsDocumentId:   number;
  spaceCode:       string;
  typeCode:        string;
  title:           string;
  statusCode:      string;    // DRAFT | REVIEW | APPROVED | PUBLISHED | ARCHIVED
  version:         string;
  containers:      CmsContainer[];
  createdBy:       number;
  createdDate:     string;
  lastModifiedDate:string;
}

table_chart ContentSource Matrix (CMS context)

info
In the CMS designer, sources are labelled differently from the WI designer: Write = Inline · From Library = CMS · Upload = FileStore · Embed URL = ExternalURL
Block Type Write (Inline) From Library (CMS) Upload (FileStore) Embed URL (External)
RichText ✓✓——
Image ✓✓✓✓
Video ✓✓✓✓
PDF ✓✓✓✓
Checklist ✓✓——
Warning ✓———
DecisionBranch ✓———
File —✓✓✓
QRCode ✓——✓
DataTable ✓✓——
Banner ✓✓——

timeline CMS Document Lifecycle States

DRAFT
→
IN REVIEW
→
APPROVED
→
PUBLISHED
→
ARCHIVED
statusCode Editable Renderable in WI/LMS Transitions to Notes
DRAFT ✓— IN_REVIEW, PUBLISHED Default on create; visible only to author & admins
IN_REVIEW Restricted— APPROVED, DRAFT (rejected) Minor edits permitted; major changes require DRAFT reversion
APPROVED —Preview only PUBLISHED Locked; awaiting publish scheduling
PUBLISHED —✓ ARCHIVED, DRAFT (new version) Renderable by WI renderer & LMS; new edit creates v+1 as DRAFT
ARCHIVED —— DRAFT (restore) Retained for audit; not rendered; can be restored to DRAFT
warning
A WI step that references a CMS block with ContentSource.CMS will fail to render if the referenced document is not in PUBLISHED state. The WI renderer shows a placeholder block. Always verify referenced documents are published before publishing the WI.