Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

Commit

Permalink
Merge pull request #4 from EpocDotFr/direct-jira-ticket-link
Browse files Browse the repository at this point in the history
Direct Jira ticket link
  • Loading branch information
EpocDotFr authored Apr 21, 2020
2 parents 391e6ed + 344de57 commit 1d40f0d
Show file tree
Hide file tree
Showing 7 changed files with 281 additions and 32 deletions.
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ A browser extension that enhance all Merge Requests lists on any instance of Git
- Button allowing to copy Merge Request information (useful when sharing the Merge Request on e.g instant messaging softwares)
- Can be disabled in the extension preferences
- Text format is customizable (with support of placeholders)
- Direct Jira ticket link
- Can be enabled/disabled in the extension preferences
- Ticket ID is automatically detected in source branch name or Merge Request title
- Base Jira URL is configured in extension preferences
- The ticket ID or an icon can be displayed as the link label (configured in extension preferences)
- Compatible with all GitLab editions (GitLab CE, GitLab EE, GitLab.com) (look at the prerequisites, though)
- No configuration needed

Expand All @@ -37,11 +42,14 @@ You can also install this add-on manually by using one of the ZIP files on the [

👉 = current version

- **1.0** - Initial release (display Merge Request source and target branches)
- **1.0** - Initial release (display Merge Request source and target branches name)
- **1.1** - Copy source and target branches name
- 👉 **1.2** - Copy Merge Request information (intended for sharing on e.g instant messaging softwares)
- **1.3** - Direct Jira ticket link (automatic detection of ticket ID in branch name or Merge Request title)
- **1.2** - Copy Merge Request information (intended for sharing on e.g instant messaging softwares)
- 👉 **1.3** - Direct Jira ticket link (automatic detection of ticket ID in source branch name or Merge Request title)
- **1.4** - WIP / unWIP toggle button
- **1.5**
- New option: enable display Merge Request source and target branches
- New options: enable copy source and target branches name button (one option for each branches)

## License

Expand Down
91 changes: 80 additions & 11 deletions css/options.css
Original file line number Diff line number Diff line change
@@ -1,19 +1,48 @@
/************************************************************************
* Global styles */
* Browser-specific styles */

/* Firefox *************************************************************/

/* Dark theme styles override */
@media (prefers-color-scheme: dark) {
body.is-firefox {
background-color: #202023;
color: rgb(249, 249, 250);
}

.is-firefox input[type="url"],
.is-firefox textarea {
background-color: #2a2a2e;
color: #fff;
border-color: rgba(249, 249, 250, 0.2);
border-width: 1px;
}
}

/* Chrome **************************************************************/

body.is-chrome {
width: 660px;
}

/* Dark theme styles override */
@media (prefers-color-scheme: dark) {
body.is-chrome {
background-color: #292a2d;
color: rgb(232, 234, 237);
}

/*
* Firefox for Mac: use a dark theme if the OS/browser also uses one.
* This doesn't affects Firefox for Windows (as for now), as dark/light theme detection isn't implemented for this OS.
*/
@supports (-moz-appearance:none) {
@media (prefers-color-scheme: dark) {
body {
background-color: #202023;
color: rgb(249, 249, 250);
}
.is-chrome input[type="url"],
.is-chrome textarea {
background-color: #202023;
color: rgb(232, 234, 237);
border-color: rgba(255, 255, 255, 0.1);
}
}

/************************************************************************
* Global styles */

.is-hidden {
display: none;
}
Expand All @@ -29,17 +58,29 @@
font-family: monospace;
}

.txt-muted {
opacity: 0.8;
}

/************************************************************************
* Sizing styles */

.w100 {
width: 100%;
}

.w50 {
width: 50%;
}

.w40p {
width: 40px;
}

.w300p {
width: 300px;
}

/************************************************************************
* Layout styles */

Expand All @@ -63,14 +104,42 @@
padding-top: 5px;
}

.ptm {
padding-top: 10px;
}

.pbs {
padding-bottom: 5px;
}

.pbm {
padding-bottom: 10px;
}

.prm {
padding-right: 10px;
}

.pll {
padding-left: 40px;
}

.mlm {
margin-left: 10px;
}

.man {
margin: 0;
}

.mtn {
margin-top: 0;
}

.mbn {
margin-bottom: 0;
}

.mrn {
margin-right: 0;
}
30 changes: 24 additions & 6 deletions html/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
</head>
<body>
<form>
<div class="pts pbs man row">
<div class="pts pbs row">
<div class="w40p txt-center browser-style">
<input type="checkbox" id="enable_buttons_to_copy_source_and_target_branches_name">
</div>
<div>
<label for="enable_buttons_to_copy_source_and_target_branches_name">Enable buttons allowing to copy source and target branches name</label>
</div>
</div>
<div class="pts man row">
<div class="pts row">
<div class="w40p txt-center browser-style">
<input type="checkbox" id="enable_button_to_copy_mr_info">
</div>
Expand All @@ -23,14 +23,32 @@
</div>
</div>
<div class="pbs pll">
<small>Useful when sharing the Merge Request on e.g instant messaging softwares</small>
<small class="txt-muted">Useful when sharing the Merge Request on e.g instant messaging softwares</small>
</div>
<div class="pll pts pbs man" id="copy-mr-info-options">
<div class="pll pts pbs" id="copy-mr-info-options">
<div class="pbs"><label for="copy_mr_info_format">Text format:</label></div>
<div><textarea class="browser-style w100 monospaced man pas" id="copy_mr_info_format" rows="6" required></textarea></div>
<div class="pts"><small>Available placeholders: <code>{MR_TITLE}</code>, <code>{MR_ID}</code>, <code>{MR_URL}</code>, <code>{MR_DIFFS_URL}</code>, <code>{MR_AUTHOR_NAME}</code>, <code>{MR_STATUS}</code>, <code>{MR_SOURCE_BRANCH_NAME}</code>, <code>{MR_TARGET_BRANCH_NAME}</code></small></div>
<div class="pts"><small class="txt-muted">Available placeholders: <code>{MR_TITLE}</code>, <code>{MR_ID}</code>, <code>{MR_URL}</code>, <code>{MR_DIFFS_URL}</code>, <code>{MR_AUTHOR_NAME}</code>, <code>{MR_STATUS}</code>, <code>{MR_SOURCE_BRANCH_NAME}</code>, <code>{MR_TARGET_BRANCH_NAME}</code>, <code>{MR_JIRA_TICKET_ID}</code>, <code>{MR_JIRA_TICKET_URL}</code></small></div>
</div>
<div class="txt-center pts pbs man"><button type="submit" class="browser-style">Save preferences</button></div>
<div class="pts row">
<div class="w40p txt-center browser-style">
<input type="checkbox" id="enable_jira_ticket_link">
</div>
<div>
<label for="enable_jira_ticket_link">Enable Jira ticket link</label>
</div>
</div>
<div class="pll pbs" id="jira-ticket-link-options">
<div class="pbs"><small class="txt-muted">The ticket ID is automatically searched for in the source branch name and in the Merge Request title. The position of the ticket ID in these locations doesn't matter. Only the first uppercase ticket ID occurence will be matched.</small></div>
<div class="browser-style pbs man"><label for="base_jira_url">Base Jira URL:</label></div>
<div class="browser-style man pbm"><input type="url" id="base_jira_url" class="w100 man pas" required></div>
<div class="pbs">What should be displayed as the label of the Jira ticket link?</div>
<div class="pll">
<div class="browser-style man pbs"><input type="radio" required name="jira_ticket_link_label_type" value="ticket_id" id="jira_ticket_link_label_type_ticket_id" class="man"> <label for="jira_ticket_link_label_type_ticket_id">The ticket ID</label></div>
<div class="browser-style man"><input type="radio" required name="jira_ticket_link_label_type" value="icon" id="jira_ticket_link_label_type_icon" class="man"> <label for="jira_ticket_link_label_type_icon">An icon</label></div>
</div>
</div>
<div class="txt-center pts pbs"><button type="submit" class="browser-style">Save preferences</button></div>
</form>

<script src="../js/preferences.js"></script>
Expand Down
102 changes: 93 additions & 9 deletions js/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,39 @@

this.setDataAttributesToMergeRequestContainer(mergeRequestContainer, mergeRequest);

// -----------------------------------------------
// Jira ticket link (data attributes are set in setDataAttributesToMergeRequestContainer, above)

if (('jiraTicketId' in mergeRequestContainer.dataset) && ('jiraTicketUrl' in mergeRequestContainer.dataset)) {
let jiraTicketLinkLabel = null;

switch (this.preferences.jira_ticket_link_label_type) {
case 'ticket_id':
jiraTicketLinkLabel = mergeRequestContainer.dataset.jiraTicketId;

break;
case 'icon':
jiraTicketLinkLabel = '<button class="btn btn-secondary btn-md btn-default btn-transparent btn-clipboard has-tooltip" title="Jira ticket ' + mergeRequestContainer.dataset.jiraTicketId + '">' +
'<i class="fa fa-ticket" aria-hidden="true"></i>' +
'</button>';

break;
default:
console.error('Invalid link label type ' + this.preferences.jira_ticket_link_label_type);
}

if (jiraTicketLinkLabel) {
let jiraTicketLink = '<a href="' + mergeRequestContainer.dataset.jiraTicketUrl + '" class="issuable-milestone">' +
jiraTicketLinkLabel +
'</a> ';

this.parseHtmlAndPrepend(
mergeRequestContainer.querySelector('.merge-request-title'),
jiraTicketLink
);
}
}

// -----------------------------------------------
// Copy MR info button

Expand Down Expand Up @@ -276,6 +309,55 @@
mergeRequestContainer.dataset.status = mergeRequest.state;
mergeRequestContainer.dataset.sourceBranchName = mergeRequest.source_branch;
mergeRequestContainer.dataset.targetBranchName = mergeRequest.target_branch;

if (this.preferences.enable_jira_ticket_link) {
let jiraTicketId = this.findFirstJiraTicketId(mergeRequest);

if (jiraTicketId) {
mergeRequestContainer.dataset.jiraTicketId = jiraTicketId;
mergeRequestContainer.dataset.jiraTicketUrl = this.createJiraTicketUrl(jiraTicketId);
}
}
}

/**
* Finds a Jira ticket ID in the given Merge Request object. It first tris in the source branch name, then
* fallbacks to the Merge Request title.
*/
findFirstJiraTicketId(mergeRequest) {
let jiraTicketIdRegex = new RegExp('[A-Z]{1,10}-\\d+');

// First try in the source branch name
let results = jiraTicketIdRegex.exec(mergeRequest.source_branch);

if (results) {
return results[0];
}

// Fallback to the Merge Request title if none found in the source branch name
results = jiraTicketIdRegex.exec(mergeRequest.title);

if (results) {
return results[0];
}

return null;
}

/**
* Creates an URL to a given Jira ticket ID, pointing to the Jira base URL the user has defined in its
* preferences.
*/
createJiraTicketUrl(jiraTicketId) {
let baseJiraUrl = new URL(this.preferences.base_jira_url);

if (!baseJiraUrl.pathname.endsWith('/')) {
baseJiraUrl.pathname += '/';
}

baseJiraUrl.pathname += 'browse/' + jiraTicketId;

return baseJiraUrl.toString();
}

/**
Expand Down Expand Up @@ -324,21 +406,23 @@
*/
buildMergeRequestInfoText(mergeRequestContainer) {
let placeholders = {
'MR_TITLE': mergeRequestContainer.dataset.title,
'MR_ID': mergeRequestContainer.dataset.iid,
'MR_URL': mergeRequestContainer.dataset.url,
'MR_DIFFS_URL': mergeRequestContainer.dataset.diffsUrl,
'MR_AUTHOR_NAME': mergeRequestContainer.dataset.authorName,
'MR_STATUS': mergeRequestContainer.dataset.status,
'MR_SOURCE_BRANCH_NAME': mergeRequestContainer.dataset.sourceBranchName,
'MR_TARGET_BRANCH_NAME': mergeRequestContainer.dataset.targetBranchName
MR_TITLE: mergeRequestContainer.dataset.title,
MR_ID: mergeRequestContainer.dataset.iid,
MR_URL: mergeRequestContainer.dataset.url,
MR_DIFFS_URL: mergeRequestContainer.dataset.diffsUrl,
MR_AUTHOR_NAME: mergeRequestContainer.dataset.authorName,
MR_STATUS: mergeRequestContainer.dataset.status,
MR_SOURCE_BRANCH_NAME: mergeRequestContainer.dataset.sourceBranchName,
MR_TARGET_BRANCH_NAME: mergeRequestContainer.dataset.targetBranchName,
MR_JIRA_TICKET_ID: ('jiraTicketId' in mergeRequestContainer.dataset) ? mergeRequestContainer.dataset.jiraTicketId : '',
MR_JIRA_TICKET_URL: ('jiraTicketUrl' in mergeRequestContainer.dataset) ? mergeRequestContainer.dataset.jiraTicketUrl : ''
};

let placeholdersReplaceRegex = new RegExp('{(' + Object.keys(placeholders).join('|') + ')}', 'g');

return this.preferences.copy_mr_info_format.replace(placeholdersReplaceRegex, function(_, placeholder) {
return placeholders[placeholder];
});
}).trim();
}
}

Expand Down
Loading

0 comments on commit 1d40f0d

Please sign in to comment.