DevOps Projects Overview

Zeigt alle Projekte für alle Organisationen

Verze ze dne 23. 05. 2024. Zobrazit nejnovější verzi.

  1. // ==UserScript==
  2. // @name DevOps Projects Overview
  3. // @namespace Violentmonkey Scripts
  4. // @match https://dev.azure.com/*
  5. // @grant none
  6. // @version 1.0.2
  7. // @author Der_Floh
  8. // @description Zeigt alle Projekte für alle Organisationen
  9. // @license MIT
  10. // @icon https://www.svgrepo.com/show/448271/azure-devops.svg
  11. // @homepageURL https://greatest.deepsurf.us/de/scripts/469631-evalea-dsgvo-test-solver
  12. // @supportURL https://greatest.deepsurf.us/de/scripts/469631-evalea-dsgvo-test-solver/feedback
  13. // ==/UserScript==
  14.  
  15. // jshint esversion: 8
  16.  
  17. window.addEventListener('load', async () => {
  18. if (window.location.toString().endsWith('?projectonly')) {
  19. addErrorHideCss();
  20. convertToProjectOnly();
  21. } else if (window.location.toString().endsWith('?default')) {
  22. return;
  23. } else {
  24. addErrorHideCss();
  25. showAllProjects();
  26. }
  27. });
  28.  
  29. async function showAllProjects() {
  30. const navigation = await waitForElementToExistQuery(document.body, 'div[class*="top-level-navigation"][role="navigation"]');
  31. const showMoreButton = await waitForElementToExistQuery(navigation, 'span[class*="action-link top-navigation-item"][role="button"]');
  32. if (showMoreButton)
  33. showMoreButton.click();
  34.  
  35. const projectsContainer = await waitForElementToExistId('skip-to-main-content');
  36. const currentProject = await waitForElementToExistQuery(projectsContainer, 'li[class="project-card flex-row flex-grow"]');
  37. const container = currentProject.parentNode;
  38. container.classList.add('wrap-ul');
  39. addWrapUlCss();
  40.  
  41. await addProjectCards(container);
  42. }
  43.  
  44. function addWrapUlCss() {
  45. const css = `
  46. ul.wrap-ul {
  47. display: flex;
  48. flex-wrap: wrap;
  49. padding: 0;
  50. list-style-type: none;
  51. margin: 0;
  52. }
  53.  
  54. ul.wrap-ul li,
  55. ul.wrap-ul iframe {
  56. flex: 1 1 auto;
  57. margin: 5px;
  58. box-sizing: border-box;
  59. }
  60. `;
  61. const style = document.createElement('style');
  62. style.type = 'text/css';
  63. style.innerHTML = css;
  64. document.head.appendChild(style);
  65. }
  66.  
  67. function addErrorHideCss() {
  68. const css = `
  69. .tfs-unhandled-error {
  70. display: none;
  71. }
  72. `;
  73. const style = document.createElement('style');
  74. style.type = 'text/css';
  75. style.innerHTML = css;
  76. document.head.appendChild(style);
  77. }
  78.  
  79. async function convertToProjectOnly() {
  80. const projectsContainer = await waitForElementToExistId('skip-to-main-content');
  81. const currentProject = await waitForElementToExistQuery(projectsContainer, 'li[class="project-card flex-row flex-grow"]');
  82. const container = currentProject.parentNode.parentNode;
  83. container.style.height = '100%';
  84. container.firstChild.style.height = '100%';
  85. container.firstChild.firstChild.style.height = '100%';
  86. container.firstChild.firstChild.firstChild.style.height = '100%';
  87. container.firstChild.firstChild.firstChild.firstChild.style.height = '100%';
  88. const projectName = currentProject.querySelector('div[class*="project-name"]').textContent;
  89. container.onclick = (event) => {
  90. event.preventDefault();
  91. const url = `https://dev.azure.com/${window.frameElement.getAttribute('name')}/${projectName}`;
  92. window.open(url, '_blank');
  93. };
  94. keepOnlyElementAndAncestors(container);
  95. }
  96.  
  97. function keepOnlyElementAndAncestors(element) {
  98. const elementsToKeep = new Set();
  99. let currentElement = element;
  100.  
  101. while (currentElement) {
  102. elementsToKeep.add(currentElement);
  103. currentElement = currentElement.parentElement;
  104. }
  105.  
  106. function addDescendantsToSet(element) {
  107. elementsToKeep.add(element);
  108. Array.from(element.children).forEach(child => {
  109. addDescendantsToSet(child);
  110. });
  111. }
  112. addDescendantsToSet(element);
  113.  
  114. const allElements = document.body.getElementsByTagName('*');
  115. Array.from(allElements).forEach(el => {
  116. if (!elementsToKeep.has(el)) {
  117. el.remove();
  118. }
  119. });
  120. }
  121.  
  122. async function addProjectCards(baseNode) {
  123. const projectCards = [];
  124. await waitForElementToExistQuery(document.body, 'a[class*="host-link navigation-link"][role="option"]');
  125. const projects = Array.from(document.body.querySelectorAll('a[class*="host-link navigation-link"][role="option"]'));
  126. projects.shift();
  127. for (const project of projects) {
  128. if (project.href.startsWith('https://dev.azure.com'))
  129. createIFrameForProject(baseNode, project);
  130. }
  131. }
  132.  
  133. function createIFrameForProject(baseNode, project) {
  134. const iframe = document.createElement('iframe');
  135. iframe.id = project.id.replace('__bolt-host-', 'project_');
  136. iframe.setAttribute('name', project.querySelector('span').textContent);
  137. iframe.src = project.href + '?projectonly';
  138. iframe.style.border = 'none';
  139. baseNode.appendChild(iframe);
  140. }
  141.  
  142. async function waitForElementToExistId(elementId) {
  143. return new Promise(async (resolve) => {
  144. async function checkElement() {
  145. const element = document.getElementById(elementId);
  146. if (element !== null)
  147. resolve(element);
  148. else
  149. setTimeout(checkElement, 100);
  150. }
  151. await checkElement();
  152. });
  153. }
  154.  
  155. async function waitForElementToExistQuery(baseNode, query, timeout = 2000) {
  156. return new Promise((resolve, reject) => {
  157. const startTime = Date.now();
  158. async function checkElement() {
  159. const element = baseNode.querySelector(query);
  160. if (element !== null) {
  161. resolve(element);
  162. } else {
  163. if (Date.now() - startTime > timeout) {
  164. reject(new Error(`Timeout: Element with query '${query}' did not appear within ${timeout}ms`));
  165. } else {
  166. setTimeout(checkElement, 100);
  167. }
  168. }
  169. }
  170. checkElement();
  171. });
  172. }