Changing the way ticket conversation aligns messages

James Roberts
Giga Contributor

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.

1 ACCEPTED SOLUTION

SatheeshKumar
Kilo Sage

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

View solution in original post

6 REPLIES 6

Mike Patel
Tera Sage

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'}"

Both of those just puts all comments to the right

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

 

 

SatheeshKumar
Kilo Sage

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