ECOD-Jira-List-Copier

Copy JIRA issues from sprints in one big list

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         ECOD-Jira-List-Copier
// @namespace    ecod.jira.list-copy
// @version      1.1.3
// @description  Copy JIRA issues from sprints in one big list
// @author       CRK
// @require      https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js
// @match        https://jira.fsc.atos-services.net/secure/RapidBoard.jspa?rapidView=2102&projectKey=ECOD&view=planning.nodetail*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    function hookTheMonkeyForJira() {
        var scriptFunctionCopyList = document.createElement('script');
        scriptFunctionCopyList.innerText = `function copyList(node, $case) {
            let tiles =  node.parentNode.parentNode.parentNode.parentNode.nextElementSibling.querySelectorAll("div[data-issue-key]");
            let listData = [];
            tiles.forEach(function (tile) {
                if(tile.attributes && tile.attributes){
                    let id = tile.attributes['data-issue-key'].value;
                    let issue = '?';
                    let type = '?';
                    let status = '?';
                    let priority = '?';
                    let link = '?';

                    if(tile.childNodes[0] && tile.childNodes[0].attributes && tile.childNodes[0].attributes['aria-label']){
                        issue = tile.childNodes[0].attributes['aria-label'].value;
                    }

                    if(tile.childNodes[1] && tile.childNodes[1].childNodes[0] && tile.childNodes[1].childNodes[0].childNodes[0] && tile.childNodes[1].childNodes[0].childNodes[0].attributes['title']){
                        type = tile.childNodes[1].childNodes[0].childNodes[0].attributes['title'].value;
                    }

                    if(tile.childNodes[1] && tile.childNodes[1].childNodes[0] && tile.childNodes[1].childNodes[0].childNodes[1] && tile.childNodes[1].childNodes[0].childNodes[1].childNodes[0].attributes['title']){
                        priority = tile.childNodes[1].childNodes[0].childNodes[1].childNodes[0].attributes['title'].value;
                    }

                    if(tile.childNodes[1] && tile.childNodes[1].childNodes[0] && tile.childNodes[1].childNodes[0].childNodes[2] && tile.childNodes[1].childNodes[0].childNodes[2].childNodes[0].attributes['href']){
                        link = 'https://jira.fsc.atos-services.net' + tile.childNodes[1].childNodes[0].childNodes[2].childNodes[0].attributes['href'].value;
                    }

                    if(tile.childNodes[1] && tile.childNodes[1].childNodes[1] && tile.childNodes[1].childNodes[1].childNodes[0] && tile.childNodes[1].childNodes[1].childNodes[0].childNodes[0]){
                        status = tile.childNodes[1].childNodes[1].childNodes[0].childNodes[0].innerText;
                    }

                    listData.push({'id': id, 'issue': issue, 'type': type, 'status': status, 'priority': priority, 'link': link});
                }
            });

            listData.sort((a, b) => {
                if (a.type < b.type) return -1;
                if (a.type > b.type) return 1;
                if (a.priority < b.priority) return -1;
                if (a.priority > b.priority) return 1;
                return 0;
            });

            clip = '';

            switch($case){
                case 'simple':
                   listData.forEach((row) => clip += ' - ' + row.issue + '\\r\\n');
                break;
                case 'lines':
                   listData.forEach((row) => clip += '[' + row.type + '] [' + row.priority + '] ' +  row.issue + '\\r\\n');
                break;
                case 'csv':
                   listData.forEach((row) => clip += row.type + ';' + row.priority + ';' +  row.issue + ';' + row.status + ';' + row.link + '\\r\\n');
                break;
                default:
                    listData.forEach((row) => clip += '[' + row.type + '][' + row.priority + ']' +  row.issue + '\\r\\n');

            };

            navigator.clipboard.writeText(_.unescape(clip));
            node.className='monkeyCtlButtons elementToFadeInAndOut';
            setTimeout(
                function(){
                    node.className='monkeyCtlButtons';
                }, 500
            );
        }`;

        var style = document.createElement('style');
        style.innerText = `
        .elementToFadeInAndOut {
            animation: fadeInOut 0.5s linear forwards;
        }
        @keyframes fadeInOut {
            0% { opacity:0; }
            50% { opacity:0.5; }
            100% { opacity:1; }
        }
        .monkeyCtlButtons {
            cursor:pointer;
            background-color: #371cf2;;
            width: auto;
            border-radius: 5px;
            color: white;
            font-size: 12px;
            font-weight: bold;
            padding-top: 2px;
            padding-left: 5px;
            padding-right: 5px;
            padding-bottom: 2px;
            display: inline-block;
        }`;

        document.body.appendChild(scriptFunctionCopyList);
        document.body.appendChild(style);
    }

    function addButtonToNode(node) {
        var copyButton0 = document.createElement('span');
        copyButton0.setAttribute("style" ,"cursor:pointer; display: inline; padding-left: 0px; padding-right: 10px;");
        copyButton0.classList.add('ghx-sprint-info');
        copyButton0.innerHTML = `<span
                                     class='monkeyCtlButtons'
                                     name='copyList'
                                     id='copyList'
                                     onclick='copyList(this, "simple");'
                                     title='Copy list as simple list'>Copy as Simple-list</span>`;

        var copyButton1 = document.createElement('span');
        copyButton1.setAttribute("style" ,"cursor:pointer; display: inline; padding-left: 0px; padding-right: 10px;");
        copyButton1.classList.add('ghx-sprint-info');
        copyButton1.innerHTML = `<span
                                     class='monkeyCtlButtons'
                                     name='copyList'
                                     id='copyList'
                                     onclick='copyList(this, "lines");'
                                     title='Copy list as Detailed List'>Copy as Detailed-list</span>`;

        var copyButton2 = document.createElement('span');
        copyButton2.setAttribute("style" ,"cursor:pointer; display: inline; padding-left: 0px; margin-right:40px;");
        copyButton2.classList.add('ghx-sprint-info');
        copyButton2.innerHTML = `<span
                                     class='monkeyCtlButtons'
                                     name='copyList'
                                     id='copyList'
                                     onclick='copyList(this, "csv");'
                                     title='Copy list as CSV'>Copy as CSV-export</span>`;


        if(node && node.firstChild && node.firstChild.getElementsByClassName('ghx-sprint-info')) {
            let divContainer = document.createElement('div');
            divContainer.appendChild(copyButton0);
            divContainer.appendChild(copyButton1);
            divContainer.appendChild(copyButton2);
            divContainer.setAttribute("style", "margin-left: auto;");
            if(node.firstChild.getElementsByClassName('ghx-sprint-info')[0]){
                node.firstChild.getElementsByClassName('ghx-sprint-info')[0].appendChild(divContainer);
            }
        }
    }

    // Callback function to observe mutations in the DOM
    function handleJiraMutations(mutationsList, observer) {
        for (var mutation of mutationsList) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                mutation.addedNodes.forEach(function(node) {
                    //console.debug("NODE: ", node.attributes && node.attributes['data-sprint-id']);
                    if (node.attributes && node.attributes['data-sprint-id']) {
                        //console.debug("FOUND: " + node.id);
                        addButtonToNode(node);
                    }
                });
            }
        }
    }

    hookTheMonkeyForJira();
    // Create a new MutationObserver
    var boardObserver = new MutationObserver(handleJiraMutations);

    // Start observing the body for DOM changes
    boardObserver.observe(document.body, {
        childList: true,
        subtree: true
    });

})();