Tick Conversation Widget Not Showing All Activity

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎01-21-2018 03:02 PM
I'd like the Ticket Conversation widget to show ALL activity that's viewable on a ticket when looking at the Platform view/form. I'm finding though that the status changes, email updates, etc don't show up (i.e. the "automatically generated" stuff). I've noticed that these entries also have a different colour (light grey) in the Platform view, so there's some sort of flag or type on them that's different.
I've gone over the widget code but can't spot the problem. Does anyone know why these entries don't show and how to make them show?
PLATFORM:
SERVICE PORTAL - TICKET CONVERSATIONS:
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎01-21-2018 07:17 PM
Hi nadon,
At first glance it looks like the "How" to getting the entire activity in the Portal is to clone the widget and add that functionality.
I saw that an option of includeExtended can be set to a boolean value. By default it's set to false and there is no option set in the options schema. So to change that setting you would add an object with the a property of the corresponding name and value of true in the "Additional option json format" field in the widget Intance.
{
" includeExtended": true;
}
However, all that did was add the ability to add work notes from the Portal side.
So cloning and scripting your own way of getting the additional desired information (possibly from the audit table) and adding it to the client side where it merges the activities would be the "How".
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎01-21-2018 07:27 PM
Thank you Chris ! I will give a try.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎01-21-2018 07:17 PM
All good. I've commented out the $sp.getStream and I'm just constructing my own stream with the same structure, but using the sys_audit table instead. Seems to be working well but I just need to test with a few more tickets in case there's any weirdness.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎02-12-2018 11:58 PM
Sounds good Steve
Would you be able to post your solution?
Thanks

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎02-13-2018 01:32 PM
Sure. It's not the cleanest solution in the world, but it does appear to work just fine.
You want to comment out this line in the server script:
data.stream = $sp.getStream(data.table, data.sys_id);
And replace it with this (or something like it):
// BEGIN REPLACEMENT FOR THE $sp.getStream STREAM USING sys_audit TABLE
data.stream = {};
data.stream.display_value = data.number;
data.stream.label = gr.getLabel();
data.stream.number = data.number;
data.stream.short_description = gr.getDisplayValue("short_description");
data.stream.sys_id = data.sys_id;
data.stream.table = data.table;
data.stream.user_full_name = gs.getUserDisplayName();
data.stream.user_login = gs.getUserName();
data.stream.user_sys_id = gs.getUserID();
data.stream.journal_fields = [];
// A COUPLE OF THINGS WE'LL NEED LATER FOR TESTING WORK_NOTE ACCESS
var hasItilRole = UserHasSpecificRole(gs.getUserName(), "ITIL");
var isInWorkNotesList = false;
if (gr.getValue("work_notes_list"))
isInWorkNotesList = gr.getValue("work_notes_list").toString().indexOf(gs.getUserID()) == -1 ? false : true;
// ALL OF THE ACTIVITY TYPES THAT WE'D LIKE TO LOOK AT
data.stream.journal_fields.push( { can_read: true, can_write: true, color: "transparent", label: "Additional Comments", name: "comments" } );
data.stream.journal_fields.push( { can_read: true, can_write: true, color: "gold", label: "Work Notes", name: "work_notes" } );
data.stream.journal_fields.push( { can_read: true, can_write: true, color: "green", label: "State", name: "state" } );
data.stream.journal_fields.push( { can_read: true, can_write: true, color: "blue", label: "Priority", name: "priority" } );
data.stream.journal_fields.push( { can_read: true, can_write: true, color: "red", label: "Category", name: "category" } );
data.stream.journal_fields.push( { can_read: true, can_write: true, color: "red", label: "Subcategory", name: "subcategory" } );
data.stream.journal_fields.push( { can_read: true, can_write: true, color: "aqua", label: "Assigned To", name: "assigned_to" } );
data.stream.journal_fields.push( { can_read: true, can_write: true, color: "aqua", label: "Assignment Group", name: "assignment_group" } );
data.stream.journal_fields.push( { can_read: true, can_write: true, color: "pink", label: "Short Description", name: "short_description" } );
fieldTypes = []; // THIS JUST SAVES US TYPING THEM ALL OUT AGAIN BELOW
for (var i = 0; i < data.stream.journal_fields.length; i++)
{
fieldTypes.push(data.stream.journal_fields[i].name);
}
// ALL OF THE ENTRIES WE WANT TO SEE
data.stream.entries = [];
var rs2 = new GlideRecord("sys_audit");
rs2.addQuery("documentkey", data.sys_id.toString());
rs2.addQuery("tablename", data.table.toString());
rs2.addQuery("fieldname", "IN", fieldTypes.join(","));
rs2.orderByDesc("sys_created_on");
rs2.query();
while (rs2.next())
{
// TEST USER'S ABILITY TO SEE WORK_NOTES - IF THEY CAN'T, AND THIS IS A WORK_NOTE, SKIP IT!
if (rs2.fieldname.getValue().toString() == "work_notes" && !hasItilRole && !isInWorkNotesList)
continue;
data.stream.entries.push( {
element: rs2.fieldname.getValue(),
field_label: rs2.fieldname.getDisplayValue(),
initials: gs.getUserDisplayName().split(" ")[0].toString().substr(0, 1) +gs.getUserDisplayName().split(" ")[1].toString().substr(0, 1),
login_name: GetUserNameByUserId(rs2.user.getDisplayValue()),
name: GetUserNameByUserId(rs2.user.getDisplayValue()),
sys_created_on: rs2.sys_created_on.getValue(),
sys_created_on_adjusted: rs2.sys_created_on.getDisplayValue(),
sys_id: rs2.sys_id.toString(),
user_sys_id: GetSysIdByUserId(rs2.user.getDisplayValue()),
value: (rs2.newvalue.toString().indexOf("[code]") != -1) ? rs2.newvalue.toString().replace("[code]", "").replace("[/code]", "") : htmlEscape(rs2.newvalue.toString())
} );
if (rs2.fieldname.toString() == "state")
data.stream.entries[data.stream.entries.length - 1].value = "State changed from <b>" +GetChoiceLabelByValue(data.table.toString(), rs2.oldvalue.toString(), "state") +"</b> to <b>" +GetChoiceLabelByValue(data.table.toString(), rs2.newvalue.toString(), "state") +"</b>";
if (rs2.fieldname.toString() == "priority")
data.stream.entries[data.stream.entries.length - 1].value = "Priority changed from <b>" +GetChoiceLabelByValue(data.table.toString(), rs2.oldvalue.toString(), "priority") +"</b> to <b>" +GetChoiceLabelByValue(data.table.toString(), rs2.newvalue.toString(), "priority") +"</b>";
if (rs2.fieldname.toString() == "category")
data.stream.entries[data.stream.entries.length - 1].value = "Category changed from <b>" +GetChoiceLabelByValue(data.table.toString(), rs2.oldvalue.toString(), "category") +"</b> to <b>" +GetChoiceLabelByValue(data.table.toString(), rs2.newvalue.toString(), "category") +"</b>";
if (rs2.fieldname.toString() == "subcategory")
data.stream.entries[data.stream.entries.length - 1].value = "Subcategory changed from <b>" +GetChoiceLabelByValue(data.table.toString(), rs2.oldvalue.toString(), "subcategory") +"</b> to <b>" +GetChoiceLabelByValue(data.table.toString(), rs2.newvalue.toString(), "subcategory") +"</b>";
if (rs2.fieldname.toString() == "assigned_to")
data.stream.entries[data.stream.entries.length - 1].value = "Assigned To changed from <b>" +GetUserNameBySysId(rs2.oldvalue.toString()) +"</b> to <b>" +GetUserNameBySysId(rs2.newvalue.toString()) +"</b>";
if (rs2.fieldname.toString() == "assignment_group")
data.stream.entries[data.stream.entries.length - 1].value = "Assignment Group changed from <b>" +GetGroupNameBySysId(rs2.oldvalue.toString()) +"</b> to <b>" +GetGroupNameBySysId(rs2.newvalue.toString()) +"</b>";
if (rs2.fieldname.toString() == "short_description")
data.stream.entries[data.stream.entries.length - 1].value = "Short Description changed from <b>" +rs2.oldvalue.toString() +"</b> to <b>" +rs2.newvalue.toString() +"</b>";
}
// END REPLACEMENT FOR THE $sp.getStream STREAM USING sys_audit TABLE
I've also got a bunch of fairly ugly little functions that I'm using throughout this, mostly just to look up users, groups, etc. These could definitely be done cleaner, but I didn't spend a lot of time on them. You can just tack these on at the end somewhere:
// EXTRA FUNCTIONS NEEDED FOR REPLACEMENT CODE
function UserHasSpecificRole(_user, _role)
{
// SERVER-SIDE ONLY GIVES US HASROLE() WHICH IS OVERRIDDEN BY ADMIN - CUSTOM VERSION WORKS FOR EVERYTHING
var rs = new GlideRecord("sys_user_has_role");
rs.addQuery("user.user_name", _user);
rs.addQuery("role.name", _role);
rs.query();
return rs.hasNext();
}
function GetUserNameByUserId(_id)
{
var rs = new GlideRecord("sys_user");
rs.addQuery("user_name", _id);
rs.query();
if (rs.next())
return rs.getDisplayValue();
else
return "ServiceNow"; // IF WE CAN'T FIND IT, IT'S MOST LIKELY A BUSINESS RULE OR OTHER INTERNAL PROCESS
}
function GetSysIdByUserId(_id)
{
var rs = new GlideRecord("sys_user");
rs.addQuery("user_name", _id);
rs.query();
if (rs.next())
return rs.sys_id.toString();
else
return "";
}
function GetUserNameBySysId(_id)
{
var rs = new GlideRecord("sys_user");
rs.addQuery("sys_id", _id);
rs.query();
if (rs.next())
return rs.name.getDisplayValue();
else
return "Unknown";
}
function GetGroupNameBySysId(_id)
{
var rs = new GlideRecord("sys_user_group");
rs.addQuery("sys_id", _id);
rs.query();
if (rs.next())
return rs.name.getDisplayValue();
else
return "Unknown";
}
function GetChoiceLabelByValue(_table, _value, _element)
{
var rs = new GlideRecord("sys_choice");
rs.addQuery("name", _table);
rs.addQuery("value", _value);
rs.addQuery("element", _element);
rs.query();
if (rs.next())
return rs.label.toString();
else
{
// WE DIDN'T FIND IT, ASSUME IT'S UP ON THE TASK TABLE. MANUALLY CHECK ONCE FOR IT, UGLY BUT ALRIGHT
var rs1 = new GlideRecord("sys_choice");
rs1.addQuery("name", "task");
rs1.addQuery("value", _value);
rs1.addQuery("element", _element);
rs1.query();
if (rs1.next())
return rs1.label.toString();
else
return "None";
}
}
// END EXTRA FUNCTIONS NEEDED FOR REPLACEMENT CODE