To add to the general topic of opaque FreeMarker context objects: I know the pain of debugging those and i have come up with something like an "inspector" for FreeMarker objects (the code of which I'm not able to share [too convoluted/complicated], but I gladly run any context objects through it if you ask nicely 😘). The code MattV posted above goes in the same directions, but you'll eventually hit roadblocks, promise, been there, done that...
Back to it, the following example output of my "inspector" is for the coreNode context object:
"coreNode": {
"name": "lithium.eval.velocity.CommunityTemplateModel@d7a4737b",
"type": "string, hash, hash_ex",
"properties": {
"parent": "null",
"shortTitle": "redacted",
"permissions": "lithium.eval.velocity.NodePermissionsTemplateModel@2acc5ec1",
"ancestorNodes": {
"type": "sequence, enumerable, indexable",
"values": []
},
"id": "redacted",
"ancestors": {
"type": "sequence, enumerable, indexable",
"values": []
},
"settings": "lithium.eval.velocity.SettingsTemplateModel@88b91bb",
"nodeType": "community",
"nodeTypeName": "Community",
"webUi": "lithium.eval.velocity.CoreNodeWebUiTemplateModel@1de296b0",
"displayId": "redacted",
"nodeId": 1,
"nodeCreationDate": "02.07.2013",
"description": "redacted",
"membership": "lithium.eval.velocity.MembershipTemplateModel@3e413c48",
"title": "redacted",
"urls": "lithium.eval.velocity.urls.CommunityUrlsTemplateModel@6a137472",
"authenticationUrls": "lithium.eval.velocity.urls.AuthenticationManagerUrlsTemplateModel@f62e1e0"
},
"methods": {
"getClass()": "method,indexable",
"getWebUi()": "method,indexable",
"getNodeCreationDate()": "method,indexable",
"getAncestorNodes()": "method,indexable",
"getMembership()": "method,indexable",
"getTitle()": "method,indexable",
"getNodeType()": "method,indexable",
"getRootCategory()": "method,indexable",
"rawNodeIsA()": "method,indexable",
"getNodeId()": "method,indexable",
"getPermissions()": "method,indexable",
"rawNodeAsA()": "method,indexable",
"getAncestors()": "method,indexable",
"hasAncestor()": "method,indexable",
"getSettings()": "method,indexable",
"getShortTitle()": "method,indexable",
"getId()": "method,indexable",
"getDescription()": "method,indexable",
"getNodeCreationDateForUser()": "method,indexable",
"getNodeTypeName()": "method,indexable",
"hashCode()": "method,indexable",
"getUrls()": "method,indexable"
}
}
I always find it interesting to look at that and compare it to the official docs 😉...
Let's do the same for your page object:
{
"name": "lithium.web2.data.page.PageTemplateModel@6c1dbccc",
"type": "string, hash, hash_ex",
"methods": {
"getVersion()": "method,indexable",
"getClass()": "method,indexable",
"getName()": "method,indexable",
"getContent()": "method,indexable",
"getInteractionStyle()": "method,indexable",
"hashCode()": "method,indexable",
"equals()": "method,indexable",
"toString()": "method,indexable"
},
"properties": {
"rtl": false,
"version": "v2.0",
"content": "lithium.web2.data.page.PageContentTemplateModel@c8a65f5",
"name": "BizAppsPage",
"context": "PageContextTemplateModel{threadTemplateModel=null, messageTemplateModel=null, userTemplateModel=null, restV2EntityTemplateModel=null}",
"interactionStyle": "none"
}
}
and for page.context:
{
"name": "PageContextTemplateModel{threadTemplateModel=lithium.eval.velocity.ThreadTemplateModel@db22dc20, messageTemplateModel=lithium.eval.velocity.MessageTemplateModel@5a60cda0, userTemplateModel=null, restV2EntityTemplateModel=null}",
"type": "string, hash, hash_ex",
"methods": {
"getClass()": "method,indexable",
"getUser()": "method,indexable",
"getMessage()": "method,indexable",
"getEntity()": "method,indexable",
"hashCode()": "method,indexable",
"equals()": "method,indexable",
"getThread()": "method,indexable",
"toString()": "method,indexable"
},
"properties": {
"thread": "lithium.eval.velocity.ThreadTemplateModel@db22dc20",
"message": "lithium.eval.velocity.MessageTemplateModel@5a60cda0",
"user": "null" // afaik only available on profile page
}
}
have to go level by level, now for context.page.thread:
{
"name": "lithium.eval.velocity.ThreadTemplateModel@db22dc20",
"type": "string, hash, hash_ex",
"properties": {
"discussionStyle": "forum",
"topicMessage": "lithium.eval.velocity.MessageTemplateModel@78d2c42d",
"webUi": "lithium.eval.velocity.ThreadWebUiTemplateModel@1c5d086f",
"lastReply": "lithium.eval.velocity.MessageTemplateModel@1b2c0f44"
},
"methods": {
"getClass()": "method,indexable",
"getBoard()": "method,indexable",
"getTopicMessage()": "method,indexable",
"getWebUi()": "method,indexable",
"getLastReply()": "method,indexable",
"hashCode()": "method,indexable",
"equals()": "method,indexable",
"getDiscussionStyle()": "method,indexable",
"toString()": "method,indexable"
}
}
I'm not seeing a property "board" here, but maybe it's not enumerable? I see the method "getBoard()" though, so lets see if that returns something (EDIT: I did try to call page.context.thread.board directly and it returned the same, so it seems that this might be the reason it is not documented, because it does not even show up as a property when doing this kind of "code analysis"):
{
"name": "lithium.eval.velocity.BoardTemplateModel@2fbe5e84",
"type": "string, hash, hash_ex",
"properties": {
"parent": "lithium.eval.velocity.CoreNodeTemplateModel@bf665dc6",
"shortTitle": "redacted",
"permissions": "lithium.eval.velocity.NodePermissionsTemplateModel@6923aecb",
"ancestorNodes": {
"type": "sequence, enumerable, indexable",
"values": []
},
"id": "redacted",
"ancestors": {
"type": "sequence, enumerable, indexable",
"values": []
},
"settings": "lithium.eval.velocity.SettingsTemplateModel@2eb40570",
"nodeType": "board",
"nodeTypeName": "Board",
"webUi": "lithium.eval.velocity.CoreNodeWebUiTemplateModel@ea82e1a",
"displayId": "redacted",
"nodeId": 197,
"nodeCreationDate": "11.11.2013",
"discussionStyle": "forum",
"description": "",
"membership": "lithium.eval.velocity.MembershipTemplateModel@7b20459e",
"title": "redacted"
},
"methods": {
"getClass()": "method,indexable",
"getWebUi()": "method,indexable",
"getNodeCreationDate()": "method,indexable",
"getAncestorNodes()": "method,indexable",
"getMembership()": "method,indexable",
"getTitle()": "method,indexable",
"getNodeType()": "method,indexable",
"getRootCategory()": "method,indexable",
"rawNodeIsA()": "method,indexable",
"getNodeId()": "method,indexable",
"getPermissions()": "method,indexable",
"rawNodeAsA()": "method,indexable",
"getDiscussionStyle()": "method,indexable",
"getAncestors()": "method,indexable",
"hasAncestor()": "method,indexable",
"getSettings()": "method,indexable",
"getShortTitle()": "method,indexable",
"getId()": "method,indexable",
"getDescription()": "method,indexable",
"getDiscussionStyleString()": "method,indexable",
"getNodeCreationDateForUser()": "method,indexable",
"getNodeTypeName()": "method,indexable",
"hashCode()": "method,indexable"
}
}
that should give you a decent idea of what is available in terms of properties and methods (which in most cases should never be called directly btw).