Jira Deployment Helper

Adds Jira Code Review button to Subversion Commits tab, highlights database changes in the comments or description that have "dbo." in them, highlights tasks in list view and dashboard gadgets based on original time estimate and due date.

当前为 2015-03-31 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Jira Deployment Helper
  3. // @namespace http://www.mapyourshow.com/
  4. // @description Adds Jira Code Review button to Subversion Commits tab, highlights database changes in the comments or description that have "dbo." in them, highlights tasks in list view and dashboard gadgets based on original time estimate and due date.
  5. // @include */secure/dashboard*
  6. // @include */browse/*
  7. // @include */issues/*
  8. // @grant GM_log
  9. // @version 1.2.6
  10. // @require http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
  11. // ==/UserScript==
  12.  
  13. function scriptLoader(callback) {
  14. var script = document.createElement("script");
  15. script.textContent = "(" + callback.toString() + ")(window.AJS.$);";
  16. document.body.appendChild(script);
  17. }
  18.  
  19. mainCode = function($) {
  20. var version;
  21.  
  22. // This is for the main Dashboard Gadgets
  23. $(document).on('DOMNodeInserted', 'div.gadget', function(e) {
  24. var $elem = $(e.target);
  25.  
  26. $elem.on('DOMNodeInserted', function(e) {
  27. initJiraPlugin($('iframe').contents().find('table'));
  28. });
  29. });
  30.  
  31. // These are for the Issues page
  32. JIRA.bind(JIRA.Events.NEW_CONTENT_ADDED, function(e, context, reason) {
  33. initJiraPlugin($('table#issuetable'));
  34. });
  35.  
  36. JIRA.ViewIssueTabs.onTabReady(function() {
  37. initJiraPlugin($('table#issuetable'));
  38. });
  39.  
  40. function initJiraPlugin(issueTable) {
  41. version = parseFloat($('meta[name=ajs-version-number]').attr('content'));
  42.  
  43. // This colorizes the tasks in list view based on Original Estimate and Due Date
  44. colorTasks(issueTable, version);
  45.  
  46. // This highlights all instances of database names with "dbo." in them
  47. highlightDB(version);
  48.  
  49. // This highlights all instances of "ERRORFIXED"
  50. highlightErrorFixed(version);
  51.  
  52. // This adds the Code Review button in the Subversion tab
  53. addCodeReviewButton(version);
  54. }
  55.  
  56. function Change(label, url) {
  57. this.label = label;
  58. var s = url.split(url.indexOf('#') > 0 ? '#' : '?r=');
  59. this.ref = s[0];
  60. this.revision = s[1];
  61. this.commits = 1;
  62. this.pref = new RegExp('/[a-zA-Z-_]+/', '');
  63.  
  64. this.asA = function() {
  65. return this.label;
  66. };
  67.  
  68. this.setMaxRevision = function(revision) {
  69. if (revision > this.revision)
  70. this.revision = revision;
  71. this.commits++;
  72. };
  73.  
  74. this.samePrefix = function(prefix) {
  75. return getPrefix() == prefix;
  76. };
  77.  
  78. this.getPrefix = function() {
  79. return this.label.match(this.pref)[0];
  80. };
  81. };
  82.  
  83. function createReviewView() {
  84. var index = 0;
  85. var changes = [];
  86. var mapChanges = [];
  87. //select dev with id issueContent
  88. //select div with id issue_actions_container in it
  89. //select tables in it
  90. //find rows with text 'Files Changed'
  91. //select next sibling tr (in terminology of jquery it is 'next adjacent selector'
  92. //select td in it
  93. //select a in it
  94. $('div#issue_actions_container table tr:contains("Files Changed") + tr td').each(function() {
  95. var lines = $(this).text().split('\n');
  96. $.each(lines, function() {
  97. if (this.trim().substr(0, 7) == '/trunk/' || this.trim().substr(0, 10) == '/branches/') {
  98. var change = new Change(this, this);
  99. if (mapChanges[change.label]) {
  100. mapChanges[change.label].setMaxRevision(change.revision);
  101. } else {
  102. changes[index++] = change;
  103. mapChanges[change.label] = change;
  104. }
  105. }
  106. });
  107. });
  108. changes.sort(function(a, b) {
  109. return (b.label.toLowerCase() < a.label.toLowerCase()) - (a.label.toLowerCase() < b.label.toLowerCase());
  110. });
  111. var res = '<div class="issuePanelContainer" id="issue_actions_container"><table cellpadding="2" cellspacing="0" border="0" width="100%">';
  112. res += '<tbody><tr><td bgcolor="#f0f0f0"><b>Commits</b></td><td bgcolor="#f0f0f0"><b>Changed File</b></td></tr>';
  113. var prefix;
  114. var sep = false;
  115. for (var i = 0; i < index; i++) {
  116. sep = (prefix && (prefix != changes[i].getPrefix()));
  117. res = res + '<tr><td' + style(sep) + '>' + changes[i].commits + '</td><td' + style(sep) + '>' + changes[i].asA() + '</td></tr>';
  118. prefix = changes[i].getPrefix();
  119. }
  120. return res + '</tbody></table></div>';
  121. };
  122.  
  123. function style(sep) {
  124. return sep ? ' style="border-top: solid #BBB 1px;"' : '';
  125. };
  126.  
  127. function highlightDB(version) {
  128. if ($('#description-val').length) {
  129. var $descriptionDiv = $('#description-val');
  130. } else if ($('#issue-description').length) {
  131. var $descriptionDiv = $('#issue-description');
  132. }
  133. if ($descriptionDiv) {
  134. var result = $descriptionDiv.html();
  135. var regex = new RegExp('[\\w.*_]*dbo\\.[\\w]*','ig');
  136. $descriptionDiv.html(result.replace(regex, '<span style="background-color:yellow;">$&</span>'));
  137. }
  138.  
  139. $('.action-body').each(function() {
  140. result = $(this).html();
  141. regex = new RegExp('[\\w.*_]*dbo\\.[\\w]*','ig');
  142. $(this).html(result.replace(regex, '<span style="background-color:yellow;">$&</span>'));
  143. });
  144. }
  145.  
  146. function highlightErrorFixed(version) {
  147. if ($('#description-val').length) {
  148. var $descriptionDiv = $('#description-val');
  149. } else if ($('#issue-description').length) {
  150. var $descriptionDiv = $('#issue-description');
  151. }
  152. if ($descriptionDiv) {
  153. var result = $descriptionDiv.html();
  154. var regex = new RegExp('ERRORFIXED','g');
  155. $descriptionDiv.html(result.replace(regex, '<span style="background-color:yellow;">$&</span>'));
  156. }
  157.  
  158. $('.action-body').each(function() {
  159. result = $(this).html();
  160. regex = new RegExp('ERRORFIXED','g');
  161. $(this).html(result.replace(regex, '<span style="background-color:yellow;">$&</span>'));
  162. });
  163. }
  164.  
  165. function addCodeReviewButton(version) {
  166. if (!$('#review_button_div').length) {
  167. if (version >= 6) {
  168. if ($('li#subversion-commits-tabpanel').hasClass('active') == 1) { //where the Subversion Commits is but not inside a tag
  169. $('div#issue_actions_container:first').prepend('<div id="review_button_div" style="width: 100%;margin-bottom:10px;"><button id="review_button">Code Review</button></div>');
  170. var view = createReviewView();
  171. var revView = false;
  172. $('#review_button').click(function() {
  173. var oldView = $('table', 'div#issue_actions_container').detach();
  174. $('div#review_button_div').after(view);
  175. $('#review_button').text(revView ? 'Code Review' : 'All Commits');
  176. view = oldView;
  177. revView = !revView;
  178. });
  179. }
  180. } else {
  181. if ($('li#subversion-commits-tabpanel').hasClass('active') == 1) { //where the Subversion Commits is but not inside a tag
  182. $('ul#issue-tabs').closest('div').after('<div id="review_button_div" style="width: 100%;margin-bottom:10px;"><button id="review_button">Code Review</button></div>');
  183. var view = createReviewView();
  184. var revView = false;
  185. $('#review_button').click(function() {
  186. var oldView = $('div#issue_actions_container').detach();
  187. $('div#review_button_div').after(view);
  188. $('#review_button').text(revView ? 'Code Review' : 'All Commits');
  189. view = oldView;
  190. revView = !revView;
  191. });
  192. }
  193. }
  194. }
  195. }
  196.  
  197. function colorTasks(issueTable, version) {
  198. if (issueTable.length && (issueTable.find('td.timeoriginalestimate').length || issueTable.find('td.aggregatetimeoriginalestimate').length) && issueTable.find('td.duedate').length) {
  199. var $issueTable = issueTable,
  200. $issueTableRows = issueTable.find('tr:gt(0)'); // skip the header row
  201.  
  202. $issueTableRows.each(function(index) {
  203. var $originalEstimate = ($.trim($('td.timeoriginalestimate', this).html()) == '' ? $.trim($('td.aggregatetimeoriginalestimate', this).html()) : $.trim($('td.timeoriginalestimate', this).html())),
  204. $dueDate = (typeof($('td.duedate', this).html()) == 'string' ? $('td.duedate', this).html() : ''),
  205. $originalEstimateDays,
  206. $originalEstimateMinutes,
  207. $numberDays,
  208. thisdate,
  209. today;
  210.  
  211. // Get total estimated minutes
  212. $originalEstimateMinutes = ($originalEstimate.match(/\d+(?= week)/ig) == null ? 0 : parseInt($originalEstimate.match(/\d+(?= week)/ig)) * 2400)
  213. + ($originalEstimate.match(/\d+(?= day)/ig) == null ? 0 : parseInt($originalEstimate.match(/\d+(?= day)/ig)) * 480)
  214. + ($originalEstimate.match(/\d+(?= hour)/ig) == null ? 0 : parseInt($originalEstimate.match(/\d+(?= hour)/ig)) * 60)
  215. + ($originalEstimate.match(/\d+(?= minute)/ig) == null ? 0 : parseInt($originalEstimate.match(/\d+(?= minute)/ig)));
  216.  
  217. // Now let's account for weekends
  218. $numberDays = Math.ceil($originalEstimateMinutes / 480);
  219.  
  220. thisdate = new Date();
  221. today = thisdate.getDay();
  222.  
  223. switch (true) {
  224. case ($numberDays == 0):
  225. if (today == 6) {
  226. $originalEstimateMinutes += 480;
  227. } else if (today == 5) {
  228. $originalEstimateMinutes += 960;
  229. }
  230. break;
  231. case ($numberDays == 1):
  232. if (today == 6) {
  233. $originalEstimateMinutes += 480;
  234. } else if (today == 5) {
  235. $originalEstimateMinutes += 960;
  236. }
  237. break;
  238. case ($numberDays == 2):
  239. if (today == 6) {
  240. $originalEstimateMinutes += 480;
  241. } else if (today >= 4) {
  242. $originalEstimateMinutes += 960;
  243. }
  244. break;
  245. case ($numberDays == 3):
  246. if (today == 6) {
  247. $originalEstimateMinutes += 480;
  248. } else if (today >= 3) {
  249. $originalEstimateMinutes += 960;
  250. }
  251. break;
  252. case ($numberDays == 4):
  253. if (today == 6) {
  254. $originalEstimateMinutes += 480;
  255. } else if (today >= 2) {
  256. $originalEstimateMinutes += 960;
  257. }
  258. break;
  259. case ($numberDays == 5):
  260. if (today == 6) {
  261. $originalEstimateMinutes += 480;
  262. } else if (today >= 1) {
  263. $originalEstimateMinutes += 960;
  264. }
  265. break;
  266. case ($numberDays >= 6 && $numberDays < 11):
  267. if (today == 6) {
  268. $originalEstimateMinutes += 1440;
  269. } else if (today >= 5) {
  270. $originalEstimateMinutes += 1920;
  271. } else if (today == 0) {
  272. $originalEstimateMinutes += 960;
  273. }
  274. break;
  275. case ($numberDays >= 11 && $numberDays < 16):
  276. if (today == 6) {
  277. $originalEstimateMinutes += 2400;
  278. } else if (today >= 5) {
  279. $originalEstimateMinutes += 2880;
  280. } else if (today == 0) {
  281. $originalEstimateMinutes += 1920;
  282. }
  283. break;
  284. case ($numberDays >= 16 && $numberDays < 21):
  285. if (today == 6) {
  286. $originalEstimateMinutes += 3360;
  287. } else if (today >= 5) {
  288. $originalEstimateMinutes += 3840;
  289. } else if (today == 0) {
  290. $originalEstimateMinutes += 2880;
  291. }
  292. break;
  293. case ($numberDays >= 21 && $numberDays < 26):
  294. if (today == 6) {
  295. $originalEstimateMinutes += 4350;
  296. } else if (today >= 5) {
  297. $originalEstimateMinutes += 4800;
  298. } else if (today == 0) {
  299. $originalEstimateMinutes += 3840;
  300. }
  301. break;
  302. default:
  303. break;
  304. }
  305.  
  306. // The number of total days estimated, including weekends
  307. $originalEstimateDays = Math.ceil($originalEstimateMinutes / 480);
  308. $daysTilDueDate = Math.ceil(dateDiff($dueDate));
  309.  
  310. if ($originalEstimate.trim() != '') { // If an Original Estimate was given
  311. if (($daysTilDueDate - $originalEstimateDays) < 1) {
  312. $(this).css('background-color','#ff9999'); // Red
  313. } else if (($daysTilDueDate - $originalEstimateDays) <= 2) {
  314. $(this).css('background-color','#fed080'); // Orange
  315. } else if (($daysTilDueDate - $originalEstimateDays) <= 4) {
  316. $(this).css('background-color','#fffeab'); // Yellow
  317. }
  318. } else {
  319. if (($daysTilDueDate - $originalEstimateDays) < 1) {
  320. $(this).css('background-color','#ff9999'); // Red
  321. } else if (($daysTilDueDate - $originalEstimateDays) == 1) {
  322. $(this).css('background-color','#fed080'); // Orange
  323. } else if (($daysTilDueDate - $originalEstimateDays) == 2) {
  324. $(this).css('background-color','#fffeab'); // Yellow
  325. }
  326. }
  327. });
  328. } else if ($('.issue-list').length) {
  329. // Do nothing for now
  330. }
  331. }
  332.  
  333. function dateDiff(dueDate) {
  334. var dueDate = dueDate.replace('Jan', '0')
  335. .replace('Feb', '1')
  336. .replace('Mar', '2')
  337. .replace('Apr', '3')
  338. .replace('May', '4')
  339. .replace('Jun', '5')
  340. .replace('Jul', '6')
  341. .replace('Aug', '7')
  342. .replace('Sep', '8')
  343. .replace('Oct', '9')
  344. .replace('Nov', '10')
  345. .replace('Dec', '11');
  346. var arrDueDate = dueDate.split('/');
  347. var dt1 = new Date();
  348. var dt2 = new Date('20' + arrDueDate[2], arrDueDate[1], arrDueDate[0]);
  349. var one_day = 1000 * 60 * 60 * 24;
  350.  
  351. return (parseInt(dt2.getTime() - dt1.getTime()) / (one_day));
  352. }
  353. }
  354.  
  355. scriptLoader(mainCode);