CSNODE Quick Start Guide
originally published May 2019 | 🕐️ 14 minute read
Introduction
This document will explain how to use and extend csNode.
In a nutshell, csNode is a wrapper for LLNode, to provide a more consistent interface that can be used in a programmatic way for various applications (the REST API is the first consumer). Although still in its infancy, csNode is an API layer that can be used in lieu of certain LLNode calls.
This document assumes the reader has knowledge of Builder (10.0.x) and/or the Content Server IDE (10.5.x).
As a quick start guide, an exhaustive list of all possible actions and related input parameters will not be provided. To learn more, run the Info
method on a node, role or action.
The examples in this document are closer to pseudo-code than production ready code; often lacking error handling in order to illustrate new concepts.
CSNode OSpace Overview
The Root object has three main classes: ObjectsWithPrototypes, Prototypes and Subsystems.
ObjectsWithPrototypes
Values by themselves (without metadata to describe them) are not terribly useful. This is where prototypes come into play, which are divided into Objects and Actions. Each “object” class has its own dedicated “action” class. The Objects class contains Members, Nodes and Roles. Nodes is the wrapper for specific LLNodes while Members is the wrapper for user/group functionality. Roles encompass shared functionality across multiple subtypes (categories, classifications, etc.).
Unlike LLNode with its top-heavy inheritance model, a given csNode can take various reusable components (actions and roles) and construct itself during run-time.
Prototypes
You probably won't need to work with this class at all but this is where the supported data types for the prototypes are defined. You will notice that these are all simple data types, you will not find Dynamic and Assoc here. These “complex” data types are often misused and result in code that is more difficult to use and maintain.
Subsystems
You shouldn't need to modify anything in here, this is where subsystems are defined to support objects, actions and prototypes. There are a few public methods in here which are useful (NewNode
, GetNode
, GetRootNode
).
UnitTestStubs
This is the only orphan in the csNode ospace, which contains some test stubs that need just a bit of modification to work. Steps to create a test:
- Orphan the given test into your
<ospace>_test
module. - Modify and run the
0 Setup
method. - Modify the TestSubclassSetupValues method (if needed). Depending on the test, other methods may need to be overwritten.
- Run the test by calling the
0 Test
method.
Of course, you can orphan from UNITEST:UnitTest
to create additional tests. These are just pre-canned ones to reduce maintenance and avoid duplication.?
Using the csNode API
All actions (node and role) are called in the exact same manner; set zero or more parameters and then call the Go
method.
NOTE: Since the actions are attached at run-time, you may need to check their existence before referencing them to avoid possible script crashes. For example:
if ( csNode.HasAction( "browse" ) ) checkVal = csNode.Browse().Go() end
NOTE: Objects in OScript are not deleted when they go out of scope. Since a csNode is an object, it should be wrapped in a Frame for automatic garbage collection. Failure to do so will result in memory leaks. For example:
Frame f f = $KERNEL.FrameWrapper.New( csNode )
If you are looping though a series of csNodes, you can add each frame wrapped csNode to a List. For example:
List garbageCollection garbageCollection = List.SetAdd( garbageCollection, $KERNEL.FrameWrapper.New( csNode ) )
NOTE: Referencing local features/script is often done using the dot operator (.) which implies the current object (this.
). Since csNode makes use of the super
function, it needs to pass in the object to be acted upon (as a convention, the variable is named self
). If you see self
in the function declaration, make sure you are calling self.
and not this.
or .
, otherwise you will be working with the wrong object.
Get Node (existing)
To get a csNode, all you need is a program context and a node ID. For example:
Dynamic checkVal ObjRef csNode Frame f checkVal = $CSNODE.NodeSubsystem.GetNode( prgCtx, nodeID ) if ( checkVal.ok == TRUE) csNode = checkVal.csNode f = $KERNEL.FrameWrapper.New( csNode ) else ... end
A csNode contains both the data and the class, unlike DAPINODE (data) and LLNode (class).
Get Node Properties
Once you have a csNode, you can access its properties via the available getters. For example:
name = csNode.GetName()
A csNode does not have any setters by design. To update node properties, run the Update
action.
Update Node Properties
Set one or more properties on the Update
action and then call the Go
method. For example:
csNode.Update().SetName("New Name") csNode.Update().SetDescription("New Description") checkVal = csNode.Update().Go() if ( checkVal.ok != TRUE ) ... end
If the update was successful, the current csNode in memory will be refreshed with the new values.
Get Node (new)
To get a new csNode, all you need is a program context and a subtype. For example:
Dynamic checkVal ObjRef csNode Frame f checkVal = $CSNODE.NodeSubsystem.NewNode( prgCtx, $TypeFolder ) if ( checkVal.ok == TRUE) csNode = checkVal.csNode f = $KERNEL.FrameWrapper.New( csNode ) else ... end
You can't do too much with a new csNode other than view its class properties and call the Create
action.
Create Node
After getting a new csNode (for the desired subtype), set any properties (name and parent ID are required) on the Create action then call the Go method. For example:
csNode.Create().SetParentID(2000) csNode.Create().SetName("New Folder") checkVal = csNode.Create().Go() if ( checkVal.ok == TRUE ) nodeID = checkVal.result.id else ... end
If the create was successful, the current csNode in memory will be refreshed with the new values.
Delete Node
After getting an existing csNode, call the Go
method on the Delete
action. For example:
checkVal = csNode.Delete().Go() if ( checkVal.ok != TRUE ) ... end
The csNode will still be in memory after the delete until it goes out of scope.
Browse Node
After getting an existing csNode, set zero or more parameters on the Browse
action and then call the Go
method. For example:
csNode.Browse().SetPageSize(100) csNode.Browse().SetPageNumber(2) checkVal = csNode.Browse().Go() if ( checkVal.ok == TRUE ) data = checkVal.result.data else ... end
By default, the browse action will default to page 1 and 25 items per page.
Info
The Info
method can be called on a node, role or action to get more detailed information about the specified object.
For a given node, data
will contain the property values (name, description, etc.) while definitions
will describe each property (which could be used in UI rendering). The definitions_order
key will return a suggested order of the definitions. The definitions_map
key (if specified) will return any relationships between the definitions. The applicable actions and roles are listed in available_actions
and available_roles
respectively. If the node is a container, the valid child subtypes are listed in addable_types
. The subtype is returned in type
, the display name is returned in type_name
and additional class properties are returned in type_info
. For example:
info = csNode.Info()
For a given node action, data
will contain the values of input parameters while definitions
will describe each input parameter (which could be used in validation and/or UI rendering). The subtype is returned in type
and its display name in type_name
. The action is returned in action
and its display name in action_name
. For example:
csNode.Create().Info()
For a given role, the applicable actions are listed in available_actions
. The subtype is returned in type
and its display name in `type_name. The role is returned in role and its display name in role_name. For example:
csNode.Classifications().Info()
For a given role action, data
will contain the values of input parameters while definitions
will describe each input parameter (which could be used in validation and/or UI rendering). The subtype is returned in type
and its display name in type_name
. The action is returned in action
and its display name in action_name
. The role is returned in role
and its display name in role_name
. For example:
csNode.Classifications().Add().Info()
Extending the csNode API
This section assumes you already have an existing and working LLNode that lives outside of LLIAPI (if your node lives in LLIAPI, you can add content to the CSNODE ospace).
The instructions provided assume that Builder is the IDE (CS 10.0.x). Some steps may not be applicable in the Content Server IDE (10.5.x).
CSNode contains a series of convenience methods to create methods and populate features. Currently, the Content Server IDE implementation is a multi-step process (see Appendix B).
Common Properties
- Orphan
CSNODE:Root:ObjectsWithPrototypes:Objects:Nodes
or an appropriate child into your ospace and give it a name. - Overwrite the
Setup
function. The value forenabled
should be TRUE. The value fortype
should be the subtype of your LLNode. The value foractions
is a list of actions that your csNode will support. The value forroles
is a list of roles that your csNode will support (roles applicable to all subtypes, such as categories, do not need to be listed here). For now, leaveactions
androles
alone. When done, saveSetup
and run it. - Run the
BuildFeatures
function. This will copy certain features from LLNode/WebNode into your csNode as a convenience. You should only need to run this once, unless of course something were to change in LLNode or WebNode. - Build your ospace by running
<ospace>.Globals.BuildOSpace
and then restart Builder (may not be required in the Content Server IDE).
You should now be able to retrieve common properties (name, description, create date, etc.) on your node (see Using the csNode API - Get Node Properties).
Troubleshooting
Check the node subsystem registry to make sure your csNode was registered properly:
CSNODE:Subsystems:ObjectSubsystems:NodeSubsystem:<temp>.registry
Try running Setup
and BuildOspace
again and restart Builder.
Custom Properties
You can skip this section if your LLNode does not have subtype specific properties. You will get the common properties for free via inheritance (name, description, create date, etc.). For a custom properties example see:
CSNODE:Root:ObjectsWithPrototypes:Objects:Nodes:URL
- Overwrite the
SetupPrototypes
function. For each property, you will need to declare a prototype. Prototypes have a default set of values, so you only need overwrite where necessary (“name” is required). If the property is an ID that can be expanded, set the persona (see Appendix A). Save when finished (you can modify this at any time during design). - Run
BuildPrototypes
to create a feature and getter for each prototype defined inSetupPrototypes
. The default getters will just return the value in the feature (this behavior can be overwritten if desired).BuildPrototypes
will overwrite feature values but it will not overwrite the getters. If you remove a prototype fromSetupPrototypes
, you will need to remove the feature and getter manually. - Overwrite the
Load
function to assign values to the custom features during run-time. - You should now be able to retrieve common and custom properties on your node (see “Using the csNode API - Get Node Properties”).
Troubleshooting
Make sure you called super.SetupPrototypes
(with followAncestors
condition) when you overwrote SetupPrototype
.
Make sure you called super.Load
when you overwrote Load
.
Set a breakpoint in Load
to debug.
Common Actions
Depending on your LLNode, there may existing actions that you can add to your csNode (Browse
, Delete
, Copy
, Move
, etc.). If your LLNode does not have any custom properties, you can use the Create
and Update
actions (if applicable). If your LLNode has an associated volume, you may be able to use BrowseGeneric
(to avoid having to create a custom browse action).
- Overwrite the
Setup
function and specify any common actions. When done, saveSetup
and run it. Restart Builder as the template for a given csNode is cached after first use. - You should now be able to perform these actions on a csNode.
Troubleshooting
Check the actions feature for the particular action.
Check that the action has been registered properly:
CSNODE:Subsystems:ActionSubsystems:NodeActionSubsystem:<temp>.registry
Modifying Actions
If your LLNode does not have any custom metadata, you can skip this section. This section assumes we are modifying an existing create action (other actions would require similar steps). For a modified action example see:
CSNODE:Root:ObjectsWithPrototypes:Actions:NodeActions:Create:CreateURL
- Orphan
CSNODE:Root:ObjectsWithPrototypes:Actions:NodeActions:Create
into your ospace and name it Create (eg. for URL it would be CreateURL). - Overwrite the
Setup
function. The value forenabled
should be TRUE. If your action is read-only, setread_only
to TRUE (otherwise FALSE). The value forslot_name
should be left as “Create” and the xlate fortype_name
should be left alone. The value fortype
should uniquely identify the action (eg. CreateURL) but you do need to be careful of naming collisions (the end user will not see this name so there is some leeway). When done, saveSetup
and run it. - Overwrite
SetupPrototypes
to define any custom input parameters for your action. Save when finished. - Run
BuildPrototypes
to create a feature and setter for each prototype defined inSetupPrototypes
. The default setters will do some standard validation and, if successful, will set the value in the feature (this behavior can be overwritten if desired).BuildPrototypes
will overwrite feature values but it will not overwrite the setters. If you remove a prototype fromSetupPrototypes
, you will need to remove the feature and setter manually. - Overwrite the
GoPre
function to add any custom input parameters to the request. - Overwrite
<csNode>.Setup
to include any modified actions and save (use the value in<action>.type
). - Build your ospace and restart Builder.
- You should now be able to perform this action on a csNode.
Troubleshooting
Check the actions
feature for the particular action.
Check that the action has been registered properly:
CSNODE:Subsystems:ActionSubsystems:NodeActionSubsystem:<temp>.registry
Custom Actions
You can skip this section if your LLNode does not have subtype specific functionality.
- Orphan
CSNODE:Root:ObjectsWithPrototypes:Actions:NodeActions
into your ospace and give it a name. - Overwrite the
Setup
function and fill in the features. The value fortype
will be used to register the action, so it should be fairly unique to avoid possible naming collisions. The value forslot_name
is how the developer will call the action, so it should be meaningful but could be prone to naming collisions. When done, saveSetup
and run it. - Overwrite
SetupPrototypes
to define any custom input parameters for your action. Save when finished. - Run
BuildPrototypes
to create a feature and setter for each prototype defined inSetupPrototypes
. The default setters will do some standard validation and, if successful, will set the value in the feature (this behavior can be overwritten if desired).BuildPrototypes
will overwrite feature values but it will not overwrite the setters. If you remove a prototype fromSetupPrototypes
, you will need to remove the feature and setter manually. - Overwrite the
GoPre
function to add any custom input parameters to the request. - Overwrite the
GoSubclass
function to call a similar function LLNode. - Overwrite
<csNode>.Setup
to include your custom action and save (use the value in<action>.type
). - Build your ospace and restart Builder.
You should now be able to perform this action on a csNode.
Troubleshooting
Check the actions
feature for the particular action.
Check that the action has been registered properly:
CSNODE:Subsystems:ActionSubsystems:NodeActionSubsystem:<temp>.registry RESTAPI:Subsystems:RestAPISubsystem:<temp>.fMappings
Roles
You can skip this section if your LLNode does not have any shared functionality across multiple subtypes. A role is basically collection of actions, with its own namespace (to reduce naming collisions). For a role example see:
CSNODE:Root:ObjectsWithPrototypes:Objects:Roles:Versions
- Orphan
CSNODE:Root:ObjectsWithPrototypes:Objects:Roles
into your ospace and give it a name. - Overwrite the
Setup
function and fill in the features. The value foractions
should be actions registered with the RoleActionSubsystem (not the NodeActionSubsystem). When done, save Setup and run it. - Overwrite the
IsSupported
function to determine if the specified subtype can have this role (default behavior). A given csNode can always subscribe to a role by overwriting its ownSetup
function. If the role is common across all subtypes just return TRUE. - 4a) Orphan
CSNODE:Root:ObjectsWithPrototypes:Actions:RoleActions
into your ospace and give it a name. - 4b) Create actions for your role (these are identical to node actions, they just live in a different spot). Although role actions have their own subsystem, there can still be naming collisions. One naming convention is to use the name of the action followed by the role name (eg. AddVersion, DeleteVersion).
- Build your ospace and restart Builder.
- You should now be able to perform these actions on a csNode.
Troubleshooting
Check that the role was registered properly:
CSNODE:Root:Subsystems:ObjectSubsystems:RoleSubsystem:<temp>.registry
Check that the actions were registered properly:
CSNODE:Subsystems:ActionSubsystems:RoleActionSubsystem:<temp>.registry RESTAPI:Subsystems:RestAPISubsystem:<temp>.fMappings
Role Callback
Sometimes a role may want to have some control over a given action. These are just methods that live in a role with a certain naming convention.
Callback<action>Set
- used to set any values before an action is execute (and before Pre is called)
Callback<action>Pre
- used to perform any processing before an action is executed
Callback<action>Post
- used to perform any processing after an action is executed
Callback<action>Data
- used to append to the data structure during an Info call
Callback<action>Definitions
- used to append to the definitions structure during an Info call
For examples see:
CSNODE:Root:ObjectWithPrototypes:Objects:Roles:Categories
Appendix A: Personas
A persona is a way to identify an ID as a particular type, so that it can be processed accordingly.
node: the property is a node ID
member: the property can be a user or group ID
user: the property is a user ID
group: the property is a group ID
storage_provider: the property is a storage provider
For example:
prototype = self.AddPrototype( "id", IntegerType ) prototype.name = [CSNODE_LABEL.ID] prototype.persona = "node"
Appendix B: CS IDE “setup” Scripts
- Make a copy of the os file that will be modified.
- From the Module Explorer tab, double-click the desired “setup” script.
- Modify the script and save (memory only).
- Right-click on the same “setup” script in the Module Explorer tab and select Run Script.
- From the OScript Explorer tab, select the related os file.
- From the menu, select Source | Overwrite Object Source, click OK when prompted. Unfortunately, the process changes everything in the os file (including tabs).
- Make a copy of the new os file and merge with the old file using a diff tool.
- Copy the resolved code back into the Content Server IDE.
In some cases, it may be easier to edit some of these features by hand rather than running the “setup” scripts.
Categories
- All Categories
- 121 Developer Announcements
- 53 Articles
- 151 General Questions
- 146 Thrust Services
- 56 OpenText Hackathon
- 35 Developer Tools
- 20.6K Analytics
- 4.2K AppWorks
- 9K Extended ECM
- 917 Cloud Fax and Notifications
- 84 Digital Asset Management
- 9.4K Documentum
- 31 eDOCS
- 180 Exstream
- 39.8K TeamSite
- 1.7K Web Experience Management
- 8 XM Fax
- Follow Categories