Service Portal building conversation widget: Function from "API Server Side scoped" class: GlideSPScriptable.getStream(String table, String sysId) does not work as expected
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎11-01-2016 06:05 AM
For a client I am trying to modify the "Ticket Conversation" widget to display Activity Stream for a custom table in a similar way as the Service Portal demo does for Incidents.
I have created a custom table with journal fields and made table "auditable" in sys_dictionary. In the form (client) for the custom table the Acivity stream works as expected and produces a log for all activities.
I would now like to present the activity stream in the new Service Portal. The OOB widget "Ticket Conversations" does this in a neat way for some of the posts in the activity stream, I would like to modify it to work for a custom table and also add the possibility to see Historic changes to fields (not in the widget today).
My problem is that it seems to work ok when I extend a task table and point the widget to a record from the extended table. The issue in this case is that for this custom table we do not extend Task table. Result is that I am not able to retrieve the Activity Stream in the widget.
My question is if there is any setting on table level you have to perform to retrieve the activity stream.
If not, I believe there is a issue with the $sp.getStream(String table, String sysId) function from the API (https://developer.servicenow.com/app.do#!/api_doc?v=helsinki&type=server&scoped=true&to=method_class...).
It seems this function does not return the Stream Object as expected.
I have understood that the issue is on row #55 in "Ticket Conversation" widget Server Script. The row is: data.stream = $sp.getStream(data.table, data.sys_id);
This function call (when run on a record in a table extending Task) return the following object Object[display_value,sys_id,short_description,number,entries,user_sys_id,user_full_name,user_login,label,table,journal_fields]
When run on a table NOT extending Task it return the following: Object[table, sys_id]
If anyone has encountered any problems or has anoher solution to present the Activity Stream in Service Portal in a nice way, please comment.
Steps to reproduce issue:
- Create custom table (u_test_table)
- Add a "Journal" column and a "Journal Input" field to table
- Confirm Table is marked as "Auditable" in sys_dictionary
- Confirm Table is marked as "Live Feed" = true
- Install Service Portal for Enterprise plugin
- Copy "Ticket Conversations" widget to "My Ticket Conversations"
- Add "My Ticket Conversation" to a new page
- Access the page https://my_instance.service-now.com/sp?id=new_page&sys_id=MY_TICKET_SYSID&table=u_test_table
Result: Conversations for ticket is not shown. I have made the same steps but instead creating a custom table extending "task" table. If I do this the conversation is shown.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎11-10-2016 02:50 AM
Sounds like a good solution, our requirements are a bit more than that. We would like to see any changes from the activity log (e.g. Attachements added, changes to fields marked as "history fields". I know how to build this by creating a widget and querying correct tables but from our perspective and since service now already does this in the activity log it would be better to be able to re-use this instead of creating a custom solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎12-20-2016 04:14 AM
Hi Elias,
Could you please share the script with us, i need to implement the same.
Thanks and regards
Swamy
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎12-20-2016 04:33 AM
Hi Amarady
As per my last post, I have created 2 Widgets:
The Widget in the middle has this code:
HTML Template:
<div ng-if="data.canRead" class="panel b">
<div class="panel-heading bg-primary">
<div ng-init="spSearch.targetRequests()">
<sp-c-link target="form" table="data.table" id="data.sys_id"/>
</div>
<span ng-if="data.agent" >
${Agent working on this {{data.tableLabel}}}:
<div>{{data.agent}}</div>
</span>
<span ng-if="!data.agent && data.agentPossible" >${Your request has been submitted}</span>
<span ng-if="!data.agentPossible">${{{data.tableLabel}} comments details}</span>
</div>
<div class="panel-body">
<div ng-if="data.fields.length > 0">
<div class="row">
<h4 style="font-weight:bold">
${Comments}
</h4>
<pre class="test1"> {{data.comments}}</pre>
</div>
-----------------------------------
Client Script:
function ($scope, spUtil) {
$scope.$on('record.updated', function(name, data) {
spUtil.update($scope);
})
}
---------------------
Server Script:
(function(){
var gr = $sp.getRecord();
data.canRead = gr.canRead();
if (!data.canRead)
return;
var agent = "";
var a = $sp.getField(gr, 'assigned_to');
if (a != null)
agent = a.display_value;
var fields = $sp.getFields(gr, 'u_number,short_description,type,u_outage_kind,cmdb_ci,begin,end,sys_created_on,u_region_impacted,
u_single_location_affected,u_multiple_location_addected_in_region');
data.tableLabel = gr.getLabel();
data.fields = fields;
data.variables = $sp.getVariablesArray();
data.agent = agent;
data.agentPossible = gr.isValidField("assigned_to");
data.table = gr.getTableName();
data.sys_id = gr.getUniqueValue();
data.number = gr.u_number;
var notes = gr.u_comments.getJournalEntry(-1);
var na = notes.split("\n");
var notesTrim = notes.trim()
for (var i = 0; i < na.length; i++)
data.comments = na[i];
data.comments = notesTrim;
})()
------------------------------
Would you mark replies as Answer / Helpful as appropriate. Thanks.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎12-20-2016 04:49 AM
Hi Amaradi
This was only the first widget code now follows the second one that on the right side of the page:
HTML:
<div ng-if="data.canRead" class="panel b">
<div class="panel-heading bg-primary">
<div ng-init="spSearch.targetRequests()">
<sp-c-link target="form" table="data.table" id="data.sys_id"/>
</div>
<span ng-if="data.agent" >
${Agent working on this {{data.tableLabel}}}:
<div>{{data.agent}}</div>
</span>
<span ng-if="!data.agent && data.agentPossible" >${Your request has been submitted}</span>
<span ng-if="!data.agentPossible">${{{data.tableLabel}} record details}</span>
</div>
<div class="panel-body">
<div ng-if="data.fields.length > 0">
<div class="row">
<div class="col-md-6 col-sm-12 col-xs-12 m-b break-word" ng-repeat="field in data.fields"
ng-if="field.value && (field.type != 'decimal' || field.type == 'decimal' && field.value != 0)" >
<label style="font-weight:bold;" class="m-n">{{field.label}}</label>
<span ng-switch="field.type">
<div ng-switch-when="glide_date_time" title="{{field.display_value}}">
<sn-time-ago timestamp="::field.value" />
</div>
<div ng-switch-default >{{field.display_value}}</div>
</span>
</div>
</div>
</div>
<div ng-if="data.variables.length > 0" ng-init="variable_toggle=true">
<h4 ng-click="variable_toggle = !variable_toggle" style="cursor: pointer;">
<span style="font-weight:bold;" class="glyphicon"
ng-class="{'glyphicon-chevron-down': !variable_toggle, 'glyphicon-chevron-up': variable_toggle}"></span>
${Options}
</h4>
<div ng-if="variable_toggle">
<hr>
<div class="m-b break-word" ng-repeat="variable in data.variables">
<label style="font-size: 50px;" class="m-n">{{variable.label}}</label>
<div>{{variable.display_value}}</div>
</div>
</div>
</div>
</div>
<div ng-if="data.agentPossible && !data.agent && options.pickup_msg" class="panel-footer">
<div id="ticket_fields_footer" class="text-center text-muted" style="font-style: italic;" ng-bind-html="options.pickup_msg">
</div>
</div>
</div>
------------------
Client Script:
function ($scope, spUtil) {
$scope.$on('record.updated', function(name, data) {
spUtil.update($scope);
})
}
----------------
Server Script:
(function(){
var gr = $sp.getRecord();
data.canRead = gr.canRead();
if (!data.canRead)
return;
var agent = "";
var a = $sp.getField(gr, 'assigned_to');
if (a != null)
agent = a.display_value;
var fields = $sp.getFields(gr, 'u_number,short_description,type,u_outage_kind,cmdb_ci,begin,end,sys_created_on,u_region_impacted,u_single_location_affected,u_multiple_location_addected_in_region');
//if (gr.getValue("sys_mod_count") > 0)
//fields.push($sp.getField(gr, 'u_comments').getJournalEntry(-1));
data.tableLabel = gr.getLabel();
data.fields = fields;
data.variables = $sp.getVariablesArray();
data.agent = agent;
data.agentPossible = gr.isValidField("assigned_to");
data.table = gr.getTableName();
data.sys_id = gr.getUniqueValue();
data.number = gr.u_number;
})()
----------------------
Would you mark replies as Answer / Helpful as appropriate. Thanks.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎12-20-2016 05:13 AM
Hi Elias,
Thank you !!
Can you please help me to understand how to pass values from html field to client controller.
For example i have created a text field in HTML, and i need to check whether the value is filled in variable or not. if value is not filled in then i need to alert a message and abort the update.
function () {
var c = this;
c.action = function(state) {
c.data.op = state;
c.data.state = state;
c.server.update();
}
}