// ==UserScript==
// @name RaveEx
// @version 1.0.7
// @description Customized UI for Rave
// @author leqin
// @require http://code.jquery.com/jquery-3.4.1.min.js
// @match https://rave.office.net/*
// @match https://rave.office.net/cases/*
// @match https://microsoft.sharepoint.com/teams/myIW/Lists/Case%20Follow%20Up/NewForm.aspx
// @grant none
// @namespace https://gf.qytechs.cn/users/318347
// ==/UserScript==
(function() {
'use strict';
//debugger;
var NAME = 'RaveEx'
var enabled = false
var executed = false
var tempHistoryItemDate = Date.MinValue
var lastPath = ""
var lastUrlType = ""
var isOnPremCase = false
var setFeature = function (featureName, enabled) {
setCookie (NAME+'_'+featureName, enabled, 365)
}
var getFeature = function (featureName) {
getCookie (NAME+'_'+featureName)
}
var getCookie = function (cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for(var i = 0; i <ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
var setCookie = function (cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays*24*60*60*1000));
var expires = "expires="+ d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
var addGlobalStyle = function addGlobalStyle(css) {
var head, style;
head = document.getElementsByTagName('head')[0];
if (!head) { return; }
style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
head.appendChild(style);
}
var unique = function (a) {
var prims = {"boolean":{}, "number":{}, "string":{}}, objs = [];
return a.filter(function(item) {
var type = typeof item;
if(type in prims)
return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
else
return objs.indexOf(item) >= 0 ? false : objs.push(item);
});
}
var executeOrDelayUntilConditionMeet = function (condition, callback, timeout) {
if(!timeout) { timeout = 100 }
setTimeout(function(){
if (condition()) {
callback();
}else {
executeOrDelayUntilConditionMeet(condition, callback);
}
}, timeout);
}
var executeInterval = function (callback, timeout) {
if(!timeout) { timeout = 1000 }
setInterval(function(){
callback();
}, timeout);
}
var getCurrentUrlType = function () {
var path = window.location.pathname;
if (path == '/' || path.startsWith('/cases/my')) {
return 'MyCases'
} else if (path.startsWith('/cases/unassigned')) {
return 'UnassignedCases'
} else if (path.startsWith('/teams/myIW/Lists/Case%20Follow%20Up/NewForm.aspx')) {
return 'MCE'
} else {
return 'Case'
}
}
var review = function () {
var setHistoryItem = function (element) {
var timeString = $(element).find('.chat-footer > .pull-right').first().text().trim()
var time = Date.parse(timeString) || 0
//var dateString = time.toLocaleDateString()
$(element).before('<div class="menu-bar text-center" style="margin:0;height:auto"><h4>'+timeString+'</h4></div>')
}
var setCaseHistory = function () {
$('case-history').find('[ng-repeat^="historyItem"]').each(function () {
setHistoryItem(this)
})
}
var reviewCase = function (showNewerFirst) {
var caseController = angular.element($('#breadcrumb')).data().$scope.caseController
addGlobalStyle('.feedback-bar { display: none !important; }'); /*hide bottom float feedback bar*/
addGlobalStyle('.absoluteLeftPane { display: none !important; }'); /*hide left float panel (customer profile, quick facts)*/
addGlobalStyle('.absoluteRightPane { display: none !important; }'); /*hide right float panel*/
addGlobalStyle('.floatingLeftPane { display: none !important; }'); /*hide left float panel (customer profile, quick facts)*/
addGlobalStyle('#incorrect-user-div { display: none !important; }');
addGlobalStyle('.chat-bubble-ViewMore { display: none !important; }');
addGlobalStyle('.solutions { display: none !important; }');
caseController.ShowSelectedCaseHistory('Full History')
//caseController.ShowFullCaseHistory()
showNewerFirst? caseController.ShowNewerFirstCaseHistory(): caseController.ShowOlderFirstCaseHistory()
caseController.ExpandAllMessages()
$('[ng-if^="comController.CommunicationData.ToEmailAddresses"]').hide()
$('[ng-if^="comController.CommunicationData.CcEmailAddresses"]').hide()
$('[ng-if^="comController.CommunicationData.Subject"]').hide()
/*hide case attachments*/
$('[ng-if^="caseController.CaseAttachments"]').hide()
$('[ng-repeat^="attachment in caseController.CaseAttachments"]').hide()
addGlobalStyle('.sync-attachment-btn { display: none !important; }');
$('[ng-if^="comController.ShowAttachments"]').hide()
$('[ng-repeat^="attachment in comController.CommunicationData.Attachments"]').hide()
$('#editorSection').hide()
$('#rightPane').hide()
$('#centerPane').removeClass('col-xs-8').addClass('col-xs-10')
$('.attachment-container').hide()
if (!executed) {
setCaseHistory()
executed = true
}
}
var init = function () {
executeOrDelayUntilConditionMeet(
function () {
return $('.menu-bar').length
},
function(){
if (!$('#RaveEx-Review').length) {
$('.menu-bar').append(`<div id="RaveEx-Review" class="dropdown filter-dropdown" style="width:340px"><div class="button-container"><button onclick="RaveEx.review(false)" class="drop-button btn btn-success"><span class="ng-scope">*** Older First</span></button><button class="drop-button btn btn-success"><span class="ng-scope">RaveEx: Review</span></button><button onclick="RaveEx.review(true)" class="drop-button btn btn-success"><span class="ng-scope">Newer First ***</span></button></div></div>`)
}
}
)
}
var exec = function (showNewerFirst) {
if (lastUrlType == 'Case') {
reviewCase(showNewerFirst)
}
}
return {
'init': init,
'exec': exec
}
}
var initialResponse = function () {
var addEmail = function (mails, newMails) {
var mailAaray = mails ? mails.split(';') : []
var newMailArray = newMails ? newMails.split(';') : []
return unique(mailAaray.concat(newMailArray)).toString().replace(/,/g, ';');
}
var init = function () {
executeOrDelayUntilConditionMeet(
function () {
return $('#compose-email') && $('#compose-email').length
},
function(){
if (!$('#RaveEx-InitialResponse').length) {
$('#compose-email > div.panel-heading').append(`<button id="RaveEx-InitialResponse" class="btn btn-attach pull-right ng-scope" onclick="Javascript:RaveEx.initialResponse()"><span translate="" class="ng-scope">RaveEx: Initial Response</span></button>`)
}
}
)
}
var exec = function () {
var caseController = angular.element($('#breadcrumb')).data().$scope.caseController
if (!caseController.IsAddingInternalNote) {
if (!caseController.IsEmailCc) {
caseController.ShowEmailCc()
}
var ownerName = caseController.AgentInfo.FullName
var ownerEmail = caseController.AgentInfo.PartnerData.Email
var tamEmail = caseController.TAMEmail
var caseNumber = caseController.DefaultTicketSubject
var contactName = caseController.Request.RequestData.UserFirstName+' '+caseController.Request.RequestData.UserLastName
var title = caseController.appContext.SelectedRequest.RequestData.Title
var description = caseController.appContext.SelectedRequest.RequestData.UserDescription
var emailCc = caseController.EmailCc
var subject = title ? title : description.slice(0,100)
var irTemplate = '<div><span>Hello '+contactName+',<br></span><div><br></div><div>Thank you for contacting Microsoft Support. My name is '+ownerName+'. I am the Support Professional who will be working with you on this Service Request. You may reach me using the contact information listed below, referencing the '+caseNumber+'.<br></div><div><br></div><div>If you have any questions or concerns, please let me know. <br></div><div><br></div><div>Best Regards,<br></div><span></span><br></div>'
caseController.EmailCc = addEmail(emailCc, ownerEmail+';'+tamEmail)
caseController.EmailSubject ? null : (caseController.EmailSubject = subject)
//$('#emailComposeBox').prepend(irTemplate)
$('[editor-manager="caseController.EmailEditorManager"] > div').first().prepend(irTemplate)
}
}
return {
'init': init,
'exec': exec
}
}
var laborReminder = function () {
var caseController = angular.element($('#breadcrumb')).data().$scope.caseController
var historyController = angular.element($('[ng-if^="historyController.showFullHistory"]')).data().$scope.historyController
var ownerId = historyController.appContext.PartnerData.PartnerId
}
var issueDescriptionTag = function () {
var init = function () {
/*add tags area*/
executeOrDelayUntilConditionMeet(
function () {
return $('[name="caseController.issueDetailsForm"]') && $('[name="caseController.issueDetailsForm"]').length
},
function(){
if (!$('#RaveEx-IssueDescriptionTag').length) {
$('[name="caseController.issueDetailsForm"]').append(`<div id="RaveEx-IssueDescriptionTag" class="form-group"><div class="row"><div class="col-sm-12"><label translate="" class="ng-scope">Tags</label></div></div><div class="row"><div class="col-sm-12"><span onclick="RaveEx.tag('#Pool Split')" title="SQL Resource constraint required rebalancing.">#Pool Split </span><span onclick="RaveEx.tag('#Auto Resolved')" title="No known action taken.">#Auto Resolved </span><span onclick="RaveEx.tag('#R/O Failover')" title="Farm was failed over to backup.">#R/O Failover </span><span onclick="RaveEx.tag('#App Throttle')" title="High request volume application was throttled to restore service health.">#App Throttle </span><span onclick="RaveEx.tag('#Azure Outage')" title="High impact event caused by factors outside of SharePoint Online.">#Azure Outage </span><span onclick="RaveEx.tag('#Client Side')" title="Any case where the customer took action to resolve the bottleneck.">#Client Side </span></div></div></div>`)
}
}
)
}
var exec = function (tag) {
var caseController = angular.element($('#breadcrumb')).data().$scope.caseController
var issueDetails = caseController.RequestDetails.RequestDetailsData.IssueDetails
caseController.RequestDetails.RequestDetailsData.IssueDetails = tag+' '+(issueDetails ? issueDetails : "")
}
return {
'init': init,
'exec': exec
}
}
var openSDTicket = function () {
var init = function () {
executeOrDelayUntilConditionMeet(
function () {
return $('rct-case-summary label:contains("Secondary ticket")').length
},
function(){
if (!$('#RaveEx-OpenSDTicket').length) {
$('rct-case-summary label:contains("Secondary ticket")').after(`<button id="RaveEx-OpenSDTicket" class="align-right btn btn-success" onclick="RaveEx.openSD()"><span translate="" class="ng-scope">Open</span></button>`)
}
}
)
}
var exec = function (tag) {
var caseController = angular.element($('#breadcrumb')).data().$scope.caseController
var sdTicket = caseController.Request.RequestData.SecondaryTicketNumber
window.open('https://servicedesk.microsoft.com/#/customer/case/'+sdTicket)
}
return {
'init': init,
'exec': exec
}
}
var updateCaseStatus = function () {
var init = function () {
/*add case internal status monitor*/
executeOrDelayUntilConditionMeet(
function () {
return $('#case-internal-status select > option') && $('#case-internal-status select > option').length
},
function(){
$('#case-internal-status select').on('change', function (e) { exec() })
}
)
}
var exec = function () {
var caseController = angular.element($('#breadcrumb')).data().$scope.caseController
caseController.UpdateInternalState()
}
return {
'init': init,
'exec': exec
}
}
var setResolutionDefault = function () {
var init = function () {
executeOrDelayUntilConditionMeet(
function () {
return $('select[name="resolution"]').length
},
function(){
$('select[name="resolution"]').on('change', function (e) { exec() })
}
)
}
var exec = function () {
var caseController = angular.element($('#breadcrumb')).data().$scope.caseController
var resolution = caseController.RequestDetails.RequestDetailsData.Resolution
isOnPremCase = caseController.IsOnPremCase
if (resolution) {
caseController.RequestDetails.RequestDetailsData.ContactOutcome = 'Contacted'
caseController.RequestDetails.RequestDetailsData.ResolutionOutcome = 'Customer'
caseController.AlchemyProposedSolutionHelp = false
submitMCE().init()
if (isOnPremCase) {
caseController.RequestDetails.RequestDetailsData.CaseType = "ReactiveIncident"
caseController.RequestDetails.RequestDetailsData.IssueType = "TechnicalIssue"
}
}
}
return {
'init': init,
'exec': exec
}
}
var submitMCE = function () {
var init = function () {
if (!$('#Rave-SubmitMCE').length) {
$('select[name="resolution"]').parent().after(`<div id="Rave-SubmitMCE" class="col-xs-2 col-sm-2"><button class="align-right btn btn-success full-width" onclick="RaveEx.MCE()"><span translate="" class="ng-scope">Submit MCE</span></button></div>`)
}
}
var exec = function () {
var mceJson = {}
var caseController = angular.element($('#breadcrumb')).data().$scope.caseController
mceJson.caseId = caseController.Request.RequestData.ParatureTicketNumber
mceJson.cxName = caseController.Request.RequestData.UserFirstName+' '+caseController.Request.RequestData.UserLastName
mceJson.cxPhone = caseController.Request.RequestData.UserPhone
mceJson.cxEmail = caseController.Request.RequestData.UserEmail
mceJson.cxRealName = caseController.Request.RequestData.UserFirstName+' '+caseController.Request.RequestData.UserLastName
mceJson.caseDescription = caseController.RequestDetails.RequestDetailsData.IssueDetails
mceJson.resolution = caseController.RequestDetails.RequestDetailsData.Resolution
mceJson.callingCountry = ""
mceJson.team = "SharePoint"
var mceWindow = window.open("https://microsoft.sharepoint.com/teams/myIW/Lists/Case%20Follow%20Up/NewForm.aspx", JSON.stringify(mceJson))
}
return {
'init': init,
'exec': exec
}
}
var fillMCE = function () {
var init = function () {
executeOrDelayUntilConditionMeet(
function () {
return $('#onetIDListForm').length
},
function(){
exec()
}
)
}
var exec = function () {
var mceJson = JSON.parse(window.name)
$('[title="Case ID"]').val(mceJson.caseId)
$('[title="Cu-Name"]').val(mceJson.cxName)
$('[title="Cu-Phone"]').val(mceJson.cxPhone)
$('[title="Cu-Email"]').val(mceJson.cxEmail)
$('textarea[title="Actual Customer Name Required Field"]').val(mceJson.cxRealName)
$('textarea[title="Case Description"]').val(mceJson.caseDescription)
mceJson.resolution == 'Unresolved' ? $('[title="Case Resolution Required Field"]').val('Not resolved') : $('[title="Case Resolution Required Field"]').val('MS Resolved')
$('input:radio[value="70-90 VSAT"]').prop("checked", true);
$('input:radio[value="No"]').prop("checked", true);
$('input:radio[value="SEA"]').prop("checked", true);
$('[title="Team Required Field"]').val(mceJson.team)
}
return {
'init': init,
'exec': exec
}
}
var skipReInit = function () {
var skip = false
var pathChanged = false
var currentPath = window.location.pathname;
var urlTypeChanged = false
var currentUrlType = getCurrentUrlType();
if (!lastPath || lastPath != currentPath) {
pathChanged = true
lastPath = currentPath;
}
if (!lastUrlType || lastUrlType != currentUrlType) {
urlTypeChanged = true
lastUrlType = currentUrlType;
}
switch (currentUrlType) {
case 'MyCases':
skip = !urlTypeChanged
break;
case 'Case':
skip = !pathChanged
break;
case 'MCE':
skip = !urlTypeChanged
break;
default:
}
return skip
}
var appInit = function () {
switch (getCurrentUrlType()) {
case 'MyCases':
break;
case 'Case':
if (!skipReInit()) {
review().init()
initialResponse().init()
issueDescriptionTag().init()
updateCaseStatus().init()
setResolutionDefault().init()
openSDTicket().init()
}
break;
case 'MCE':
break;
default:
}
}
window.RaveEx = {
"review": review().exec,
"initialResponse": initialResponse().exec,
"tag": issueDescriptionTag().exec,
"MCE": submitMCE().exec,
"openSD": openSDTicket().exec
};
if (getCurrentUrlType() == 'MCE') {
fillMCE().init()
} else {
setInterval(appInit, 3000);
}
})();