Merge remote-tracking branch 'upstream/master' into 8998_skip_pending_commits_if_not_head
* upstream/master: (58 commits) Fix icon name error Rewrite system note helper Change edit icon Leave icon area blank if legacy note; remove diamond icon Fix positioning of note icons Fix newline errors Add remaining system note icons Add system notes icon helper; add icons Fixed specs Updated JS that was causing the hints to appear & then disappear Update tests Fix broken spinach test Reuse code Improve shortcuts code Adds ShortcutsDashboardNavigation to globals comment since its a global variable Fix shortcut specs Reorganize shortcut help menu Change todos shortcut to shift Change shortcuts Switch global shortcuts to shift; reuse key styles from help menu Map bindings to lowercase letters; only show key bindings when using keyboard shortcut ...
This commit is contained in:
commit
f8fe64a630
|
@ -20,6 +20,7 @@ import eventHub from '../eventhub';
|
|||
list: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
rootPath: {
|
||||
type: String,
|
||||
|
@ -31,6 +32,26 @@ import eventHub from '../eventhub';
|
|||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
cardUrl() {
|
||||
return `${this.issueLinkBase}/${this.issue.id}`;
|
||||
},
|
||||
assigneeUrl() {
|
||||
return `${this.rootPath}${this.issue.assignee.username}`;
|
||||
},
|
||||
assigneeUrlTitle() {
|
||||
return `Assigned to ${this.issue.assignee.name}`;
|
||||
},
|
||||
avatarUrlTitle() {
|
||||
return `Avatar for ${this.issue.assignee.name}`;
|
||||
},
|
||||
issueId() {
|
||||
return `#${this.issue.id}`;
|
||||
},
|
||||
showLabelFooter() {
|
||||
return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showLabel(label) {
|
||||
if (!this.list) return true;
|
||||
|
@ -67,35 +88,41 @@ import eventHub from '../eventhub';
|
|||
},
|
||||
template: `
|
||||
<div>
|
||||
<h4 class="card-title">
|
||||
<i
|
||||
class="fa fa-eye-slash confidential-icon"
|
||||
v-if="issue.confidential"></i>
|
||||
<a
|
||||
:href="issueLinkBase + '/' + issue.id"
|
||||
:title="issue.title">
|
||||
{{ issue.title }}
|
||||
</a>
|
||||
</h4>
|
||||
<div class="card-footer">
|
||||
<span
|
||||
class="card-number"
|
||||
v-if="issue.id">
|
||||
#{{ issue.id }}
|
||||
</span>
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">
|
||||
<i
|
||||
class="fa fa-eye-slash confidential-icon"
|
||||
v-if="issue.confidential"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<a
|
||||
class="js-no-trigger"
|
||||
:href="cardUrl"
|
||||
:title="issue.title">{{ issue.title }}</a>
|
||||
<span
|
||||
class="card-number"
|
||||
v-if="issue.id"
|
||||
>
|
||||
{{ issueId }}
|
||||
</span>
|
||||
</h4>
|
||||
<a
|
||||
class="card-assignee has-tooltip js-no-trigger"
|
||||
:href="rootPath + issue.assignee.username"
|
||||
:title="'Assigned to ' + issue.assignee.name"
|
||||
:href="assigneeUrl"
|
||||
:title="assigneeUrlTitle"
|
||||
v-if="issue.assignee"
|
||||
data-container="body">
|
||||
data-container="body"
|
||||
>
|
||||
<img
|
||||
class="avatar avatar-inline s20 js-no-trigger"
|
||||
:src="issue.assignee.avatar"
|
||||
width="20"
|
||||
height="20"
|
||||
:alt="'Avatar for ' + issue.assignee.name" />
|
||||
:alt="avatarUrlTitle"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-footer" v-if="showLabelFooter">
|
||||
<button
|
||||
class="label color-label has-tooltip js-no-trigger"
|
||||
v-for="label in issue.labels"
|
||||
|
|
|
@ -38,9 +38,35 @@ showTooltip = function(target, title) {
|
|||
};
|
||||
|
||||
$(function() {
|
||||
var clipboard;
|
||||
|
||||
clipboard = new Clipboard('[data-clipboard-target], [data-clipboard-text]');
|
||||
const clipboard = new Clipboard('[data-clipboard-target], [data-clipboard-text]');
|
||||
clipboard.on('success', genericSuccess);
|
||||
return clipboard.on('error', genericError);
|
||||
clipboard.on('error', genericError);
|
||||
|
||||
// This a workaround around ClipboardJS limitations to allow the context-specific copy/pasting of plain text or GFM.
|
||||
// The Ruby `clipboard_button` helper sneaks a JSON hash with `text` and `gfm` keys into the `data-clipboard-text`
|
||||
// attribute that ClipboardJS reads from.
|
||||
// When ClipboardJS creates a new `textarea` (directly inside `body`, with a `readonly` attribute`), sets its value
|
||||
// to the value of this data attribute, focusses on it, and finally programmatically issues the 'Copy' command,
|
||||
// this code intercepts the copy command/event at the last minute to deconstruct this JSON hash and set the
|
||||
// `text/plain` and `text/x-gfm` copy data types to the intended values.
|
||||
$(document).on('copy', 'body > textarea[readonly]', function(e) {
|
||||
const clipboardData = e.originalEvent.clipboardData;
|
||||
if (!clipboardData) return;
|
||||
|
||||
const text = e.target.value;
|
||||
|
||||
let json;
|
||||
try {
|
||||
json = JSON.parse(text);
|
||||
} catch (ex) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json.text || !json.gfm) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
clipboardData.setData('text/plain', json.text);
|
||||
clipboardData.setData('text/x-gfm', json.gfm);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,10 +13,6 @@ class Diff {
|
|||
|
||||
$diffFile.each((index, file) => new gl.ImageFile(file));
|
||||
|
||||
if (this.diffViewType() === 'parallel') {
|
||||
$('.content-wrapper .container-fluid').removeClass('container-limited');
|
||||
}
|
||||
|
||||
if (!isBound) {
|
||||
$(document)
|
||||
.on('click', '.js-unfold', this.handleClickUnfold.bind(this))
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
/* global Search */
|
||||
/* global Admin */
|
||||
/* global NamespaceSelects */
|
||||
/* global ShortcutsDashboardNavigation */
|
||||
/* global Project */
|
||||
/* global ProjectAvatar */
|
||||
/* global CompareAutocomplete */
|
||||
|
@ -378,7 +377,6 @@ const ShortcutsBlob = require('./shortcuts_blob');
|
|||
break;
|
||||
case 'dashboard':
|
||||
case 'root':
|
||||
shortcut_handler = new ShortcutsDashboardNavigation();
|
||||
new UserCallout();
|
||||
break;
|
||||
case 'groups':
|
||||
|
|
|
@ -31,12 +31,6 @@ export default Vue.component('environment-folder-view', {
|
|||
cssContainerClass: environmentsData.cssClass,
|
||||
canCreateDeployment: environmentsData.canCreateDeployment,
|
||||
canReadEnvironment: environmentsData.canReadEnvironment,
|
||||
|
||||
// svgs
|
||||
commitIconSvg: environmentsData.commitIconSvg,
|
||||
playIconSvg: environmentsData.playIconSvg,
|
||||
terminalIconSvg: environmentsData.terminalIconSvg,
|
||||
|
||||
// Pagination Properties,
|
||||
paginationInformation: {},
|
||||
pageNumber: 1,
|
||||
|
@ -163,9 +157,6 @@ export default Vue.component('environment-folder-view', {
|
|||
:environments="state.environments"
|
||||
:can-create-deployment="canCreateDeploymentParsed"
|
||||
:can-read-environment="canReadEnvironmentParsed"
|
||||
:play-icon-svg="playIconSvg"
|
||||
:terminal-icon-svg="terminalIconSvg"
|
||||
:commit-icon-svg="commitIconSvg"
|
||||
:service="service"/>
|
||||
|
||||
<table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, prefer-arrow-callback, consistent-return, object-shorthand, no-unused-vars, one-var, one-var-declaration-per-line, no-else-return, comma-dangle, max-len */
|
||||
/* global Mousetrap */
|
||||
/* global findFileURL */
|
||||
import findAndFollowLink from './shortcuts_dashboard_navigation';
|
||||
|
||||
(function() {
|
||||
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
|
||||
|
@ -14,11 +15,33 @@
|
|||
}
|
||||
Mousetrap.bind('?', this.onToggleHelp);
|
||||
Mousetrap.bind('s', Shortcuts.focusSearch);
|
||||
Mousetrap.bind('f', (function(_this) {
|
||||
return function(e) {
|
||||
return _this.focusFilter(e);
|
||||
};
|
||||
})(this));
|
||||
Mousetrap.bind('f', (e => this.focusFilter(e)));
|
||||
|
||||
const $globalDropdownMenu = $('.global-dropdown-menu');
|
||||
const $globalDropdownToggle = $('.global-dropdown-toggle');
|
||||
|
||||
$('.global-dropdown').on('hide.bs.dropdown', () => {
|
||||
$globalDropdownMenu.removeClass('shortcuts');
|
||||
});
|
||||
|
||||
Mousetrap.bind('n', () => {
|
||||
$globalDropdownMenu.toggleClass('shortcuts');
|
||||
$globalDropdownToggle.trigger('click');
|
||||
|
||||
if (!$globalDropdownMenu.is(':visible')) {
|
||||
$globalDropdownToggle.blur();
|
||||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind('shift+t', () => findAndFollowLink('.shortcuts-todos'));
|
||||
Mousetrap.bind('shift+a', () => findAndFollowLink('.dashboard-shortcuts-activity'));
|
||||
Mousetrap.bind('shift+i', () => findAndFollowLink('.dashboard-shortcuts-issues'));
|
||||
Mousetrap.bind('shift+m', () => findAndFollowLink('.dashboard-shortcuts-merge_requests'));
|
||||
Mousetrap.bind('shift+p', () => findAndFollowLink('.dashboard-shortcuts-projects'));
|
||||
Mousetrap.bind('shift+g', () => findAndFollowLink('.dashboard-shortcuts-groups'));
|
||||
Mousetrap.bind('shift+l', () => findAndFollowLink('.dashboard-shortcuts-milestones'));
|
||||
Mousetrap.bind('shift+s', () => findAndFollowLink('.dashboard-shortcuts-snippets'));
|
||||
|
||||
Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], this.toggleMarkdownPreview);
|
||||
if (typeof findFileURL !== "undefined" && findFileURL !== null) {
|
||||
Mousetrap.bind('t', function() {
|
||||
|
|
|
@ -1,43 +1,12 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-arrow-callback, consistent-return, no-return-assign */
|
||||
/* global Mousetrap */
|
||||
/* global Shortcuts */
|
||||
/**
|
||||
* Helper function that finds the href of the fiven selector and updates the location.
|
||||
*
|
||||
* @param {String} selector
|
||||
*/
|
||||
export default (selector) => {
|
||||
const link = document.querySelector(selector).getAttribute('href');
|
||||
|
||||
require('./shortcuts');
|
||||
|
||||
(function() {
|
||||
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||
hasProp = {}.hasOwnProperty;
|
||||
|
||||
this.ShortcutsDashboardNavigation = (function(superClass) {
|
||||
extend(ShortcutsDashboardNavigation, superClass);
|
||||
|
||||
function ShortcutsDashboardNavigation() {
|
||||
ShortcutsDashboardNavigation.__super__.constructor.call(this);
|
||||
Mousetrap.bind('g a', function() {
|
||||
return ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-activity');
|
||||
});
|
||||
Mousetrap.bind('g i', function() {
|
||||
return ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-issues');
|
||||
});
|
||||
Mousetrap.bind('g m', function() {
|
||||
return ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-merge_requests');
|
||||
});
|
||||
Mousetrap.bind('g t', function() {
|
||||
return ShortcutsDashboardNavigation.findAndFollowLink('.shortcuts-todos');
|
||||
});
|
||||
Mousetrap.bind('g p', function() {
|
||||
return ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-projects');
|
||||
});
|
||||
}
|
||||
|
||||
ShortcutsDashboardNavigation.findAndFollowLink = function(selector) {
|
||||
var link;
|
||||
link = $(selector).attr('href');
|
||||
if (link) {
|
||||
return window.location = link;
|
||||
}
|
||||
};
|
||||
|
||||
return ShortcutsDashboardNavigation;
|
||||
})(Shortcuts);
|
||||
}).call(window);
|
||||
if (link) {
|
||||
window.location = link;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, max-len, no-var, one-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-arrow-callback, consistent-return, no-return-assign */
|
||||
/* global Mousetrap */
|
||||
/* global Shortcuts */
|
||||
import findAndFollowLink from './shortcuts_dashboard_navigation';
|
||||
|
||||
require('./shortcuts');
|
||||
|
||||
|
@ -13,59 +14,23 @@ require('./shortcuts');
|
|||
|
||||
function ShortcutsNavigation() {
|
||||
ShortcutsNavigation.__super__.constructor.call(this);
|
||||
Mousetrap.bind('g p', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-project');
|
||||
});
|
||||
Mousetrap.bind('g e', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-project-activity');
|
||||
});
|
||||
Mousetrap.bind('g f', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-tree');
|
||||
});
|
||||
Mousetrap.bind('g c', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-commits');
|
||||
});
|
||||
Mousetrap.bind('g b', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-builds');
|
||||
});
|
||||
Mousetrap.bind('g n', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-network');
|
||||
});
|
||||
Mousetrap.bind('g g', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-repository-charts');
|
||||
});
|
||||
Mousetrap.bind('g i', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-issues');
|
||||
});
|
||||
Mousetrap.bind('g l', function() {
|
||||
ShortcutsNavigation.findAndFollowLink('.shortcuts-issue-boards');
|
||||
});
|
||||
Mousetrap.bind('g m', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-merge_requests');
|
||||
});
|
||||
Mousetrap.bind('g t', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-todos');
|
||||
});
|
||||
Mousetrap.bind('g w', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-wiki');
|
||||
});
|
||||
Mousetrap.bind('g s', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-snippets');
|
||||
});
|
||||
Mousetrap.bind('i', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-new-issue');
|
||||
});
|
||||
Mousetrap.bind('g p', () => findAndFollowLink('.shortcuts-project'));
|
||||
Mousetrap.bind('g e', () => findAndFollowLink('.shortcuts-project-activity'));
|
||||
Mousetrap.bind('g f', () => findAndFollowLink('.shortcuts-tree'));
|
||||
Mousetrap.bind('g c', () => findAndFollowLink('.shortcuts-commits'));
|
||||
Mousetrap.bind('g j', () => findAndFollowLink('.shortcuts-builds'));
|
||||
Mousetrap.bind('g n', () => findAndFollowLink('.shortcuts-network'));
|
||||
Mousetrap.bind('g d', () => findAndFollowLink('.shortcuts-repository-charts'));
|
||||
Mousetrap.bind('g i', () => findAndFollowLink('.shortcuts-issues'));
|
||||
Mousetrap.bind('g b', () => findAndFollowLink('.shortcuts-issue-boards'));
|
||||
Mousetrap.bind('g m', () => findAndFollowLink('.shortcuts-merge_requests'));
|
||||
Mousetrap.bind('g t', () => findAndFollowLink('.shortcuts-todos'));
|
||||
Mousetrap.bind('g w', () => findAndFollowLink('.shortcuts-wiki'));
|
||||
Mousetrap.bind('g s', () => findAndFollowLink('.shortcuts-snippets'));
|
||||
Mousetrap.bind('i', () => findAndFollowLink('.shortcuts-new-issue'));
|
||||
this.enabledHelp.push('.hidden-shortcut.project');
|
||||
}
|
||||
|
||||
ShortcutsNavigation.findAndFollowLink = function(selector) {
|
||||
var link;
|
||||
link = $(selector).attr('href');
|
||||
if (link) {
|
||||
return window.location = link;
|
||||
}
|
||||
};
|
||||
|
||||
return ShortcutsNavigation;
|
||||
})(Shortcuts);
|
||||
}).call(window);
|
||||
|
|
|
@ -187,6 +187,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
.shortcut-mappings {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.shortcuts .shortcut-mappings {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
.timeline {
|
||||
@include basic-list;
|
||||
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
.timeline-entry {
|
||||
padding: $gl-padding $gl-btn-padding 11px;
|
||||
padding: $gl-padding $gl-btn-padding 14px;
|
||||
border-color: $white-normal;
|
||||
color: $gl-text-color;
|
||||
border-bottom: 1px solid $border-white-light;
|
||||
|
||||
.timeline-entry-inner {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&:target {
|
||||
background: $line-target-blue;
|
||||
}
|
||||
|
|
|
@ -197,7 +197,7 @@
|
|||
|
||||
.card {
|
||||
position: relative;
|
||||
padding: 10px $gl-padding;
|
||||
padding: 11px 10px 11px $gl-padding;
|
||||
background: $white-light;
|
||||
border-radius: $border-radius-default;
|
||||
box-shadow: 0 1px 2px $issue-boards-card-shadow;
|
||||
|
@ -217,6 +217,8 @@
|
|||
}
|
||||
|
||||
.confidential-icon {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
@ -224,34 +226,43 @@
|
|||
.card-title {
|
||||
margin: 0;
|
||||
font-size: 1em;
|
||||
line-height: inherit;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
color: $gl-text-color;
|
||||
word-wrap: break-word;
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
min-height: 20px;
|
||||
|
||||
.card-assignee {
|
||||
margin-left: auto;
|
||||
margin-right: 5px;
|
||||
padding-left: 10px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
margin-top: 5px;
|
||||
line-height: 25px;
|
||||
margin: 0 0 5px;
|
||||
|
||||
.label {
|
||||
margin-right: 5px;
|
||||
font-size: (14px / $issue-boards-font-size) * 1em;
|
||||
}
|
||||
|
||||
.card-assignee {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
margin-top: 5px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-number {
|
||||
margin-right: 5px;
|
||||
font-size: 12px;
|
||||
color: $gl-text-color-secondary;
|
||||
}
|
||||
|
||||
.issue-boards-search {
|
||||
|
|
|
@ -16,6 +16,15 @@ ul.notes {
|
|||
|
||||
.timeline-icon {
|
||||
float: left;
|
||||
|
||||
svg {
|
||||
width: 18px;
|
||||
height: auto;
|
||||
fill: $gray-darkest;
|
||||
position: absolute;
|
||||
left: 30px;
|
||||
top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
|
@ -33,117 +42,6 @@ ul.notes {
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.system-note {
|
||||
font-size: 14px;
|
||||
padding: 0;
|
||||
clear: both;
|
||||
|
||||
&.timeline-entry::after {
|
||||
clear: none;
|
||||
}
|
||||
|
||||
.system-note-message {
|
||||
display: inline;
|
||||
|
||||
&::first-letter {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $gl-link-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
p {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
|
||||
&::first-letter {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
padding: 14px 10px;
|
||||
}
|
||||
|
||||
.note-body {
|
||||
overflow: hidden;
|
||||
|
||||
.system-note-commit-list-toggler {
|
||||
color: $gl-link-color;
|
||||
display: none;
|
||||
padding: 10px 0 0;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
&:hover {
|
||||
color: $gl-link-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.note-text {
|
||||
& p:first-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.system-note-commit-list {
|
||||
max-height: 70px;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
|
||||
ul {
|
||||
margin: 3px 0 3px 16px !important;
|
||||
|
||||
.gfm-commit {
|
||||
font-family: $monospace_font;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
p:first-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 67px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(rgba($white-light, 0.1) -100px, $white-light 100%);
|
||||
}
|
||||
|
||||
&.hide-shade {
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-icon {
|
||||
display: none;
|
||||
|
||||
.avatar {
|
||||
visibility: hidden;
|
||||
|
||||
.discussion-body & {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.discussion-body {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
@ -239,7 +137,109 @@ ul.notes {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.system-note {
|
||||
font-size: 14px;
|
||||
padding: 0;
|
||||
clear: both;
|
||||
|
||||
&.timeline-entry::after {
|
||||
clear: none;
|
||||
}
|
||||
|
||||
.system-note-message {
|
||||
display: inline;
|
||||
|
||||
&::first-letter {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $gl-link-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
p {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
|
||||
&::first-letter {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
padding: 14px 10px;
|
||||
}
|
||||
|
||||
.note-header {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.note-body {
|
||||
overflow: hidden;
|
||||
|
||||
.system-note-commit-list-toggler {
|
||||
color: $gl-link-color;
|
||||
display: none;
|
||||
padding: 10px 0 0;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
&:hover {
|
||||
color: $gl-link-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.note-text {
|
||||
& p:first-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.system-note-commit-list {
|
||||
max-height: 70px;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
|
||||
ul {
|
||||
margin: 3px 0 3px 16px !important;
|
||||
|
||||
.gfm-commit {
|
||||
font-family: $monospace_font;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
p:first-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 67px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(rgba($white-light, 0.1) -100px, $white-light 100%);
|
||||
}
|
||||
|
||||
&.hide-shade {
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ module BlobHelper
|
|||
if Gitlab::MarkupHelper.previewable?(filename)
|
||||
'Preview'
|
||||
else
|
||||
'Preview Changes'
|
||||
'Preview changes'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -210,13 +210,13 @@ module BlobHelper
|
|||
end
|
||||
|
||||
def copy_file_path_button(file_path)
|
||||
clipboard_button(clipboard_text: file_path, class: 'btn-clipboard btn-transparent prepend-left-5', title: 'Copy file path to clipboard')
|
||||
clipboard_button(text: file_path, gfm: "`#{file_path}`", class: 'btn-clipboard btn-transparent prepend-left-5', title: 'Copy file path to clipboard')
|
||||
end
|
||||
|
||||
def copy_blob_content_button(blob)
|
||||
return if markup?(blob.name)
|
||||
|
||||
clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{blob.id}']", class: "btn btn-sm", title: "Copy content to clipboard")
|
||||
clipboard_button(target: ".blob-content[data-blob-id='#{blob.id}']", class: "btn btn-sm", title: "Copy content to clipboard")
|
||||
end
|
||||
|
||||
def open_raw_file_button(path)
|
||||
|
|
|
@ -1,23 +1,42 @@
|
|||
module ButtonHelper
|
||||
# Output a "Copy to Clipboard" button
|
||||
#
|
||||
# data - Data attributes passed to `content_tag`
|
||||
# data - Data attributes passed to `content_tag` (default: {}):
|
||||
# :text - Text to copy (optional)
|
||||
# :gfm - GitLab Flavored Markdown to copy, if different from `text` (optional)
|
||||
# :target - Selector for target element to copy from (optional)
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# # Define the clipboard's text
|
||||
# clipboard_button(clipboard_text: "Foo")
|
||||
# clipboard_button(text: "Foo")
|
||||
# # => "<button class='...' data-clipboard-text='Foo'>...</button>"
|
||||
#
|
||||
# # Define the target element
|
||||
# clipboard_button(clipboard_target: "div#foo")
|
||||
# clipboard_button(target: "div#foo")
|
||||
# # => "<button class='...' data-clipboard-target='div#foo'>...</button>"
|
||||
#
|
||||
# See http://clipboardjs.com/#usage
|
||||
def clipboard_button(data = {})
|
||||
css_class = data[:class] || 'btn-clipboard btn-transparent'
|
||||
title = data[:title] || 'Copy to clipboard'
|
||||
|
||||
# This supports code in app/assets/javascripts/copy_to_clipboard.js that
|
||||
# works around ClipboardJS limitations to allow the context-specific copy/pasting of plain text or GFM.
|
||||
if text = data.delete(:text)
|
||||
data[:clipboard_text] =
|
||||
if gfm = data.delete(:gfm)
|
||||
{ text: text, gfm: gfm }
|
||||
else
|
||||
text
|
||||
end
|
||||
end
|
||||
|
||||
target = data.delete(:target)
|
||||
data[:clipboard_target] = target if target
|
||||
|
||||
data = { toggle: 'tooltip', placement: 'bottom', container: 'body' }.merge(data)
|
||||
|
||||
content_tag :button,
|
||||
icon('clipboard', 'aria-hidden': 'true'),
|
||||
class: "btn #{css_class}",
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
module SystemNoteHelper
|
||||
ICON_NAMES_BY_ACTION = {
|
||||
'commit' => 'icon_commit',
|
||||
'merge' => 'icon_merge',
|
||||
'merged' => 'icon_merged',
|
||||
'opened' => 'icon_status_open',
|
||||
'closed' => 'icon_status_closed',
|
||||
'time_tracking' => 'icon_stopwatch',
|
||||
'assignee' => 'icon_user',
|
||||
'title' => 'icon_pencil',
|
||||
'task' => 'icon_check_square_o',
|
||||
'label' => 'icon_tags',
|
||||
'cross_reference' => 'icon_random',
|
||||
'branch' => 'icon_code_fork',
|
||||
'confidential' => 'icon_eye_slash',
|
||||
'visible' => 'icon_eye',
|
||||
'milestone' => 'icon_clock_o',
|
||||
'discussion' => 'icon_comment_o',
|
||||
'moved' => 'icon_arrow_circle_o_right'
|
||||
}.freeze
|
||||
|
||||
def icon_for_system_note(note)
|
||||
icon_name = ICON_NAMES_BY_ACTION[note.system_note_metadata&.action]
|
||||
custom_icon(icon_name) if icon_name
|
||||
end
|
||||
end
|
|
@ -103,18 +103,13 @@ module Ci
|
|||
end
|
||||
|
||||
def playable?
|
||||
project.builds_enabled? && has_commands? &&
|
||||
action? && manual?
|
||||
action? && manual?
|
||||
end
|
||||
|
||||
def action?
|
||||
self.when == 'manual'
|
||||
end
|
||||
|
||||
def has_commands?
|
||||
commands.present?
|
||||
end
|
||||
|
||||
def play(current_user)
|
||||
# Try to queue a current build
|
||||
if self.enqueue
|
||||
|
@ -131,8 +126,7 @@ module Ci
|
|||
end
|
||||
|
||||
def retryable?
|
||||
project.builds_enabled? && has_commands? &&
|
||||
(success? || failed? || canceled?)
|
||||
success? || failed? || canceled?
|
||||
end
|
||||
|
||||
def retried?
|
||||
|
|
|
@ -17,6 +17,12 @@ module Ci
|
|||
has_many :builds, foreign_key: :commit_id
|
||||
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id
|
||||
|
||||
has_many :pending_builds, -> { pending }, foreign_key: :commit_id, class_name: 'Ci::Build'
|
||||
has_many :retryable_builds, -> { latest.failed_or_canceled }, foreign_key: :commit_id, class_name: 'Ci::Build'
|
||||
has_many :cancelable_statuses, -> { cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus'
|
||||
has_many :manual_actions, -> { latest.manual_actions }, foreign_key: :commit_id, class_name: 'Ci::Build'
|
||||
has_many :artifacts, -> { latest.with_artifacts_not_expired }, foreign_key: :commit_id, class_name: 'Ci::Build'
|
||||
|
||||
delegate :id, to: :project, prefix: true
|
||||
|
||||
validates :sha, presence: { unless: :importing? }
|
||||
|
@ -169,10 +175,6 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
def artifacts
|
||||
builds.latest.with_artifacts_not_expired.includes(project: [:namespace])
|
||||
end
|
||||
|
||||
def valid_commit_sha
|
||||
if self.sha == Gitlab::Git::BLANK_SHA
|
||||
self.errors.add(:sha, " cant be 00000000 (branch removal)")
|
||||
|
@ -209,20 +211,16 @@ module Ci
|
|||
!tag?
|
||||
end
|
||||
|
||||
def manual_actions
|
||||
builds.latest.manual_actions.includes(project: [:namespace])
|
||||
end
|
||||
|
||||
def stuck?
|
||||
builds.pending.includes(:project).any?(&:stuck?)
|
||||
pending_builds.any?(&:stuck?)
|
||||
end
|
||||
|
||||
def retryable?
|
||||
builds.latest.failed_or_canceled.any?(&:retryable?)
|
||||
retryable_builds.any?
|
||||
end
|
||||
|
||||
def cancelable?
|
||||
statuses.cancelable.any?
|
||||
cancelable_statuses.any?
|
||||
end
|
||||
|
||||
def auto_canceled?
|
||||
|
|
|
@ -116,6 +116,7 @@ class Project < ActiveRecord::Base
|
|||
has_one :mock_ci_service, dependent: :destroy
|
||||
has_one :mock_deployment_service, dependent: :destroy
|
||||
has_one :mock_monitoring_service, dependent: :destroy
|
||||
has_one :microsoft_teams_service, dependent: :destroy
|
||||
|
||||
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
|
||||
has_one :forked_from_project, through: :forked_project_link
|
||||
|
@ -171,6 +172,8 @@ class Project < ActiveRecord::Base
|
|||
has_many :environments, dependent: :destroy
|
||||
has_many :deployments, dependent: :destroy
|
||||
|
||||
has_many :active_runners, -> { active }, through: :runner_projects, source: :runner, class_name: 'Ci::Runner'
|
||||
|
||||
accepts_nested_attributes_for :variables, allow_destroy: true
|
||||
accepts_nested_attributes_for :project_feature
|
||||
|
||||
|
@ -1086,15 +1089,15 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def shared_runners
|
||||
shared_runners_available? ? Ci::Runner.shared : Ci::Runner.none
|
||||
@shared_runners ||= shared_runners_available? ? Ci::Runner.shared : Ci::Runner.none
|
||||
end
|
||||
|
||||
def active_shared_runners
|
||||
@active_shared_runners ||= shared_runners.active
|
||||
end
|
||||
|
||||
def any_runners?(&block)
|
||||
if runners.active.any?(&block)
|
||||
return true
|
||||
end
|
||||
|
||||
shared_runners.active.any?(&block)
|
||||
active_runners.any?(&block) || active_shared_runners.any?(&block)
|
||||
end
|
||||
|
||||
def valid_runners_token?(token)
|
||||
|
|
|
@ -2,11 +2,23 @@ require 'slack-notifier'
|
|||
|
||||
module ChatMessage
|
||||
class BaseMessage
|
||||
attr_reader :markdown
|
||||
attr_reader :user_name
|
||||
attr_reader :user_avatar
|
||||
attr_reader :project_name
|
||||
attr_reader :project_url
|
||||
|
||||
def initialize(params)
|
||||
raise NotImplementedError
|
||||
@markdown = params[:markdown] || false
|
||||
@project_name = params.dig(:project, :path_with_namespace) || params[:project_name]
|
||||
@project_url = params.dig(:project, :web_url) || params[:project_url]
|
||||
@user_name = params.dig(:user, :username) || params[:user_name]
|
||||
@user_avatar = params.dig(:user, :avatar_url) || params[:user_avatar]
|
||||
end
|
||||
|
||||
def pretext
|
||||
return message if markdown
|
||||
|
||||
format(message)
|
||||
end
|
||||
|
||||
|
@ -17,6 +29,10 @@ module ChatMessage
|
|||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def activity
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def message
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
module ChatMessage
|
||||
class IssueMessage < BaseMessage
|
||||
attr_reader :user_name
|
||||
attr_reader :title
|
||||
attr_reader :project_name
|
||||
attr_reader :project_url
|
||||
attr_reader :issue_iid
|
||||
attr_reader :issue_url
|
||||
attr_reader :action
|
||||
|
@ -11,9 +8,7 @@ module ChatMessage
|
|||
attr_reader :description
|
||||
|
||||
def initialize(params)
|
||||
@user_name = params[:user][:username]
|
||||
@project_name = params[:project_name]
|
||||
@project_url = params[:project_url]
|
||||
super
|
||||
|
||||
obj_attr = params[:object_attributes]
|
||||
obj_attr = HashWithIndifferentAccess.new(obj_attr)
|
||||
|
@ -27,15 +22,24 @@ module ChatMessage
|
|||
|
||||
def attachments
|
||||
return [] unless opened_issue?
|
||||
return description if markdown
|
||||
|
||||
description_message
|
||||
end
|
||||
|
||||
def activity
|
||||
{
|
||||
title: "Issue #{state} by #{user_name}",
|
||||
subtitle: "in #{project_link}",
|
||||
text: issue_link,
|
||||
image: user_avatar
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def message
|
||||
case state
|
||||
when "opened"
|
||||
if state == 'opened'
|
||||
"[#{project_link}] Issue #{state} by #{user_name}"
|
||||
else
|
||||
"[#{project_link}] Issue #{issue_link} #{state} by #{user_name}"
|
||||
|
@ -64,7 +68,7 @@ module ChatMessage
|
|||
end
|
||||
|
||||
def issue_title
|
||||
"##{issue_iid} #{title}"
|
||||
"#{Issue.reference_prefix}#{issue_iid} #{title}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,36 +1,36 @@
|
|||
module ChatMessage
|
||||
class MergeMessage < BaseMessage
|
||||
attr_reader :user_name
|
||||
attr_reader :project_name
|
||||
attr_reader :project_url
|
||||
attr_reader :merge_request_id
|
||||
attr_reader :merge_request_iid
|
||||
attr_reader :source_branch
|
||||
attr_reader :target_branch
|
||||
attr_reader :state
|
||||
attr_reader :title
|
||||
|
||||
def initialize(params)
|
||||
@user_name = params[:user][:username]
|
||||
@project_name = params[:project_name]
|
||||
@project_url = params[:project_url]
|
||||
super
|
||||
|
||||
obj_attr = params[:object_attributes]
|
||||
obj_attr = HashWithIndifferentAccess.new(obj_attr)
|
||||
@merge_request_id = obj_attr[:iid]
|
||||
@merge_request_iid = obj_attr[:iid]
|
||||
@source_branch = obj_attr[:source_branch]
|
||||
@target_branch = obj_attr[:target_branch]
|
||||
@state = obj_attr[:state]
|
||||
@title = format_title(obj_attr[:title])
|
||||
end
|
||||
|
||||
def pretext
|
||||
format(message)
|
||||
end
|
||||
|
||||
def attachments
|
||||
[]
|
||||
end
|
||||
|
||||
def activity
|
||||
{
|
||||
title: "Merge Request #{state} by #{user_name}",
|
||||
subtitle: "in #{project_link}",
|
||||
text: merge_request_link,
|
||||
image: user_avatar
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def format_title(title)
|
||||
|
@ -50,11 +50,15 @@ module ChatMessage
|
|||
end
|
||||
|
||||
def merge_request_link
|
||||
link("merge request !#{merge_request_id}", merge_request_url)
|
||||
link(merge_request_title, merge_request_url)
|
||||
end
|
||||
|
||||
def merge_request_title
|
||||
"#{MergeRequest.reference_prefix}#{merge_request_iid} #{title}"
|
||||
end
|
||||
|
||||
def merge_request_url
|
||||
"#{project_url}/merge_requests/#{merge_request_id}"
|
||||
"#{project_url}/merge_requests/#{merge_request_iid}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,70 +1,74 @@
|
|||
module ChatMessage
|
||||
class NoteMessage < BaseMessage
|
||||
attr_reader :message
|
||||
attr_reader :user_name
|
||||
attr_reader :project_name
|
||||
attr_reader :project_url
|
||||
attr_reader :note
|
||||
attr_reader :note_url
|
||||
attr_reader :title
|
||||
attr_reader :target
|
||||
|
||||
def initialize(params)
|
||||
params = HashWithIndifferentAccess.new(params)
|
||||
@user_name = params[:user][:username]
|
||||
@project_name = params[:project_name]
|
||||
@project_url = params[:project_url]
|
||||
super
|
||||
|
||||
params = HashWithIndifferentAccess.new(params)
|
||||
obj_attr = params[:object_attributes]
|
||||
obj_attr = HashWithIndifferentAccess.new(obj_attr)
|
||||
@note = obj_attr[:note]
|
||||
@note_url = obj_attr[:url]
|
||||
noteable_type = obj_attr[:noteable_type]
|
||||
|
||||
case noteable_type
|
||||
when "Commit"
|
||||
create_commit_note(HashWithIndifferentAccess.new(params[:commit]))
|
||||
when "Issue"
|
||||
create_issue_note(HashWithIndifferentAccess.new(params[:issue]))
|
||||
when "MergeRequest"
|
||||
create_merge_note(HashWithIndifferentAccess.new(params[:merge_request]))
|
||||
when "Snippet"
|
||||
create_snippet_note(HashWithIndifferentAccess.new(params[:snippet]))
|
||||
end
|
||||
@target, @title = case obj_attr[:noteable_type]
|
||||
when "Commit"
|
||||
create_commit_note(params[:commit])
|
||||
when "Issue"
|
||||
create_issue_note(params[:issue])
|
||||
when "MergeRequest"
|
||||
create_merge_note(params[:merge_request])
|
||||
when "Snippet"
|
||||
create_snippet_note(params[:snippet])
|
||||
end
|
||||
end
|
||||
|
||||
def attachments
|
||||
return note if markdown
|
||||
|
||||
description_message
|
||||
end
|
||||
|
||||
def activity
|
||||
{
|
||||
title: "#{user_name} #{link('commented on ' + target, note_url)}",
|
||||
subtitle: "in #{project_link}",
|
||||
text: formatted_title,
|
||||
image: user_avatar
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def message
|
||||
"#{user_name} #{link('commented on ' + target, note_url)} in #{project_link}: *#{formatted_title}*"
|
||||
end
|
||||
|
||||
def format_title(title)
|
||||
title.lines.first.chomp
|
||||
end
|
||||
|
||||
def create_commit_note(commit)
|
||||
commit_sha = commit[:id]
|
||||
commit_sha = Commit.truncate_sha(commit_sha)
|
||||
commented_on_message(
|
||||
"commit #{commit_sha}",
|
||||
format_title(commit[:message]))
|
||||
def formatted_title
|
||||
format_title(title)
|
||||
end
|
||||
|
||||
def create_issue_note(issue)
|
||||
commented_on_message(
|
||||
"issue ##{issue[:iid]}",
|
||||
format_title(issue[:title]))
|
||||
["issue #{Issue.reference_prefix}#{issue[:iid]}", issue[:title]]
|
||||
end
|
||||
|
||||
def create_commit_note(commit)
|
||||
commit_sha = Commit.truncate_sha(commit[:id])
|
||||
|
||||
["commit #{commit_sha}", commit[:message]]
|
||||
end
|
||||
|
||||
def create_merge_note(merge_request)
|
||||
commented_on_message(
|
||||
"merge request !#{merge_request[:iid]}",
|
||||
format_title(merge_request[:title]))
|
||||
["merge request #{MergeRequest.reference_prefix}#{merge_request[:iid]}", merge_request[:title]]
|
||||
end
|
||||
|
||||
def create_snippet_note(snippet)
|
||||
commented_on_message(
|
||||
"snippet ##{snippet[:id]}",
|
||||
format_title(snippet[:title]))
|
||||
["snippet #{Snippet.reference_prefix}#{snippet[:id]}", snippet[:title]]
|
||||
end
|
||||
|
||||
def description_message
|
||||
|
@ -74,9 +78,5 @@ module ChatMessage
|
|||
def project_link
|
||||
link(project_name, project_url)
|
||||
end
|
||||
|
||||
def commented_on_message(target, title)
|
||||
@message = "#{user_name} #{link('commented on ' + target, note_url)} in #{project_link}: *#{title}*"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
module ChatMessage
|
||||
class PipelineMessage < BaseMessage
|
||||
attr_reader :ref_type, :ref, :status, :project_name, :project_url,
|
||||
:user_name, :duration, :pipeline_id
|
||||
attr_reader :ref_type
|
||||
attr_reader :ref
|
||||
attr_reader :status
|
||||
attr_reader :duration
|
||||
attr_reader :pipeline_id
|
||||
|
||||
def initialize(data)
|
||||
super
|
||||
|
||||
@user_name = data.dig(:user, :name) || 'API'
|
||||
|
||||
pipeline_attributes = data[:object_attributes]
|
||||
@ref_type = pipeline_attributes[:tag] ? 'tag' : 'branch'
|
||||
@ref = pipeline_attributes[:ref]
|
||||
@status = pipeline_attributes[:status]
|
||||
@duration = pipeline_attributes[:duration]
|
||||
@pipeline_id = pipeline_attributes[:id]
|
||||
|
||||
@project_name = data[:project][:path_with_namespace]
|
||||
@project_url = data[:project][:web_url]
|
||||
@user_name = (data[:user] && data[:user][:name]) || 'API'
|
||||
end
|
||||
|
||||
def pretext
|
||||
|
@ -25,17 +28,24 @@ module ChatMessage
|
|||
end
|
||||
|
||||
def attachments
|
||||
return message if markdown
|
||||
|
||||
[{ text: format(message), color: attachment_color }]
|
||||
end
|
||||
|
||||
def activity
|
||||
{
|
||||
title: "Pipeline #{pipeline_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status}",
|
||||
subtitle: "in #{project_link}",
|
||||
text: "in #{duration} #{time_measure}",
|
||||
image: user_avatar || ''
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def message
|
||||
"#{project_link}: Pipeline #{pipeline_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status} in #{duration} #{'second'.pluralize(duration)}"
|
||||
end
|
||||
|
||||
def format(string)
|
||||
Slack::Notifier::LinkFormatter.format(string)
|
||||
"#{project_link}: Pipeline #{pipeline_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status} in #{duration} #{time_measure}"
|
||||
end
|
||||
|
||||
def humanized_status
|
||||
|
@ -74,5 +84,9 @@ module ChatMessage
|
|||
def pipeline_link
|
||||
"[##{pipeline_id}](#{pipeline_url})"
|
||||
end
|
||||
|
||||
def time_measure
|
||||
'second'.pluralize(duration)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,33 +3,43 @@ module ChatMessage
|
|||
attr_reader :after
|
||||
attr_reader :before
|
||||
attr_reader :commits
|
||||
attr_reader :project_name
|
||||
attr_reader :project_url
|
||||
attr_reader :ref
|
||||
attr_reader :ref_type
|
||||
attr_reader :user_name
|
||||
|
||||
def initialize(params)
|
||||
super
|
||||
|
||||
@after = params[:after]
|
||||
@before = params[:before]
|
||||
@commits = params.fetch(:commits, [])
|
||||
@project_name = params[:project_name]
|
||||
@project_url = params[:project_url]
|
||||
@ref_type = Gitlab::Git.tag_ref?(params[:ref]) ? 'tag' : 'branch'
|
||||
@ref = Gitlab::Git.ref_name(params[:ref])
|
||||
@user_name = params[:user_name]
|
||||
end
|
||||
|
||||
def pretext
|
||||
format(message)
|
||||
end
|
||||
|
||||
def attachments
|
||||
return [] if new_branch? || removed_branch?
|
||||
return commit_messages if markdown
|
||||
|
||||
commit_message_attachments
|
||||
end
|
||||
|
||||
def activity
|
||||
action = if new_branch?
|
||||
"created"
|
||||
elsif removed_branch?
|
||||
"removed"
|
||||
else
|
||||
"pushed to"
|
||||
end
|
||||
|
||||
{
|
||||
title: "#{user_name} #{action} #{ref_type}",
|
||||
subtitle: "in #{project_link}",
|
||||
text: compare_link,
|
||||
image: user_avatar
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def message
|
||||
|
@ -59,7 +69,7 @@ module ChatMessage
|
|||
end
|
||||
|
||||
def commit_messages
|
||||
commits.map { |commit| compose_commit_message(commit) }.join("\n")
|
||||
commits.map { |commit| compose_commit_message(commit) }.join("\n\n")
|
||||
end
|
||||
|
||||
def commit_message_attachments
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
module ChatMessage
|
||||
class WikiPageMessage < BaseMessage
|
||||
attr_reader :user_name
|
||||
attr_reader :title
|
||||
attr_reader :project_name
|
||||
attr_reader :project_url
|
||||
attr_reader :wiki_page_url
|
||||
attr_reader :action
|
||||
attr_reader :description
|
||||
|
||||
def initialize(params)
|
||||
@user_name = params[:user][:username]
|
||||
@project_name = params[:project_name]
|
||||
@project_url = params[:project_url]
|
||||
super
|
||||
|
||||
obj_attr = params[:object_attributes]
|
||||
obj_attr = HashWithIndifferentAccess.new(obj_attr)
|
||||
|
@ -29,9 +24,20 @@ module ChatMessage
|
|||
end
|
||||
|
||||
def attachments
|
||||
return description if markdown
|
||||
|
||||
description_message
|
||||
end
|
||||
|
||||
def activity
|
||||
{
|
||||
title: "#{user_name} #{action} #{wiki_page_link}",
|
||||
subtitle: "in #{project_link}",
|
||||
text: title,
|
||||
image: user_avatar
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def message
|
||||
|
|
|
@ -49,10 +49,7 @@ class ChatNotificationService < Service
|
|||
|
||||
object_kind = data[:object_kind]
|
||||
|
||||
data = data.merge(
|
||||
project_url: project_url,
|
||||
project_name: project_name
|
||||
)
|
||||
data = custom_data(data)
|
||||
|
||||
# WebHook events often have an 'update' event that follows a 'open' or
|
||||
# 'close' action. Ignore update events for now to prevent duplicate
|
||||
|
@ -68,8 +65,7 @@ class ChatNotificationService < Service
|
|||
opts[:channel] = channel_name if channel_name
|
||||
opts[:username] = username if username
|
||||
|
||||
notifier = Slack::Notifier.new(webhook, opts)
|
||||
notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback)
|
||||
return false unless notify(message, opts)
|
||||
|
||||
true
|
||||
end
|
||||
|
@ -92,6 +88,18 @@ class ChatNotificationService < Service
|
|||
|
||||
private
|
||||
|
||||
def notify(message, opts)
|
||||
Slack::Notifier.new(webhook, opts).ping(
|
||||
message.pretext,
|
||||
attachments: message.attachments,
|
||||
fallback: message.fallback
|
||||
)
|
||||
end
|
||||
|
||||
def custom_data(data)
|
||||
data.merge(project_url: project_url, project_name: project_name)
|
||||
end
|
||||
|
||||
def get_message(object_kind, data)
|
||||
case object_kind
|
||||
when "push", "tag_push"
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
class MicrosoftTeamsService < ChatNotificationService
|
||||
def title
|
||||
'Microsoft Teams Notification'
|
||||
end
|
||||
|
||||
def description
|
||||
'Receive event notifications in Microsoft Teams'
|
||||
end
|
||||
|
||||
def self.to_param
|
||||
'microsoft_teams'
|
||||
end
|
||||
|
||||
def help
|
||||
'This service sends notifications about projects events to Microsoft Teams channels.<br />
|
||||
To set up this service:
|
||||
<ol>
|
||||
<li><a href="https://msdn.microsoft.com/en-us/microsoft-teams/connectors">Getting started with 365 Office Connectors For Microsoft Teams</a>.</li>
|
||||
<li>Paste the <strong>Webhook URL</strong> into the field below.</li>
|
||||
<li>Select events below to enable notifications.</li>
|
||||
</ol>'
|
||||
end
|
||||
|
||||
def webhook_placeholder
|
||||
'https://outlook.office.com/webhook/…'
|
||||
end
|
||||
|
||||
def event_field(event)
|
||||
end
|
||||
|
||||
def default_channel_placeholder
|
||||
end
|
||||
|
||||
def default_fields
|
||||
[
|
||||
{ type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" },
|
||||
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
|
||||
{ type: 'checkbox', name: 'notify_only_default_branch' },
|
||||
]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def notify(message, opts)
|
||||
MicrosoftTeams::Notifier.new(webhook).ping(
|
||||
title: message.project_name,
|
||||
pretext: message.pretext,
|
||||
activity: message.activity,
|
||||
attachments: message.attachments
|
||||
)
|
||||
end
|
||||
|
||||
def custom_data(data)
|
||||
super(data).merge(markdown: true)
|
||||
end
|
||||
end
|
|
@ -237,6 +237,7 @@ class Service < ActiveRecord::Base
|
|||
slack_slash_commands
|
||||
slack
|
||||
teamcity
|
||||
microsoft_teams
|
||||
]
|
||||
if Rails.env.development?
|
||||
service_names += %w[mock_ci mock_deployment mock_monitoring]
|
||||
|
|
|
@ -69,13 +69,13 @@ class PipelineEntity < Grape::Entity
|
|||
alias_method :pipeline, :object
|
||||
|
||||
def can_retry?
|
||||
pipeline.retryable? &&
|
||||
can?(request.user, :update_pipeline, pipeline)
|
||||
can?(request.user, :update_pipeline, pipeline) &&
|
||||
pipeline.retryable?
|
||||
end
|
||||
|
||||
def can_cancel?
|
||||
pipeline.cancelable? &&
|
||||
can?(request.user, :update_pipeline, pipeline)
|
||||
can?(request.user, :update_pipeline, pipeline) &&
|
||||
pipeline.cancelable?
|
||||
end
|
||||
|
||||
def detailed_status
|
||||
|
|
|
@ -13,7 +13,15 @@ class PipelineSerializer < BaseSerializer
|
|||
|
||||
def represent(resource, opts = {})
|
||||
if resource.is_a?(ActiveRecord::Relation)
|
||||
resource = resource.includes(project: :namespace)
|
||||
resource = resource.preload([
|
||||
:retryable_builds,
|
||||
:cancelable_statuses,
|
||||
:trigger_requests,
|
||||
:project,
|
||||
{ pending_builds: :project },
|
||||
{ manual_actions: :project },
|
||||
{ artifacts: :project }
|
||||
])
|
||||
end
|
||||
|
||||
if paginated?
|
||||
|
|
|
@ -7,9 +7,7 @@ module Ci
|
|||
raise Gitlab::Access::AccessDeniedError
|
||||
end
|
||||
|
||||
pipeline.builds.latest.failed_or_canceled.find_each do |build|
|
||||
next unless build.retryable?
|
||||
|
||||
pipeline.retryable_builds.find_each do |build|
|
||||
Ci::RetryBuildService.new(project, current_user)
|
||||
.reprocess(build)
|
||||
end
|
||||
|
|
|
@ -30,5 +30,5 @@
|
|||
= link_to 'Block user', block_admin_user_path(user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: "btn btn-sm btn-block"
|
||||
- else
|
||||
.btn.btn-sm.disabled.btn-block
|
||||
Already Blocked
|
||||
Already blocked
|
||||
= link_to 'Remove report', [:admin, abuse_report], remote: true, method: :delete, class: "btn btn-sm btn-block btn-close js-remove-tr"
|
||||
|
|
|
@ -148,7 +148,7 @@
|
|||
Sign-in enabled
|
||||
- if omniauth_enabled? && button_based_providers.any?
|
||||
.form-group
|
||||
= f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth Sign-In sources', class: 'control-label col-sm-2'
|
||||
= f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth sign-in sources', class: 'control-label col-sm-2'
|
||||
.col-sm-10
|
||||
.btn-group{ data: { toggle: 'buttons' } }
|
||||
- oauth_providers_checkboxes.each do |source|
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
%p.light
|
||||
System OAuth applications don't belong to any user and can only be managed by admins
|
||||
%hr
|
||||
%p= link_to 'New Application', new_admin_application_path, class: 'btn btn-success'
|
||||
%p= link_to 'New application', new_admin_application_path, class: 'btn btn-success'
|
||||
%table.table.table-striped
|
||||
%thead
|
||||
%tr
|
||||
|
|
|
@ -125,7 +125,7 @@
|
|||
= link_to admin_projects_path do
|
||||
%h1= number_with_delimiter(Project.cached_count)
|
||||
%hr
|
||||
= link_to('New Project', new_project_path, class: "btn btn-new")
|
||||
= link_to('New project', new_project_path, class: "btn btn-new")
|
||||
.col-sm-4
|
||||
.light-well.well-centered
|
||||
%h4 Users
|
||||
|
@ -133,7 +133,7 @@
|
|||
= link_to admin_users_path do
|
||||
%h1= number_with_delimiter(User.count)
|
||||
%hr
|
||||
= link_to 'New User', new_admin_user_path, class: "btn btn-new"
|
||||
= link_to 'New user', new_admin_user_path, class: "btn btn-new"
|
||||
.col-sm-4
|
||||
.light-well.well-centered
|
||||
%h4 Groups
|
||||
|
@ -141,7 +141,7 @@
|
|||
= link_to admin_groups_path do
|
||||
%h1= number_with_delimiter(Group.count)
|
||||
%hr
|
||||
= link_to 'New Group', new_admin_group_path, class: "btn btn-new"
|
||||
= link_to 'New group', new_admin_group_path, class: "btn btn-new"
|
||||
|
||||
.row.prepend-top-10
|
||||
.col-md-4
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
%h3.page-title.deploy-keys-title
|
||||
Public deploy keys (#{@deploy_keys.count})
|
||||
.pull-right
|
||||
= link_to 'New Deploy Key', new_admin_deploy_key_path, class: 'btn btn-new btn-sm btn-inverted'
|
||||
= link_to 'New deploy key', new_admin_deploy_key_path, class: 'btn btn-new btn-sm btn-inverted'
|
||||
|
||||
- if @deploy_keys.any?
|
||||
.table-holder.deploy-keys-list
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
= link_to admin_groups_path(sort: sort_value_largest_group, name: project_name) do
|
||||
= sort_title_largest_group
|
||||
= link_to new_admin_group_path, class: "btn btn-new" do
|
||||
New Group
|
||||
New group
|
||||
%ul.content-list
|
||||
= render @groups
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@
|
|||
group members
|
||||
%span.badge= @group.members.size
|
||||
.pull-right
|
||||
= link_to icon('pencil-square-o', text: 'Manage Access'), polymorphic_url([@group, :members]), class: "btn btn-xs"
|
||||
= link_to icon('pencil-square-o', text: 'Manage access'), polymorphic_url([@group, :members]), class: "btn btn-xs"
|
||||
%ul.well-list.group-users-list.content-list
|
||||
= render partial: 'shared/members/member', collection: @members, as: :member, locals: { show_controls: false }
|
||||
.panel-footer
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
= f.check_box :enable_ssl_verification
|
||||
%strong Enable SSL verification
|
||||
.form-actions
|
||||
= f.submit "Add System Hook", class: "btn btn-create"
|
||||
= f.submit "Add system hook", class: "btn btn-create"
|
||||
%hr
|
||||
|
||||
- if @hooks.any?
|
||||
|
@ -62,7 +62,7 @@
|
|||
- @hooks.each do |hook|
|
||||
%li
|
||||
.controls
|
||||
= link_to 'Test Hook', admin_hook_test_path(hook), class: "btn btn-sm"
|
||||
= link_to 'Test hook', admin_hook_test_path(hook), class: "btn btn-sm"
|
||||
= link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-remove btn-sm"
|
||||
.monospace= hook.url
|
||||
%div
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
- page_title "Identities", @user.name, "Users"
|
||||
= render 'admin/users/head'
|
||||
|
||||
= link_to 'New Identity', new_admin_user_identity_path, class: 'pull-right btn btn-new'
|
||||
= link_to 'New identity', new_admin_user_identity_path, class: 'pull-right btn btn-new'
|
||||
- if @identities.present?
|
||||
.table-holder
|
||||
%table.table
|
||||
|
|
|
@ -159,7 +159,7 @@
|
|||
%span.badge= @group_members.size
|
||||
.pull-right
|
||||
= link_to admin_group_path(@group), class: 'btn btn-xs' do
|
||||
= icon('pencil-square-o', text: 'Manage Access')
|
||||
= icon('pencil-square-o', text: 'Manage access')
|
||||
%ul.well-list.content-list
|
||||
= render partial: 'shared/members/member', collection: @group_members, as: :member, locals: { show_controls: false }
|
||||
.panel-footer
|
||||
|
@ -173,7 +173,7 @@
|
|||
project members
|
||||
%span.badge= @project.users.size
|
||||
.pull-right
|
||||
= link_to icon('pencil-square-o', text: 'Manage Access'), polymorphic_url([@project, :members]), class: "btn btn-xs"
|
||||
= link_to icon('pencil-square-o', text: 'Manage access'), polymorphic_url([@project, :members]), class: "btn btn-xs"
|
||||
%ul.well-list.project_members.content-list
|
||||
= render partial: 'shared/members/member', collection: @project_members, as: :member, locals: { show_controls: false }
|
||||
.panel-footer
|
||||
|
|
|
@ -35,5 +35,5 @@
|
|||
= link_to 'Block user', block_admin_user_path(user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: "btn btn-xs"
|
||||
- else
|
||||
.btn.btn-xs.disabled
|
||||
Already Blocked
|
||||
Already blocked
|
||||
= link_to 'Remove log', [:admin, spam_log], remote: true, method: :delete, class: "btn btn-xs btn-close js-remove-tr"
|
||||
|
|
|
@ -37,6 +37,6 @@
|
|||
- if user.can_be_removed? && can?(current_user, :destroy_user, @user)
|
||||
%li.divider
|
||||
%li
|
||||
= link_to 'Delete User', [:admin, user], data: { confirm: "USER #{user.name} WILL BE REMOVED! All issues, merge requests and groups linked to this user will also be removed! Consider cancelling this deletion and blocking the user instead. Are you sure?" },
|
||||
= link_to 'Delete user', [:admin, user], data: { confirm: "USER #{user.name} WILL BE REMOVED! All issues, merge requests and groups linked to this user will also be removed! Consider cancelling this deletion and blocking the user instead. Are you sure?" },
|
||||
class: 'btn btn-remove btn-block',
|
||||
method: :delete
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
= sort_title_recently_updated
|
||||
= link_to admin_users_path(sort: sort_value_oldest_updated, filter: params[:filter]) do
|
||||
= sort_title_oldest_updated
|
||||
= link_to 'New User', new_admin_user_path, class: 'btn btn-new btn-search'
|
||||
= link_to 'New user', new_admin_user_path, class: 'btn btn-new btn-search'
|
||||
|
||||
.nav-block
|
||||
%ul.nav-links.wide.scrolling-tabs.white.scrolling-tabs
|
||||
|
|
|
@ -11,4 +11,4 @@
|
|||
= render 'shared/groups/dropdown'
|
||||
- if current_user.can_create_group?
|
||||
= link_to new_group_path, class: "btn btn-new" do
|
||||
New Group
|
||||
New group
|
||||
|
|
|
@ -19,4 +19,4 @@
|
|||
= render 'shared/projects/dropdown'
|
||||
- if current_user.can_create_project?
|
||||
= link_to new_project_path, class: 'btn btn-new' do
|
||||
New Project
|
||||
New project
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
.nav-controls
|
||||
= link_to params.merge(rss_url_options), class: 'btn has-tooltip', title: 'Subscribe' do
|
||||
= icon('rss')
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue"
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue"
|
||||
|
||||
= render 'shared/issuable/filter', type: :issues
|
||||
= render 'shared/issues'
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
.top-area
|
||||
= render 'shared/issuable/nav', type: :merge_requests
|
||||
.nav-controls
|
||||
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New Merge Request"
|
||||
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request"
|
||||
|
||||
= render 'shared/issuable/filter', type: :merge_requests
|
||||
= render 'shared/merge_requests'
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
= render 'shared/milestones_filter', counts: @milestone_states
|
||||
|
||||
.nav-controls
|
||||
= render 'shared/new_project_item_select', path: 'milestones/new', label: 'New Milestone', include_groups: true
|
||||
= render 'shared/new_project_item_select', path: 'milestones/new', label: 'New milestone', include_groups: true
|
||||
|
||||
.milestones
|
||||
%ul.content-list
|
||||
|
|
|
@ -10,5 +10,5 @@
|
|||
#{time_ago_with_tooltip(event.created_at)}
|
||||
|
||||
.pull-right
|
||||
= link_to new_mr_path_from_push_event(event), title: "New Merge Request", class: "btn btn-info btn-sm" do
|
||||
Create Merge Request
|
||||
= link_to new_mr_path_from_push_event(event), title: "New merge request", class: "btn btn-info btn-sm" do
|
||||
Create merge request
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
= custom_icon("icon_status_closed")
|
||||
- else
|
||||
.profile-icon.fork-icon
|
||||
= custom_icon("code_fork")
|
||||
= custom_icon("icon_code_fork")
|
||||
|
||||
.event-title
|
||||
%span{ class: event.action_name }
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.profile-icon
|
||||
= custom_icon("comment_o")
|
||||
= custom_icon("icon_comment_o")
|
||||
|
||||
.event-title
|
||||
= event.action_name
|
||||
|
|
|
@ -51,4 +51,4 @@
|
|||
%strong Removed group can not be restored!
|
||||
|
||||
.form-actions
|
||||
= link_to 'Remove Group', @group, data: {confirm: 'Removed group can not be restored! Are you sure?'}, method: :delete, class: "btn btn-remove"
|
||||
= link_to 'Remove group', @group, data: {confirm: 'Removed group can not be restored! Are you sure?'}, method: :delete, class: "btn btn-remove"
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
= icon('rss')
|
||||
%span.icon-label
|
||||
Subscribe
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue"
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue"
|
||||
|
||||
= render 'shared/issuable/filter', type: :issues
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
.nav-controls
|
||||
- if can?(current_user, :admin_milestones, @group)
|
||||
= link_to new_group_milestone_path(@group), class: "btn btn-new" do
|
||||
New Milestone
|
||||
New milestone
|
||||
|
||||
.row-content-block
|
||||
Only milestones from
|
||||
|
|
|
@ -39,5 +39,5 @@
|
|||
= render "shared/milestones/form_dates", f: f
|
||||
|
||||
.form-actions
|
||||
= f.submit 'Create Milestone', class: "btn-create btn"
|
||||
= f.submit 'Create milestone', class: "btn-create btn"
|
||||
= link_to "Cancel", group_milestones_path(@group), class: "btn btn-cancel"
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
- if can? current_user, :admin_group, @group
|
||||
.controls
|
||||
= link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do
|
||||
New Project
|
||||
New project
|
||||
%ul.well-list
|
||||
- @projects.each do |project|
|
||||
%li
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
%tr
|
||||
%th
|
||||
%th Global Shortcuts
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key n
|
||||
%td Main Navigation
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key s
|
||||
|
@ -39,24 +43,46 @@
|
|||
.key
|
||||
%i.fa.fa-arrow-up
|
||||
%td Edit last comment (when focused on an empty textarea)
|
||||
%tbody
|
||||
%tr
|
||||
%th
|
||||
%th Project Files browsing
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%i.fa.fa-arrow-up
|
||||
%td Move selection up
|
||||
.key shift t
|
||||
%td
|
||||
Go to todos
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%i.fa.fa-arrow-down
|
||||
%td Move selection down
|
||||
.key shift a
|
||||
%td
|
||||
Go to the activity feed
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key enter
|
||||
%td Open Selection
|
||||
.key shift p
|
||||
%td
|
||||
Go to projects
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key shift i
|
||||
%td
|
||||
Go to issues
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key shift m
|
||||
%td
|
||||
Go to merge requests
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key shift g
|
||||
%td
|
||||
Go to groups
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key shift l
|
||||
%td
|
||||
Go to milestones
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key shift s
|
||||
%td
|
||||
Go to snippets
|
||||
%tbody
|
||||
%tr
|
||||
%th
|
||||
|
@ -79,51 +105,8 @@
|
|||
%td.shortcut
|
||||
.key esc
|
||||
%td Go back
|
||||
%tbody
|
||||
%tr
|
||||
%th
|
||||
%th Project File
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key y
|
||||
%td Go to file permalink
|
||||
|
||||
.col-lg-4
|
||||
%table.shortcut-mappings
|
||||
%tbody.hidden-shortcut.project{ style: 'display:none' }
|
||||
%tr
|
||||
%th
|
||||
%th Global Dashboard
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key a
|
||||
%td
|
||||
Go to the activity feed
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key p
|
||||
%td
|
||||
Go to projects
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key i
|
||||
%td
|
||||
Go to issues
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key m
|
||||
%td
|
||||
Go to merge requests
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key t
|
||||
%td
|
||||
Go to todos
|
||||
%tbody
|
||||
%tr
|
||||
%th
|
||||
|
@ -155,7 +138,7 @@
|
|||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key b
|
||||
.key j
|
||||
%td
|
||||
Go to jobs
|
||||
%tr
|
||||
|
@ -167,7 +150,7 @@
|
|||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key g
|
||||
.key d
|
||||
%td
|
||||
Go to repository charts
|
||||
%tr
|
||||
|
@ -179,7 +162,7 @@
|
|||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key l
|
||||
.key b
|
||||
%td
|
||||
Go to issue boards
|
||||
%tr
|
||||
|
@ -194,6 +177,12 @@
|
|||
.key s
|
||||
%td
|
||||
Go to snippets
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key w
|
||||
%td
|
||||
Go to wiki
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key t
|
||||
|
@ -202,6 +191,33 @@
|
|||
%td.shortcut
|
||||
.key i
|
||||
%td New issue
|
||||
|
||||
%tbody
|
||||
%tr
|
||||
%th
|
||||
%th Project Files browsing
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%i.fa.fa-arrow-up
|
||||
%td Move selection up
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%i.fa.fa-arrow-down
|
||||
%td Move selection down
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key enter
|
||||
%td Open Selection
|
||||
%tbody
|
||||
%tr
|
||||
%th
|
||||
%th Project File
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key y
|
||||
%td Go to file permalink
|
||||
.col-lg-4
|
||||
%table.shortcut-mappings
|
||||
%tbody.hidden-shortcut.network{ style: 'display:none' }
|
||||
|
|
|
@ -225,7 +225,7 @@
|
|||
%ul.dropdown-menu
|
||||
%li
|
||||
%a{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
.dropdown.inline.pull-right
|
||||
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } }
|
||||
Dropdown
|
||||
|
@ -233,7 +233,7 @@
|
|||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
%li
|
||||
%a{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
.example
|
||||
%div
|
||||
.dropdown.inline
|
||||
|
@ -243,7 +243,7 @@
|
|||
%ul.dropdown-menu.dropdown-menu-selectable
|
||||
%li
|
||||
%a.is-active{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
.example
|
||||
%div
|
||||
.dropdown.inline
|
||||
|
@ -262,26 +262,26 @@
|
|||
%ul
|
||||
%li
|
||||
%a.is-active{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
%li
|
||||
%a{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
%li.divider
|
||||
%li
|
||||
%a{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
%li
|
||||
%a{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
%li
|
||||
%a{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
%li
|
||||
%a{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
%li
|
||||
%a{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
.dropdown-footer
|
||||
%strong Tip:
|
||||
If an author is not a member of this project, you can still filter by his name while using the search field.
|
||||
|
@ -301,26 +301,26 @@
|
|||
%ul
|
||||
%li
|
||||
%a.is-active{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
%li
|
||||
%a{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
%li.divider
|
||||
%li
|
||||
%a{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
%li
|
||||
%a{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
%li
|
||||
%a{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
%li
|
||||
%a{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
%li
|
||||
%a{ href: "#" }
|
||||
Dropdown Option
|
||||
Dropdown option
|
||||
.dropdown-footer
|
||||
%strong Tip:
|
||||
If an author is not a member of this project, you can still filter by his name while using the search field.
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
To import a GitHub project, you first need to authorize GitLab to access
|
||||
the list of your GitHub repositories:
|
||||
|
||||
= link_to 'List Your GitHub Repositories', status_import_github_path, class: 'btn btn-success'
|
||||
= link_to 'List your GitHub repositories', status_import_github_path, class: 'btn btn-success'
|
||||
|
||||
%hr
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
|||
= form_tag personal_access_token_import_github_path, method: :post, class: 'form-inline' do
|
||||
.form-group
|
||||
= text_field_tag :personal_access_token, '', class: 'form-control', placeholder: "Personal Access Token", size: 40
|
||||
= submit_tag 'List Your GitHub Repositories', class: 'btn btn-success'
|
||||
= submit_tag 'List your GitHub repositories', class: 'btn btn-success'
|
||||
|
||||
- unless github_import_configured?
|
||||
%hr
|
||||
|
|
|
@ -29,11 +29,11 @@
|
|||
- if current_user
|
||||
- if session[:impersonator_id]
|
||||
%li.impersonation
|
||||
= link_to admin_impersonation_path, method: :delete, title: "Stop Impersonation", aria: { label: 'Stop Impersonation' }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
|
||||
= link_to admin_impersonation_path, method: :delete, title: "Stop impersonation", aria: { label: 'Stop impersonation' }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
|
||||
= icon('user-secret fw')
|
||||
- if current_user.is_admin?
|
||||
%li
|
||||
= link_to admin_root_path, title: 'Admin Area', aria: { label: "Admin Area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
|
||||
= link_to admin_root_path, title: 'Admin area', aria: { label: "Admin area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
|
||||
= icon('wrench fw')
|
||||
- if current_user.can_create_project?
|
||||
%li
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
%ul
|
||||
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
|
||||
= link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
|
||||
.shortcut-mappings
|
||||
.key
|
||||
= icon('arrow-up', 'aria-label' => 'hidden')
|
||||
P
|
||||
%span
|
||||
Projects
|
||||
= nav_link(path: 'dashboard#activity') do
|
||||
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
|
||||
.shortcut-mappings
|
||||
.key
|
||||
= icon('arrow-up', 'aria-label' => 'hidden')
|
||||
A
|
||||
%span
|
||||
Activity
|
||||
- if koding_enabled?
|
||||
|
@ -13,25 +21,45 @@
|
|||
%span
|
||||
Koding
|
||||
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
|
||||
= link_to dashboard_groups_path, title: 'Groups' do
|
||||
= link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups', title: 'Groups' do
|
||||
.shortcut-mappings
|
||||
.key
|
||||
= icon('arrow-up', 'aria-label' => 'hidden')
|
||||
G
|
||||
%span
|
||||
Groups
|
||||
= nav_link(controller: 'dashboard/milestones') do
|
||||
= link_to dashboard_milestones_path, title: 'Milestones' do
|
||||
= link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: 'Milestones' do
|
||||
.shortcut-mappings
|
||||
.key
|
||||
= icon('arrow-up', 'aria-label' => 'hidden')
|
||||
L
|
||||
%span
|
||||
Milestones
|
||||
= nav_link(path: 'dashboard#issues') do
|
||||
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
|
||||
.shortcut-mappings
|
||||
.key
|
||||
= icon('arrow-up', 'aria-label' => 'hidden')
|
||||
I
|
||||
%span
|
||||
Issues
|
||||
.badge= number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened))
|
||||
= nav_link(path: 'dashboard#merge_requests') do
|
||||
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
|
||||
.shortcut-mappings
|
||||
.key
|
||||
= icon('arrow-up', 'aria-label' => 'hidden')
|
||||
M
|
||||
%span
|
||||
Merge Requests
|
||||
.badge= number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened))
|
||||
= nav_link(controller: 'dashboard/snippets') do
|
||||
= link_to dashboard_snippets_path, title: 'Snippets' do
|
||||
= link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do
|
||||
.shortcut-mappings
|
||||
.key
|
||||
= icon('arrow-up', 'aria-label' => 'hidden')
|
||||
S
|
||||
%span
|
||||
Snippets
|
||||
%li.divider
|
||||
|
|
|
@ -49,14 +49,14 @@
|
|||
%p
|
||||
Status: #{current_user.two_factor_enabled? ? 'Enabled' : 'Disabled'}
|
||||
- if current_user.two_factor_enabled?
|
||||
= link_to 'Manage Two-Factor Authentication', profile_two_factor_auth_path, class: 'btn btn-info'
|
||||
= link_to 'Manage two-factor authentication', profile_two_factor_auth_path, class: 'btn btn-info'
|
||||
= link_to 'Disable', profile_two_factor_auth_path,
|
||||
method: :delete,
|
||||
data: { confirm: "Are you sure? This will invalidate your registered applications and U2F devices." },
|
||||
class: 'btn btn-danger'
|
||||
- else
|
||||
.append-bottom-10
|
||||
= link_to 'Enable Two-Factor Authentication', profile_two_factor_auth_path, class: 'btn btn-success'
|
||||
= link_to 'Enable two-factor authentication', profile_two_factor_auth_path, class: 'btn btn-success'
|
||||
|
||||
%hr
|
||||
- if button_based_providers.any?
|
||||
|
|
|
@ -33,17 +33,17 @@
|
|||
%li
|
||||
= @primary
|
||||
%span.pull-right
|
||||
%span.label.label-success Primary Email
|
||||
%span.label.label-success Primary email
|
||||
- if @primary === current_user.public_email
|
||||
%span.label.label-info Public Email
|
||||
%span.label.label-info Public email
|
||||
- if @primary === current_user.notification_email
|
||||
%span.label.label-info Notification Email
|
||||
%span.label.label-info Notification email
|
||||
- @emails.each do |email|
|
||||
%li
|
||||
= email.email
|
||||
%span.pull-right
|
||||
- if email.email === current_user.public_email
|
||||
%span.label.label-info Public Email
|
||||
%span.label.label-info Public email
|
||||
- if email.email === current_user.notification_email
|
||||
%span.label.label-info Notification Email
|
||||
%span.label.label-info Notification email
|
||||
= link_to 'Remove', profile_email_path(email), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-warning prepend-left-10'
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
Your New Personal Access Token
|
||||
.form-group
|
||||
= text_field_tag 'created-personal-access-token', flash[:personal_access_token], readonly: true, class: "form-control", 'aria-describedby' => "created-personal-access-token-help-block"
|
||||
= clipboard_button(clipboard_text: flash[:personal_access_token], title: "Copy personal access token to clipboard", placement: "left")
|
||||
= clipboard_button(text: flash[:personal_access_token], title: "Copy personal access token to clipboard", placement: "left")
|
||||
%span#created-personal-access-token-help-block.help-block.text-danger Make sure you save it - you won't be able to access it again.
|
||||
|
||||
%hr
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
= label_tag :pin_code, nil, class: "label-light"
|
||||
= text_field_tag :pin_code, nil, class: "form-control", required: true
|
||||
.prepend-top-default
|
||||
= submit_tag 'Register with Two-Factor App', class: 'btn btn-success'
|
||||
= submit_tag 'Register with two-factor app', class: 'btn btn-success'
|
||||
|
||||
%hr
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.form-actions
|
||||
= button_tag 'Commit Changes', class: 'btn commit-btn js-commit-button btn-create'
|
||||
= button_tag 'Commit changes', class: 'btn commit-btn js-commit-button btn-create'
|
||||
= link_to 'Cancel', cancel_path,
|
||||
class: 'btn btn-cancel', data: {confirm: leave_edit_message}
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
= link_to namespace_project_find_file_path(@project.namespace, @project, @ref), class: 'btn btn-grouped shortcuts-find-file', rel: 'nofollow' do
|
||||
= icon('search')
|
||||
%span Find File
|
||||
%span Find file
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
- if @project && event.project != @project
|
||||
%span at
|
||||
%strong= link_to_project event.project
|
||||
= clipboard_button(clipboard_text: event.ref_name, class: 'btn-clipboard btn-transparent', title: 'Copy branch to clipboard')
|
||||
= clipboard_button(text: event.ref_name, class: 'btn-clipboard btn-transparent', title: 'Copy branch to clipboard')
|
||||
#{time_ago_with_tooltip(event.created_at)}
|
||||
|
||||
.pull-right
|
||||
= link_to new_mr_path_from_push_event(event), title: "New Merge Request", class: "btn btn-info btn-sm" do
|
||||
Create Merge Request
|
||||
= link_to new_mr_path_from_push_event(event), title: "New merge request", class: "btn btn-info btn-sm" do
|
||||
Create merge request
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
-# only show normal/blame view links for text files
|
||||
- if blob_text_viewable?(blob)
|
||||
- if current_page? namespace_project_blame_path(@project.namespace, @project, @id)
|
||||
= link_to 'Normal View', namespace_project_blob_path(@project.namespace, @project, @id),
|
||||
= link_to 'Normal view', namespace_project_blob_path(@project.namespace, @project, @id),
|
||||
class: 'btn btn-sm'
|
||||
- else
|
||||
= link_to 'Blame', namespace_project_blame_path(@project.namespace, @project, @id),
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
.template-type-selector.js-template-type-selector-wrap.hidden
|
||||
= dropdown_tag("Choose type", options: { toggle_class: 'btn js-template-type-selector', title: "Choose a template type" } )
|
||||
.license-selector.js-license-selector-wrap.js-template-selector-wrap.hidden
|
||||
= dropdown_tag("Apply a License template", options: { toggle_class: 'btn js-license-selector', title: "Apply a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select, project: @project.name, fullname: @project.namespace.human_name } } )
|
||||
= dropdown_tag("Apply a license template", options: { toggle_class: 'btn js-license-selector', title: "Apply a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select, project: @project.name, fullname: @project.namespace.human_name } } )
|
||||
.gitignore-selector.js-gitignore-selector-wrap.js-template-selector-wrap.hidden
|
||||
= dropdown_tag("Apply a .gitignore template", options: { toggle_class: 'btn js-gitignore-selector', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } )
|
||||
.gitlab-ci-yml-selector.js-gitlab-ci-yml-selector-wrap.js-template-selector-wrap.hidden
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
.controls.hidden-xs<
|
||||
- if merge_project && create_mr_button?(@repository.root_ref, branch.name)
|
||||
= link_to create_mr_path(@repository.root_ref, branch.name), class: 'btn btn-default' do
|
||||
Merge Request
|
||||
Merge request
|
||||
|
||||
- if branch.name != @repository.root_ref
|
||||
= link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: "btn btn-default #{'prepend-left-10' unless merge_project}", method: :post, title: "Compare" do
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
= link_to 'Get started with CI/CD Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
|
||||
|
||||
= link_to ci_lint_path, class: 'btn btn-default' do
|
||||
%span CI Lint
|
||||
%span CI lint
|
||||
|
||||
.content-list.builds-content-list
|
||||
= render "table", builds: @builds, project: @project
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
.page-content-header
|
||||
.header-main-content
|
||||
%strong Commit #{@commit.short_id}
|
||||
= clipboard_button(clipboard_text: @commit.id, title: "Copy commit SHA to clipboard")
|
||||
= clipboard_button(text: @commit.id, title: "Copy commit SHA to clipboard")
|
||||
%span.hidden-xs authored
|
||||
#{time_ago_with_tooltip(@commit.authored_date)}
|
||||
%span by
|
||||
|
@ -20,7 +20,7 @@
|
|||
= icon('comment')
|
||||
= @notes_count
|
||||
= link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-default append-right-10 hidden-xs hidden-sm" do
|
||||
Browse Files
|
||||
Browse files
|
||||
.dropdown.inline
|
||||
%a.btn.btn-default.dropdown-toggle{ data: { toggle: "dropdown" } }
|
||||
%span Options
|
||||
|
|
|
@ -37,6 +37,6 @@
|
|||
.commit-actions.flex-row.hidden-xs
|
||||
- if commit.status(ref)
|
||||
= render_commit_status(commit, ref: ref)
|
||||
= clipboard_button(clipboard_text: commit.id, title: "Copy commit SHA to clipboard")
|
||||
= clipboard_button(text: commit.id, title: "Copy commit SHA to clipboard")
|
||||
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-short-id btn btn-transparent"
|
||||
= link_to_browse_code(project, commit)
|
||||
|
|
|
@ -18,16 +18,16 @@
|
|||
.block-controls.hidden-xs.hidden-sm
|
||||
- if @merge_request.present?
|
||||
.control
|
||||
= link_to "View Open Merge Request", namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn'
|
||||
= link_to "View open merge request", namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn'
|
||||
- elsif create_mr_button?(@repository.root_ref, @ref)
|
||||
.control
|
||||
= link_to "Create Merge Request", create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success'
|
||||
= link_to "Create merge request", create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success'
|
||||
|
||||
.control
|
||||
= form_tag(namespace_project_commits_path(@project.namespace, @project, @id), method: :get, class: 'commits-search-form') do
|
||||
= search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false }
|
||||
.control
|
||||
= link_to namespace_project_commits_path(@project.namespace, @project, @ref, rss_url_options), title: "Commits Feed", class: 'btn' do
|
||||
= link_to namespace_project_commits_path(@project.namespace, @project, @ref, rss_url_options), title: "Commits feed", class: 'btn' do
|
||||
= icon("rss")
|
||||
|
||||
%div{ id: dom_id(@project) }
|
||||
|
|
|
@ -21,6 +21,6 @@
|
|||
|
||||
= button_tag "Compare", class: "btn btn-create commits-compare-btn"
|
||||
- if @merge_request.present?
|
||||
= link_to "View Open Merge Request", namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'prepend-left-10 btn'
|
||||
= link_to "View open merge request", namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'prepend-left-10 btn'
|
||||
- elsif create_mr_button?
|
||||
= link_to "Create Merge Request", create_mr_path, class: 'prepend-left-10 btn'
|
||||
= link_to "Create merge request", create_mr_path, class: 'prepend-left-10 btn'
|
||||
|
|
|
@ -8,7 +8,4 @@
|
|||
|
||||
#environments-folder-list-view{ data: { "can-create-deployment" => can?(current_user, :create_deployment, @project).to_s,
|
||||
"can-read-environment" => can?(current_user, :read_environment, @project).to_s,
|
||||
"css-class" => container_class,
|
||||
"commit-icon-svg" => custom_icon("icon_commit"),
|
||||
"terminal-icon-svg" => custom_icon("icon_terminal"),
|
||||
"play-icon-svg" => custom_icon("icon_play") } }
|
||||
"css-class" => container_class } }
|
||||
|
|
|
@ -22,4 +22,4 @@
|
|||
%p
|
||||
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork", class: "btn" do
|
||||
%i.fa.fa-code-fork
|
||||
Try to Fork again
|
||||
Try to fork again
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
.email-modal-input-group.input-group
|
||||
= text_field_tag :issue_email, email, class: "monospace js-select-on-focus form-control", readonly: true
|
||||
.input-group-btn
|
||||
= clipboard_button(clipboard_target: '#issue_email')
|
||||
= clipboard_button(target: '#issue_email')
|
||||
%p
|
||||
The subject will be used as the title of the new issue, and the message will be the description.
|
||||
|
||||
|
|
|
@ -24,9 +24,9 @@
|
|||
issue: { assignee_id: issues_finder.assignee.try(:id),
|
||||
milestone_id: issues_finder.milestones.first.try(:id) }),
|
||||
class: "btn btn-new",
|
||||
title: "New Issue",
|
||||
title: "New issue",
|
||||
id: "new_issue_link" do
|
||||
New Issue
|
||||
New issue
|
||||
= render 'shared/issuable/search_bar', type: :issues
|
||||
|
||||
.issues-holder
|
||||
|
|
|
@ -53,5 +53,6 @@
|
|||
|
||||
:javascript
|
||||
var merge_request = new MergeRequest({
|
||||
action: "#{(@show_changes_tab ? 'new/diffs' : 'new')}"
|
||||
action: "#{(@show_changes_tab ? 'new/diffs' : 'new')}",
|
||||
setUrl: false,
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
%p
|
||||
%strong Step 1.
|
||||
Fetch and check out the branch for this merge request
|
||||
= clipboard_button(clipboard_target: "pre#merge-info-1", title: "Copy commands to clipboard")
|
||||
= clipboard_button(target: "pre#merge-info-1", title: "Copy commands to clipboard")
|
||||
%pre.dark#merge-info-1
|
||||
- if @merge_request.for_fork?
|
||||
:preserve
|
||||
|
@ -25,7 +25,7 @@
|
|||
%p
|
||||
%strong Step 3.
|
||||
Merge the branch and fix any conflicts that come up
|
||||
= clipboard_button(clipboard_target: "pre#merge-info-3", title: "Copy commands to clipboard")
|
||||
= clipboard_button(target: "pre#merge-info-3", title: "Copy commands to clipboard")
|
||||
%pre.dark#merge-info-3
|
||||
- if @merge_request.for_fork?
|
||||
:preserve
|
||||
|
@ -38,7 +38,7 @@
|
|||
%p
|
||||
%strong Step 4.
|
||||
Push the result of the merge to GitLab
|
||||
= clipboard_button(clipboard_target: "pre#merge-info-4", title: "Copy commands to clipboard")
|
||||
= clipboard_button(target: "pre#merge-info-4", title: "Copy commands to clipboard")
|
||||
%pre.dark#merge-info-4
|
||||
:preserve
|
||||
git push origin #{h @merge_request.target_branch}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
- if can_remove_source_branch
|
||||
= link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), remote: true, method: :delete, class: "btn btn-default remove_source_branch" do
|
||||
= icon('trash-o')
|
||||
Remove Source Branch
|
||||
Remove source branch
|
||||
- if mr_can_be_reverted
|
||||
= revert_commit_link(@merge_request.merge_commit, namespace_project_merge_request_path(@project.namespace, @project, @merge_request), btn_class: "close")
|
||||
- if mr_can_be_cherry_picked
|
||||
|
|
|
@ -10,24 +10,24 @@
|
|||
- if @pipeline && @pipeline.active?
|
||||
%span.btn-group
|
||||
= button_tag class: "btn btn-info js-merge-when-pipeline-succeeds-button merge-when-pipeline-succeeds" do
|
||||
Merge When Pipeline Succeeds
|
||||
Merge when pipeline succeeds
|
||||
- unless @project.only_allow_merge_if_pipeline_succeeds?
|
||||
= button_tag class: "btn btn-info dropdown-toggle", 'data-toggle' => 'dropdown' do
|
||||
= icon('caret-down')
|
||||
%span.sr-only
|
||||
Select Merge Moment
|
||||
Select merge moment
|
||||
%ul.js-merge-dropdown.dropdown-menu.dropdown-menu-right{ role: 'menu' }
|
||||
%li
|
||||
= link_to "#", class: "merge_when_pipeline_succeeds" do
|
||||
= icon('check fw')
|
||||
Merge When Pipeline Succeeds
|
||||
Merge when pipeline succeeds
|
||||
%li
|
||||
= link_to "#", class: "accept-merge-request" do
|
||||
= icon('warning fw')
|
||||
Merge Immediately
|
||||
Merge immediately
|
||||
- else
|
||||
= f.button class: "btn btn-grouped js-merge-button accept-merge-request" do
|
||||
Accept Merge Request
|
||||
Accept merge request
|
||||
- if @merge_request.force_remove_source_branch?
|
||||
.accept-control
|
||||
The source branch will be removed.
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
- if remove_source_branch_button
|
||||
= link_to merge_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, merge_params(@merge_request)), remote: true, method: :post, class: "btn btn-grouped btn-primary btn-sm remove_source_branch" do
|
||||
= icon('times')
|
||||
Remove Source Branch When Merged
|
||||
Remove source branch when merged
|
||||
|
||||
- if user_can_cancel_automatic_merge
|
||||
= link_to cancel_merge_when_pipeline_succeeds_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request), remote: true, method: :post, class: "btn btn-grouped btn-sm" do
|
||||
Cancel Automatic Merge
|
||||
Cancel automatic merge
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
.nav-controls
|
||||
= render 'shared/milestones_sort_dropdown'
|
||||
- if can?(current_user, :admin_milestone, @project)
|
||||
= link_to new_namespace_project_milestone_path(@project.namespace, @project), class: 'btn btn-new', title: 'New Milestone' do
|
||||
New Milestone
|
||||
= link_to new_namespace_project_milestone_path(@project.namespace, @project), class: 'btn btn-new', title: 'New milestone' do
|
||||
New milestone
|
||||
|
||||
.milestones
|
||||
%ul.content-list
|
||||
|
|
|
@ -23,9 +23,9 @@
|
|||
.milestone-buttons
|
||||
- if can?(current_user, :admin_milestone, @project)
|
||||
- if @milestone.active?
|
||||
= link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-nr btn-grouped"
|
||||
= link_to 'Close milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-nr btn-grouped"
|
||||
- else
|
||||
= link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-nr btn-grouped"
|
||||
= link_to 'Reopen milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-nr btn-grouped"
|
||||
|
||||
= link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped btn-nr" do
|
||||
Edit
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
.note-form-actions.clearfix
|
||||
.settings-message.note-edit-warning.js-edit-warning
|
||||
Finish editing this message first!
|
||||
= submit_tag 'Save Comment', class: 'btn btn-nr btn-save js-comment-button'
|
||||
= submit_tag 'Save comment', class: 'btn btn-nr btn-save js-comment-button'
|
||||
%button.btn.btn-nr.btn-cancel.note-edit-cancel{ type: 'button' }
|
||||
Cancel
|
||||
|
|
|
@ -5,8 +5,11 @@
|
|||
%li.timeline-entry{ id: dom_id(note), class: ["note", "note-row-#{note.id}", ('system-note' if note.system)], data: {author_id: note.author.id, editable: note_editable, note_id: note.id} }
|
||||
.timeline-entry-inner
|
||||
.timeline-icon
|
||||
%a{ href: user_path(note.author) }
|
||||
= image_tag avatar_icon(note.author), alt: '', class: 'avatar s40'
|
||||
- if note.system
|
||||
= icon_for_system_note(note)
|
||||
- else
|
||||
%a{ href: user_path(note.author) }
|
||||
= image_tag avatar_icon(note.author), alt: '', class: 'avatar s40'
|
||||
.timeline-content
|
||||
.note-header
|
||||
%a.visible-xs{ href: user_path(note.author) }
|
||||
|
|
|
@ -46,4 +46,4 @@
|
|||
\...
|
||||
%span.js-details-content.hide
|
||||
= link_to @pipeline.sha, namespace_project_commit_path(@project.namespace, @project, @pipeline.sha), class: "monospace commit-hash-full"
|
||||
= clipboard_button(clipboard_text: @pipeline.sha, title: "Copy commit SHA to clipboard")
|
||||
= clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
%tr.tag
|
||||
%td
|
||||
= escape_once(tag.name)
|
||||
= clipboard_button(clipboard_text: "docker pull #{tag.path}")
|
||||
= clipboard_button(text: "docker pull #{tag.path}")
|
||||
%td
|
||||
- if tag.revision
|
||||
%span.has-tooltip{ title: "#{tag.revision}" }
|
||||
|
|
|
@ -22,14 +22,14 @@
|
|||
.col-sm-10.col-xs-12.input-group
|
||||
= text_field_tag :display_name, "GitLab / #{@project.name_with_namespace}", class: 'form-control input-sm', readonly: 'readonly'
|
||||
.input-group-btn
|
||||
= clipboard_button(clipboard_target: '#display_name')
|
||||
= clipboard_button(target: '#display_name')
|
||||
|
||||
.form-group
|
||||
= label_tag :description, 'Description', class: 'col-sm-2 col-xs-12 control-label'
|
||||
.col-sm-10.col-xs-12.input-group
|
||||
= text_field_tag :description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
|
||||
.input-group-btn
|
||||
= clipboard_button(clipboard_target: '#description')
|
||||
= clipboard_button(target: '#description')
|
||||
|
||||
.form-group
|
||||
= label_tag nil, 'Command trigger word', class: 'col-sm-2 col-xs-12 control-label'
|
||||
|
@ -46,7 +46,7 @@
|
|||
.col-sm-10.col-xs-12.input-group
|
||||
= text_field_tag :request_url, service_trigger_url(subject), class: 'form-control input-sm', readonly: 'readonly'
|
||||
.input-group-btn
|
||||
= clipboard_button(clipboard_target: '#request_url')
|
||||
= clipboard_button(target: '#request_url')
|
||||
|
||||
.form-group
|
||||
= label_tag nil, 'Request method', class: 'col-sm-2 col-xs-12 control-label'
|
||||
|
@ -57,14 +57,14 @@
|
|||
.col-sm-10.col-xs-12.input-group
|
||||
= text_field_tag :response_username, 'GitLab', class: 'form-control input-sm', readonly: 'readonly'
|
||||
.input-group-btn
|
||||
= clipboard_button(clipboard_target: '#response_username')
|
||||
= clipboard_button(target: '#response_username')
|
||||
|
||||
.form-group
|
||||
= label_tag :response_icon, 'Response icon', class: 'col-sm-2 col-xs-12 control-label'
|
||||
.col-sm-10.col-xs-12.input-group
|
||||
= text_field_tag :response_icon, asset_url('gitlab_logo.png'), class: 'form-control input-sm', readonly: 'readonly'
|
||||
.input-group-btn
|
||||
= clipboard_button(clipboard_target: '#response_icon')
|
||||
= clipboard_button(target: '#response_icon')
|
||||
|
||||
.form-group
|
||||
= label_tag nil, 'Autocomplete', class: 'col-sm-2 col-xs-12 control-label'
|
||||
|
@ -75,14 +75,14 @@
|
|||
.col-sm-10.col-xs-12.input-group
|
||||
= text_field_tag :autocomplete_hint, '[help]', class: 'form-control input-sm', readonly: 'readonly'
|
||||
.input-group-btn
|
||||
= clipboard_button(clipboard_target: '#autocomplete_hint')
|
||||
= clipboard_button(target: '#autocomplete_hint')
|
||||
|
||||
.form-group
|
||||
= label_tag :autocomplete_description, 'Autocomplete description', class: 'col-sm-2 col-xs-12 control-label'
|
||||
.col-sm-10.col-xs-12.input-group
|
||||
= text_field_tag :autocomplete_description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
|
||||
.input-group-btn
|
||||
= clipboard_button(clipboard_target: '#autocomplete_description')
|
||||
= clipboard_button(target: '#autocomplete_description')
|
||||
|
||||
%hr
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
.col-sm-10.col-xs-12.input-group
|
||||
= text_field_tag :url, service_trigger_url(subject), class: 'form-control input-sm', readonly: 'readonly'
|
||||
.input-group-btn
|
||||
= clipboard_button(clipboard_target: '#url')
|
||||
= clipboard_button(target: '#url')
|
||||
|
||||
.form-group
|
||||
= label_tag nil, 'Method', class: 'col-sm-2 col-xs-12 control-label'
|
||||
|
@ -51,7 +51,7 @@
|
|||
.col-sm-10.col-xs-12.input-group
|
||||
= text_field_tag :customize_name, 'GitLab', class: 'form-control input-sm', readonly: 'readonly'
|
||||
.input-group-btn
|
||||
= clipboard_button(clipboard_target: '#customize_name')
|
||||
= clipboard_button(target: '#customize_name')
|
||||
|
||||
.form-group
|
||||
= label_tag nil, 'Customize icon', class: 'col-sm-2 col-xs-12 control-label'
|
||||
|
@ -68,21 +68,21 @@
|
|||
.col-sm-10.col-xs-12.input-group
|
||||
= text_field_tag :autocomplete_description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
|
||||
.input-group-btn
|
||||
= clipboard_button(clipboard_target: '#autocomplete_description')
|
||||
= clipboard_button(target: '#autocomplete_description')
|
||||
|
||||
.form-group
|
||||
= label_tag :autocomplete_usage_hint, 'Autocomplete usage hint', class: 'col-sm-2 col-xs-12 control-label'
|
||||
.col-sm-10.col-xs-12.input-group
|
||||
= text_field_tag :autocomplete_usage_hint, '[help]', class: 'form-control input-sm', readonly: 'readonly'
|
||||
.input-group-btn
|
||||
= clipboard_button(clipboard_target: '#autocomplete_usage_hint')
|
||||
= clipboard_button(target: '#autocomplete_usage_hint')
|
||||
|
||||
.form-group
|
||||
= label_tag :descriptive_label, 'Descriptive label', class: 'col-sm-2 col-xs-12 control-label'
|
||||
.col-sm-10.col-xs-12.input-group
|
||||
= text_field_tag :descriptive_label, 'Perform common operations on GitLab project', class: 'form-control input-sm', readonly: 'readonly'
|
||||
.input-group-btn
|
||||
= clipboard_button(clipboard_target: '#descriptive_label')
|
||||
= clipboard_button(target: '#descriptive_label')
|
||||
|
||||
%hr
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
%i.fa.fa-angle-right
|
||||
%small.light
|
||||
= link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace"
|
||||
= clipboard_button(clipboard_text: @commit.id, title: "Copy commit SHA to clipboard")
|
||||
= clipboard_button(text: @commit.id, title: "Copy commit SHA to clipboard")
|
||||
= time_ago_with_tooltip(@commit.committed_date)
|
||||
\-
|
||||
= @commit.full_title
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
%td
|
||||
- if can?(current_user, :admin_trigger, trigger)
|
||||
%span= trigger.token
|
||||
= clipboard_button(clipboard_text: trigger.token, title: "Copy trigger token to clipboard")
|
||||
= clipboard_button(text: trigger.token, title: "Copy trigger token to clipboard")
|
||||
- else
|
||||
%span= trigger.short_token
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
- if (@page && @page.persisted?)
|
||||
- if can?(current_user, :create_wiki, @project)
|
||||
= link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
|
||||
New Page
|
||||
New page
|
||||
= link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn" do
|
||||
Page History
|
||||
Page history
|
||||
- if can?(current_user, :create_wiki, @project) && @page.latest?
|
||||
= link_to namespace_project_wiki_edit_path(@project.namespace, @project, @page), class: "btn" do
|
||||
Edit
|
||||
|
|
|
@ -18,4 +18,4 @@
|
|||
Tip: You can specify the full path for the new file.
|
||||
We will automatically create any missing directories.
|
||||
.form-actions
|
||||
= button_tag 'Create Page', class: 'build-new-wiki btn btn-create'
|
||||
= button_tag 'Create page', class: 'build-new-wiki btn btn-create'
|
||||
|
|
|
@ -22,10 +22,10 @@
|
|||
.nav-controls
|
||||
- if can?(current_user, :create_wiki, @project)
|
||||
= link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
|
||||
New Page
|
||||
New page
|
||||
- if @page.persisted?
|
||||
= link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn" do
|
||||
Page History
|
||||
Page history
|
||||
- if can?(current_user, :admin_wiki, @project)
|
||||
= link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-danger" do
|
||||
Delete
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
= text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true
|
||||
.input-group-btn
|
||||
= clipboard_button(clipboard_target: '#project_clone', title: "Copy URL to clipboard")
|
||||
= clipboard_button(target: '#project_clone', title: "Copy URL to clipboard")
|
||||
|
||||
:javascript
|
||||
$('ul.clone-options-dropdown a').on('click',function(e){
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue