Skip to content
PDF

Content Script: Retrive information

Nodes

Getting Content Server nodes

All the objects stored on OpentText Content Server are referred as nodes in Content Script.

The base interface representing a node is the CSNode interface. CSNode is the base interface for most of the Content Script API objects.

Almost all the Content Script API Objects inherit from CSNodeImpl which is the base-class implementing the CSNode interface. As said a node represents an object on Content Server.

Different Objects correspond to different implementation of the CSNode interface (e.g. Folders(SubType=0) are implemented by CSFolderImpl, Documents(SubType=144) correspond to CSDocumentImpl).

A CSNode (more generally speaking any Content Script API Object) features:

  • Properties: this is information specific to the Content Server object (e.g. name, subtype, size, creation date) and may vary for each CSNode implementation. In order to be recognized as properties the CSNode fields must be decoretad with the @ContentScriptAPIField;
  • API Methdos: these are the APIs used to manipulate and retrieve information associated with objects;
  • Features: these are additional features that are not strictly related to objects (their are not object's properties) but depend on external factors: the way Content Server is configured (which modules are available, how are they configuration), on object's configuration, on the user's permissions on the objects, on the context in which the features are accessed etc.

The Content Script API service you are going to use the most for retriving nodes is the docman{:style="color:red"} service.

docman{:style="color:red"} features several methods that allows you to retrive a node given:

  • its unique numeric identifier;
  • its path
  • its name and the container in which is located;
  • its nickname;
  • etc..

The Base API also features a asCSNode{:style="color:#e7853c;font-size:bold;"} method that serves as a shortcut for the above mentioned use cases.

Performances-tip: Lazy loading

In order to optimize performances, Content Scripts lazy-loads information from OTCS 'database, which means that such information is not available until firstly accessed. docman{:style="color:red"} APIs allow you to specify which information you want to load beforehand. Retriving the minimum amount of information necessary is tipically done using the APIs ending with the Fast suffix and is to be consider a best practice and might have a significant impact over your's application performances.

1
2
3
4
5
def node = docman.getNode(123456)

if((node.Invoice.Status as String) == "Paid"){ // The node is loaded with regular method at line (1) since we know we access node's category at line (3)
    ...
}
1
2
3
4
node = docman.getNode(123545)
if(node.parentID == -1 ){ //ParentID is a base property for CSNode and since we are only accessing it we should have used docman.getNodeFast(1235) at line (1)
    ...
}

Getting a node given its ID

def node = docman.getNode(2000, //NodeID (on most of the environments 2000 identifies the Enterprise Workspace)
                      true, //'true' if Reference information shall be loaded
                      true, //'true' if Reservation information shall be loaeded
                      true, //'true' if Versions information shall be loaeded
                      true, //-true- if Current Version  shall be loaeded
                      true, //'true' if Node's features shall be loaeded
                      true, //'true' if Metadata  shall be loaeded
                      true, //'true' if Permissions information shall be loaeded
                     )

node = docman.getNode(2000) //this is a shortcut for docman.getNode(2000, true, true, false, false, true, true, true) (loads the node without its versions)

node = docman.getNodeFast(2000) //this is a shortcut for docman.getNode(2000, false, false, false, false, false, false, false) (loads just the base node's information)

node = asCSNode(id:2000) //this is a shortcut for docman.getNode(2000)

node = asCSNode(2000)//this is a shortcut for asCSNode(id:2000)

Get a list of nodes given their IDs

docman.getNodesFastWith(
                        [2000L, 2006L], // List of nodes IDs
                        ["GIF", "promotedCmds", "defaultLink", "size", "tableName"], // List of additional features to retrive
                        params, //Current request parameters
                        true, //'true' if Versions information shall be loaeded
                        true, //'true' if Node's features shall be loaeded
                        true //'true' if Permissions information shall be loaeded
                       )

docman.getNodesFast(2000L, 2006L) //this is a shortcut for docman.getNodesFastWith([2000L,2006L], [], [:], false, false, false)

docman.getNodes(2000L, 2006L) //this is a shortcut for docman.getNodesFastWith([2000L, 2006L], [], [:], true, true, true)

Get Volumes

The most common volumes can be easily accessed using a dedicated API featured by the docman service. If an API is not available a volume can be retrieved using a simple SQL query based on its subtype. Volumes come in handy when you want to retrieve a node by its path.

docman.getEnterpriseWS() //Enterprise Workspace

docman.getPersonalWS()   //Personal Workspace

docman.getCategoryWS()   //Category Workspace

docman.getContentScriptVolume() //Content Script Volume

/*
 161 -- Workflow Volume    
 198 -- Classification Volume
 211 -- Reports Volume
 233 -- Database Lookups
 236 -- Database Connections
 274 -- Best Bets
 405 -- Recycle Bin
 541 -- Content Server Templates
 862 -- Connected Workspaces
 863 -- Workspace Types
*/

def node =  docman.getNodeFast(sql.runSQLFast("""Select "DataID" 
                  FROM DTree 
                  Where SubType = 161""", false, false, 0
              ).rows[0].DataID)

Get Nodes By Path

def ews = docman.getEnterpriseWS()

node = docman.getNodeByPath(ews, "Training:Folder")

node = docman.getNodeByPath( "Training:Folder") //this is a shortcut for docman.getNodeByPath(docman.getEnterpriseWS(), "Training:Folder")

node = asCSNode(path:"Training:Folder")//this is a shortcut for docman.getNodeByPath("Training:Folder")

Performances-tip: Use the variable to avoid reloading the same information

In order to optimize performances, you should always assign information you know is not going to change (during your script execution) to Content Script variables so to avoid to reload them everytime they are accessed.

def ews = docman.getEnterpriseWS()

node = docman.getNodeByPath(ews, "Training:Folder")

node = docman.getNodeByPath(ews, "An:Other:Path")
node = docman.getNodeByPath( docman.getEnterpriseWS(), "Training:Folder")

node = docman.getNodeByPath( docman.getEnterpriseWS(), "An:Other:Path")

Users and Groups

Getting Content Server Users and Groups

Content Server Users and Groups are managed by the users service in the Content Script. users service operates with CSMember, CSUser and CSGroup classes. CSUser and CSGroup are the classes that are providing API to work with the Content Server Users and Groups correspondingly. CSMember is an abstract class for for CSUser and CSGroup objects. It is used in the API where both Users and Groups classes can be passed as a parameter or return as a method return value. users service provides set of methods to retrieve User or a Group:

  • get current user (user who is actually executing Content Script)
  • get user/group by id
  • get group by name
  • get user by login name
  • list group members
  • etc..

Get current User

def user = users.current // Will return CSUser object of the user that is executing the script

Get by member ID

CSMember member

// Pass User or Group by ID. Method will return CSUser or CSGroup class objects

//Pass ID of the Content Server User
member = users.getMemberById(1000) 
out << member instanceof CSUserImpl // will display true

//Pass ID of the Content Server User
member = users.getMemberById(1001) 
out << ( member instanceof CSGroupImpl ) // will return true

//Get User by ID
member = users.getUserById(1000) // will return CSUser class object

//Get group by ID
member = users.getGroupById(1001) // will return CSGroup class object

Get member by the name

CSMember member

//Get Member using User Login Name
member = users.getMemberByLoginName("Admin") // Will return CSUser class object

//Get Member using Group Name
member = users.getMemberByLoginName("DefaultGroup") // Will return CSGroup class object

//Get User by UserName
member = users.getUserByLoginName("Admin")

//Get Group by Name
member = users.getGroupByName("DefaultGroup")

Get members by ID

def members

//Get by IDs
members = users.getMembersByID(1000,1001)

//members[0] - is object of CSUser class
//members[1] - is object of CSGroup class

Permissions

Getting Content Server Node Permissions

Content Script docman service allows script developers to perform operations with the Content Server permissions model. To get get node permissions:

CSNode node = asCSNode(33561)
//Node permissions can be retrieved either
//calling CSNode getRigths() method
CSNodeRights nodeRights = node.getRights()

//or by calling docman method and passing node as an attribute
nodeRights = docman.getRights(node)

Content Server permissions model is represented as two classes CSNodeRights and CSNodeRight. CSNodeRights class contains all the permissions of the node. It's fields correspond to Content Server node permission type. ownerRight - Owner Permissions ownerGroupRight - Owner Group Permissions publicRight - Public Access Permissions ACLRights - list of Assigned permissions Every permission is an CSNodeRight object, with following fields: rightID - ID of the User/Group to whom this Right is assigned permissions - list of permissions set. Following options are possible:

1
[SEE, SEECONTENTS, MODIFY, EDITATTRIBUTES, RESERVE, ADDITEMS, DELETEVERSIONS, DELETE, EDITPERMISSIONS]

To get node permissions:

//To get Owner Permissions
out << nodeRights.ownerRight.permissions

//To get Assignemt Permissions Users with their permissions
def assignedAccessUsers = [:]

nodeRights.ACLRights.each{ right ->
    def currUser = users.getMemberById(right.rightID);
    assignedAccessUsers[currUser.name] = right.permissions
}

out << assignedAccessUsers

There are set of methods to check if current user has special permissions against the node. Methods to check permission are implemented for CSNode and they are prefixed with "has" and than following permissions description:

  • hasAddItemPermission()
  • hasDeletePermission()
  • hasDeleteVersionsPermission()
  • hasEditAttributesPermission()
  • hasEditPermissionsPermission()
  • hasModifyPermission()
  • hasReservePermission()
  • hasSeeContentsPermission()
  • hasSeePermission()

Sample validation:

CSNode node = asCSNode(33561)

out << node.hasDeletePermission() //will return TRUE if current user has Delete pemissions on a node

Categories

Getting Node Categories

Content Script docman service allows to performs full set of actions related to Content Server categories. Below you will find samples how to get Category definition and get Content Server node categories along with its attribute values.

def category = docman.getCategory(self.parent, "User Info") // Object of type CSCategory

def attributesMap = category.getAttributes() // Get map with Category Attributes

def firstNameAttr = category.getAttribute(attributesMap[2 as Long]) // get definition of the attribute with ID 2 CSCategoryAttribute

out <<  "Attribute ${firstNameAttr.getDisplayName()} has default value set to: ${firstNameAttr.values()}" // get default value for the attribue

Get value of the category attributes applied to a node:

def node = docman.getNodeByName(self.parent, "Folder With Categoty")

//Get Attribute value
def attrValue = node."User Info"."First Name" as String
out << "The current value of First Name is now ${attrValue} <br/>"

//get first attribute value
attrValue = node."User Info".Phone
out << "Get first Phone attribute value ${attrValue} <br/>"

//get all attribute values
attrValue = node."User Info".Phone as List
out << "Get all Phone attribute values ${attrValue} <br/>"

You can always export the category as a map, and later on update it from the very same map:

out << node."User Info" as Map

Classification

Manipulation with a node Classifications in Content Script is performed by the classification service. This sections describes how to get classifications applied to a node.

First of all if you need to check if node is classifiable:

def node = docman.getNodeByName( self.parent, "Test Folder")

//Check if Classification can be applied to the node
out << "Classification can be applied to a node: ${classification.isClassifiable(node)}"
out << "<br>"

//List classifialbe subtypes
out << "Classification can be applied to following node subtypes:"
out << "<br>"
out << classification.listClassifiebleSubTypes()

To get classifications:

def node = docman.getNodeByName( self.parent, "Test Folder")

// get node classifications
def classifications = classification.getClassifications(node)

//Will return list of classifications applied to a node
out << classifications.collect { it.name }

Executing SQL queries

Content Script API allows execution of SQL statements against Content Server database, without the need for creatomg a LiveReport object. sql service has a set of methods allowing developer to run SQL queries.

Not all DBMS are equal

Please keep in mind DBMS server SQL specific syntax of the queries used. Adapt provided queries to the DBMS server type in your environment.

Execute a simple SQL query

out << sql.runSQL("""select * from DTree where %1 and ParentID = %2 and ModifyDate > %3""", //SQL Code to be executed
           true, // true if the query must be executed using a cursor
           true, // true if the query must be wrapped in a transaction (required administrative privilagies)
           10,   // numer of records to be returned

                              // Below the list of optional parameters
           "#FilterObject:0", // Parameters can be a LiveReport query template expression
           2000,              // Integers
           1.year.ago).rows   // Dates
                              // Strings 

The above query is executed with three parameters, specified as %N in the SQL statement.

SQL execution methods are returning CSReportResult class object. To get query executing result rows feature should be used, as in the example above.

Another option to run SQL queis utilization of the sql.runSQLFast() methods. Syntax for "Fast" methods is the same. These methods are faster implementation of the SQL execution script, but the compromise is that they are not ThreadSafe (i.e. not to be used in multi-threaded scripts).

Execute a SQL query with pagination

In some cases it is required to implement queries that return paginated data, e.g. for browsing pages. sql exposes a set of methods that allow developers to easily build such queries The example below provides an overview of the usage of sql.runPaginatedSql() API:

def sqlProjections = "DataID, Name" 
def fromClause = "DTree dt"
def whereClause = "SubType = 0"
def pageSize = 5
def transaction = true

def runPaginatedQuery = { firstRow ->

    def sqlResult = sql.runPaginatedSql(sqlProjections, fromClause, whereClause, firstRow, pageSize, "dt", "DataID", "ASC", transaction)

    out << "<br>"
    out << "Start row ${firstRow}"
    sqlResult.rows.each { row ->
        out << "<br>"   
        out << "Folder Name: ${row.name}. Name: ${row.dataid}"
    }
}

runPaginatedQuery(1)
runPaginatedQuery(6)

Working with Forms

Content Server Forms and Form Templates objects can be manipulated with Content Script through the forms service API.
The most important Service API Objects returned by the aformentioned service are: CSForm, CSFormTemplate and Form

While CSForm is used to manipulate the Content Server Forms objects (e.g. changing name, applying categories and classifications, changing permissions etc...) the Form type is used to represent the data submitted (record) through the form.

Objects used in this paragraph's examples

The examples presented in this paragraph are all making use of a Form Object named HowTo Form associated to a FormTemplate object named HowTo having the following structure.

The FormTemplate object has been configured to be associated to an SQL Table named Z_HowTo.
At the time of configuration Content Server produced the following SQL DDL instructions:

create table Z_HowTo
(
VolumeID bigint not null,
DataID bigint not null,
VersionNum bigint not null,
Seq bigint null,
RowSeqNum int default 1 not null,
IterationNum int default 1 not null,
Field nvarchar(255) null,
Other_Field nvarchar(255) null
)
/

create index Z_HowTo_Index1
on Z_HowTo ( VolumeID, DataID, VersionNum, Seq )
/

create table Z_HowToSet
(
VolumeID bigint not null,
DataID bigint not null,
VersionNum bigint not null,
Seq bigint null,
SubSeq int null,
RowSeqNum int default 1 not null,
IterationNum int default 1 not null,
Field_In_Set nvarchar(255) null
)
/

create index Z_HowToSet_Index1
on Z_HowToSet ( VolumeID, DataID, VersionNum, Seq )
/
Which once executed, resulted in the creation of two tables: Z_HowTo and Z_HowToSet

The Form object uses, as a submission mechanism, the SQL Storage option, while no revision mechanism has been associated to it.

Retrive submitted data

To get the Content Server Form associated submitted data you can leverage the listFormData* APIs, these APIs accept an optional filters parameter, which can be used only for Forms having SQL Table as associated submission mechanism. Filters are Maps having as keys the names of the tables you want to filter data from and as values a valid SQL where clause:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def writer = new StringWriter()
def html = new MarkupBuilder(writer)
out<< template.evaluateTemplate("#csresource(['bootstrap'])")
html.table(class:"table"){
    thead{
        tr(class:"danger"){
            th("Field")
            th("Other Field")
            th("Set")
        }
    }
    tbody{
        formNode.listFormData(["Z_HowTo":" Seq in (select Seq from Z_HowToSet where Field_In_Set = 'two') "], true).each{ form -> //Form object
            tr{
                td(form.field.value)
                td(form.otherField.value)
                td{
                    table(class:"table table-condensed"){
                        thead{
                            tr(class:"danger"){
                                th("Field in Set")
                            }
                        }
                        tbody{
                            form.set.each{ row->
                                tr{
                                    td(row.fieldInSet.value)
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
out << writer.toString()

def formNode = docman.getNodeByName(self.parent, "User Info Form") //returns a CSFormImpl node
def submittedData = formNode.listFormData()

In the script above formNode in CSForm object type that has API implemented to work with Content Server Forms. submittedData is a list of Form object types that corresponds to certain record of the submitted form data. To access fields of the form:

    //List sumbitted data
    //Access Form fields
    submittedData.each {form ->
        out << "User ${form.firstName[0]} ${form.lastName as String}. Age ${form.age as String}"
        out << "<br>"
    }

In the example above following form attributes are accessed:

Field Name Normalized
First Name firstName
Last Name lastName
Age age

In scripts, form field values can be accessed using the following notation form.normalizedname.value

where normalization is performed by the Content Suite Framework.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Initalize form field values: some examples

form.wordsWithSpaces.value = TEST VALUE E // Form template field name: words with spaces

form.camelcase.value = TEST VALUE D // Form template field name: camelCase

form.capitalized.value = TEST VALUE C // Form template field name: Capitalized

form.uppercase.value = TEST VALUE B // Form template field name: UPPERCASE

form.lowercase.value = TEST VALUE A // Form template field name: lowercase

Also it is possible to represent Form attributed values as a Map. This allows easy access to the form data:

    out << "List Form data as a Map <br>"

    //List all form Records as a Map
    submittedData.each {form ->
        out << "<br>"
        out << "${forms.formToMap(form)}"
    }

Reverse logic is kept as well, meaning Form data cat be set from a Map utilizing forms.MapToForm(Map map, Form form)