// ==UserScript== // @name OdooTasksLinks // @original https://greasyfork.org/es/scripts/390531-tgithub // @author Eduardo de Miguel // @version 2.05 // @grant none // @run-at document-idle // @namespace moduon // @include /^https?:\/\/(?:www\.)?github\.com\/?.*$/ // @include /^https?:\/\/(?:www\.)?gitlab\.com\/?.*$/ // @include /^https?:\/\/(?:www\.)?moduon\.team\/?.*$/ // @include /^https?:\/\/(?:www\.)?loom\.com\/?.*$/ // @include /^https?:\/\/(?:www\.)?youtube\.com\/?.*$/ // @description Adds some features on github.com, gitlab.com, moduon.team, loom.com and youtube.com to integrate it with Moduon Team and Odoo // @downloadURL none // ==/UserScript== (function (window) { "use strict"; var MTTaskLinks = { ODOO_SERVER: 'https://www.moduon.team', COMPANY_NAME: 'Moduon', COPY_REF_BUTTON_ID: 'OTL_copy_ref', REGEX_TEMPLATES: {}, QUERY_SELECTORS: { 'github': ['.comment-body',], 'gitlab': ['.note-text', '.description'], 'moduon': ['.oe_form_field_html>.o_readonly', '.o_Message_prettyBody', '.o_Activity_summary', '.o_Activity_note', 'td.o_data_cell'], 'loom': ['.comments-view', '.below-video'], 'youtube': ['div[id="title"]>h1.ytd-watch-metadata>yt-formatted-string.ytd-watch-metadata', 'span[id="plain-snippet-text"]', 'yt-formatted-string[id="content-text"]'] }, COMMON_QUERY_SELECTORS: '.commit-description,', init: function () { this._addRegexTemplate('MT', new RegExp(/\bMT-(\d+)/gi), `${this.COMPANY_NAME} - Task #$1`); this._addRegexTemplate('OPW', new RegExp(/\bOPW-(\d+)/gi), `Odoo - Ticket #$1`); this._replaceTask(); if (this._isLocationHost('github')) { this._ghAddNavbarOptions(); }else if (this._isLocationHost('moduon')) { this._moduonAddNavbarOptions(); } }, /* CORE FUNCTIONS */ _isLocationHost: function (host) { return this._getLocationHost().includes(host); }, _getLocationHost: function() { return document.location.host.toLowerCase(); }, _addRegexTemplate: function (templateName, regex, html) { this.REGEX_TEMPLATES[templateName] = { regex: regex, html: html }; }, _executeRegexReplace: function (templateName, text) { if (templateName in this.REGEX_TEMPLATES && text.match(this.REGEX_TEMPLATES[templateName].regex)) { return text.replace(this.REGEX_TEMPLATES[templateName].regex, this.REGEX_TEMPLATES[templateName].html); } return false; }, /* COMMON FUNCTIONS */ _replaceTask: function () { const searchAndParse = () => { var hostQuerySelector = this.QUERY_SELECTORS[this._getLocationHost()] ? this.QUERY_SELECTORS[this._getLocationHost()].join(',') : Object.values(this.QUERY_SELECTORS).map(function(v){return v.join(',')}).join(','); // Replace Company Links document.querySelectorAll(this.COMMON_QUERY_SELECTORS + hostQuerySelector).forEach((elm) => { const htmlTemplate = this._executeRegexReplace('MT', elm.innerHTML); if (htmlTemplate) { elm.innerHTML = htmlTemplate; } }); // Replace Odoo Links document.querySelectorAll(this.COMMON_QUERY_SELECTORS + hostQuerySelector).forEach((elm) => { const htmlTemplate = this._executeRegexReplace('OPW', elm.innerHTML); if (htmlTemplate) { elm.innerHTML = htmlTemplate; } }); }; // Mutation Observer if (typeof this.observer === 'undefined') { let targetNode = undefined; if (this._isLocationHost('github')) { targetNode = document.getElementsByTagName('main')[0]; } else if (this._isLocationHost('gitlab')) { targetNode = document.getElementById('notes-list'); } else if (this._isLocationHost('moduon')) { targetNode = document.getElementsByClassName('o_web_client')[0]; } else if (this._isLocationHost('loom')) { targetNode = document.getElementsByClassName('mainContent')[0]; } else if (this._isLocationHost('youtube')) { targetNode = document.getElementById('content'); } if (typeof targetNode !== 'undefined') { this.observer = new MutationObserver(searchAndParse); this.observer.observe(targetNode, { childList: true, subtree: true }); } } searchAndParse(); }, /* GITHUB FUNCTIONS */ _ghAddNavbarOptions: function () { const targetNode = document.getElementsByTagName('nav')[0]; if (typeof targetNode !== 'undefined') { const exampleItem = targetNode.querySelector("a[href^='/pulls']"); const menuItem = document.createElement("A"); menuItem.className = exampleItem.className; menuItem.style.cssText = exampleItem.style.cssText menuItem.textContent = this.COMPANY_NAME; menuItem.href = `/pulls?q=is%3Aopen+is%3Apr+archived%3Afalse+involves%3A${this.COMPANY_NAME}`; targetNode.insertAdjacentElement('afterbegin', menuItem); } }, _moduonAddNavbarOptions: function () { let moduonObserver = new MutationObserver((mutations) => { const in_project_task = window.location.href.includes("model=project.task"); const is_project_task = window.location.href.includes("id="); if (in_project_task && is_project_task) { mutations.forEach((mutation) => { if (!mutation.addedNodes) return for (let i = 0; i < mutation.addedNodes.length; i++) { try { if (mutation.addedNodes[i].querySelector(".o_statusbar_buttons")) { if (document.getElementById(this.COPY_REF_BUTTON_ID)) return const statusBar = document.getElementsByClassName("o_statusbar_buttons")[0]; const copyButton = document.createElement("button"); copyButton.className = 'btn btn-light'; copyButton.type = 'button'; copyButton.title = 'Copy Task Reference'; copyButton.id = this.COPY_REF_BUTTON_ID; copyButton.innerHTML = "Copiar MT"; copyButton.onclick = function(){ let params = window.location.hash.replaceAll("#", "").split("&"); for (let p of params) { if (p.startsWith("id=")){ navigator.clipboard.writeText("MT-" + p.slice(3)); break; } } this.innerHTML = "¡Copiado!"; } statusBar.insertAdjacentElement('afterbegin', copyButton); this.moduonObserver.disconnect(); } } catch (e) {} } }) } }) moduonObserver.observe(document.body, { childList: true, subtree: true, attributes: false, characterData: false }) }, }; MTTaskLinks.init(); })(window)