Service Portal building conversation widget: Function from "API Server Side scoped" class: GlideSPScriptable.getStream(String table, String sysId) does not work as expected

peternilsson
Kilo Contributor

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.

32 REPLIES 32

HI Amaradi,



in the Client Script you can use the c.data variable to check if it is empty.



I have modified the sample widget Hello World that you can find on the portal:



HTML:


<div>


Enter your name:


<input   type="text" ng-model="c.data.sometext" ng-change="c.display()"/>


<h1>{{ c.data.message }}</h1>


</div>


-----



client Script :



function($scope, spUtil) {


var c = this;  
  c.display = function() {  



// c.data.sometext is the variable that is connected to your html variable. via ng-model="c.data.sometext"
  if(!c.data.sometext){         //HERE YOU CAN CHECK IF YOUR TEXTFIELD IS EMPTY OR NOT.


        alert('DATA IS EMPTY');


    return;



  } else {
  c.data.message = (c.data.sometext) ? 'Hello ' + c.data.sometext + '!' : '';
  }
}

c.display();
}


------------------------------------



Would you mark replies as Answer / Helpful as appropriate. Thanks.


If the user clicks on reject button in "approval info" widget then i thought display a modal dailog window to ask for the comments and once the user enters it should update in the record.



User can't click on "close" button if comments is not filled in. Please help me to acheive this.


HTML:



<script>


  <div class="panel panel-default">


  <div class="panel-heading">


  <h4 class="panel-title">Modal Window</h4>


  </div>


  <div class="panel-body wrapper-xl">


  Hello


  </div>


  <div class="panel-footer text-right">


  <button class="btn btn-primary" ng-click="c.closeModal()">${Close Modal}</button>


  </div>


  </div>


</script><div>


  <button class="btn btn-primary" ng-click="c.openModal()">${Open Modal}</button>


</div>


<div class="panel panel-{{::c.options.color}} b">


  <div class="panel-heading">


      <h4 class="panel-title" ng-if="c.data.state == 'requested'">${This {{c.data.label}} requires your approval}</h4>


      <h4 class="panel-title" ng-if="c.data.state == 'approved'">${Approved} <sn-time-ago timestamp="::c.data.sys_updated_on" /></h4>


      <h4 class="panel-title" ng-if="c.data.state == 'rejected'">${Rejected} <sn-time-ago timestamp="::c.data.sys_updated_on" /></h4>


  </div>


  <div class="panel-body">


      <form ng-submit="$event.preventDefault()" class="form-horizontal">


   


          <div ng-if="c.data.fields.length > 0">


              <div ng-repeat="field in c.data.fields" class="m-b-xs" ng-if="field.value">


                  <label 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 ng-if="c.data.state == 'requested'" class="question">


              <button type="button" name="approve" class="btn btn-success btn-question" ng-click="c.action('approved')">${Approve}</button>


              <div class="spacer"></div>


              <button type="button" name="reject" class="btn btn-default btn-question" ng-click="c.action('rejected')">${Reject}</button>


       


              <input   type="text" ng-model="c.data.sometext"/>


       


          </div>


      </form>


  </div>


</div>


<script>


  <div class="panel panel-default">


  <div class="panel-heading">


  <h4 class="panel-title">Modal Window</h4>


  </div>


  <div class="panel-body wrapper-xl">


  Comments: <input type="text" ng-model="c.data.sometext" required>


  </div>


  <div class="panel-footer text-right">


  <button class="btn btn-primary" ng-click="c.closeModal()">${Close Modal}</button>


  </div>


  </div>


</script>



Client controller:


function ($scope, $uibModal) {


  var c = this;



   


  c.action = function(state) {



  c.modalInstance = $uibModal.open({


  templateUrl: 'modalTemplate',


  scope: $scope


  });




  c.closeModal = function() {


  if(c.data.sometext == '')


  {



  return;


  }


  c.modalInstance.close();


  }


  c.okay = function() {


  $modalInstance.close();


  c.data.op = state;


  c.data.state = state;


  if(state == 'rejected')


  {


  c.data.comments = c.data.sometext;


                        }




  c.server.update();



            }



  }


}



Server script:



var gr = $sp.getRecord();


if (input && input.op && gr) {


  gr.state = input.op;


  gr.update();


}



var fields = $sp.getFields(gr, 'state,sys_created_on');



if (gr) {


  if (gr.sys_mod_count > 0)


  fields.push($sp.getField(gr, 'sys_updated_on'));


  data.fields = fields;


  data.state = gr.state.toString();


  data.sys_updated_on = gr.sys_updated_on.toString();


  data.sys_id = gr.getUniqueValue();


  data.table = gr.getTableName();


  data.label = getRecordBeingApproved(gr).getLabel();


  data.input = input.myInput;


  data.comments = gr.comments;


}


function getRecordBeingApproved(gr) {


  if (!gr.sysapproval.nil())


  return gr.sysapproval.getRefRecord();


  return gr.document_id.getRefRecord();


}


Hi Amaradi



as far as I understood, you want that the Button "Close Modal" only display when the Text Field Comment is not empty.



if so this what you simple should do.



in the HTML Code:



just add: ng-if="c.data.sometext"     on the DIV Panel.



  <div class="panel panel-default">  


  <div class="panel-heading">  


  <h4 class="panel-title">Modal Window</h4>  


  </div>  


  <div class="panel-body wrapper-xl">  


  Comments: <input type="text" ng-model="c.data.sometext" required>  


  </div>  


  <div ng-if="c.data.sometext" class="panel-footer text-right">           // <--------       here it will be the additional code.


  <button class="btn btn-primary" ng-click="c.closeModal()">${Close Modal}</button>  


  </div>  


  </div>  


Hi Elais,



Thanks again for your reply.



My requirement is like when user clicks on "Reject" button then a window should open to ask for the comments as request can't be rejected without filling comments.



If the user didn't provide comments then it should not be rejected. Sorry for confusing you.



Please help me to acheive this by modifying "Approval Info" widget. Presently it is just rejecting with out asking for comments.



Regards


Swamy


Try this:



modify the HTML line 20 to:


<h4 class="panel-title" ng-if="c.data.state == 'rejected' && c.data.comment">${Rejected} <sn-time-ago timestamp="::c.data.sys_updated_on" /></h4>



Add to the HTML this DIV:


<div ng-if="c.data.state == 'rejected'" ng-show="c.comment">


              You answered <span>{{c.comment}}</span>


      </div>



Add the client script this function: (dont forget to add to the main function the spModal:   function (spUtil, spModal)


modify the c.action = function to:


c.action = function(state) {    


  if(state == "rejected"){


    c.onPrompt();


  }


add this function:



c.onPrompt = function() {
                spModal.open({
                      title: 'Give me a comment',
                      message: 'Your comment please?',
                      input: true,
                      value: c.comment
              }).then(function(comment) {
                      c.comment = comment;
            c.data.comment = comment;
              })


      }



to not allow to update on the server if the comment is empty modify on the server side this:



if (input && input.op && gr && input.comment) {


gr.state = input.op;


gr.update();


}




thanks


Elias