Additional comments field on approval form: SP
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎11-01-2017 01:54 PM
Hello everyone
I have an Issue related to Additional comments field on Approval form, Service Portal. As per our design end users are redirected to Service Portal. End users/approvers can view the approval record and can accept/reject it directly from the form.
I see an issue whenever an end user tries to reject the request, it gives a pop up message 'Do not forget to enter comments' but it does not show the field to enter the comments or I can say there is no field for them to update the comments and reject,
Actually there is a field which is residing on the 'Activity stream' on the approval record but only visible to ITIL and admins if they navigate to SP. The yellow part in the image is where approvers can enter their comments and hit reject.
If you can look at my two screenshots, the 1st one is visible only to end users who do not have any roles. The second one is for ITIL and Admin.
I see this form is coming from a page (id=approval) and I have checked the widget code as well, but no luck
FYI - We are experiencing this Issue in Istanbul as we recently upgraded from helsinki. In helsinki we don't see this issue and works for everyone.
Any suggestions or thoughts on this part?
Thanks
- Labels:
-
Service Portal Development
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎11-11-2017 05:18 PM
Not sure why this is happening, it was working fine for me. Could you please the widget html code? Also please confirm you have commented the correct line in your html code, as there is other part of the widget where this line of code is being used.
Please hit correct/helpful based on impact of answer
Thanks
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎11-13-2017 04:59 AM
i am posting the modified code of the conversation widget..
HTML:
<div>
<!--<div ng-if="!data.canRead">
${Requested record not found}
</div>-->
<div class="panel panel-{{options.color}} b ticket_conversation" >
<div class="panel-heading">
<h4 class="panel-title">
<div class="pull-right">
<sp-attachment-button></sp-attachment-button>
<a href="javascript:void(0)" class="panel-button" ng-show="isNative" ng-click="scanBarcode()" title="{{data.scanBarcodeMsg}}">
<span class="glyphicon glyphicon-barcode"></span>
</a>
<a href="javascript:void(0)" ng-show="data.showLocationIcon" class="panel-button" ng-click="checkInLocation()" title="{{data.checkInLocMsg}}">
<span class="glyphicon glyphicon-globe"></span>
</a>
</div>
{{data.ticketTitle}}
</h4>
</div>
<div class="panel-body">
<div>
<form ng-submit="postEntry(data.journalEntry)" id="sand">
<div class="input-group">
<textarea ng-keypress="keyPress($event)" sn-resize-height="trim" rows="1" id="post-input" class="form-control no-resize" ng-model='data.journalEntry' placeholder="{{placeholder}}" autocomplete="off" ng-change="userTyping(data.journalEntry)" />
<span class="journal-field-indicator" ng-style="{'background-color': data.useSecondaryJournalField ? data.secondaryJournalField.color : data.primaryJournalField.color}"></span>
<span class="input-group-btn" style="vertical-align: top">
<input type="submit" class="btn btn-primary" value="{{data.btnLabel}}" ng-disabled="data.isPosting || data.journalEntry.length == 0"/>
</span>
</div>
<div ng-if="data.secondaryJournalField && data.secondaryJournalField.can_write">
<label class="pull-right"><input type="checkbox" ng-model="data.useSecondaryJournalField"/> {{::data.secondaryJournalField.label}}</label>
</div>
</form>
<ul class="list-group m-b-none">
<li class="list-group-item user-typing m-t" ng-repeat="u in typing">${{{::u.user_display_name}} is typing}</li>
</ul>
<ul class="list-group m-b-none m-t" ng-if="msg">
<li class="list-group-item user-typing">{{msg}}</li>
</ul>
<div class="timeline-container">
<ul class="timeline">
<li class="timeline-item" ng-class="{'timeline-inverted': e.user_sys_id == data.stream.user_sys_id} " ng-repeat="e in data.mergedEntries">
<div class="timeline-badge">
<sn-avatar primary="e.user_sys_id" class="avatar-large" show-presence="true" enable-context-menu="false"></sn-avatar>
</div>
<div class="timeline-panel">
<div class="timeline-panel-inner" style="border-color: {{::getFieldColor(e.element)}}">
<div class="timeline-heading">
<h4 class="timeline-title">{{::e.name}}</h4>
<p>
<small class="text-muted">
<span class="glyphicon glyphicon-time " />
<sn-time-ago timestamp="::e.sys_created_on" />
</small>
</p>
</div>
<div class="timeline-body">
<p ng-if="e.element != 'attachment'" ng-bind-html="::e.value"></p>
<div ng-if="e.element == 'attachment'">
<a target="_blank" href="/sys_attachment.do?view=true&sys_id={{e.attachment.sys_id}}" title="{{dataViewMsg}}" >
<img ng-if="e.attachment.thumbnail_path" alt="" ng-src="/{{e.attachment.path}}?t=medium" class="img-responsive"/>
</a>
<div>
<a href="/sys_attachment.do?sys_id={{e.attachment.sys_id}}" target="_blank" title="{{dataViewMsg}}"><strong>{{e.attachment.file_name}}</strong></a><br/>
{{e.attachment.size}}
</div>
</div>
</div>
</div>
</div>
</li>
<li class="timeline-item timeline-inverted">
<div class="timeline-badge">
<sn-avatar primary="data.stream.user_sys_id" class="avatar-large" show-presence="false" enable-context-menu="false"></sn-avatar>
</div>
<div class="timeline-panel timeline-panel-first-item">
<div class="timeline-heading">
<h4 class="timeline-title">{{data.stream.user_full_name}}</h4>
<p>
<small class="text-muted">
<span class="glyphicon glyphicon-time " />
<sn-time-ago timestamp="data.created_on" />
</small>
</p>
</div>
<div class="timeline-body">
<p>{{data.number}} ${Created}</p>
</div>
</div>
</li>
<li class="timeline-start">
<div class="timeline-badge success">
${Start}
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
Server Script:
(function() {
data.uploadingAttachmentMsg = gs.getMessage("Uploading attachment...");
data.sharingLocMsg = gs.getMessage("Sharing location...");
data.scanBarcodeMsg = gs.getMessage("Scan barcode");
data.checkInLocMsg = gs.getMessage("Check in location");
data.viewMsg = gs.getMessage("View");
data.sys_id = input.sys_id || options.sys_id || $sp.getParameter("sys_id");
data.table = input.table || options.table || $sp.getParameter("table");
// don't use options.title unless sys_id and table also come from options
if (options && options.sys_id && options.table)
data.ticketTitle = options.title;
data.placeholder = options.placeholder || gs.getMessage("Type your message here...");
data.placeholderNoEntries = options.placeholderNoEntries || gs.getMessage("Type your message here...");
data.btnLabel = options.btnLabel || gs.getMessage("Send");
data.includeExtended = options.includeExtended || false;
var gr = new GlideRecord(data.table);
if (!gr.isValid())
return true;
gr.get(data.sys_id);
if (!gr.canRead())
return true;
data.number = gr.getDisplayValue('number');
data.created_on = gr.getValue('sys_created_on');
if (input) { // if we have input then we're saving
if (input.journalEntry && input.journalEntryField){
if (gr.canWrite(input.journalEntryField)){
gr.setDisplayValue(input.journalEntryField, input.journalEntry);
gr.update();
$sp.logStat('Comments', data.table, data.sys_id, input.journalEntry);
}
}
data.ticketTitle = input.ticketTitle;
data.placeholder = input.placeholder;
data.btnLabel = input.btnLabel;
data.includeExtended = input.includeExtended;
} else {
if (!data.ticketTitle) {
if (gr.short_description.canRead())
data.ticketTitle = gr.getDisplayValue("short_description");
if (!data.ticketTitle)
data.ticketTitle = data.number;
}
$sp.logStat('Task View', data.table, data.sys_id);
}
data.canWrite = gr.canWrite();
data.canAttach = gs.hasRole(gs.getProperty("glide.attachment.role")) && GlideTableDescriptor.get(data.table).getED().getAttribute("no_attachment") != "true";
data.canRead = gr.canRead();
data.isNewRecord = data.sys_id == -1 || gr.isNewRecord();
data.hasWritableJournalField = false;
data.hasReadableJournalField = false;
if (data.canRead && !data.isNewRecord) {
data.stream = $sp.getStream(data.table, data.sys_id);
// Journal fields come in correct order already
// so grab the first 2 writeable fields
if ('journal_fields' in data.stream) {
var jf = data.stream.journal_fields;
for(var i=0; i < jf.length; i++){
if (jf[i].can_read === true)
data.hasReadableJournalField = true;
if (jf[i].can_write === true){
data.hasWritableJournalField = true;
if (!data.primaryJournalField)
data.primaryJournalField = jf[i];
else if (data.includeExtended && !data.secondaryJournalField)
data.secondaryJournalField = jf[i];
else
break;
}
}
}
}
data.tableLabel = gr.getLabel();
})()
Client Script:
function(scope, elm) {
// Set the focus back on the input for IE11
scope.setFocus = function() {
var input = $(elm[0]).find('textarea#post-input');
if (input[0])
input[0].focus();
}
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎11-15-2017 11:40 AM
Hi Nitin,
I'm in a similar situation as you were in:
We want our users to be able to submit Additional Comments for Problem and Enhancement records.
While removing the ng-show="data.primaryJournalField" attribute allows the Comment text field to appear, users are unable to submit the actual comment itself.
I've already checked that the ACL we created to permit *.comment actions. e.g.,
"problem.comments" set to (advanced)
current.opened_by == gs.getUserID() || current.u_caller_id == gs.getUserID() || current.caller_id == gs.getUserID() || current.watch_list.indexOf(gs.getUserID()) > -1;
Essentially, for the ACL we are stating that if you created the record, are the Contact for the record, or are on the Watch List, we want you to be able to add Comments to a Problem. (This is the same for Enhancement, the other table we want users to add Comments to).
So even when exposing the Comment field in the ticketconversations widget, the end user cannot post a comment, in our testing. In the same widget, I ran a $sp.log(data.stream.journal_fields) to see what the issue was. The result is the following:
{can_read: true, can_write: false, color: "LightGreen", label: "Additional comments", name: "comments"}
As you can see, the "can_write: false" is the roadblock here. This leads me to believe that write access is still blocked likely by an ACL. Am I missing something?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎09-12-2018 07:53 AM
Did anyone find a resolution for this issue?
We're facing the same issue. The comment-field is not there for the approver (end user). The approver can post comments from the standard user interface, but not from Service Portal. ITIL users can post comments from Service Portal.
Have tried to create an ACL (write) with no role conditions but that does not change anything.
I've tried Nitins solution, remove "ng-show="data.primaryJournalField"" - the field is now visible but the approver is not able to send the comment.
I'm stuck 🙂
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎09-12-2018 11:51 AM
Found a solution on this awsome community that after some modifications works for us.
I deactivated the old ACL: sc_req_item - NONE (record - write) and created a new one with the following script:
itemWrite();
function itemWrite()
{
if(current.opened_by == gs.getUserID() || current.request.requested_for == gs.getUserID() || current.u_requested_for == gs.getUserID())
{
return true;
}
var getApprover = new GlideRecord('sysapproval_approver');
getApprover.addQuery('approver', gs.getUserID());
getApprover.addQuery('sysapproval', current.sys_id);
getApprover.query();
if(getApprover.next())
{
return true;
}
return false;
}
The approver is now able to comment from the approval record on our Service Portal.