Process Flow dynamicly shows a Process Formatter...

DrewW
Mega Sage
Mega Sage

So I needed the Process Flow formatter to show a Flow Formatter dynamically and since some of the java code was not available I updated the UI Macro "process_flow" to do what I needed. Including it all here for those who mite be interested in such an enhancement.

Add the field "u_view_condition" with a type of "conditions" to the sys_process_flow table.

Here is the new code for the UI Macro "process_flow"



<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<link rel="stylesheet" type="text/css" href="styles/process_flow_formatter.cssx"></link>
<tr>
<td colspan="2" width="100%" nowrap="true">
<div>
<table width="100%" cellpadding="0" cellspacing="0">
<tr>
<g2:flow_formatter var="jvar_flows" table="$[__ref__.getRecordClass()]" current="$[__ref__]" />
<g2:evaluate var="jvar_table" jelly="true">
var afterCurrent = false;
var tableText = "";
var show = [];
var label = "";
var gr = new GlideRecord("sys_process_flow");
gr.addQuery("table", current.sys_class_name);
gr.orderBy("order");
gr.query();
while(gr.next()){
//If you do not do this you get the last value of gr.label in all elements, do not know why exactly.
label = new String(gr.label);
//Check to see if the record should be displayed. If so add element to array.
//Array info
// show[] -> Each element is a flow formatter record to display
// show[][0] -> Label for the flow formatter to display.
// show[][1] -> state: used to indicate the state of the flow formatter.
// Possible values: past, current, future
// show[][2] -> next_state: used to indicate the state of the next flow formatter.
// Possible values: past, current, future
if(Packages.com.glide.script.Filter.checkRecord(current, gr.u_view_condition)){
show.push(new Array(3));
show[show.length - 1][0] = label;
//Check if this is the current flow that should be green/selected.
if(Packages.com.glide.script.Filter.checkRecord(current, gr.condition)){
show[show.length - 1][1] = "current";
show[show.length - 1][2] = "future";
//Once current is found update the previous element
if(show.length > 1){
show[show.length - 2][2] = "current";
}
afterCurrent = true;
} else {
if(afterCurrent){
show[show.length - 1][1] = "future";
show[show.length - 1][2] = "future";
} else {
show[show.length - 1][1] = "past";
show[show.length - 1][2] = "past";
}
}
}
}
//clear the next_state element so that the process flow has a pointer at the end.
show[show.length - 1][2] = "";
if(!afterCurrent){
show[show.length - 1][1] = "future";
}
//Build the table
for(i in show){
tableText += '<td class="process_flow ' + show<i>[1] + '" title="' + show<i>[0] + '">';
tableText += show<i>[0];
tableText += '</td>';
tableText += '<td width="16" height="100%">';
tableText += ' <img style="height: 24px; width: 21px; margin: 0px; padding: 0px;" src="images/chevron_' + show<i>[1] + '_' + show<i>[2] + '.pngx" />';
tableText += '</td>';
}
tableText;
</g2:evaluate>
$[jvar_table]
</tr>
</table>
</div>
</td>
</tr>
</j:jelly>


1 ACCEPTED SOLUTION

DrewW
Mega Sage
Mega Sage

For those of you how would like to use the updated version that I put on ServiceNow Share with out having to load the update set here is the updated UI Macro code.



<?xml version="1.0" encoding="utf-8" ?>


<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">


      <link rel="stylesheet" type="text/css" href="styles/process_flow_formatter.cssx"></link>


      <tr>


              <td colspan="2" width="100%" nowrap="true">


                      <div>


                              <table width="100%" cellpadding="0" cellspacing="0">


                                      <tr>


                                              <g2:flow_formatter var="jvar_flows" table="$[__ref__.getRecordClass()]" current="$[__ref__]" />


                                              <g2:evaluate var="jvar_elements" jelly="true" object="true">


                                                      var afterCurrent = false;


                                                      var tableText = '';


                                                      var show = [];


                                                      var label = "";


                                                      var gr = new GlideRecord("sys_process_flow");


                                                      gr.addQuery("table", current.sys_class_name);


                                                      gr.orderBy("order");


                                                      gr.query();


                                                      while(gr.next()){


                                                              //Check to see if the record should be displayed.   If so add element to array.


                                                              //Array info


                                                              //     show[] -> Each element is a flow formatter record to display


                                                              //           show[].label -> Label for the flow formatter to display.


                                                              //           show[].state -> used to indicate the state of the flow formatter.


                                                              //                           Possible values: past, current, future


                                                              //           show[].next_state -> used to indicate the state of the next flow formatter.


                                                              //                           Possible values: past, current, future


                                                              if(GlideFilter.checkRecord(current, gr.u_view_condition)){


                                                                      var item = new Object();


                                                                      item.label = gr.getValue("label");


                                                                      //Check if this is the current flow that should be green/selected.


                                                                      if(GlideFilter.checkRecord(current, gr.condition)){


                                                                              item.state = "current";


                                                                              item.next_state = "future";


                                                                              //Once current is found update the previous element


                                                                              if(show.length > 1){


                                                                                      show[show.length - 1].next_state = "current";


                                                                              }


                                                                              afterCurrent = true;


                                                                      } else {


                                                                              if(afterCurrent){


                                                                                      item.state = "future";


                                                                                      item.next_state = "future";


                                                                              } else {


                                                                                      item.state = "past";


                                                                                      item.next_state = "past";


                                                                              }


                                                                      }


                                                                      show.push(item);


                                                              }


                                                      }


                                                      //clear the next_state element so that the process flow has a pointer at the end.


                                                      show[show.length - 1].next_state = "";


                                                      if(!afterCurrent){


                                                              show[show.length - 1].state = "future";


                                                      }


                                                      show;


                                              </g2:evaluate>


                                              <j2:forEach items="$[jvar_elements]" var="jvar_el">


                                                      <g2:evaluate jelly="true">


                                                              var label = jelly.jvar_el.label;


                                                              var state = jelly.jvar_el.state;


                                                              var next_state = jelly.jvar_el.next_state;


                                                      </g2:evaluate>


                                                      <td nowrap="nowrap" class="process_flow $[state]" title="$[label]">


                                                              $[label]


                                                      </td>


                                                      <td width="16" height="100%">


                                                              <img style="height: 24px; width: 21px; margin: 0px; padding: 0px;" src="images/chevron_$[state]_$[next_state].pngx" />


                                                      </td>


                                              </j2:forEach>


                                      </tr>


                              </table>


                      </div>


              </td>


      </tr>


</j:jelly>


View solution in original post

69 REPLIES 69

garyopela
ServiceNow Employee
ServiceNow Employee

Yes, I was figuring this would expose an HTML Injection vulnerability if not used properly. I understand why it worked the way it did, it is similar to how when writing webpages in ASP.net you should never directly pass SQL to the database connection, rather you should wrap it in a variable and then pass it to prevent SQL Injection.

Thanks for the heads up as well. Injection vulnerabilities are something we should all be conscious of.

Gary Opela


It also occurs to my that you mite need to check some of the system properties to see if you are escaping HTML output.

Also check the actual HTML that is being sent to the browser. When I wrote this I had some odd issues the the browser getting HTML that was inside double quotes so it would not render it as HTML.


adam_shaw
ServiceNow Employee
ServiceNow Employee

I have updated the script to work in later releases as the package calls have been replaced ... Packages Call Replacement Script Objects - ServiceNow Enterprise Wiki



Code below...



<?xml version="1.0" encoding="utf-8" ?>


<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">


      <link rel="stylesheet" type="text/css" href="styles/process_flow_formatter.cssx"></link>


      <tr>


              <td colspan="2" width="100%" nowrap="true">


                      <div>


                              <table width="100%" cellpadding="0" cellspacing="0">


                                      <tr>


                                              <g2:flow_formatter var="jvar_flows" table="$[__ref__.getRecordClass()]" current="$[__ref__]" />


                                              <g2:evaluate var="jvar_table" jelly="true">


                                                      var afterCurrent = false;


                                                      var tableText = "";


                                                      var show = [];


                                                      var label = "";


                                                      var gr = new GlideRecord("sys_process_flow");


                                                      gr.addQuery("table", current.sys_class_name);


                                                      gr.orderBy("order");


                                                      gr.query();


                                                      while(gr.next()){


                                                              //If you do not do this you get the last value of gr.label in all elements, do not know why exactly.


                                                              label = new String(gr.label);


                                                              //Check to see if the record should be displayed.   If so add element to array.


                                                              //Array info


                                                              //     show[] -> Each element is a flow formatter record to display


                                                              //           show[][0] -> Label for the flow formatter to display.


                                                              //           show[][1] -> state: used to indicate the state of the flow formatter.


                                                              //                           Possible values: past, current, future


                                                              //           show[][2] -> next_state: used to indicate the state of the next flow formatter.


                                                              //                           Possible values: past, current, future


                                                              if(GlideFilter.checkRecord(current, gr.u_view_condition)){


                                                                      show.push(new Array(3));


                                                                      show[show.length - 1][0] = label;


                                                                      //Check if this is the current flow that should be green/selected.


                                                                      if(GlideFilter.checkRecord(current, gr.condition)){


                                                                              show[show.length - 1][1] = "current";


                                                                              show[show.length - 1][2] = "future";


                                                                              //Once current is found update the previous element


                                                                              if(show.length > 1){


                                                                                      show[show.length - 2][2] = "current";


                                                                              }


                                                                              afterCurrent = true;


                                                                      } else {


                                                                              if(afterCurrent){


                                                                                      show[show.length - 1][1] = "future";


                                                                                      show[show.length - 1][2] = "future";


                                                                              } else {


                                                                                      show[show.length - 1][1] = "past";


                                                                                      show[show.length - 1][2] = "past";


                                                                              }


                                                                      }


                                                              }


                                                      }


                                                      //clear the next_state element so that the process flow has a pointer at the end.


                                                      show[show.length - 1][2] = "";


                                                      if(!afterCurrent){


                                                          show[show.length - 1][1] = "future";


                                                      }


                                                      //Build the table


                                                      for(i in show){


                                                              tableText += '<td class="process_flow ' + show[i][1] + '" title="' + show[i][0] + '">';


                                                              tableText += show[i][0];


                                                              tableText += '</td>';


                                                              tableText += '<td width="16" height="100%">';


                                                              tableText += '<img onmouseover="" style="opacity:1; height: 24px; width: 21px; margin: 0px; padding: 0px;" src="images/chevron_' + show[i][1] + '_' + show[i][2] + '.pngx" />';


                                                              tableText += '</td>';


                                                      }


                                                      tableText;


                                              </g2:evaluate>


                                              $[jvar_table]


                                      </tr>


                              </table>


                      </div>


              </td>


      </tr>


</j:jelly>


Good point.   With share out now I should probably post this there.


DrewW
Mega Sage
Mega Sage

Please note I have posted this to share at


ServiceNow Share


It is an updated and better version that does not require the changing of system properties and also no longer uses a package call.