2. Create ServiceNow Widget
Enter “Service Portal” in search panel and find “Widgets” in sub menu list:
Click “New” to create a new widget and enter some basic information (Name, ID):
Enter the following contents for each subsequent field.
Body HTM template:
<script language="javascript" type="text/javascript" src="/scripts/openframe/latest/openFrameAPI.min.js"></script> <div id="brekeke_iframe_container" class="full-height"></div>
CCS:
.full-height { height: 100%; } #brekeke_ad { margin: 0; padding: 0; border: none; width: 100%; height: 100%; }
Server Script:
(function () { if (input.action == 'getUserInfo') { var gr = new GlideRecord('customer_contact'); gr.addQuery('phone', 'CONTAINS', input.phoneNumber).addOrCondition('mobile_phone', 'CONTAINS', input.phoneNumber); gr.orderByDesc('sys_created_on'); gr.query(); data.customerInfos = []; while (gr.next()) { var customer = { name: gr.name.toString(), phone: gr.phone.getDisplayValue().toString(), email: gr.email.toString(), contact: gr.getValue('sys_id') }; data.customerInfos.push(customer); } } else if (input.action == 'saveCallLog') { try { var gr = new GlideRecord('sn_openframe_phone_log'); gr.initialize(); var brekekeData = input.data; gr.call_type = brekekeData.Call_Type; var start = new GlideDateTime() start.setNumericValue(brekekeData.Call_Start_Time); var end = new GlideDateTime(); end.setNumericValue(brekekeData.Call_End_Time); gr.start_time = start; gr.end_time = end; gr.duration = new GlideDuration(Number(brekekeData.Call_Duration)); gr.u_phone_number_us = input.data.Phone_Number; gr.u_comment = brekekeData.Description; gr.u_result = brekekeData.Call_Result; if (brekekeData.contactId == null) { var grc = new GlideRecord('customer_contact'); grc.setLimit(1); grc.addQuery('phone', 'CONTAINS', input.data.Phone_Number).addOrCondition('mobile_phone', 'CONTAINS', input.data.Phone_Number); grc.orderByDesc('sys_created_on'); grc.query(); while (grc.next()) { gr.contact = grc.getValue('sys_id'); } } else { gr.contact = brekekeData.contactId; } var recordId = gr.insertWithReferences(); data.result = { success: true, recordId: recordId }; } catch (e) { data.result = { success: false, error: e }; } } else if (input.action == 'updateVoiceURL') { try { var gr = new GlideRecord('sn_openframe_phone_log'); gr.setLimit(1); gr.addQuery('sys_id', input.recordId); gr.query(); if (gr.next()) { gr.u_recording_file = input.voiceURL; gr.update(); } } catch (e) { data.result = { success: false, error: e }; } } else if (input.action == 'sendInfoMessage') { gs.addInfoMessage(input.msg); } else if (input.action == 'updateCallLogForNewContact') { // find new contact of that phone number: var gr = new GlideRecord('customer_contact'); gr.setLimit(1); gr.addQuery('phone', 'CONTAINS', input.phoneNumber).addOrCondition('mobile_phone', 'CONTAINS', input.phoneNumber); gr.orderByDesc('sys_created_on'); gr.query(); var contactId = null; while (gr.next()) { contactId = gr.getValue('sys_id'); } // find any call log with that phone number gr = new GlideRecord('sn_openframe_phone_log'); gr.addQuery('u_phone_number_us', 'CONTAINS', input.phoneNumber) gr.addNullQuery('contact'); gr.query(); while (gr.next()) { if (contactId != null) { gr.contact = contactId; gr.update(); } } } })();
Client Controller:
Paste the following contents. Then change the following line to the appropriate address.
c.widget_address =’https://<Your servers address>’
c.brekekeIframe.src = c.widget_address + ‘:8443/cim/apps/servicenow/index.html’;
api.controller = function ($scope) { /* widget controller */ var c = $scope; c.widget_address = 'https://<Your servers address>'; var AGENT_STATE_WORK = 5; var AGENT_STATE_ONCALL = 3; c.currentCaller = ''; c.openContactPage = function (phoneNumber) { // search customer info by phone number if exist navigate to contactPage var callerNumber = formatPhoneNumber(phoneNumber); c.server.get({ action: 'getUserInfo', phoneNumber: callerNumber }).then(function (resp) { var queryStr; if (resp.data && resp.data.customerInfos.length > 0) { var currentSysId = getCurrentSysId(); var customerInfo = findCorrectCustomerContact(resp.data.customerInfos, currentSysId) c.currentCaller = customerInfo.contact; queryStr = 'sys_id=' + customerInfo.contact; // send customer info to widget to display var msg = {data: customerInfo, command: 'updateCustomerInfo'}; _sendMsgToWidget(msg); // only redirect if current page is not a contact page. if (customerInfo.contact !== currentSysId) { openFrameAPI.openServiceNowForm({ entity: 'customer_contact', query: queryStr }); } } else { // open new contact page queryStr = 'sys_id=-1&sysparm_query=phone=' + callerNumber; openFrameAPI.openServiceNowForm({ entity: 'customer_contact', query: queryStr }); } }); }; c.sendInfoMessage = function (msg) { c.server.get({ action: 'sendInfoMessage', msg: msg }) } c.updateVoiceURL = function (recordId, voiceURL) { c.server.get({ action: 'updateVoiceURL', recordId: recordId, voiceURL: voiceURL }).then(function (resp) { }); }; c.updateAgentInfo = function (json) { sessionStorage.setItem('phoneId', json.loginPhoneId); sessionStorage.setItem('tenant', json.tenant); sessionStorage.setItem('agentStatus', json.agentStatus); }; c.insertCallLog = function (json) { if(!json.data.contactId) { json.data.contactId = c.currentCaller; } if (json.data.Phone_Number) { json.data.Phone_Number = formatPhoneNumber(json.data.Phone_Number); } c.server.get(json).then(function (result) { var servicenow_result_ev = {}; servicenow_result_ev.command = 'callback'; servicenow_result_ev.name = 'SERVICENOW.CRM.insertCallLog'; servicenow_result_ev.data = result.data; _sendMsgToWidget(servicenow_result_ev); }); } c.clickToCallRequest = function (json) { // check for agent status var agentStatus = sessionStorage.getItem('agentStatus'); if (!agentStatus || parseInt(agentStatus) === 0) { openFrameAPI.show(); c.sendInfoMessage('Please login first.'); return; } else if (agentStatus && parseInt(agentStatus) === AGENT_STATE_ONCALL) { return; } else if (parseInt(agentStatus) !== AGENT_STATE_WORK) { openFrameAPI.show(); c.sendInfoMessage('Please change your status to Work.'); return; } if (json) { // open contact page: c.currentSysId = json.currentSysId; c.openContactPage(json.phoneNumber); var msg = {data: json.phoneNumber, command: 'Dial'}; _sendMsgToWidget(msg); } }; c.handleEventFromMainPage = function (json) { if (json.action && json.action === 'updateCallLogForNewContact') { setTimeout(function() { c.updateCallLogForNewContact(json.phoneNumber); }, 20000); } else { c.clickToCallRequest(json); } }; c.updateCallLogForNewContact = function (newContactPhone) { c.server.get({ action: 'updateCallLogForNewContact', phoneNumber: newContactPhone }).then(function (resp) { }); }; initialize(); c.brekekeIframe = null; function initialize() { openFrameAPI.subscribe(openFrameAPI.EVENTS.COMMUNICATION_EVENT, c.handleEventFromMainPage); createAgentDesktopIframe(); window.addEventListener('message', function (e) { // IMPORTANT: check the origin of the data! if (event.origin.startsWith(c.widget_address)) { var json = JSON.parse(e.data); var servicenow_result_ev = {}; servicenow_result_ev.command = 'callback'; servicenow_result_ev.name = json.command; switch (json.command) { case 'SERVICENOW.CRM.LoggedIn': delete json['command']; c.updateAgentInfo(json); openFrameAPI.setSize(441,410); break; case 'SERVICENOW.CRM.LoggedOut': delete json['command']; sessionStorage.setItem('agentStatus', json.agentStatus); openFrameAPI.setSize(325,325); break; case 'SERVICENOW.CRM.AgentStateChanges': delete json['command']; sessionStorage.setItem('agentStatus', json.agentStatus); break; case 'SERVICENOW.CRM.UI.Dialer.maximize': delete json['command']; // show widget openFrameAPI.show(); // check phone number from ServiceNow CRM: c.openContactPage(json.customerNumber); break; case 'SERVICENOW.CRM.insertCallLog': delete json['command']; var serverRequest = { action: 'saveCallLog', data: json.APIData }; c.insertCallLog(serverRequest); break; case 'SERVICENOW.CRM.updateVoiceURL': delete json['command']; // check phone number from ServiceNow CRM: c.updateVoiceURL(json.RecordID, json.VoiceURL); break; case 'SERVICENOW.CRM.API.searchRecord': delete json['command']; c.openContactPage(json.customerNumber); break; default: break; } } else { return; } }); } function createAgentDesktopIframe() { c.brekekeIframe = document.createElement('iframe'); c.brekekeIframe.id = 'brekeke_ad'; c.brekekeIframe.allow = 'microphone; camera; geolocation'; c.brekekeIframe.src = c.widget_address + ':8443/cim/apps/servicenow/index.html'; //Need to change a port number to the appropriate one. var frameContainer = document.getElementById('brekeke_iframe_container'); frameContainer.appendChild(c.brekekeIframe); var agentStatus = sessionStorage.getItem('agentStatus'); if (!agentStatus || parseInt(agentStatus) === 0) { openFrameAPI.setSize(325,325); } else { openFrameAPI.setSize(441,410); } } function _sendMsgToWidget(ev) { var txt = JSON.stringify(ev); try { var brekekeIframe = document.getElementById('brekeke_ad'); brekekeIframe.contentWindow.postMessage(txt, "*"); } catch (e) { console.log(e); } } function formatPhoneNumber(phoneNumber) { var pattern = /^\(\d{3}\)\s\d{3}-\d{4}$/; //(xxx) xxx-xxxx var formattedNumber = phoneNumber; if (!pattern.test(phoneNumber) && phoneNumber.length == 10) { formattedNumber = '(' + phoneNumber.substr(0, 3) + ')' + ' ' + phoneNumber.substr(3, 3) + '-' + phoneNumber.substr(6, 4); } return formattedNumber; } function getCurrentSysId() { var urlStr = window.parent.location.href; var urlPrettify = decodeURIComponent(urlStr.replace('nav_to.do?uri','nav_to.do_uri')); var url = new URL(urlPrettify); return url.searchParams.get("sys_id"); } // in case more than 2 contacts have same phone number function findCorrectCustomerContact(customerInfos, currentSysId){ for (var i = 0; i < customerInfos.length; i++) { if (customerInfos[i].contact === currentSysId ) { return customerInfos[i]; } } return customerInfos[0]; } };