Name

global.WorkflowIcons

Description

WorkflowIcons - Assist in the generation of the workflow icons

Script

gs.include("PrototypeServer");

var WorkflowIcons = Class.create();

WorkflowIcons.prototype = {
  
  initialize : function(ref) {
  	this.au = new ArrayUtil();
  	this.elementName = ref;
  	this.element = eval(ref);
  	
  	this.finishers = {
  		'rejected' : true,
  		'complete' : true,
  		'request_cancelled' : true
  	};
  	
  	this.visited = this.getPreviousValues();
  },

  getVisited: function() {
  	return this.visited;
  },
  
  process : function(c) {
  	var choices = c.clone();

  	if (choices.getSize() < 1)
  		return;
  	
  	for (var i = 0; i < choices.getSize(); i++) {
  		var c = choices.getChoice(i);
  		this.processChoice(c, i, choices.getSelectedIndex());

  		if (c.selected && this.isFinisher(c.value)) {
  			for (var l = choices.getSize() - 1; l > i; l--) 
  				choices.removeChoice(l);
  			
  			i = choices.getSize();
  		}
  	}
  	return choices;
  },

  getElement : function() {
  	return this.element;
  },

  hasBeenUsed : function(c) {
  	return this.au.indexOf(this.visited, c.value + '') != -1;
  },

  isOverdue : function() {
  	var gr = this.getElement().getGlideRecord();
  	var end = gr.getElement("planned_end_date");

  	if (end == null)
  		return false;

  	var now = new GlideDateTime();
  	var nowValue = now.getNumericValue();
  	var endValue = end.dateNumericValue();

  	if (endValue > 0 && endValue < nowValue) {
  		var wf = new String(this.getElement());
  		if (!wf.startsWith("complete") && !wf.startsWith("reject"))
  			return true;
  	}
  	return false;
  },

  isFinisher : function(x) {
  	return this.finishers[x];
  },

  updateVisited : function(newValue) {
  	var gr = this.getElement().getGlideRecord();
  	var fieldName = this.getElement().getName();
  	var audited = this.getFieldValuesUsed(gr, fieldName);
  	
  	// Lets ensure that this is really a stage we have hit 
  	// before just adding it to our visited list
  	if (audited.indexOf(newValue) == -1)
  		return;
  	
  	this.visited.push(newValue);	    
  	var s1 = new StageStatus(this.grStatus.getValue("stage_status"));
  	s1.setVisited(this.visited);
  	this.grStatus.setValue("stage_status", s1.toString1());
  	this.grStatus.update();
  },

  updateValue : function(newValue) {
  	var ss = new StageStatus(this.grStatus.getValue("stage_status"));
  	ss.setValue(newValue);
  	this.grStatus.setValue("stage_status", ss.toString1());
  	this.grStatus.update();
  },

  getPreviousValues : function() {
  	var gr = this.getElement().getGlideRecord();
  	var fieldName = this.getElement().getName();

  	var grStatus = GlideRecord("stage_state");
  	grStatus.addQuery("table", gr.getTableName());
  	grStatus.addQuery("id",    gr.getUniqueValue());
  	grStatus.query();
  	this.grStatus = grStatus;

  	if (grStatus.next())
  		return readCurrentState(grStatus);

  	var status = createStageState(gr, fieldName, grStatus);
  	
  	return status.getVisited();


  	function readCurrentState(grS) {
  		var json = grS.getValue("stage_status");
  		var status = new StageStatus(json);
  		return status.getVisited();
  	}

  	function createStageState(gr, fieldName, grStatus) {
  		var s = new StageStatus();
  		var currentValue = gr.getValue(fieldName);
  		s.setValue(currentValue);
  		s.setVisited(computeVisitedFromAudit(gr, fieldName, currentValue));

  		grStatus.initialize();
  		grStatus.setValue("table", gr.getTableName());
  		grStatus.setValue("id",    gr.getUniqueValue());
  		grStatus.setValue("stage_status", s.toString1());
  		grStatus.insert();
  		return s;
  	}
  	
  	function computeVisitedFromAudit(gr, fieldName, currentValue) {
  		var values = [];
  		var gaAudit = new GlideAggregate("sys_audit");
  		gaAudit.addQuery("tablename",   gr.getTableName());
  		gaAudit.addQuery("fieldname",   fieldName);
  		gaAudit.addQuery("documentkey", gr.getUniqueValue());
  		gaAudit.addQuery("sys_created_on", ">=", gr.getValue("sys_created_on"));
  		gaAudit.addAggregate("COUNT");
  		gaAudit.groupBy("newvalue");
  		gaAudit.query();

  		while (gaAudit.next()) {
  			addValue("newvalue");
  			addValue("oldvalue");
  		}
  		
  		return values;


  		function addValue(name) {
  			var v = gaAudit.getValue(name)+'';
  			
  			if (this._visitedArrayContains(v))
  				return;
  			
  			if (v && v != currentValue)
  			   values.push(v);
  		}
  	}		
  },

  processChoice : function(choice, index, selectedIndex) {
  
  	// If we already have an image specified for this choice, use it
  	//
  	if (choice.image)
  		return;
  
  	var newValue = choice.value + '';
  	
  	// If 
  	//   a) we have a new non-empty value, and, 
  	//   b) it is not currently in the visted list, and,
  	//   c) choice passed to us is before the selected (current) choice
  	//
  	if (newValue && !this._visitedArrayContains(newValue) &&  index < selectedIndex)
  		this.updateVisited(newValue);
  	
  	var selected = (index == selectedIndex);

  	// everything after the selected item is 'pending'
  	//
  	if (index > selectedIndex) {
  		choice.image = "icon-empty-circle";
  		this._appendStateText(choice, gs.getMessage("Pending - has not started"));
  		return;
  	}

  	var stateText = "";
  	if (selected) {
  		this.updateValue(choice.value);
  		choice.image = "icon-arrow-right color-accent";
  		stateText = "In progress";

  		if (choice.value == 'rejected') {
  			choice.image = "icon-cross-circle color-negative";
  			stateText = "";
  		} else {
  			if (this.isOverdue()) {
  				choice.image = "icon-alert color-negative";
  				stateText = "Overdue";
  			} else {
  				if (choice.value == 'complete') {
  					choice.image = "icon-check-circle color-positive";
  					stateText = "Completed";
  				} else if (choice.value == 'on hold') {
  					choice.image = "icon-clear color-warning";
  					stateText = "On hold";
  				}
  			}
  		}
  	} else {
  		if (this.hasBeenUsed(choice)) {
  			choice.image = "icon-check-circle color-positive";
  			stateText = "Completed";
  		} else {
  			choice.image = "icon-step-over";
  			stateText = "Skipped";
  		}
  	}

  	if (stateText)
  		this._appendStateText(choice, gs.getMessage(stateText));
  	

  },

  getFieldValuesUsed : function (gr, fieldname) {
  	var audit = new GlideRecord("sys_audit");
  	audit.addQuery("fieldname", fieldname);
  	audit.addQuery("documentkey", gr.getUniqueValue());
  	audit.addOrderBy("record_checkpoint");
  	audit.addReturnField("newvalue");
  	audit.addReturnField("oldValue");

  	//If the table has sys_class_name, prioritize it over table name
  	// since record may originate from a different table that extends
  	// current table
  	if(gr.isValidField("sys_class_name"))
  		audit.addQuery("tablename", gr.getValue("sys_class_name"));
  	else
  		audit.addQuery("tablename", gr.getTableName());
  	audit.query();

  	var gotInitial = false;
  	values = [];
  	while(audit.next()) {
  		values.push(audit.getValue("newvalue"));
  		if(gotInitial)
  			continue;
  		values.push(audit.getValue("oldvalue"));
  		gotInitial = true;
  	}
  	return values;
  },

  _visitedArrayContains : function (newValue) {
  	return this.au.indexOf(this.visited, newValue) != -1;
  },
  
  _appendStateText : function(choice, text) {
  	if (text)
  		choice.setParameter("title", choice.label);
  	choice.label = text;
  },

  type : "WorkflowIcons"
};

Sys ID

5aa383480a0a0b8c002a23660eb48e61

Offical Documentation

Official Docs: