Today we are going to learn about a little-known language with limited documentation: JellyScript.
I was asked to create a KB article template for automatically generated Technology Process policies that would recursively go through all of the policies that had the Technology Process policy as the parent record and then use each of their related control objectives to print out a bulleted list containing their technology process, starting reference ID, the objective statement ID, and the objective statements.
This is the final product below:
(Potentially sensitive content redacted.)
I made the KB article template for our client and wanted to share my findings with you, the reader. Below is the full code I used for this task.
<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<g:evaluate object="true" var="jvar_statements">
var controlPolicies = new GlideRecord('sn_compliance_policy');
controlPolicies.addQuery('parent', current.sys_id +'');
controlPolicies.query();
var controlObj = new GlideRecord('sn_compliance_policy_statement');
var mtom = controlObj.addJoinQuery('sn_compliance_m2m_policy_policy_statement', 'sys_id', 'content');
mtom.addCondition('document', current.sys_id + '');
while(controlPolicies.next()){
mtom.addOrCondition('document', controlPolicies.sys_id + '');
}
controlObj.orderBy('reference');
controlObj.query();
controlObj;
</g:evaluate>
<table border="5" style="width:100%;border-collapse: separate">
<caption ><h3 style="font-weight: bold">${current.name} - Control Statements</h3></caption>
<tr>
<th style="width:10%;text-align:center">Control Objective</th>
<th style="width:5%;text-align:center">Objective ID</th>
<th style="width:5%;text-align:center">Statement ID</th>
<th style="text-align:center">Control Statements</th>
</tr>
<j:while test="${jvar_statements.next()}">
<tr>
<td style="padding-left: 10px;padding-right: 10px;padding-top: 10px;padding-bottom: 10px;text-align:center"><a href="/sn_compliance_policy?sys_id=${jvar_statements.getValue('u_policy_control_objective')}" target="_blank">${jvar_statements.getDisplayValue('u_policy_control_objective')}</a></td>
<td style="padding-left: 10px;padding-right: 10px;padding-top: 10px;padding-bottom: 10px;text-align:center">${jvar_statements.getValue('u_policy_control_objective.u_tcf_id')}</td>
<td style="font-weight: bold;text-decoration: underline;text-align:center"><a href="/sn_compliance_policy_statement?sys_id=${jvar_statements.getUniqueValue()}" target="_blank">${jvar_statements.getValue('reference')}</a></td>
<td style="padding-left: 10px;padding-right: 10px;padding-top: 10px;padding-bottom: 10px">${jvar_statements.getValue('name')}</td>
</tr>
</j:while>
</table>
</j:jelly>
We will start with the languages involved. We have XML as a wrapper, JellyScript as the main operator, JavaScript as the logic, with HTML and CSS for formatting and display.
XML:
This is the standard header for an XML document and simply signals to the compiler to recognize the script as such.
<?xml version="1.0" encoding="utf-8" ?>
JellyScript:
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
//code
</j:jelly>
<g:evaluate object="true" var="jvar_statements">
//code
</g:evaluate>
<j:while test="${jvar_statements.next()}">
//code
</j:while>
Now that we have a grasp of what Jelly does in relation to the other code, we will dig into the JavaScript logic that drives the data you see in the image above.
JavaScript:
I’ll try to simplify this as best I can, it’s a semi-complex process.
The Ask:
The Breakdown:
var controlPolicies = new GlideRecord('sn_compliance_policy');
controlPolicies.addQuery('parent', current.sys_id +'');
controlPolicies.query();
var controlObj = new GlideRecord('sn_compliance_policy_statement');
var mtom = controlObj.addJoinQuery('sn_compliance_m2m_policy_policy_statement', 'sys_id', 'content');
mtom.addCondition('document', current.sys_id + '');
while(controlPolicies.next()){
mtom.addOrCondition('document', controlPolicies.sys_id + '');
}
controlObj.orderBy('reference');
controlObj.query();
controlObj;
HTML / CSS:
These things go hand-in-hand so I’ll be brief about them. Here are the highlights:
<table border="5" style="width:100%;border-collapse: separate">
<caption ><h3 style="font-weight: bold">${current.name} - Control Statements</h3></caption>
<tr>
<th style="width:10%;text-align:center">Control Objective</th>
<th style="width:5%;text-align:center">Objective ID</th>
<th style="width:5%;text-align:center">Statement ID</th>
<th style="text-align:center">Control Statements</th>
</tr>
<j:while test="${jvar_statements.next()}">
<tr>
<td style="padding-left: 10px;padding-right: 10px;padding-top: 10px;padding-bottom: 10px;text-align:center"><a href="/sn_compliance_policy?sys_id=${jvar_statements.getValue('u_policy_control_objective')}" target="_blank">${jvar_statements.getDisplayValue('u_policy_control_objective')}</a></td>
<td style="padding-left: 10px;padding-right: 10px;padding-top: 10px;padding-bottom: 10px;text-align:center">${jvar_statements.getValue('u_policy_control_objective.u_tcf_id')}</td>
<td style="font-weight: bold;text-decoration: underline;text-align:center"><a href="/sn_compliance_policy_statement?sys_id=${jvar_statements.getUniqueValue()}" target="_blank">${jvar_statements.getValue('reference')}</a></td>
<td style="padding-left: 10px;padding-right: 10px;padding-top: 10px;padding-bottom: 10px">${jvar_statements.getValue('name')}</td>
</tr>
</j:while>
</table>
I hope this makes more sense than when you first started looking at JellyScript, and from the bottom of my heart I hope you never have to write anything in it. If you do, this is here to reference