--- name: contentbox-cfml-content-types description: "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." applyTo: "**/*.{cfc,cfm,cfml}" --- # ContentBox Content Types & Custom Fields (CFML) Extend ContentBox content types and add custom fields using CFML. ContentBox supports entries, pages, and ContentStore items with extensible custom field capabilities. ## Content Types ContentBox has three primary content types: | Type | Entity | Service | Description | |------|--------|---------|-------------| | **Entries** | `Entry` | `entryService@contentbox` | Blog posts with dates, categories, comments | | **Pages** | `Page` | `pageService@contentbox` | Static pages with hierarchical structure | | **ContentStore** | `ContentStore` | `contentStoreService@contentbox` | Key-value content blocks | ### Entry Entity ```cfml 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 ```cfml 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 ```cfml 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: ```cfml property name="customFieldService" inject="customFieldService@contentbox"; // Create custom field var 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 var fields = customFieldService.findByContentType( "entry" ); ``` ### Custom Field Types | Type | Description | |------|-------------| | `text` | Single-line text input | | `textarea` | Multi-line text area | | `boolean` | Checkbox toggle | | `select` | Dropdown select | | `date` | Date picker | | `number` | Numeric input | | `media` | Media picker | | `html` | HTML editor | ### Accessing Custom Field Values ```cfml // Get custom field value from entry var subtitle = entry.getCustomFieldValue( "subtitle" ); // Get all custom field values var fields = entry.getCustomFields(); ``` ## Content Helpers (Mixins) Inject mixins into all content objects via settings: ```cfml // In ModuleConfig.cfc configure() settings.contentHelpers = [ "mymodule.models.mixins.MyContentMixin" ]; ``` ### Creating a Content Mixin ```cfml ``` ### Using Content Mixins ```cfml // After injecting, methods are available on all content objects var excerpt = entry.getExcerpt( 150 ); var readTime = entry.getReadingTime(); ``` ## Content Rendering ### Content Renderers ContentBox uses interceptors to process content before rendering: | Renderer | Class | Purpose | |----------|-------|---------| | **Link Renderer** | `LinkRenderer@contentbox` | Process internal links | | **Widget Renderer** | `WidgetRenderer@contentbox` | Process `{widget:...}` shortcodes | | **Setting Renderer** | `SettingRenderer@contentbox` | Process `{setting:...}` shortcodes | | **Markdown Renderer** | `MarkdownRenderer@contentbox` | Process Markdown content | ### Widget Shortcode ``` {widget:WidgetName arg1="value" arg2="value"} ``` ### Setting Shortcode ``` {setting:cb_site_title} ``` ## Content Lifecycle ### Entry Lifecycle ```cfml // 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 ```cfml // Interception points cbadmin_prePageSave cbadmin_postPageSave cbadmin_prePageRemove cbadmin_postPageRemove cbadmin_onPageStatusUpdate cb_onContentRendering ``` ## Content Queries ### EntryService Queries ```cfml property name="entryService" inject="entryService@contentbox"; // Find published entries var entries = entryService.findPublishedContent( max : 10, category : "news", searchTerm: "search term", sortOrder : "publishedDate DESC", siteID : siteId ); // Find by slug var entry = entryService.findBySlug( "my-entry-slug" ); // Find by category var entries = entryService.findByCategory( "news" ); // Find by author var entries = entryService.findByAuthor( authorId ); // Count entries var count = entryService.countPublishedContent( siteID : siteId ); ``` ### PageService Queries ```cfml property name="pageService" inject="pageService@contentbox"; // Find all pages var pages = pageService.findAllWhere( { siteID : siteId, isPublished : true } ); // Find by slug var page = pageService.findBySlug( "about" ); // Find children var children = pageService.findChildren( parentId ); // Find hierarchical var 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 **CFML engines** (Lucee 5+, Adobe ColdFusion 2018+). For BoxLang-specific syntax and features, see the BoxLang variant of this skill.