๐Ÿ”ท Core

contentbox-boxlang-content-types

Use this skill when implementing ContentBox content models and rendering flows, including entries/pages/content stores, custom fields, entity relationships, lifecycle callbacks, and content-type specific behaviors.

$ npx skills add coldbox/skills/contentbox-boxlang/content-types
$ coldbox ai skills install coldbox/skills/contentbox-boxlang/content-types
๐Ÿ”— https://skills.boxlang.io/skills/raw/coldbox/skills/contentbox-boxlang~content-types

ContentBox Content Types & Custom Fields (BoxLang)

Extend ContentBox content types and add custom fields using BoxLang. ContentBox supports entries, pages, and ContentStore items with extensible custom field capabilities.

Content Types

ContentBox has three primary content types:

TypeEntityServiceDescription
EntriesEntryentryService@contentboxBlog posts with dates, categories, comments
PagesPagepageService@contentboxStatic pages with hierarchical structure
ContentStoreContentStorecontentStoreService@contentboxKey-value content blocks

Entry Entity

property name="entryService" inject="entryService@contentbox"

// Entry properties
entry.getEntryID()
entry.getTitle()
entry.getSlug()
entry.getHTMLContent()
entry.getPlainTextContent()
entry.getPublishedDate()
entry.getCreatedDate()
entry.getModifiedDate()
entry.getIsActive()
entry.getIsPublished()
entry.getHits()
entry.getNumberOfComments()

// Relationships
entry.getAuthor()              // Author entity
entry.getCategories()          // Query of categories
entry.getComments()            // Query of comments
entry.getSite()                // Site entity
entry.getFeaturedImage()       // Media entity

// Status
entry.getStatus()              // "published", "draft", "pending"
entry.isPublished()
entry.isDraft()

Page Entity

property name="pageService" inject="pageService@contentbox"

// Page properties
page.getPageID()
page.getTitle()
page.getSlug()
page.getHTMLContent()
page.getPlainTextContent()
page.getCreatedDate()
page.getModifiedDate()
page.getIsActive()
page.getIsPublished()
page.getMenuOrder()            // For ordering in menus
page.getParent()               // Parent page (hierarchical)
page.getChildren()             // Child pages

// Relationships
page.getAuthor()
page.getSite()
page.getFeaturedImage()

ContentStore Entity

property name="contentStoreService" inject="contentStoreService@contentbox"

// ContentStore is a key-value store
contentStoreService.getValue( "key" )
contentStoreService.setValue( "key", "value" )
contentStoreService.deleteValue( "key" )
contentStoreService.getAll()

// ContentStore entity
item.getContentStoreID()
item.getSlug()                 // The key
item.getContent()              // The value
item.getCreatedDate()
item.getModifiedDate()

Custom Fields

ContentBox supports custom fields through the cbCustomField entity and service:

property name="customFieldService" inject="customFieldService@contentbox"

// Create custom field
field = customFieldService.new( {
	name        : "subtitle",
	label       : "Subtitle",
	type        : "text",         // text, textarea, boolean, select, date, number
	required    : false,
	defaultValue: "",
	contentType : "entry",        // "entry", "page", or "all"
	order       : 1
} )
customFieldService.save( field )

// Get custom fields for content type
fields = customFieldService.findByContentType( "entry" )

Custom Field Types

TypeDescription
textSingle-line text input
textareaMulti-line text area
booleanCheckbox toggle
selectDropdown select
dateDate picker
numberNumeric input
mediaMedia picker
htmlHTML editor

Accessing Custom Field Values

// Get custom field value from entry
subtitle = entry.getCustomFieldValue( "subtitle" )

// Get all custom field values
fields = entry.getCustomFields()

Content Helpers (Mixins)

Inject mixins into all content objects via settings:

// In ModuleConfig.bx configure()
settings.contentHelpers = [
	"mymodule.models.mixins.MyContentMixin"
]

Creating a Content Mixin

// models/mixins/MyContentMixin.bx
component {

	function getExcerpt( numeric maxLength = 200 ){
		return left( this.getPlainTextContent(), arguments.maxLength ) & "..."
	}

	function getReadingTime(){
		wordCount = arrayLen( listToArray( this.getPlainTextContent(), " " ) )
		return ceiling( wordCount / 200 )
	}

}

Using Content Mixins

// After injecting, methods are available on all content objects
excerpt  = entry.getExcerpt( 150 )
readTime = entry.getReadingTime()

Content Rendering

Content Renderers

ContentBox uses interceptors to process content before rendering:

RendererClassPurpose
Link RendererLinkRenderer@contentboxProcess internal links
Widget RendererWidgetRenderer@contentboxProcess {widget:...} shortcodes
Setting RendererSettingRenderer@contentboxProcess {setting:...} shortcodes
Markdown RendererMarkdownRenderer@contentboxProcess Markdown content

Widget Shortcode

{widget:WidgetName arg1="value" arg2="value"}

Setting Shortcode

{setting:cb_site_title}

Content Lifecycle

Entry Lifecycle

// Interception points
cbadmin_preEntrySave      // Before entry saved
cbadmin_postEntrySave     // After entry saved
cbadmin_preEntryRemove    // Before entry deleted
cbadmin_postEntryRemove   // After entry deleted
cbadmin_onEntryStatusUpdate  // When status changes
cb_onContentRendering     // Before content rendered

Page Lifecycle

// Interception points
cbadmin_prePageSave
cbadmin_postPageSave
cbadmin_prePageRemove
cbadmin_postPageRemove
cbadmin_onPageStatusUpdate
cb_onContentRendering

Content Queries

EntryService Queries

property name="entryService" inject="entryService@contentbox"

// Find published entries
entries = entryService.findPublishedContent(
	max       : 10,
	category  : "news",
	searchTerm: "search term",
	sortOrder : "publishedDate DESC",
	siteID    : siteId
)

// Find by slug
entry = entryService.findBySlug( "my-entry-slug" )

// Find by category
entries = entryService.findByCategory( "news" )

// Find by author
entries = entryService.findByAuthor( authorId )

// Count entries
count = entryService.countPublishedContent( siteID : siteId )

PageService Queries

property name="pageService" inject="pageService@contentbox"

// Find all pages
pages = pageService.findAllWhere( { siteID : siteId, isPublished : true } )

// Find by slug
page = pageService.findBySlug( "about" )

// Find children
children = pageService.findChildren( parentId )

// Find hierarchical
tree = pageService.getHierarchicalPages( siteId )

Best Practices

  1. Use services โ€” entryService, pageService, contentStoreService
  2. Use findBySlug() โ€” for human-readable URL lookups
  3. Always filter by siteID โ€” in multi-site installations
  4. Use content helpers โ€” for reusable content methods
  5. Use interception points โ€” for content lifecycle hooks
  6. Validate custom fields โ€” ensure data integrity
  7. Use getPlainTextContent() โ€” for excerpts and search
  8. Use getHTMLContent() โ€” for rendering
  9. Handle soft deletes โ€” check isDeleted flag
  10. Cache expensive queries โ€” use CacheBox for performance

Engine Compatibility

This skill targets BoxLang engine. For CFML-specific syntax (Lucee 5+, Adobe ColdFusion 2018+), see the CFML variant of this skill.

Key BoxLang advantages:

  • Cleaner script syntax without <cfcomponent> / <cffunction> tags
  • No parentheses needed for zero-argument function calls
  • #{...}# for inline expression output in .bx templates
  • Modern syntax: ?: null coalescing, ?. safe navigation
  • Native support for modern data structures