- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
09-05-2019 09:37 AM
Hi all
I would like to change the way the ticket conversation places comments in the ticket conversation.
as far as i can tell it currently puts comments from the creator to the right of the timeline and all other comments to the left.
I would like it change it so all comments made by users that do not have the itil role to be on the right of the time line and all the other comments to be on the left
I have already duplicated the widget and modified it with some other settings but i still cant get my head around how it determines which side of the time line comments go.
i think its to do with "ng-class="::{'timeline-inverted': e.user_sys_id == data.stream.user_sys_id}" but not sure how to go about checking the roles.
Solved! Go to Solution.
- Labels:
-
Service Portal Development

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
09-06-2019 07:05 AM
try the below solution:
update your server code with below:
(function() {
data.maxAttachmentSize = parseInt(gs.getProperty("com.glide.attachment.max_size", 1024));
if (isNaN(data.maxAttachmentSize))
data.maxAttachmentSize = 24;
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.messagePostedMsg = gs.getMessage("Message has been sent");
data.viewMsg = gs.getMessage("View");
data.attachAddedMsg = gs.getMessage("Attachment added");
data.attachFailMsg = gs.getMessage("Failed to add attachment");
data.scanFailedMsg = gs.getMessage("File failed security scan");
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;
data.use_dynamic_placeholder = options.use_dynamic_placeholder;
data.isNewRecord = data.sys_id == -1 || gr.isNewRecord();
data.hideAttachmentBtn = options.hideAttachmentBtn;
var gr = new GlideRecord(data.table);
if (!gr.isValid())
return;
gr.get(data.sys_id);
if (!gr.canRead())
return;
data.table = gr.getRecordClassName(); // use actual table for the record
options.no_readable_journal_field_message = options.no_readable_journal_field_message || gs.getMessage("No readable comment field");
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.hasWritableJournalField = false;
data.hasReadableJournalField = false;
if (data.canRead && !data.isNewRecord) {
data.stream = $sp.getStream(data.table, data.sys_id);
if ('entries' in data.stream) {
var ent = data.stream.entries;
for(var k=0; k < ent.length; k++){
console.log("entries" + ent[k].user_sys_id);
var role = new GlideRecord('sys_user_has_role');
role.addQuery('user', ent[k].user_sys_id);
role.addQuery('role.name','itil');
role.setLimit(1);
role.query();
if(role.hasNext()){
ent[k].hasItil = true;
}
else{
ent[k].hasItil = false;
}
}
}
//gs.addInfoMessage(data.stream);
// 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;
}
}
}
}
function checkItil(id)
{
}
data.tableLabel = gr.getLabel();
})()
html
<div>
<div ng-if="!data.canRead && !data.isNewRecord">
${Requested record not found}
</div>
<div ng-if="data.canRead && !data.isNewRecord" class="panel panel-{{options.color}} b ticket_conversation" >
<div class="panel-heading">
<div class="h2 panel-title panel-title-container">
<h2 class="h4 panel-title" aria-label="{{::data.ticketTitle}} ${Ticket history}" >{{::data.ticketTitle}}</h2>
<div class="pull-right panel-title-icons">
<ul>
<li>
<button href ng-show="data.showLocationIcon && data.canWrite" class="panel-button btn btn-link" ng-click="checkInLocation()" title="{{data.checkInLocMsg}}">
<span class="glyphicon glyphicon-globe"></span>
</button>
</li>
<li>
<button href class="panel-button btn btn-link" ng-show="isNative" ng-click="scanBarcode()" title="{{data.scanBarcodeMsg}}">
<span class="glyphicon glyphicon-barcode"></span>
</button>
</li>
<li ng-if="::(!data.hideAttachmentBtn)"><sp-attachment-button ng-if="::data.canWrite && data.canAttach"></sp-attachment-button></li>
</ul>
</div>
</div>
</div>
<div class="panel-body">
<div ng-if="data.hasReadableJournalField">
<form ng-submit="postEntry(data.journalEntry)" id="sand">
<div ng-show="data.hasWritableJournalField" class="input-group">
<textarea ng-keypress="keyPress($event)"
sn-resize-height="trim"
rows="1" id="post-input"
class="form-control no-resize overflow-hidden"
ng-model='data.journalEntry'
ng-model-options='{debounce: 250}'
ng-attr-placeholder="{{getPlaceholder()}}"
aria-label="{{getPlaceholder()}}"
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"/>
</span>
</div>
<div ng-if="::(data.secondaryJournalField && data.secondaryJournalField.can_write)">
<label class="pull-right">
<input type="checkbox" ng-model="::data.useSecondaryJournalField" ng-change="updateFormWithJournalFields()"/>
<span> {{::data.secondaryJournalField.label}}</span>
</label>
</div>
</form>
<ul class="list-group m-b-none" ng-if="typing.length > 0">
<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 role="list" class="timeline" aria-label="${Ticket history}">
<li class="timeline-item" ng-class="::{'timeline-inverted': !e.hasItil} " ng-repeat="e in data.mergedEntries">
<div class="timeline-badge">
<sn-avatar-once
ng-if="hasLiveProfile(e.user_sys_id)"
primary="getLiveProfileByUserId(e.user_sys_id)"
class="avatar-large"
show-presence="false"
enable-context-menu="false">
</sn-avatar-once>
</div>
<div class="timeline-panel">
<div class="timeline-panel-inner" ng-style="::{'border-color': getFieldColor(e.element)}">
<div class="timeline-heading">
<div class="timeline-title h4">{{::e.name}}</div>
<p class = "time-text">
<small class="text-muted" >
<span class="glyphicon glyphicon-time" aria-hidden="true" tabindex="-1" />
<sn-time-ago timestamp="::e.sys_created_on" />
</small>
<i ng-if="::e.field_label" class="fa fa-circle text-muted" aria-hidden="true"></i>
<small class = "text-muted journal-type">{{::e.field_label}}</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 ng-if="(e.attachment.state == 'available')" target="_blank" href="/sys_attachment.do?view=true&sys_id={{::e.attachment.sys_id}}" title="${View}">
<img ng-if="e.attachment.thumbnail_path" alt="" ng-src="/{{::e.attachment.path}}?t=medium" class="img-responsive"/>
</a>
<a ng-if="(e.attachment.state == '' || e.attachment.state == 'pending' || e.attachment.state == 'available_conditionally')" ng-click="scanAttachment(e.attachment)" href="javascript:void(0)" title="${View}">
<img ng-if="e.attachment.thumbnail_path" alt="" ng-src="/{{::e.attachment.path}}?t=medium" class="img-responsive"/>
</a>
<div>
<div ng-if="(e.attachment.state == 'available')">
<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 ng-if="(e.attachment.state == 'not_available')">
<span title="{{dataViewMsg}}" class="not_available">{{e.attachment.file_name}}</span><br/>
<span class="error">{{::data.scanFailedMsg}}</span>
</div>
<div ng-if="(e.attachment.state == '' || e.attachment.state == 'pending' || e.attachment.state == 'available_conditionally')">
<a href="javascript:void(0)" ng-click="scanAttachment(e.attachment)" title="{{dataViewMsg}}"><strong>{{e.attachment.file_name}}</strong></a><br/>
{{::e.attachment.size}}
</div>
</div>
</div>
</div>
</div>
</li>
<li role="listitem" class="timeline-item timeline-inverted" aria-label="{{data.stream.user_full_name}}">
<div class="timeline-badge">
<sn-avatar-once
ng-if="hasLiveProfile(data.stream.user_sys_id)"
primary="getLiveProfileByUserId(data.stream.user_sys_id)"
class="avatar-large"
show-presence="false"
enable-context-menu="false">
</sn-avatar-once>
</div>
<div class="timeline-panel timeline-panel-first-item">
<div class="timeline-heading">
<div class="timeline-title h4">{{data.stream.user_full_name}}</div>
<p>
<small class="text-muted">
<span class="glyphicon glyphicon-time" aria-hidden="true" tabindex="-1" />
<sn-time-ago timestamp="data.created_on" />
</small>
</p>
</div>
<div class="timeline-body">
<p>{{data.number}} ${Created}</p>
</div>
</div>
</li>
<li role="presentation" aria-hidden="true">
<div class="timeline-badge-wrap">
<div class="timeline-badge success">
<span>${Start}</span>
</div>
</div>
</li>
</ul>
</div>
</div>
<div ng-if="!data.hasReadableJournalField">
{{options.no_readable_journal_field_message}}
</div>
</div>
</div>
</div>
this will do what you are looking for.
let me know if you have any questions!!!
-satheesh

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
09-05-2019 10:46 AM
try doing below
"ng-class="::{'timeline-inverted': (e.user_sys_id == data.stream.user_sys_id ||e.user_sys_id != data.stream.user_sys_id)}"
or do remove it totally like
"ng-class="::{'timeline-inverted'}"
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
09-06-2019 03:04 AM
Both of those just puts all comments to the right

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
09-06-2019 05:45 AM
hey,
i think you need to add additional property(flag) to the below object in line no 69 to see the user added comment has itil role or not.(may need additional function call to determine has itil role)
data.stream = $sp.getStream(data.table, data.sys_id);
and then use that flag to decided the side of comment in below html
"ng-class="::{'timeline-inverted': e.user_sys_id == data.stream.user_sys_id}"
below is the structure of stream object, we need to find a way t o add additional property to this object that will say the commented user has itil or not for each commnet.
{
display_value: "INC0010003",
sys_id: "9ea620444f9f2300209b4a318110c751",
short_description: "reset password",
number: "INC0010003", entries:
[
{ sys_created_on_adjusted: "2019-09-06 05:43:22", sys_id: "698e67d8db333300b6db8e4748961912", login_name: "Satheeshkumar ", user_sys_id: "6816f79cc0a8016401c5a33be04be441", initials: "S", sys_created_on: "2019-09-06 12:43:22", field_label: "Additional comments", name: "Satheeshkumar ", value: "test", element: "comments" },
{ sys_created_on_adjusted: "2019-09-06 05:38:53", sys_id: "f77de398db333300b6db8e47489619d3", login_name: "Satheeshkumar ", user_sys_id: "6816f79cc0a8016401c5a33be04be441", initials: "S", sys_created_on: "2019-09-06 12:38:53", field_label: "Additional comments", name: "Satheeshkumar ", value: "qwwwwwwwwwwwwwwwwwwweewe", element: "comments" },
{ sys_created_on_adjusted: "2019-09-06 05:33:35", sys_id: "da4c27d4db333300b6db8e47489619f3", login_name: "Satheeshkumar ", user_sys_id: "6816f79cc0a8016401c5a33be04be441", initials: "S", sys_created_on: "2019-09-06 12:33:35", field_label: "Additional comments", name: "Satheeshkumar ", value: "test", element: "comments" }
],
user_sys_id: "6816f79cc0a8016401c5a33be04be441", user_full_name: "Satheeshkumar ", user_login: "admin", label: "Incident", table: "incident", journal_fields: [ { can_read: true, color: "transparent", can_write: true, name: "comments", label: "Additional comments" }, { can_read: true, color: "gold", can_write: true, name: "work_notes", label: "Work notes" } ] }
-satheesh

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
09-06-2019 07:05 AM
try the below solution:
update your server code with below:
(function() {
data.maxAttachmentSize = parseInt(gs.getProperty("com.glide.attachment.max_size", 1024));
if (isNaN(data.maxAttachmentSize))
data.maxAttachmentSize = 24;
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.messagePostedMsg = gs.getMessage("Message has been sent");
data.viewMsg = gs.getMessage("View");
data.attachAddedMsg = gs.getMessage("Attachment added");
data.attachFailMsg = gs.getMessage("Failed to add attachment");
data.scanFailedMsg = gs.getMessage("File failed security scan");
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;
data.use_dynamic_placeholder = options.use_dynamic_placeholder;
data.isNewRecord = data.sys_id == -1 || gr.isNewRecord();
data.hideAttachmentBtn = options.hideAttachmentBtn;
var gr = new GlideRecord(data.table);
if (!gr.isValid())
return;
gr.get(data.sys_id);
if (!gr.canRead())
return;
data.table = gr.getRecordClassName(); // use actual table for the record
options.no_readable_journal_field_message = options.no_readable_journal_field_message || gs.getMessage("No readable comment field");
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.hasWritableJournalField = false;
data.hasReadableJournalField = false;
if (data.canRead && !data.isNewRecord) {
data.stream = $sp.getStream(data.table, data.sys_id);
if ('entries' in data.stream) {
var ent = data.stream.entries;
for(var k=0; k < ent.length; k++){
console.log("entries" + ent[k].user_sys_id);
var role = new GlideRecord('sys_user_has_role');
role.addQuery('user', ent[k].user_sys_id);
role.addQuery('role.name','itil');
role.setLimit(1);
role.query();
if(role.hasNext()){
ent[k].hasItil = true;
}
else{
ent[k].hasItil = false;
}
}
}
//gs.addInfoMessage(data.stream);
// 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;
}
}
}
}
function checkItil(id)
{
}
data.tableLabel = gr.getLabel();
})()
html
<div>
<div ng-if="!data.canRead && !data.isNewRecord">
${Requested record not found}
</div>
<div ng-if="data.canRead && !data.isNewRecord" class="panel panel-{{options.color}} b ticket_conversation" >
<div class="panel-heading">
<div class="h2 panel-title panel-title-container">
<h2 class="h4 panel-title" aria-label="{{::data.ticketTitle}} ${Ticket history}" >{{::data.ticketTitle}}</h2>
<div class="pull-right panel-title-icons">
<ul>
<li>
<button href ng-show="data.showLocationIcon && data.canWrite" class="panel-button btn btn-link" ng-click="checkInLocation()" title="{{data.checkInLocMsg}}">
<span class="glyphicon glyphicon-globe"></span>
</button>
</li>
<li>
<button href class="panel-button btn btn-link" ng-show="isNative" ng-click="scanBarcode()" title="{{data.scanBarcodeMsg}}">
<span class="glyphicon glyphicon-barcode"></span>
</button>
</li>
<li ng-if="::(!data.hideAttachmentBtn)"><sp-attachment-button ng-if="::data.canWrite && data.canAttach"></sp-attachment-button></li>
</ul>
</div>
</div>
</div>
<div class="panel-body">
<div ng-if="data.hasReadableJournalField">
<form ng-submit="postEntry(data.journalEntry)" id="sand">
<div ng-show="data.hasWritableJournalField" class="input-group">
<textarea ng-keypress="keyPress($event)"
sn-resize-height="trim"
rows="1" id="post-input"
class="form-control no-resize overflow-hidden"
ng-model='data.journalEntry'
ng-model-options='{debounce: 250}'
ng-attr-placeholder="{{getPlaceholder()}}"
aria-label="{{getPlaceholder()}}"
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"/>
</span>
</div>
<div ng-if="::(data.secondaryJournalField && data.secondaryJournalField.can_write)">
<label class="pull-right">
<input type="checkbox" ng-model="::data.useSecondaryJournalField" ng-change="updateFormWithJournalFields()"/>
<span> {{::data.secondaryJournalField.label}}</span>
</label>
</div>
</form>
<ul class="list-group m-b-none" ng-if="typing.length > 0">
<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 role="list" class="timeline" aria-label="${Ticket history}">
<li class="timeline-item" ng-class="::{'timeline-inverted': !e.hasItil} " ng-repeat="e in data.mergedEntries">
<div class="timeline-badge">
<sn-avatar-once
ng-if="hasLiveProfile(e.user_sys_id)"
primary="getLiveProfileByUserId(e.user_sys_id)"
class="avatar-large"
show-presence="false"
enable-context-menu="false">
</sn-avatar-once>
</div>
<div class="timeline-panel">
<div class="timeline-panel-inner" ng-style="::{'border-color': getFieldColor(e.element)}">
<div class="timeline-heading">
<div class="timeline-title h4">{{::e.name}}</div>
<p class = "time-text">
<small class="text-muted" >
<span class="glyphicon glyphicon-time" aria-hidden="true" tabindex="-1" />
<sn-time-ago timestamp="::e.sys_created_on" />
</small>
<i ng-if="::e.field_label" class="fa fa-circle text-muted" aria-hidden="true"></i>
<small class = "text-muted journal-type">{{::e.field_label}}</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 ng-if="(e.attachment.state == 'available')" target="_blank" href="/sys_attachment.do?view=true&sys_id={{::e.attachment.sys_id}}" title="${View}">
<img ng-if="e.attachment.thumbnail_path" alt="" ng-src="/{{::e.attachment.path}}?t=medium" class="img-responsive"/>
</a>
<a ng-if="(e.attachment.state == '' || e.attachment.state == 'pending' || e.attachment.state == 'available_conditionally')" ng-click="scanAttachment(e.attachment)" href="javascript:void(0)" title="${View}">
<img ng-if="e.attachment.thumbnail_path" alt="" ng-src="/{{::e.attachment.path}}?t=medium" class="img-responsive"/>
</a>
<div>
<div ng-if="(e.attachment.state == 'available')">
<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 ng-if="(e.attachment.state == 'not_available')">
<span title="{{dataViewMsg}}" class="not_available">{{e.attachment.file_name}}</span><br/>
<span class="error">{{::data.scanFailedMsg}}</span>
</div>
<div ng-if="(e.attachment.state == '' || e.attachment.state == 'pending' || e.attachment.state == 'available_conditionally')">
<a href="javascript:void(0)" ng-click="scanAttachment(e.attachment)" title="{{dataViewMsg}}"><strong>{{e.attachment.file_name}}</strong></a><br/>
{{::e.attachment.size}}
</div>
</div>
</div>
</div>
</div>
</li>
<li role="listitem" class="timeline-item timeline-inverted" aria-label="{{data.stream.user_full_name}}">
<div class="timeline-badge">
<sn-avatar-once
ng-if="hasLiveProfile(data.stream.user_sys_id)"
primary="getLiveProfileByUserId(data.stream.user_sys_id)"
class="avatar-large"
show-presence="false"
enable-context-menu="false">
</sn-avatar-once>
</div>
<div class="timeline-panel timeline-panel-first-item">
<div class="timeline-heading">
<div class="timeline-title h4">{{data.stream.user_full_name}}</div>
<p>
<small class="text-muted">
<span class="glyphicon glyphicon-time" aria-hidden="true" tabindex="-1" />
<sn-time-ago timestamp="data.created_on" />
</small>
</p>
</div>
<div class="timeline-body">
<p>{{data.number}} ${Created}</p>
</div>
</div>
</li>
<li role="presentation" aria-hidden="true">
<div class="timeline-badge-wrap">
<div class="timeline-badge success">
<span>${Start}</span>
</div>
</div>
</li>
</ul>
</div>
</div>
<div ng-if="!data.hasReadableJournalField">
{{options.no_readable_journal_field_message}}
</div>
</div>
</div>
</div>
this will do what you are looking for.
let me know if you have any questions!!!
-satheesh