NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Tab Titles for ticket system ManageEngine ServiceDesk Plus // @name:de-DE Tabtitel für Ticketsystem ManageEngine ServiceDesk Plus // @description Automatically make ticket ids clickable and embed IK svg. (Less features than in v1.5 because GM_config needs code changes due to updates.) // @description:de-DE Automatisch Ticket-Links klickbar machen und IK SVG einbetten. (Weniger Features als in v1.5, da für GM_config Code-Anpassungen notwendig wären.) // @namespace jandunker // @version 1.6 // @match http://servicedesk/* // @match http://servicedesk.hgroup.intra/* // @match https://servicedesk/* // @match https://servicedesk.hgroup.intra/* // @exclude */framework/html/blank.html // @noframes // @license GPL-3.0-or-later; http://www.gnu.org/licenses/gpl-3.0.txt // @copyright 2020-2024, eckende (https://openuserjs.org/users/eckende) // @updateURL https://openuserjs.org/meta/eckende/Tab_Titles_for_ticket_system_ManageEngine_ServiceDesk_Plus.meta.js // @downloadURL https://openuserjs.org/install/eckende/Tab_Titles_for_ticket_system_ManageEngine_ServiceDesk_Plus.user.js // @icon data:image/gif;base64,R0lGODlhEAAQAHAAACH5BAkAAAMALAAAAAAQABAAgQAAAHSK///TdAAAAAI7nI85wIDtDhAiGhCCXXRj/XRP5nXNQp4TJX4ZuLKOS3KVmjK2V0smlGuYTKkUS0bLHYEvHUS3eCqmiAIAOw== // @icon64 data:image/gif;base64,R0lGODlhQABAAHAAACH5BAkAAAMALAAAAABAAEAAgQAAAHSK///TdAAAAAL/nI+py+0Po5y02ouzPqD7DwJUSHpjSZ4oqK7m5LJw/Ep0HX3Czu9fGwgKg79Zp9cr2jzDoTLnQfKekE9TSH3opIKsw3oNeBtb6ZgBvp4XZeRakW6+E+0krsoMzxH16V2bp/b3FcW1xxEoN0hWaLaIluj0yNboNgkXiXVJV2nXAaS3ydfpdzPaESZ2I4J4ZLh6CpC6ymrQ5wPbKhtqqsvVlWubSRQ8cPtr+ambyqypLOyKjPwU1yz4bEwq7VmbjWo9i3O8DYxdDe7cPb5NPYzeHk2erO4ODg8g/2peb32f77jvGzpFAfHpoxErXC9oBgEiXMbrIcNftBJGjBGLYrFzoMQWems4D9S1bnjicRNJkCQgk6VUEhKYsoVGbCV3jZR5kBZHVTpBzOzpDqjPnEAVCgXJTahRoT+PlVgalERTbeVAQIUpKcRUlrhCXLWZ0qlUoljT7SzxbxqJgc3QpiULlm1Yqmmfyr1I95/du3O16l0ll1ZdwGwF/70ReNVgxIVT6BIK6ajLj5ApSY58FHNlTJctZ94AOrTo0aRLmz5NoQAAOw== // @author Jan Dunker // ==/UserScript== // global variable declaration var ticketlinksuccess = false; // Language // 0: de // 1: en const lang = navigator.language == "de" ? 0 : 1; // debug mode var logLevel = 0; // 0, 1 or 2 // clickable ticket no. links // // If activated, the texts on ticket pages are searched for ticket no.. Results get replaced by links. const doAddTicketLinks = true; // Open ticket links in new tabs const openLinksInNewTab = true; // The search runs every x ms: const ticketlinksinterval = 5000; // Regex to detect tickets const ticketregex = new RegExp('\\b([5-9]\\d{5})\\b'); // 6 digits starting with 5, 6, 7, 8 or 9 // embed svg screenshot attachment // /!\ This is a company specific feature. You can disable this if you work for a different employer. const doEmbedSVG = true; // Update interval for svg embed // The smaller the number, the more often and more bad for performance const updateinterval = 1000 // 1 Sekunde const emptyfield = ""; // document.querySelector with handling function querySel(selector){ var elem = document.querySelector(selector); if (elem) { if (elem.innerText.trim() != "") return elem.innerText.trim(); else return elem.title.trim(); } else { console.log('[UserScript Tab Titles] "' + selector + '" not found, returning "' + emptyfield + '" instead. Try document.querySelector("' + selector + '")'); return emptyfield; } } // adding ticket links: where to look/replace depending on current page function addTicketLinks(){ // if tab is not open (page is not visible) do not waste computing power and skip the update // except if tab title has not been set yet (if the tab was opened in the background) or if the tab was just hidden (see event handler) if (document.visibilityState != 'visible' && ticketlinksuccess){ return; } let url = location.href; if (url.match(/^https?:\/\/[\w\d.-]*\/WorkOrder\.do\?(?:.*&)?woMode=viewWO/)) { addTicketLinksForTicket(); } else if (url.match(/^https?:\/\/[\w\d.-]*\/SearchN\.do/) && document.getElementById("requestId")) { addTicketLinksForTicket(); } } // adding ticket links: where to look/replace on ticket pages function addTicketLinksForTicket(){ ticketlinksuccess = true; if (logLevel > 0) console.time('addTicketLinksForTicket'); // Subject if (logLevel > 1) console.time('addTicketLinksForTicket_Subject'); addTicketLinksToId('req_subject'); if (logLevel > 1) console.timeEnd('addTicketLinksForTicket_Subject'); // Body if (logLevel > 1) console.time('addTicketLinksForTicket_Description'); addTicketLinksToId('req-desc-body'); if (logLevel > 1) console.timeEnd('addTicketLinksForTicket_Description'); // Conversations if (logLevel > 1) console.time('addTicketLinksForTicket_Conversations'); addTicketLinksToQuery('.panel-collapse.collapse'); if (logLevel > 1) console.timeEnd('addTicketLinksForTicket_Conversations'); if (logLevel > 0) console.timeEnd('addTicketLinksForTicket'); } // adding ticket links: call function for element function addTicketLinksToId(id){ // if not found log to console and return let elem = document.getElementById(id); if (!elem) { console.log('[UserScript Tab Titles] Adding ticket links failed: "' + id + '" not found. Try document.getElementById("' + id + '")'); return; } addTicketLinksToElem(elem); } // adding ticket links: call function for all elements function addTicketLinksToQuery(selector){ // if not found log to console and return let elems = document.querySelectorAll(selector); if (!elems) { console.log('[UserScript Tab Titles] Adding ticket links failed: "' + selector + '" not found. Try document.querySelectorAll("' + selector + '")'); return; } for (let i=0; i<elems.length; i++) { addTicketLinksToElem(elems[i]) } } // adding ticket links: the actual work function addTicketLinksToElem(elem){ if (elem) { // get all text nodes (within element) let iter = document.createNodeIterator(elem, NodeFilter.SHOW_TEXT); let textnode; // iterate through all text nodes while (textnode = iter.nextNode()) { // skip if parent is link to avoid nesting links if (textnode.parentNode.tagName != 'A') { // split text (e.g. ['My ticket is #', '123456', '. Thank you.']) let arr = textnode.nodeValue.split(ticketregex); for (let i = 0; i < arr.length; i++) { let a; if (ticketregex.test(arr[i])){ // if ticket number (and != current ticket) -> create a element a = document.createElement('a'); a.href = '/WorkOrder.do?woMode=viewWO&woID=' + arr[i]; if (openLinksInNewTab) a.target = '_blank'; a.innerText = arr[i]; } else { // else -> create text node a = document.createTextNode(arr[i]); } // insert replacement nodes textnode.parentNode.insertBefore(a, textnode); } // remove original text node textnode.parentNode.removeChild(textnode); } } } } // SVG Embed // Embedding SVG can be done in many ways - here it's done by inserting the svg element into the html. This way, text can be selected and copied. function embedSVG(){ if (document.querySelector('a[download^=Screenshot_]') && document.getElementById('desc-content').innerText.match(/\[IK_SupportMail\]\s*$/) && !document.getElementById('ik_screenshot')) { if (logLevel > 0) console.time('embedSVG_all'); if (logLevel > 1) console.time('embedSVG_prepare'); // get screenshot attachment url let url = document.querySelector('a[download^=Screenshot_]').href; // prepare localized error messages just in case let embeddingErrorMsg; if (lang == 0) embeddingErrorMsg = 'Nachträgliches Einbetten des Screenshots fehlgeschlagen. Stattdessen den Anhang öffnen.' else embeddingErrorMsg = 'Dynamic embedding of the screenshot failed. Open the attachment instead.'; // create new ik_screenshot container let section = document.createElement('template'); section.innerHTML = '<div id="desc-ik_screenshot" class="atp-container-target atp-container m0 p10 clearfix"><p class="sb">Screenshot</p><hr class="mt0"><div id="ik_screenshot"></div></div>'; section = section.content.firstChild; // add at new position let att = document.getElementById('desc-attachments'); att.parentNode.insertBefore(section, att); // until SVG file is replaced, show 'Loading...' document.getElementById('ik_screenshot').innerHTML = 'Loading...'; if (logLevel > 1) console.time('embedSVG_download'); // fetch SVG file and insert contents into the container fetch(url).then( (response) => { response.text() .then( (text) => { if (logLevel > 1) console.timeEnd('embedSVG_download'); if (logLevel > 1) console.time('embedSVG_insert'); // use a sandboxed iframe to avoid XSS document.getElementById('ik_screenshot').innerHTML = '<iframe id="ik_screenshot_iframe" sandbox style="border: 1px solid #bbb; width: 100%; height: 400px; resize: vertical"></iframe>'; document.getElementById('ik_screenshot_iframe').srcdoc = text; if (logLevel > 1) console.timeEnd('embedSVG_insert'); if (logLevel > 0) console.timeEnd('embedSVG_all'); } ) .catch( (e) => { if (logLevel > 1) console.timeEnd('embedSVG_download'); console.log('Response failed: ' + e.message); document.getElementById('ik_screenshot').innerHTML = '[UserScript Tab Title] ' + embeddingErrorMsg + ' (Response, ' + e.message + ')'; if (logLevel > 1) console.timeEnd('embedSVG_insert'); if (logLevel > 0) console.timeEnd('embedSVG_all'); } ) } ) .catch( (e) => { if (logLevel > 1) console.timeEnd('embedSVG_download'); console.log('Fetch failed: ' + e.message); document.getElementById('ik_screenshot').innerHTML = '[UserScript Tab Title] ' + embeddingErrorMsg + ' (Fetch, ' + e.message + ')'; if (logLevel > 1) console.timeEnd('embedSVG_insert'); if (logLevel > 0) console.timeEnd('embedSVG_all'); } ); if (logLevel > 1) console.timeEnd('embedSVG_prepare'); } } // Hauptfunktion / Core Loop function update(){ // if tab is not open (page is not visible) do not waste computing power and skip the update // except if tab title has not been set yet (if the tab was opened in the background) or if the tab was just hidden (see event handler) if (document.visibilityState != 'visible' && ticketlinksuccess){ return; } if (logLevel > 0) console.time('update'); // embed SVG if available if (doEmbedSVG) embedSVG(); if (logLevel > 0) console.timeEnd('update'); } console.log('[UserScript Tab Title] Injected at \'' + window.location + '\''); // make sure update runs once after a page is hidden addEventListener('visibilitychange', event => { if (document.visibilityState == 'hidden') { ticketlinksuccess = false; } }); // run first update update(); // schedule to run every x ms if (updateinterval > 50) window.setInterval(update, updateinterval); // add ticket links if (doAddTicketLinks && ticketlinksinterval > 50) window.setInterval(addTicketLinks, ticketlinksinterval);