CNG - Scripting API
Scripting API
CNG uses Groovy for scripting. Groovy is build on top of Java and you can read more about it here.
Depending on the context different types of data will be available for manipulation. Below follows a listing of what properties are available in different contexts.
1. String based data type validation code
|
|
value | Value to validate. Value can currently be an instance of BigDecimal, BigInteger, String or an Object representing the date (see 1.1 section for more details). 2013/04/04 |
1.1 properties available for date object
|
|
year | year as integer |
month | month as integer (1-12) |
day | day a integer (1-31) |
asString | string representation. will take the form of "Y-MM-DD". e.g: 2013-04-01 |
format("Y/MM/DD") | formats the date according to the passed in format |
The actual java type of the object used for representing date is 'no.more.cng.core.service.scripting.form.DateObjCtxObj.java'. However for groovy scripts this has been given the alias 'DateObj'.
So within scripts following is possible.
// assuming 'value' variable refers to a date object
if (value instanceof DateObj) {
/* if check would pass for a object representing date */
}1.1.1 Clarification of date format
The date format uses the following special characters: Y, M and D to represent year, month and day. Repeating the same character more than once means that you set the minimum number of digits to use for those items. Stating YYY means you want year to be expressed as at least three characters, if it needs more then that then it will take that space. In the case of year, since DateObj can only express Gregorian dates (year 1582 October 15 - year 9999 December 31), that means a year will always take four characters no matter what. This is a difference to other date formats, but means there can be no ambiguity when expressing a date. Does 13 mean e.g. year 1913 or 2013 is thereby a situation that can never happen. Note also that using e.g. YMMDD is an ok date format even though there are no separators given that month and day never uses more than two characters.
2. Scripting within a form
You currently can use scripting within a form for the following:
Question input validation
Display condition on question
Display condition on question group
Display condition on page elements
Form answer population on Form Population Button
Some properties are available in all contexts, while other are available only in some. Below follows a list of examples for what properties are available.
2.1 General properties for all form contexts
|
|
Q42.answer | Get answer of Q42, where Q42 is a value input question. If there are multiple instances of Q42 you will get the closest match. If Q42 resides in a different question group you will get the version in the first instance of that question group, e.g. QG1[0].QG2[0].Q42. Note that both Q42 and answer can be null (might want to use the safe navigation operator like: Q42?.answer). 2013/01/09 |
QG1.Q42.answer | Gets closest instance of QG1 and the answer to Q42 for that question group, provided there is one. If QG1 resides inside another question group you will get the first occurrence of QG1 in same way as per questions. 2013/01/09 |
formAnswer.Q42.answer | Gets answer to first occurrence of Q42 in form. Note difference to just using Q42 is that if e.g. Q42 resides inside question group QG1, and QG1 is a root question group, then with form.Q42 you will always get same as formAnswer.QG[0].Q42. If you however use Q42 only and you reference it from a question, or other item, within QG[2], then you will get form.QG[2].Q42. 2013/01/09 |
formAnswer.questionAnswers | Returns a set of all question answers at root level of form. TODO: In addition to being able to iterate through question answers, it should ideally be possible to write formAnswer.questionAnswers.Q42, which should return Q42 only if it is present in the set. 2013/01/09 |
formAnswer.questionGroupAnswers | Returns a set of lists of all question group answers at root level of form. TODO: In addition to being able to iterate through question group answers, it should ideally be possible to write formAnswer.questionGroupAnswers.Q42 or formAnswer.questionGroupAnswers.QG1, which should return Q42 or QG1 respectively, only if they are present in the set, or any question group answer below this question group answer set. Note that if e.g. Q42 exists only at root level it should not be found via formAnswer.questionGroupAnswers.Q42. 2013/01/09 |
form | This will return an object representing details of a particular form version. Please refer section 2.3 for what properties/methods can be invoked on this. |
There will be more properties available later, like e.g. being able to iterate through form views.
2.2 Additional properties for questions
|
|
Id of question, e.g. "Q12". * 2012/11/14 | |
Q42.answer | Get answer of Q42, where Q42 is a value input question. If there are multiple instances of Q42 you will get the closest match. If Q42 resides in a different question group you will get the version in the first instance of that question group, e.g. QG1[0].QG2[0].Q42. Note that both Q42 and answer can be null (might want to use the safe navigation operator like: Q42?.answer). 2013/01/09 |
QG1.Q42.answer | Gets first instance of QG1. If QG1 resides inside another question group you will get the first occurrence of QG1 in same way as per questions. 2013/01/09 |
Q12.question.label.text | This gives the localized question label for the language used by the respondent. i.e: if the workflow version has the language set to 'nob' this will return the question text for 'nob' language. 2013/04/08 |
Q12.question.label.getText() | Same result as invoking "Q12.question.text". See section (2.2.4) to see what properties of the returned object can be accessed further 2013/04/08 |
Q12.question.label.getText("eng") | This gives the localized question label for the language code passed in. If null is passed default value will be returned. See section (2.2.4) to see what properties of the returned object can be accessed further. 2013/04/08 |
Questions will later expose more values than just the id.
2.2.1 Additional properties for questions of type value input
|
|
answer | Answer value that may be an instance of BigDecimal, BigInteger or String. Note that answer value will be null, from the scripts point of view, if empty or if a value that does not match the data type is filled out. 2012/11/14 |
2.2.2 Additional properties for questions of type single select
|
|
answer?.value | Answer value that may be an instance of BigDecimal, BigInteger or String. Note that use of ? might be advisable on this in many cases, since if you use this on questions an answer might not yet have been selected. * 2012/11/14 |
answer?.label.text | gets the label value of the option localized according to the respondent users language. Returned object will be a String 2013/04/08 |
answer?.label.getText() | gets the label value of the option localized according to the respondent users language. Returned object will be a String 2013/04/08 |
answer?.label.getText("eng") | gets the label value of the option localized according to the passed in language code. Returned object will be a String 2013/04/08 |
The syntax difference from value input questions is because it is intended that the option label later also will be made available.
"?" is groovy null safe operator. Use of it prevents null pointers from happening when answer is null (i.e: when no answer has been selected)
2.2.3 Additional properties for questions of type multiple select
|
|
answers | Answer values, which is a list of objects having similar properties to that of single value questions (refer section 2.2.2). To get answer value with index one you could e.g. do: answers[0].value. 2012/11/14 |
2.2.4 Available properties for formatted text
|
|
plainText | returns the text without markup information. |
html4 | returns the text with html markup information. |
markup | return the original formatted text with markup information |
xml | return the formatted text without any markup information which can be used in a xml. i.e: xml sensitive characters would be escaped |
Formatted text is represented by no.more.cng.core.service.scripting.form.FormattedText.java. This is given the alias 'FormattedText' for groovy. So the following is valid.
if (Q13.question.label.text instanceof FormattedText) {
// this check would pass because question label is in formatted text
}2.3 properties available to a Form
|
|
id | gives the id of the Form as a String. i.e: the repository item id |
version | gives the version of the Form as a String. |
Form is represented by no.more.cng.core.service.scripting.form.FormVersionCtxObj. This is given the alias 'Form' for groovy. So the following is valid
if (form instanceof Form) {
// above check would pass
}2.4 Form answer population script on a Form Population Button
A script can be specified for populating form answers on a 'Form Population Button'. Inside this script following syntax could be used for manipulating form answers.(Although the syntax is valid in other places, modified answers would not get reflected on the respondent GUI)
Value Input Questions
For Questions with base data type as 'string' following statements are supported.
|
|
Q1.answer = "populated input value" | sets the answer to the given value |
Q1.answer = 101 | sets the answer converting the value to a string |
Q1.answer = 201.13 | sets the answer converting the value to a string |
Q1.answer will always return either a string or null.
For Questions with base data type as 'integer' following statements are supported
|
|
Q2.answer = 101 | sets the answer to the given value |
Q2.answer = "101" | sets the answer converting the value to BigInteger. Exception if conversion fails |
'Q2.answer' will always return either a BigInteger or null
For Questions with base data type as 'decimal'
|
|
Q3.answer = 201.34 | sets the answer to the given value |
Q3.answer = "201.34" | sets the answer converting the value to BigDecimal. Exception if conversion fails |
'Q3.answer' will always return either a BigDecimal or null
For Questions with base data type as 'date'
|
|
Q4.answer = "2014-11-23" | sets the answer to the given value. converts the string value to Date. Exception if conversion fails. Format should be Y-MM-DD |
Q4.answer = Util.currentDate() | sets the answer to the current date |
Q4.answer = Util.newDate('2014-11-23', 'Y-MM-DD') | sets the answer to the given value. format can be specified as the 2nd arg. Exception if conversion fails. |
Q4.answer = Util.newDate('2014-11-23') | sets the answer to the given value. uses Y-MM-DD format. |
'Q4.answer' will always return either null or object representing the date.
Common for all value input questions
Instead of the static values shown above variables could also be assigned.
They will be converted to the correct data type and stored as the answer. Exceptions thrown if conversion fails (script will stop execution)
e.g: Q1.answer = Q4.answer
Single Select Questions
|
|
Q5.setAsAnswerValue('internal-option-value') | sets the answer to the given 'internal' option value. |
Q5.answer = Q6.answer | valid only if both Q5/Q6 are single select questions |
In all cases passed in value will be converted to the correct data type. Exception if conversion fails.
Multi Select Questions
|
|
Q6.setAsAnswerValues('internal-option-value1','internal-option-value2') | sets the answers to the passed in internal option values |
Q6.setAsAnswerValues(internalAnswerValueList) | 'internalAnswerValueList' is a variable of type 'java.util.List'. sets the answers to these values |
Q6.answers = Q7.answers | valid only if both Q6/Q7 are multi select questions |
In all cases passed in value will be converted to the correct data type. Exception if conversion fails.
Removing question group instances
|
|
QG1[0].markAsRemoved() | marks the first instance of QG1 as to be removed. 'markAsRemoved()' method available for any question group. |
Adding question group instances
|
|
QG1[0].addGroupInstance('QG2') | adds an instance of 'QG2' into QG1[0]. 'addGroupInstance' method is available for any question group |
addGroupInstance('QG1') | adds an instance of 'QG1'. i.e: QG1 is a root question group (it is not inside another question group) |
3. Transformation scripting on input variables
|
|
value | Value to transform. Value can currently be an instance of BigDecimal, BigInteger or String. 2012/11/14 |
It is likely that we will allow other types of inputs for the transformations later. What is stored regarding the inputs to these therefore needs to reflect where the inputs comes from like e.g. acticity_1.Q15.answer or activity_1.Q16.answer.value. We e.g. currently do not support multi-select questions.
4. Text generation scripting
Here the user is in control over what names input variables become available under. For examples below input has been used, but this could be anything as long as it does not brake naming constraints. Please also note that for text generation Groovy scriptlets are used, i.e. you need to use <% and %> for code blocks. You can read more about text generation here.
4.1 Utility methods
|
|
Util.escapeXml(value) | Escapes characters so that value can be used safely in an XML-tag. |
Util.escapeHtml4(value) | Escapes characters so that value can be used safely in an HTML4 document. |
Util.currentTime() | Gets current system time as a String in standard format: "yyyy-MM-dd HH:mm:ss.SSS". Output could be e.g. "2012-11-14 15:47:54.365" |
Util.currentTime(format) | Gets current system time as as String in given format. Input argument follows Javas SimpleDateFormat, with locale set to US. Warning: Don't use this, this will be removed in future versions of CNG, or at least changed to use a different date formatting format! |
4.2 String based data type input variables
|
|
input | Variable of type BigDecimal, BigInteger,String or object holding date information(see 1.1) matching the data type of the input variable. Variable may also be null. 2012/11/14 |
4.3. Form answer input variables
This should work for the most part in exactly the same as when scripting in forms. Basically only difference is that you are not working from a form item perspective. It is therefore e.g. not possible to get the current question or answer.
|
|
input.Q42.answer | Get answer of Q42, where Q42 is a value input question. In case of multiple answers the first occurrence will be fetched. |
input.QG1.Q42.answer | Gets first instance of QG1, i.e. the one with index zero, and the answer to Q42 provided it resides in that question group. Note that if Q42 resides in a different question group this will return null regardlessly. |
input.QG1[2].Q42.answer | Gets the QG1 instance with index 2 and answer to Q42 for this question group, provided it is available. |
input.questionAnswers | Returns a set of all question answers at root level of form in same way as for form scripting. |
input.questionGroupAnswers | Returns a set of lists of all question group answers at root level of form. |
Gives the form id (repository item id) as a String | |
input.form.version | Gives the form version as a String |
2012/11/14
-----------------
supported functionality is similar to that in the 2.1 section since the same objects are being used.
Please refer FormAnswerVariable for more details on how to deal with variables of type FormAnswer.
4.4 Key-value source input variables
TODO
4.5 Session iterator input variables
TODO
5. File based data type validation function syntax
|
|
value | Value to validate. Value can only be a sub instance of FileAnswerCtxObj class. TextBased File Answer Context Attributes XMLBased File Answer Context Attributes PDFBased Answer Context Attributes |
Sample_1
// Text file specific operations
reader = value.getTextReader("ISO-8859-1");
def val = reader.readLine()
(val.contains("application.base.url") &&
value.encoding.equals("ISO-8859-1"))
// PDF file specific operations
(value.content.size() > 70000 &&
value.pdfVersion.equals("1.3") &&
value.fileName.equals("test.pdf") &&
value.contentType.equals("application/pdf"))
Sample_2
def line;
def reader = value.textReader;
while ((line = reader.readLine()) != null) {
if(line.contains("Test Value")) {
return true;
}
}
return false;
Sample_3