fix pipelines/index.html.haml merge conflict
This commit is contained in:
commit
0a074f2e09
473 changed files with 3660 additions and 1563 deletions
|
@ -425,7 +425,7 @@ notify:slack:
|
|||
SETUP_DB: "false"
|
||||
USE_BUNDLE_INSTALL: "false"
|
||||
script:
|
||||
- ./scripts/notify_slack.sh "#development" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>"
|
||||
- ./scripts/notify_slack.sh "#development" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/pipelines>"
|
||||
when: on_failure
|
||||
only:
|
||||
- master@gitlab-org/gitlab-ce
|
||||
|
|
|
@ -7,10 +7,10 @@ exclude:
|
|||
|
||||
linters:
|
||||
AltText:
|
||||
enabled: false
|
||||
enabled: true
|
||||
|
||||
ClassAttributeWithStaticValue:
|
||||
enabled: false
|
||||
enabled: true
|
||||
|
||||
ClassesBeforeIds:
|
||||
enabled: false
|
||||
|
@ -29,14 +29,14 @@ linters:
|
|||
enabled: true
|
||||
|
||||
FinalNewline:
|
||||
enabled: false
|
||||
enabled: true
|
||||
present: true
|
||||
|
||||
HtmlAttributes:
|
||||
enabled: false
|
||||
enabled: true
|
||||
|
||||
ImplicitDiv:
|
||||
enabled: false
|
||||
enabled: true
|
||||
|
||||
LeadingCommentSpace:
|
||||
enabled: false
|
||||
|
@ -80,10 +80,10 @@ linters:
|
|||
enabled: false
|
||||
|
||||
SpaceBeforeScript:
|
||||
enabled: false
|
||||
enabled: true
|
||||
|
||||
SpaceInsideHashAttributes:
|
||||
enabled: false
|
||||
enabled: true
|
||||
style: space
|
||||
|
||||
Indentation:
|
||||
|
@ -94,7 +94,7 @@ linters:
|
|||
enabled: true
|
||||
|
||||
TrailingWhitespace:
|
||||
enabled: false
|
||||
enabled: true
|
||||
|
||||
UnnecessaryInterpolation:
|
||||
enabled: false
|
||||
|
|
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -2,6 +2,22 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 8.15.2 (2016-12-27)
|
||||
|
||||
- Fix finding the latest pipeline. !8301
|
||||
- Fix mr list timestamp alignment. !8271
|
||||
- Fix discussion overlap text in regular screens. !8273
|
||||
- Fixes mini-pipeline-graph dropdown animation and stage position in chrome, firefox and safari. !8282
|
||||
- Fix line breaking in nodes of the pipeline graph in firefox. !8292
|
||||
- Fixes confendential warning text alignment. !8293
|
||||
- Hide Scroll Top button for failed build page. !8295
|
||||
- Fix finding the latest pipeline. !8301
|
||||
- Disable PostgreSQL statement timeouts when removing unneeded services. !8322
|
||||
- Fix timeout when MR contains large files marked as binary by .gitattributes.
|
||||
- Rename "autodeploy" to "auto deploy".
|
||||
- Fixed GFM autocomplete error when no data exists.
|
||||
- Fixed resolve discussion note button color.
|
||||
|
||||
## 8.15.1 (2016-12-23)
|
||||
|
||||
- Push payloads schedule at most 100 commits, instead of all commits.
|
||||
|
|
|
@ -217,8 +217,8 @@ We welcome merge requests with fixes and improvements to GitLab code, tests,
|
|||
and/or documentation. The features we would really like a merge request for are
|
||||
listed with the label [`Accepting Merge Requests` on our issue tracker for CE][accepting-mrs-ce]
|
||||
and [EE][accepting-mrs-ee] but other improvements are also welcome. Please note
|
||||
that if an issue is marked for the current milestone either before or while you
|
||||
are working on it, a team member may take over the merge request in order to
|
||||
that if an issue is marked for the current milestone either before or while you
|
||||
are working on it, a team member may take over the merge request in order to
|
||||
ensure the work is finished before the release date.
|
||||
|
||||
If you want to add a new feature that is not labeled it is best to first create
|
||||
|
@ -300,6 +300,7 @@ you start with a very simple UI? Can you do part of the refactor? The increased
|
|||
reviewability of small MRs that leads to higher code quality is more important
|
||||
to us than having a minimal commit log. The smaller an MR is the more likely it
|
||||
is it will be merged (quickly). After that you can send more MRs to enhance it.
|
||||
The ['How to get faster PR reviews' document of Kubernetes](https://github.com/kubernetes/community/blob/master/contributors/devel/faster_reviews.md) also has some great points regarding this.
|
||||
|
||||
For examples of feedback on merge requests please look at already
|
||||
[closed merge requests][closed-merge-requests]. If you would like quick feedback
|
||||
|
|
4
Gemfile
4
Gemfile
|
@ -332,7 +332,7 @@ gem 'octokit', '~> 4.3.0'
|
|||
|
||||
gem 'mail_room', '~> 0.9.0'
|
||||
|
||||
gem 'email_reply_parser', '~> 0.5.8'
|
||||
gem 'email_reply_trimmer', '~> 0.1'
|
||||
gem 'html2text'
|
||||
|
||||
gem 'ruby-prof', '~> 0.16.2'
|
||||
|
@ -347,5 +347,5 @@ gem 'paranoia', '~> 2.2'
|
|||
gem 'health_check', '~> 2.2.0'
|
||||
|
||||
# System information
|
||||
gem 'vmstat', '~> 2.2'
|
||||
gem 'vmstat', '~> 2.3.0'
|
||||
gem 'sys-filesystem', '~> 1.1.6'
|
||||
|
|
|
@ -167,7 +167,7 @@ GEM
|
|||
railties (>= 4.2)
|
||||
dropzonejs-rails (0.7.2)
|
||||
rails (> 3.1)
|
||||
email_reply_parser (0.5.8)
|
||||
email_reply_trimmer (0.1.6)
|
||||
email_spec (1.6.0)
|
||||
launchy (~> 2.1)
|
||||
mail (~> 2.2)
|
||||
|
@ -773,7 +773,7 @@ GEM
|
|||
coercible (~> 1.0)
|
||||
descendants_tracker (~> 0.0, >= 0.0.3)
|
||||
equalizer (~> 0.0, >= 0.0.9)
|
||||
vmstat (2.2.0)
|
||||
vmstat (2.3.0)
|
||||
warden (1.2.6)
|
||||
rack (>= 1.0)
|
||||
web-console (2.3.0)
|
||||
|
@ -839,7 +839,7 @@ DEPENDENCIES
|
|||
diffy (~> 3.1.0)
|
||||
doorkeeper (~> 4.2.0)
|
||||
dropzonejs-rails (~> 0.7.1)
|
||||
email_reply_parser (~> 0.5.8)
|
||||
email_reply_trimmer (~> 0.1)
|
||||
email_spec (~> 1.6.0)
|
||||
factory_girl_rails (~> 4.7.0)
|
||||
ffaker (~> 2.0.0)
|
||||
|
@ -982,7 +982,7 @@ DEPENDENCIES
|
|||
unicorn-worker-killer (~> 0.4.4)
|
||||
version_sorter (~> 2.1.0)
|
||||
virtus (~> 1.0.1)
|
||||
vmstat (~> 2.2)
|
||||
vmstat (~> 2.3.0)
|
||||
web-console (~> 2.0)
|
||||
webmock (~> 1.21.0)
|
||||
wikicloth (= 0.8.1)
|
||||
|
|
|
@ -69,7 +69,8 @@ to add details to the issue.
|
|||
- ~UX needs help from a UX designer
|
||||
- ~Frontend needs help from a Front-end engineer. Please follow the
|
||||
["Implement design & UI elements" guidelines].
|
||||
- ~up-for-grabs is an issue suitable for first-time contributors, of reasonable difficulty and size. Not exclusive with other labels.
|
||||
- ~"Accepting Merge Requests" is a low priority, well-defined issue that we
|
||||
encourage people to contribute to. Not exclusive with other labels.
|
||||
- ~"feature proposal" is a proposal for a new feature for GitLab. People are encouraged to vote
|
||||
in support or comment for further detail. Do not use `feature request`.
|
||||
- ~bug is an issue reporting undesirable or incorrect behavior.
|
||||
|
|
|
@ -89,6 +89,14 @@
|
|||
// Set the default path for all cookies to GitLab's root directory
|
||||
Cookies.defaults.path = gon.relative_url_root || '/';
|
||||
|
||||
// `hashchange` is not triggered when link target is already in window.location
|
||||
$body.on('click', 'a[href^="#"]', function() {
|
||||
var href = this.getAttribute('href');
|
||||
if (href.substr(1) === gl.utils.getLocationHash()) {
|
||||
setTimeout(gl.utils.handleLocationHash, 1);
|
||||
}
|
||||
});
|
||||
|
||||
// prevent default action for disabled buttons
|
||||
$('.btn').click(function(e) {
|
||||
if ($(this).hasClass('disabled')) {
|
||||
|
|
|
@ -73,7 +73,7 @@ $(() => {
|
|||
});
|
||||
|
||||
gl.IssueBoardsSearch = new Vue({
|
||||
el: '#js-boards-seach',
|
||||
el: '#js-boards-search',
|
||||
data: {
|
||||
filters: Store.state.filters
|
||||
},
|
||||
|
|
|
@ -71,3 +71,5 @@ class ListIssue {
|
|||
return Vue.http.patch(url, data);
|
||||
}
|
||||
}
|
||||
|
||||
window.ListIssue = ListIssue;
|
||||
|
|
|
@ -10,3 +10,5 @@ class ListLabel {
|
|||
this.priority = (obj.priority !== null) ? obj.priority : Infinity;
|
||||
}
|
||||
}
|
||||
|
||||
window.ListLabel = ListLabel;
|
||||
|
|
|
@ -148,3 +148,5 @@ class List {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.List = List;
|
||||
|
|
|
@ -6,3 +6,5 @@ class ListMilestone {
|
|||
this.title = obj.title;
|
||||
}
|
||||
}
|
||||
|
||||
window.ListMilestone = ListMilestone;
|
||||
|
|
|
@ -8,3 +8,5 @@ class ListUser {
|
|||
this.avatar = user.avatar_url;
|
||||
}
|
||||
}
|
||||
|
||||
window.ListUser = ListUser;
|
||||
|
|
|
@ -65,4 +65,6 @@ class BoardService {
|
|||
issue
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
window.BoardService = BoardService;
|
||||
|
|
|
@ -92,8 +92,8 @@
|
|||
success: function(buildData) {
|
||||
$('.js-build-output').html(buildData.trace_html);
|
||||
if (removeRefreshStatuses.indexOf(buildData.status) >= 0) {
|
||||
this.initScrollMonitor();
|
||||
return this.$buildRefreshAnimation.remove();
|
||||
this.$buildRefreshAnimation.remove();
|
||||
return this.initScrollMonitor();
|
||||
}
|
||||
}.bind(this)
|
||||
});
|
||||
|
|
|
@ -59,9 +59,11 @@
|
|||
},
|
||||
methods: {
|
||||
updateTooltip: function () {
|
||||
$(this.$refs.button)
|
||||
.tooltip('hide')
|
||||
.tooltip('fixTitle');
|
||||
this.$nextTick(() => {
|
||||
$(this.$refs.button)
|
||||
.tooltip('hide')
|
||||
.tooltip('fixTitle');
|
||||
});
|
||||
},
|
||||
resolve: function () {
|
||||
if (!this.canResolve) return;
|
||||
|
@ -90,7 +92,7 @@
|
|||
new Flash('An error occurred when trying to resolve a comment. Please try again.', 'alert');
|
||||
}
|
||||
|
||||
this.$nextTick(this.updateTooltip);
|
||||
this.updateTooltip();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -92,3 +92,5 @@ class DiscussionModel {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
window.DiscussionModel = DiscussionModel;
|
||||
|
|
|
@ -9,3 +9,5 @@ class NoteModel {
|
|||
this.resolved_by = resolved_by;
|
||||
}
|
||||
}
|
||||
|
||||
window.NoteModel = NoteModel;
|
||||
|
|
|
@ -64,6 +64,17 @@
|
|||
new UsernameValidator();
|
||||
new ActiveTabMemoizer();
|
||||
break;
|
||||
case 'sessions:create':
|
||||
if (!gon.u2f) break;
|
||||
window.gl.u2fAuthenticate = new gl.U2FAuthenticate(
|
||||
$("#js-authenticate-u2f"),
|
||||
'#js-login-u2f-form',
|
||||
gon.u2f,
|
||||
document.querySelector('#js-login-2fa-device'),
|
||||
document.querySelector('.js-2fa-form'),
|
||||
);
|
||||
window.gl.u2fAuthenticate.start();
|
||||
break;
|
||||
case 'projects:boards:show':
|
||||
case 'projects:boards:index':
|
||||
shortcut_handler = new ShortcutsNavigation();
|
||||
|
|
|
@ -20,3 +20,5 @@ class EnvironmentsService {
|
|||
return this.environments.get();
|
||||
}
|
||||
}
|
||||
|
||||
window.EnvironmentsService = EnvironmentsService;
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
var _a, _y, regexp, match, atSymbolsWithBar, atSymbolsWithoutBar;
|
||||
atSymbolsWithBar = Object.keys(this.app.controllers).join('|');
|
||||
atSymbolsWithoutBar = Object.keys(this.app.controllers).join('');
|
||||
subtext = subtext.split(' ').pop();
|
||||
subtext = subtext.split(/\s+/g).pop();
|
||||
flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
||||
|
||||
_a = decodeURI("%C3%80");
|
||||
|
@ -367,7 +367,7 @@
|
|||
return $input.trigger('keyup');
|
||||
},
|
||||
isLoading(data) {
|
||||
if (!data) return false;
|
||||
if (!data || !data.length) return false;
|
||||
if (Array.isArray(data)) data = data[0];
|
||||
return data === this.defaultLoadingData[0] || data.name === this.defaultLoadingData[0];
|
||||
},
|
||||
|
|
|
@ -139,15 +139,12 @@
|
|||
return;
|
||||
}
|
||||
return $.getJSON($container.data('path')).error(function() {
|
||||
$container.find('.checking').hide();
|
||||
$container.find('.unavailable').show();
|
||||
return new Flash('Failed to check if a new branch can be created.', 'alert');
|
||||
}).success(function(data) {
|
||||
if (data.can_create_branch) {
|
||||
$container.find('.checking').hide();
|
||||
$container.find('.available').show();
|
||||
} else {
|
||||
$container.find('.checking').hide();
|
||||
return $container.find('.unavailable').show();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, wrap-iife, no-else-return, consistent-return, object-shorthand, comma-dangle, no-param-reassign, padded-blocks, camelcase, prefer-arrow-callback, max-len */
|
||||
/* eslint-disable func-names, no-var, object-shorthand, comma-dangle, prefer-arrow-callback */
|
||||
|
||||
// MarkdownPreview
|
||||
//
|
||||
// Handles toggling the "Write" and "Preview" tab clicks, rendering the preview,
|
||||
// and showing a warning when more than `x` users are referenced.
|
||||
//
|
||||
(function() {
|
||||
var lastTextareaPreviewed, markdownPreview, previewButtonSelector, writeButtonSelector;
|
||||
(function () {
|
||||
var lastTextareaPreviewed;
|
||||
var markdownPreview;
|
||||
var previewButtonSelector;
|
||||
var writeButtonSelector;
|
||||
|
||||
window.MarkdownPreview = (function() {
|
||||
window.MarkdownPreview = (function () {
|
||||
function MarkdownPreview() {}
|
||||
|
||||
// Minimum number of users referenced before triggering a warning
|
||||
|
@ -16,73 +19,71 @@
|
|||
|
||||
MarkdownPreview.prototype.ajaxCache = {};
|
||||
|
||||
MarkdownPreview.prototype.showPreview = function(form) {
|
||||
var mdText, preview;
|
||||
preview = form.find('.js-md-preview');
|
||||
mdText = form.find('textarea.markdown-area').val();
|
||||
MarkdownPreview.prototype.showPreview = function ($form) {
|
||||
var mdText;
|
||||
var preview = $form.find('.js-md-preview');
|
||||
if (preview.hasClass('md-preview-loading')) {
|
||||
return;
|
||||
}
|
||||
mdText = $form.find('textarea.markdown-area').val();
|
||||
|
||||
if (mdText.trim().length === 0) {
|
||||
preview.text('Nothing to preview.');
|
||||
return this.hideReferencedUsers(form);
|
||||
this.hideReferencedUsers($form);
|
||||
} else {
|
||||
preview.text('Loading...');
|
||||
return this.renderMarkdown(mdText, (function(_this) {
|
||||
return function(response) {
|
||||
preview.html(response.body);
|
||||
preview.renderGFM();
|
||||
return _this.renderReferencedUsers(response.references.users, form);
|
||||
};
|
||||
})(this));
|
||||
preview.addClass('md-preview-loading').text('Loading...');
|
||||
this.fetchMarkdownPreview(mdText, (function (response) {
|
||||
preview.removeClass('md-preview-loading').html(response.body);
|
||||
preview.renderGFM();
|
||||
this.renderReferencedUsers(response.references.users, $form);
|
||||
}).bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
MarkdownPreview.prototype.renderMarkdown = function(text, success) {
|
||||
MarkdownPreview.prototype.fetchMarkdownPreview = function (text, success) {
|
||||
if (!window.preview_markdown_path) {
|
||||
return;
|
||||
}
|
||||
if (text === this.ajaxCache.text) {
|
||||
return success(this.ajaxCache.response);
|
||||
success(this.ajaxCache.response);
|
||||
return;
|
||||
}
|
||||
return $.ajax({
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: window.preview_markdown_path,
|
||||
data: {
|
||||
text: text
|
||||
},
|
||||
dataType: 'json',
|
||||
success: (function(_this) {
|
||||
return function(response) {
|
||||
_this.ajaxCache = {
|
||||
text: text,
|
||||
response: response
|
||||
};
|
||||
return success(response);
|
||||
success: (function (response) {
|
||||
this.ajaxCache = {
|
||||
text: text,
|
||||
response: response
|
||||
};
|
||||
})(this)
|
||||
success(response);
|
||||
}).bind(this)
|
||||
});
|
||||
};
|
||||
|
||||
MarkdownPreview.prototype.hideReferencedUsers = function(form) {
|
||||
var referencedUsers;
|
||||
referencedUsers = form.find('.referenced-users');
|
||||
return referencedUsers.hide();
|
||||
MarkdownPreview.prototype.hideReferencedUsers = function ($form) {
|
||||
$form.find('.referenced-users').hide();
|
||||
};
|
||||
|
||||
MarkdownPreview.prototype.renderReferencedUsers = function(users, form) {
|
||||
MarkdownPreview.prototype.renderReferencedUsers = function (users, $form) {
|
||||
var referencedUsers;
|
||||
referencedUsers = form.find('.referenced-users');
|
||||
referencedUsers = $form.find('.referenced-users');
|
||||
if (referencedUsers.length) {
|
||||
if (users.length >= this.referenceThreshold) {
|
||||
referencedUsers.show();
|
||||
return referencedUsers.find('.js-referenced-users-count').text(users.length);
|
||||
referencedUsers.find('.js-referenced-users-count').text(users.length);
|
||||
} else {
|
||||
return referencedUsers.hide();
|
||||
referencedUsers.hide();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return MarkdownPreview;
|
||||
|
||||
})();
|
||||
}());
|
||||
|
||||
markdownPreview = new window.MarkdownPreview();
|
||||
|
||||
|
@ -92,19 +93,14 @@
|
|||
|
||||
lastTextareaPreviewed = null;
|
||||
|
||||
$.fn.setupMarkdownPreview = function() {
|
||||
var $form, form_textarea;
|
||||
$form = $(this);
|
||||
form_textarea = $form.find('textarea.markdown-area');
|
||||
form_textarea.on('input', function() {
|
||||
return markdownPreview.hideReferencedUsers($form);
|
||||
});
|
||||
return form_textarea.on('blur', function() {
|
||||
return markdownPreview.showPreview($form);
|
||||
$.fn.setupMarkdownPreview = function () {
|
||||
var $form = $(this);
|
||||
$form.find('textarea.markdown-area').on('input', function () {
|
||||
markdownPreview.hideReferencedUsers($form);
|
||||
});
|
||||
};
|
||||
|
||||
$(document).on('markdown-preview:show', function(e, $form) {
|
||||
$(document).on('markdown-preview:show', function (e, $form) {
|
||||
if (!$form) {
|
||||
return;
|
||||
}
|
||||
|
@ -115,10 +111,10 @@
|
|||
// toggle content
|
||||
$form.find('.md-write-holder').hide();
|
||||
$form.find('.md-preview-holder').show();
|
||||
return markdownPreview.showPreview($form);
|
||||
markdownPreview.showPreview($form);
|
||||
});
|
||||
|
||||
$(document).on('markdown-preview:hide', function(e, $form) {
|
||||
$(document).on('markdown-preview:hide', function (e, $form) {
|
||||
if (!$form) {
|
||||
return;
|
||||
}
|
||||
|
@ -129,34 +125,33 @@
|
|||
// toggle content
|
||||
$form.find('.md-write-holder').show();
|
||||
$form.find('textarea.markdown-area').focus();
|
||||
return $form.find('.md-preview-holder').hide();
|
||||
$form.find('.md-preview-holder').hide();
|
||||
});
|
||||
|
||||
$(document).on('markdown-preview:toggle', function(e, keyboardEvent) {
|
||||
$(document).on('markdown-preview:toggle', function (e, keyboardEvent) {
|
||||
var $target;
|
||||
$target = $(keyboardEvent.target);
|
||||
if ($target.is('textarea.markdown-area')) {
|
||||
$(document).triggerHandler('markdown-preview:show', [$target.closest('form')]);
|
||||
return keyboardEvent.preventDefault();
|
||||
keyboardEvent.preventDefault();
|
||||
} else if (lastTextareaPreviewed) {
|
||||
$target = lastTextareaPreviewed;
|
||||
$(document).triggerHandler('markdown-preview:hide', [$target.closest('form')]);
|
||||
return keyboardEvent.preventDefault();
|
||||
keyboardEvent.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', previewButtonSelector, function(e) {
|
||||
$(document).on('click', previewButtonSelector, function (e) {
|
||||
var $form;
|
||||
e.preventDefault();
|
||||
$form = $(this).closest('form');
|
||||
return $(document).triggerHandler('markdown-preview:show', [$form]);
|
||||
$(document).triggerHandler('markdown-preview:show', [$form]);
|
||||
});
|
||||
|
||||
$(document).on('click', writeButtonSelector, function(e) {
|
||||
$(document).on('click', writeButtonSelector, function (e) {
|
||||
var $form;
|
||||
e.preventDefault();
|
||||
$form = $(this).closest('form');
|
||||
return $(document).triggerHandler('markdown-preview:hide', [$form]);
|
||||
$(document).triggerHandler('markdown-preview:hide', [$form]);
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
}());
|
||||
|
|
|
@ -41,15 +41,12 @@
|
|||
}
|
||||
|
||||
beforeUpdateUsername() {
|
||||
$('.loading-username').show();
|
||||
$(this).find('.update-success').hide();
|
||||
return $(this).find('.update-failed').hide();
|
||||
$('.loading-username', this).removeClass('hidden');
|
||||
}
|
||||
|
||||
afterUpdateUsername() {
|
||||
$('.loading-username').hide();
|
||||
$(this).find('.btn-save').enable();
|
||||
return $(this).find('.loading-gif').hide();
|
||||
$('.loading-username', this).addClass('hidden');
|
||||
$('button[type=submit]', this).enable();
|
||||
}
|
||||
|
||||
onUpdateNotifs(e, data) {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
},
|
||||
data: function(term, callback) {
|
||||
var finalCallback, projectsCallback;
|
||||
var orderBy = $dropdown.data('order-by');
|
||||
finalCallback = function(projects) {
|
||||
return callback(projects);
|
||||
};
|
||||
|
@ -34,7 +35,7 @@
|
|||
if (this.groupId) {
|
||||
return Api.groupProjects(this.groupId, term, projectsCallback);
|
||||
} else {
|
||||
return Api.projects(term, this.orderBy, projectsCallback);
|
||||
return Api.projects(term, orderBy, projectsCallback);
|
||||
}
|
||||
},
|
||||
url: function(project) {
|
||||
|
|
|
@ -76,3 +76,5 @@ class ProtectedBranchDropdown {
|
|||
this.$dropdownFooter.toggleClass('hidden', !branchName);
|
||||
}
|
||||
}
|
||||
|
||||
window.ProtectedBranchDropdown = ProtectedBranchDropdown;
|
||||
|
|
|
@ -8,21 +8,26 @@
|
|||
// State Flow #1: setup -> in_progress -> authenticated -> POST to server
|
||||
// State Flow #2: setup -> in_progress -> error -> setup
|
||||
(function() {
|
||||
const global = window.gl || (window.gl = {});
|
||||
|
||||
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
||||
|
||||
this.U2FAuthenticate = (function() {
|
||||
function U2FAuthenticate(container, u2fParams) {
|
||||
global.U2FAuthenticate = (function() {
|
||||
function U2FAuthenticate(container, form, u2fParams, fallbackButton, fallbackUI) {
|
||||
this.container = container;
|
||||
this.renderNotSupported = bind(this.renderNotSupported, this);
|
||||
this.renderAuthenticated = bind(this.renderAuthenticated, this);
|
||||
this.renderError = bind(this.renderError, this);
|
||||
this.renderInProgress = bind(this.renderInProgress, this);
|
||||
this.renderSetup = bind(this.renderSetup, this);
|
||||
this.renderTemplate = bind(this.renderTemplate, this);
|
||||
this.authenticate = bind(this.authenticate, this);
|
||||
this.start = bind(this.start, this);
|
||||
this.appId = u2fParams.app_id;
|
||||
this.challenge = u2fParams.challenge;
|
||||
this.form = form;
|
||||
this.fallbackButton = fallbackButton;
|
||||
this.fallbackUI = fallbackUI;
|
||||
if (this.fallbackButton) this.fallbackButton.addEventListener('click', this.switchToFallbackUI.bind(this));
|
||||
this.signRequests = u2fParams.sign_requests.map(function(request) {
|
||||
// The U2F Javascript API v1.1 requires a single challenge, with
|
||||
// _no challenges per-request_. The U2F Javascript API v1.0 requires a
|
||||
|
@ -41,7 +46,7 @@
|
|||
|
||||
U2FAuthenticate.prototype.start = function() {
|
||||
if (U2FUtil.isU2FSupported()) {
|
||||
return this.renderSetup();
|
||||
return this.renderInProgress();
|
||||
} else {
|
||||
return this.renderNotSupported();
|
||||
}
|
||||
|
@ -77,11 +82,6 @@
|
|||
return this.container.html(template(params));
|
||||
};
|
||||
|
||||
U2FAuthenticate.prototype.renderSetup = function() {
|
||||
this.renderTemplate('setup');
|
||||
return this.container.find('#js-login-u2f-device').on('click', this.renderInProgress);
|
||||
};
|
||||
|
||||
U2FAuthenticate.prototype.renderInProgress = function() {
|
||||
this.renderTemplate('inProgress');
|
||||
return this.authenticate();
|
||||
|
@ -92,22 +92,29 @@
|
|||
error_message: error.message(),
|
||||
error_code: error.errorCode
|
||||
});
|
||||
return this.container.find('#js-u2f-try-again').on('click', this.renderSetup);
|
||||
return this.container.find('#js-u2f-try-again').on('click', this.renderInProgress);
|
||||
};
|
||||
|
||||
U2FAuthenticate.prototype.renderAuthenticated = function(deviceResponse) {
|
||||
this.renderTemplate('authenticated');
|
||||
// Prefer to do this instead of interpolating using Underscore templates
|
||||
// because of JSON escaping issues.
|
||||
return this.container.find("#js-device-response").val(deviceResponse);
|
||||
const container = this.container[0];
|
||||
container.querySelector('#js-device-response').value = deviceResponse;
|
||||
container.querySelector(this.form).submit();
|
||||
this.fallbackButton.classList.add('hidden');
|
||||
};
|
||||
|
||||
U2FAuthenticate.prototype.renderNotSupported = function() {
|
||||
return this.renderTemplate('notSupported');
|
||||
};
|
||||
|
||||
U2FAuthenticate.prototype.switchToFallbackUI = function() {
|
||||
this.fallbackButton.classList.add('hidden');
|
||||
this.container[0].classList.add('hidden');
|
||||
this.fallbackUI.classList.remove('hidden');
|
||||
};
|
||||
|
||||
return U2FAuthenticate;
|
||||
|
||||
})();
|
||||
|
||||
}).call(this);
|
||||
})();
|
|
@ -26,6 +26,7 @@
|
|||
.append-bottom-default { margin-bottom: $gl-padding; }
|
||||
.inline { display: inline-block; }
|
||||
.center { text-align: center; }
|
||||
.vertical-align-middle { vertical-align: middle; }
|
||||
|
||||
.underlined-link { text-decoration: underline; }
|
||||
.hint { font-style: italic; color: $hint-color; }
|
||||
|
@ -57,16 +58,33 @@ pre {
|
|||
border-radius: 0;
|
||||
color: $well-pre-color;
|
||||
}
|
||||
|
||||
&.wrap {
|
||||
word-break: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: $gl-padding 0;
|
||||
border-top: 1px solid darken($gray-normal, 8%);
|
||||
}
|
||||
|
||||
.str-truncated {
|
||||
@include str-truncated;
|
||||
}
|
||||
|
||||
.block-truncated {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
> div,
|
||||
.str-truncated {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
.item-title { font-weight: 600; }
|
||||
|
||||
/** FLASH message **/
|
||||
|
|
|
@ -80,29 +80,32 @@
|
|||
}
|
||||
}
|
||||
|
||||
.about-gitlab {
|
||||
color: $color-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$theme-charcoal: #3d454d;
|
||||
$theme-charcoal-light: #485157;
|
||||
$theme-charcoal-dark: #383f45;
|
||||
$theme-charcoal-text: #b9bbbe;
|
||||
$theme-charcoal-light: #b9bbbe;
|
||||
$theme-charcoal: #485157;
|
||||
$theme-charcoal-dark: #3d454d;
|
||||
$theme-charcoal-darker: #383f45;
|
||||
|
||||
$theme-blue-light: #becde9;
|
||||
$theme-blue: #2980b9;
|
||||
$theme-blue-dark: #1970a9;
|
||||
$theme-blue-darker: #096099;
|
||||
|
||||
$theme-graphite-lighter: #ccc;
|
||||
$theme-graphite-light: #777;
|
||||
$theme-graphite: #666;
|
||||
$theme-graphite-dark: #555;
|
||||
$theme-graphite-light: #ccc;
|
||||
$theme-graphite: #777;
|
||||
$theme-graphite-dark: #666;
|
||||
$theme-graphite-darker: #555;
|
||||
|
||||
$theme-gray-light: #979797;
|
||||
$theme-gray: #373737;
|
||||
$theme-gray-dark: #272727;
|
||||
$theme-gray-darker: #222;
|
||||
$theme-black-light: #979797;
|
||||
$theme-black: #373737;
|
||||
$theme-black-dark: #272727;
|
||||
$theme-black-darker: #222;
|
||||
|
||||
$theme-green-light: #adc;
|
||||
$theme-green: #019875;
|
||||
|
@ -120,15 +123,15 @@ body {
|
|||
}
|
||||
|
||||
&.ui_charcoal {
|
||||
@include gitlab-theme($theme-charcoal-text, $theme-charcoal-light, $theme-charcoal, $theme-charcoal-dark);
|
||||
@include gitlab-theme($theme-charcoal-light, $theme-charcoal, $theme-charcoal-dark, $theme-charcoal-darker);
|
||||
}
|
||||
|
||||
&.ui_graphite {
|
||||
@include gitlab-theme($theme-graphite-lighter, $theme-graphite-light, $theme-graphite, $theme-graphite-dark);
|
||||
@include gitlab-theme($theme-graphite-light, $theme-graphite, $theme-graphite-dark, $theme-graphite-darker);
|
||||
}
|
||||
|
||||
&.ui_gray {
|
||||
@include gitlab-theme($theme-gray-light, $theme-gray, $theme-gray-dark, $theme-gray-darker);
|
||||
&.ui_black {
|
||||
@include gitlab-theme($theme-black-light, $theme-black, $theme-black-dark, $theme-black-darker);
|
||||
}
|
||||
|
||||
&.ui_green {
|
||||
|
|
|
@ -199,6 +199,7 @@ ul.content-list {
|
|||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
@ -208,6 +209,11 @@ ul.content-list {
|
|||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.row-fixed-content {
|
||||
flex: 0 0 auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.row-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
@ -239,7 +245,6 @@ ul.content-list {
|
|||
}
|
||||
|
||||
ul.controls {
|
||||
padding-top: 1px;
|
||||
float: right;
|
||||
list-style: none;
|
||||
|
||||
|
|
|
@ -116,8 +116,8 @@
|
|||
padding-top: 16px;
|
||||
padding-bottom: 11px;
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
line-height: 28px;
|
||||
white-space: normal;
|
||||
|
||||
/* Small devices (phones, tablets, 768px and lower) */
|
||||
@media (max-width: $screen-xs-max) {
|
||||
|
@ -158,30 +158,24 @@
|
|||
}
|
||||
|
||||
.nav-controls {
|
||||
width: 50%;
|
||||
display: inline-block;
|
||||
float: right;
|
||||
text-align: right;
|
||||
padding: 11px 0;
|
||||
margin-bottom: 0;
|
||||
|
||||
> .dropdown {
|
||||
margin-right: $gl-padding-top;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
> .btn {
|
||||
> .btn,
|
||||
> .btn-container,
|
||||
> .dropdown,
|
||||
> input,
|
||||
> form {
|
||||
margin-right: $gl-padding-top;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,19 +183,21 @@
|
|||
float: none;
|
||||
}
|
||||
|
||||
> form {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.icon-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input {
|
||||
.btn,
|
||||
.dropdown,
|
||||
.dropdown-toggle,
|
||||
input,
|
||||
form {
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
input {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
margin-right: $gl-padding-top;
|
||||
|
||||
/* Medium devices (desktops, 992px and up) */
|
||||
@media (min-width: $screen-md-min) { width: 200px; }
|
||||
|
@ -225,6 +221,7 @@
|
|||
.btn,
|
||||
form,
|
||||
.dropdown,
|
||||
.dropdown-toggle,
|
||||
.dropdown-menu-toggle,
|
||||
.form-control {
|
||||
margin: 0 0 10px;
|
||||
|
@ -263,6 +260,10 @@
|
|||
.nav-text,
|
||||
.nav-controls {
|
||||
width: auto;
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,6 +276,10 @@
|
|||
padding: 17px 0;
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.layout-nav {
|
||||
|
@ -427,4 +432,41 @@
|
|||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
.top-area {
|
||||
flex-flow: row wrap;
|
||||
|
||||
.nav-controls {
|
||||
$controls-margin: $btn-xs-side-margin - 2px;
|
||||
flex: 0 0 100%;
|
||||
|
||||
&.controls-flex {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 0 $gl-padding-top;
|
||||
}
|
||||
|
||||
.controls-item,
|
||||
.controls-item-full,
|
||||
.controls-item:last-child {
|
||||
flex: 1 1 35%;
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: $controls-margin;
|
||||
|
||||
.btn,
|
||||
.dropdown {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.controls-item-full {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,6 +101,17 @@
|
|||
padding: 0 8px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.about-gitlab {
|
||||
padding: 7px $gl-sidebar-padding;
|
||||
font-size: $gl-font-size;
|
||||
line-height: 24px;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-action-buttons {
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
// Labels
|
||||
.label {
|
||||
padding: 4px 5px;
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
display: inline-block;
|
||||
|
|
|
@ -489,9 +489,9 @@ $project-network-controls-color: #888;
|
|||
*/
|
||||
$runner-state-shared-bg: #32b186;
|
||||
$runner-state-specific-bg: #3498db;
|
||||
$runner-status-online-color: green;
|
||||
$runner-status-offline-color: gray;
|
||||
$runner-status-paused-color: red;
|
||||
$runner-status-online-color: $green-normal;
|
||||
$runner-status-offline-color: $gray-darkest;
|
||||
$runner-status-paused-color: $red-normal;
|
||||
|
||||
/*
|
||||
Stat Graph
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
.dropdown-content {
|
||||
max-height: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.issue-board-dropdown-content {
|
||||
|
@ -109,6 +109,12 @@
|
|||
|
||||
&.has-border {
|
||||
border-top: 3px solid;
|
||||
margin-top: -1px;
|
||||
margin-right: -1px;
|
||||
margin-left: -1px;
|
||||
padding-top: 1px;
|
||||
padding-right: 1px;
|
||||
padding-left: 1px;
|
||||
|
||||
.board-title {
|
||||
padding-top: ($gl-padding - 3px);
|
||||
|
|
13
app/assets/stylesheets/pages/deploy_keys.scss
Normal file
13
app/assets/stylesheets/pages/deploy_keys.scss
Normal file
|
@ -0,0 +1,13 @@
|
|||
.deploy-keys-list {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
|
||||
table {
|
||||
border: 1px solid $table-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
.deploy-keys-title {
|
||||
padding-bottom: 2px;
|
||||
line-height: 2;
|
||||
}
|
|
@ -1,50 +1,3 @@
|
|||
// Limit MR description for side-by-side diff view
|
||||
.limit-container-width {
|
||||
.detail-page-header {
|
||||
max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2));
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.issuable-details {
|
||||
.detail-page-description,
|
||||
.mr-source-target,
|
||||
.mr-state-widget,
|
||||
.merge-manually {
|
||||
max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2));
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.merge-request-tabs-holder {
|
||||
&.affix {
|
||||
border-bottom: 1px solid $border-color;
|
||||
|
||||
.nav-links {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.container-fluid {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2));
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.diffs {
|
||||
.mr-version-controls,
|
||||
.files-changed {
|
||||
max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2));
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.issuable-details {
|
||||
section {
|
||||
.issuable-discussion {
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
}
|
||||
|
||||
.label {
|
||||
padding: 8px 9px 9px $gl-padding;
|
||||
padding: 8px 9px 9px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,19 +105,19 @@
|
|||
li {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
border-left: 1px solid $border-color;
|
||||
|
||||
&:first-of-type {
|
||||
border-left: none;
|
||||
border-top-left-radius: $border-radius-default;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
border-left: 1px solid $border-color;
|
||||
border-top-right-radius: $border-radius-default;
|
||||
}
|
||||
|
||||
&:not(.active) {
|
||||
background-color: $gray-light;
|
||||
border-left: 1px solid $border-color;
|
||||
}
|
||||
|
||||
a {
|
||||
|
|
|
@ -424,11 +424,19 @@
|
|||
.merge-request-tabs-holder {
|
||||
background-color: $white-light;
|
||||
|
||||
.container-limited {
|
||||
max-width: $limited-layout-width;
|
||||
}
|
||||
|
||||
&.affix {
|
||||
top: 100px;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
transition: right .15s;
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.affix) .container-fluid {
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
margin: auto;
|
||||
margin-top: 0;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
|
||||
@media (max-width: $screen-sm-max) {
|
||||
// On smaller devices the warning becomes the fourth item in the list,
|
||||
|
|
|
@ -557,18 +557,14 @@ ul.notes {
|
|||
&.is-active {
|
||||
color: $gl-text-green;
|
||||
|
||||
svg path {
|
||||
svg {
|
||||
fill: $gl-text-green;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
position: relative;
|
||||
color: $gray-darkest;
|
||||
|
||||
path {
|
||||
fill: $gray-darkest;
|
||||
}
|
||||
fill: $gray-darkest;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -635,8 +635,8 @@
|
|||
|
||||
.grouped-pipeline-dropdown {
|
||||
padding: 0;
|
||||
width: 191px;
|
||||
min-width: 191px;
|
||||
width: 195px;
|
||||
min-width: 195px;
|
||||
left: auto;
|
||||
right: -195px;
|
||||
top: -4px;
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
background: $theme-graphite;
|
||||
}
|
||||
|
||||
&.ui_gray {
|
||||
background: $theme-gray;
|
||||
&.ui_black {
|
||||
background: $theme-black;
|
||||
}
|
||||
|
||||
&.ui_green {
|
||||
|
|
|
@ -627,6 +627,12 @@ pre.light-well {
|
|||
}
|
||||
}
|
||||
|
||||
.commits-search-form {
|
||||
.input-short {
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.project-last-commit {
|
||||
@media (min-width: $screen-sm-min) {
|
||||
margin-top: $gl-padding;
|
||||
|
|
|
@ -10,7 +10,7 @@ class Admin::DeployKeysController < Admin::ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
@deploy_key = deploy_keys.new(deploy_key_params)
|
||||
@deploy_key = deploy_keys.new(deploy_key_params.merge(user: current_user))
|
||||
|
||||
if @deploy_key.save
|
||||
redirect_to admin_deploy_keys_path
|
||||
|
@ -39,6 +39,6 @@ class Admin::DeployKeysController < Admin::ApplicationController
|
|||
end
|
||||
|
||||
def deploy_key_params
|
||||
params.require(:deploy_key).permit(:key, :title)
|
||||
params.require(:deploy_key).permit(:key, :title, :can_push)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
class Admin::GroupsController < Admin::ApplicationController
|
||||
before_action :group, only: [:edit, :show, :update, :destroy, :project_update, :members_update]
|
||||
before_action :group, only: [:edit, :update, :destroy, :project_update, :members_update]
|
||||
|
||||
def index
|
||||
@groups = Group.all
|
||||
@groups = Group.with_statistics
|
||||
@groups = @groups.sort(@sort = params[:sort])
|
||||
@groups = @groups.search(params[:name]) if params[:name].present?
|
||||
@groups = @groups.page(params[:page])
|
||||
end
|
||||
|
||||
def show
|
||||
@group = Group.with_statistics.joins(:route).group('routes.path').find_by_full_path(params[:id])
|
||||
@members = @group.members.order("access_level DESC").page(params[:members_page])
|
||||
@requesters = AccessRequestsFinder.new(@group).execute(current_user)
|
||||
@projects = @group.projects.page(params[:projects_page])
|
||||
@projects = @group.projects.with_statistics.page(params[:projects_page])
|
||||
end
|
||||
|
||||
def new
|
||||
|
|
|
@ -3,7 +3,7 @@ class Admin::ProjectsController < Admin::ApplicationController
|
|||
before_action :group, only: [:show, :transfer]
|
||||
|
||||
def index
|
||||
@projects = Project.all
|
||||
@projects = Project.with_statistics
|
||||
@projects = @projects.in_namespace(params[:namespace_id]) if params[:namespace_id].present?
|
||||
@projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
|
||||
@projects = @projects.with_push if params[:with_push].present?
|
||||
|
|
|
@ -16,9 +16,6 @@ class Admin::UsersController < Admin::ApplicationController
|
|||
@joined_projects = user.projects.joined(@user)
|
||||
end
|
||||
|
||||
def groups
|
||||
end
|
||||
|
||||
def keys
|
||||
@keys = user.keys
|
||||
end
|
||||
|
|
|
@ -82,7 +82,7 @@ module CreatesCommit
|
|||
return @merge_request if defined?(@merge_request)
|
||||
|
||||
@merge_request = MergeRequestsFinder.new(current_user, project_id: @mr_target_project.id).execute.opened.
|
||||
find_by(source_branch: @mr_source_branch, target_branch: @mr_target_branch)
|
||||
find_by(source_branch: @mr_source_branch, target_branch: @mr_target_branch, source_project_id: @mr_source_project)
|
||||
end
|
||||
|
||||
def different_project?
|
||||
|
|
|
@ -4,6 +4,9 @@ class Dashboard::TodosController < Dashboard::ApplicationController
|
|||
def index
|
||||
@sort = params[:sort]
|
||||
@todos = @todos.page(params[:page])
|
||||
if @todos.out_of_range? && @todos.total_pages != 0
|
||||
redirect_to url_for(params.merge(page: @todos.total_pages))
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
|
|
@ -42,6 +42,8 @@ class GroupsController < Groups::ApplicationController
|
|||
@notification_setting = current_user.notification_settings_for(group)
|
||||
end
|
||||
|
||||
@nested_groups = group.children
|
||||
|
||||
setup_projects
|
||||
|
||||
respond_to do |format|
|
||||
|
@ -75,7 +77,7 @@ class GroupsController < Groups::ApplicationController
|
|||
end
|
||||
|
||||
def projects
|
||||
@projects = @group.projects.page(params[:page])
|
||||
@projects = @group.projects.with_statistics.page(params[:page])
|
||||
end
|
||||
|
||||
def update
|
||||
|
|
|
@ -16,7 +16,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
@key = DeployKey.new(deploy_key_params)
|
||||
@key = DeployKey.new(deploy_key_params.merge(user: current_user))
|
||||
set_index_vars
|
||||
|
||||
if @key.valid? && @project.deploy_keys << @key
|
||||
|
@ -53,6 +53,6 @@ class Projects::DeployKeysController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def deploy_key_params
|
||||
params.require(:deploy_key).permit(:key, :title)
|
||||
params.require(:deploy_key).permit(:key, :title, :can_push)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,6 +25,9 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
def index
|
||||
@issues = issues_collection
|
||||
@issues = @issues.page(params[:page])
|
||||
if @issues.out_of_range? && @issues.total_pages != 0
|
||||
return redirect_to url_for(params.merge(page: @issues.total_pages))
|
||||
end
|
||||
|
||||
if params[:label_name].present?
|
||||
@labels = LabelsFinder.new(current_user, project_id: @project.id, title: params[:label_name]).execute
|
||||
|
|
|
@ -38,6 +38,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
def index
|
||||
@merge_requests = merge_requests_collection
|
||||
@merge_requests = @merge_requests.page(params[:page])
|
||||
if @merge_requests.out_of_range? && @merge_requests.total_pages != 0
|
||||
return redirect_to url_for(params.merge(page: @merge_requests.total_pages))
|
||||
end
|
||||
|
||||
if params[:label_name].present?
|
||||
labels_params = { project_id: @project.id, title: params[:label_name] }
|
||||
|
|
|
@ -26,6 +26,9 @@ class Projects::SnippetsController < Projects::ApplicationController
|
|||
scope: params[:scope]
|
||||
)
|
||||
@snippets = @snippets.page(params[:page])
|
||||
if @snippets.out_of_range? && @snippets.total_pages != 0
|
||||
redirect_to namespace_project_snippets_path(page: @snippets.total_pages)
|
||||
end
|
||||
end
|
||||
|
||||
def new
|
||||
|
|
|
@ -8,7 +8,7 @@ class Projects::TagsController < Projects::ApplicationController
|
|||
before_action :authorize_admin_project!, only: [:destroy]
|
||||
|
||||
def index
|
||||
params[:sort] = params[:sort].presence || 'name'
|
||||
params[:sort] = params[:sort].presence || sort_value_recently_updated
|
||||
|
||||
@sort = params[:sort]
|
||||
@tags = TagsFinder.new(@repository, params).execute
|
||||
|
|
|
@ -61,7 +61,7 @@ module ProjectsHelper
|
|||
project_link = link_to simple_sanitize(project.name), project_path(project), { class: "project-item-select-holder" }
|
||||
|
||||
if current_user
|
||||
project_link << button_tag(type: 'button', class: "dropdown-toggle-caret js-projects-dropdown-toggle", aria: { label: "Toggle switch project dropdown" }, data: { target: ".js-dropdown-menu-projects", toggle: "dropdown" }) do
|
||||
project_link << button_tag(type: 'button', class: 'dropdown-toggle-caret js-projects-dropdown-toggle', aria: { label: 'Toggle switch project dropdown' }, data: { target: '.js-dropdown-menu-projects', toggle: 'dropdown', order_by: 'last_activity_at' }) do
|
||||
icon("chevron-down")
|
||||
end
|
||||
end
|
||||
|
@ -90,10 +90,12 @@ module ProjectsHelper
|
|||
end
|
||||
|
||||
def project_for_deploy_key(deploy_key)
|
||||
if deploy_key.projects.include?(@project)
|
||||
if deploy_key.has_access_to?(@project)
|
||||
@project
|
||||
else
|
||||
deploy_key.projects.find { |project| can?(current_user, :read_project, project) }
|
||||
deploy_key.projects.find do |project|
|
||||
can?(current_user, :read_project, project)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -171,48 +173,27 @@ module ProjectsHelper
|
|||
nav_tabs << :merge_requests
|
||||
end
|
||||
|
||||
if can?(current_user, :read_pipeline, project)
|
||||
nav_tabs << :pipelines
|
||||
end
|
||||
|
||||
if can?(current_user, :read_build, project)
|
||||
nav_tabs << :builds
|
||||
end
|
||||
|
||||
if Gitlab.config.registry.enabled && can?(current_user, :read_container_image, project)
|
||||
nav_tabs << :container_registry
|
||||
end
|
||||
|
||||
if can?(current_user, :read_environment, project)
|
||||
nav_tabs << :environments
|
||||
end
|
||||
tab_ability_map = {
|
||||
environments: :read_environment,
|
||||
milestones: :read_milestone,
|
||||
pipelines: :read_pipeline,
|
||||
snippets: :read_project_snippet,
|
||||
settings: :admin_project,
|
||||
builds: :read_build,
|
||||
labels: :read_label,
|
||||
issues: :read_issue,
|
||||
team: :read_project_member,
|
||||
wiki: :read_wiki
|
||||
}
|
||||
|
||||
if can?(current_user, :admin_project, project)
|
||||
nav_tabs << :settings
|
||||
end
|
||||
|
||||
if can?(current_user, :read_project_member, project)
|
||||
nav_tabs << :team
|
||||
end
|
||||
|
||||
if can?(current_user, :read_issue, project)
|
||||
nav_tabs << :issues
|
||||
end
|
||||
|
||||
if can?(current_user, :read_wiki, project)
|
||||
nav_tabs << :wiki
|
||||
end
|
||||
|
||||
if can?(current_user, :read_project_snippet, project)
|
||||
nav_tabs << :snippets
|
||||
end
|
||||
|
||||
if can?(current_user, :read_label, project)
|
||||
nav_tabs << :labels
|
||||
end
|
||||
|
||||
if can?(current_user, :read_milestone, project)
|
||||
nav_tabs << :milestones
|
||||
tab_ability_map.each do |tab, ability|
|
||||
if can?(current_user, ability, project)
|
||||
nav_tabs << tab
|
||||
end
|
||||
end
|
||||
|
||||
nav_tabs.flatten
|
||||
|
@ -246,11 +227,6 @@ module ProjectsHelper
|
|||
end
|
||||
end
|
||||
|
||||
def repository_size(project = @project)
|
||||
size_in_bytes = project.repository_size * 1.megabyte
|
||||
number_to_human_size(size_in_bytes, delimiter: ',', precision: 2)
|
||||
end
|
||||
|
||||
def default_url_to_repo(project = @project)
|
||||
case default_clone_protocol
|
||||
when 'ssh'
|
||||
|
@ -398,20 +374,6 @@ module ProjectsHelper
|
|||
[@project.path_with_namespace, sha, "readme"].join('-')
|
||||
end
|
||||
|
||||
def round_commit_count(project)
|
||||
count = project.commit_count
|
||||
|
||||
if count > 10000
|
||||
'10000+'
|
||||
elsif count > 5000
|
||||
'5000+'
|
||||
elsif count > 1000
|
||||
'1000+'
|
||||
else
|
||||
count
|
||||
end
|
||||
end
|
||||
|
||||
def current_ref
|
||||
@ref || @repository.try(:root_ref)
|
||||
end
|
||||
|
|
|
@ -11,6 +11,7 @@ module SortingHelper
|
|||
sort_value_due_date_soon => sort_title_due_date_soon,
|
||||
sort_value_due_date_later => sort_title_due_date_later,
|
||||
sort_value_largest_repo => sort_title_largest_repo,
|
||||
sort_value_largest_group => sort_title_largest_group,
|
||||
sort_value_recently_signin => sort_title_recently_signin,
|
||||
sort_value_oldest_signin => sort_title_oldest_signin,
|
||||
sort_value_downvotes => sort_title_downvotes,
|
||||
|
@ -92,6 +93,10 @@ module SortingHelper
|
|||
'Largest repository'
|
||||
end
|
||||
|
||||
def sort_title_largest_group
|
||||
'Largest group'
|
||||
end
|
||||
|
||||
def sort_title_recently_signin
|
||||
'Recent sign in'
|
||||
end
|
||||
|
@ -193,7 +198,11 @@ module SortingHelper
|
|||
end
|
||||
|
||||
def sort_value_largest_repo
|
||||
'repository_size_desc'
|
||||
'storage_size_desc'
|
||||
end
|
||||
|
||||
def sort_value_largest_group
|
||||
'storage_size_desc'
|
||||
end
|
||||
|
||||
def sort_value_recently_signin
|
||||
|
|
7
app/helpers/storage_helper.rb
Normal file
7
app/helpers/storage_helper.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
module StorageHelper
|
||||
def storage_counter(size_in_bytes)
|
||||
precision = size_in_bytes < 1.megabyte ? 0 : 1
|
||||
|
||||
number_to_human_size(size_in_bytes, delimiter: ',', precision: precision, significant: false)
|
||||
end
|
||||
end
|
|
@ -43,6 +43,8 @@ module Ci
|
|||
before_destroy { project }
|
||||
|
||||
after_create :execute_hooks
|
||||
after_save :update_project_statistics, if: :artifacts_size_changed?
|
||||
after_destroy :update_project_statistics
|
||||
|
||||
class << self
|
||||
def first_pending
|
||||
|
@ -584,5 +586,9 @@ module Ci
|
|||
Ci::MaskSecret.mask!(trace, token)
|
||||
trace
|
||||
end
|
||||
|
||||
def update_project_statistics
|
||||
ProjectCacheWorker.perform_async(project_id, [], [:build_artifacts_size])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -93,11 +93,8 @@ module Ci
|
|||
.select("max(#{quoted_table_name}.id)")
|
||||
.group(:ref, :sha)
|
||||
|
||||
if ref
|
||||
where(id: max_id, ref: ref)
|
||||
else
|
||||
where(id: max_id)
|
||||
end
|
||||
relation = ref ? where(ref: ref) : self
|
||||
relation.where(id: max_id)
|
||||
end
|
||||
|
||||
def self.latest_status(ref = nil)
|
||||
|
@ -105,7 +102,7 @@ module Ci
|
|||
end
|
||||
|
||||
def self.latest_successful_for(ref)
|
||||
success.latest(ref).first
|
||||
success.latest(ref).order(id: :desc).first
|
||||
end
|
||||
|
||||
def self.truncate_sha(sha)
|
||||
|
|
|
@ -92,8 +92,9 @@ module Issuable
|
|||
after_save :record_metrics
|
||||
|
||||
def update_assignee_cache_counts
|
||||
# make sure we flush the cache for both the old *and* new assignee
|
||||
User.find(assignee_id_was).update_cache_counts if assignee_id_was
|
||||
# make sure we flush the cache for both the old *and* new assignees(if they exist)
|
||||
previous_assignee = User.find_by_id(assignee_id_was) if assignee_id_was
|
||||
previous_assignee.update_cache_counts if previous_assignee
|
||||
assignee.update_cache_counts if assignee
|
||||
end
|
||||
|
||||
|
|
|
@ -20,4 +20,18 @@ class DeployKey < Key
|
|||
def destroyed_when_orphaned?
|
||||
self.private?
|
||||
end
|
||||
|
||||
def has_access_to?(project)
|
||||
projects.include?(project)
|
||||
end
|
||||
|
||||
def can_push_to?(project)
|
||||
can_push? && has_access_to?(project)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# we don't want to notify the user for deploy keys
|
||||
def notify_user
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,7 +48,13 @@ class Group < Namespace
|
|||
end
|
||||
|
||||
def sort(method)
|
||||
order_by(method)
|
||||
if method == 'storage_size_desc'
|
||||
# storage_size is a virtual column so we need to
|
||||
# pass a string to avoid AR adding the table name
|
||||
reorder('storage_size DESC, namespaces.id DESC')
|
||||
else
|
||||
order_by(method)
|
||||
end
|
||||
end
|
||||
|
||||
def reference_prefix
|
||||
|
@ -155,15 +161,17 @@ class Group < Namespace
|
|||
end
|
||||
|
||||
def has_owner?(user)
|
||||
owners.include?(user)
|
||||
members_with_parents.owners.where(user_id: user).any?
|
||||
end
|
||||
|
||||
def has_master?(user)
|
||||
members.masters.where(user_id: user).any?
|
||||
members_with_parents.masters.where(user_id: user).any?
|
||||
end
|
||||
|
||||
# Check if user is a last owner of the group.
|
||||
# Parent owners are ignored for nested groups.
|
||||
def last_owner?(user)
|
||||
has_owner?(user) && owners.size == 1
|
||||
owners.include?(user) && owners.size == 1
|
||||
end
|
||||
|
||||
def avatar_type
|
||||
|
@ -189,6 +197,14 @@ class Group < Namespace
|
|||
end
|
||||
|
||||
def refresh_members_authorized_projects
|
||||
UserProjectAccessChangedService.new(users.pluck(:id)).execute
|
||||
UserProjectAccessChangedService.new(users_with_parents.pluck(:id)).execute
|
||||
end
|
||||
|
||||
def members_with_parents
|
||||
GroupMember.where(requested_at: nil, source_id: parents.map(&:id).push(id))
|
||||
end
|
||||
|
||||
def users_with_parents
|
||||
User.where(id: members_with_parents.select(:user_id))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -57,10 +57,6 @@ class Key < ActiveRecord::Base
|
|||
)
|
||||
end
|
||||
|
||||
def notify_user
|
||||
run_after_commit { NotificationService.new.new_key(self) }
|
||||
end
|
||||
|
||||
def post_create_hook
|
||||
SystemHooksService.new.execute_hooks_for(self, :create)
|
||||
end
|
||||
|
@ -86,4 +82,8 @@ class Key < ActiveRecord::Base
|
|||
|
||||
self.fingerprint = Gitlab::KeyFingerprint.new(self.key).fingerprint
|
||||
end
|
||||
|
||||
def notify_user
|
||||
run_after_commit { NotificationService.new.new_key(self) }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,4 +5,13 @@ class LfsObjectsProject < ActiveRecord::Base
|
|||
validates :lfs_object_id, presence: true
|
||||
validates :lfs_object_id, uniqueness: { scope: [:project_id], message: "already exists in project" }
|
||||
validates :project_id, presence: true
|
||||
|
||||
after_create :update_project_statistics
|
||||
after_destroy :update_project_statistics
|
||||
|
||||
private
|
||||
|
||||
def update_project_statistics
|
||||
ProjectCacheWorker.perform_async(project_id, [], [:lfs_objects_size])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -198,7 +198,9 @@ class MergeRequest < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def diff_size
|
||||
diffs(diff_options).size
|
||||
opts = diff_options || {}
|
||||
|
||||
raw_diffs(opts).size
|
||||
end
|
||||
|
||||
def diff_base_commit
|
||||
|
@ -574,11 +576,7 @@ class MergeRequest < ActiveRecord::Base
|
|||
ext = Gitlab::ReferenceExtractor.new(project, current_user)
|
||||
ext.analyze(description)
|
||||
|
||||
issues = ext.issues
|
||||
closing_issues = Gitlab::ClosingIssueExtractor.new(project, current_user).
|
||||
closed_by_message(description)
|
||||
|
||||
issues - closing_issues
|
||||
ext.issues - closes_issues
|
||||
end
|
||||
|
||||
def target_project_path
|
||||
|
|
|
@ -9,6 +9,7 @@ class Namespace < ActiveRecord::Base
|
|||
cache_markdown_field :description, pipeline: :description
|
||||
|
||||
has_many :projects, dependent: :destroy
|
||||
has_many :project_statistics
|
||||
belongs_to :owner, class_name: "User"
|
||||
|
||||
belongs_to :parent, class_name: "Namespace"
|
||||
|
@ -38,6 +39,18 @@ class Namespace < ActiveRecord::Base
|
|||
|
||||
scope :root, -> { where('type IS NULL') }
|
||||
|
||||
scope :with_statistics, -> do
|
||||
joins('LEFT JOIN project_statistics ps ON ps.namespace_id = namespaces.id')
|
||||
.group('namespaces.id')
|
||||
.select(
|
||||
'namespaces.*',
|
||||
'COALESCE(SUM(ps.storage_size), 0) AS storage_size',
|
||||
'COALESCE(SUM(ps.repository_size), 0) AS repository_size',
|
||||
'COALESCE(SUM(ps.lfs_objects_size), 0) AS lfs_objects_size',
|
||||
'COALESCE(SUM(ps.build_artifacts_size), 0) AS build_artifacts_size',
|
||||
)
|
||||
end
|
||||
|
||||
class << self
|
||||
def by_path(path)
|
||||
find_by('lower(path) = :value', value: path.downcase)
|
||||
|
|
|
@ -44,6 +44,7 @@ class Project < ActiveRecord::Base
|
|||
after_create :ensure_dir_exist
|
||||
after_create :create_project_feature, unless: :project_feature
|
||||
after_save :ensure_dir_exist, if: :namespace_id_changed?
|
||||
after_save :update_project_statistics, if: :namespace_id_changed?
|
||||
|
||||
# set last_activity_at to the same as created_at
|
||||
after_create :set_last_activity_at
|
||||
|
@ -151,6 +152,7 @@ class Project < ActiveRecord::Base
|
|||
|
||||
has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
|
||||
has_one :project_feature, dependent: :destroy
|
||||
has_one :statistics, class_name: 'ProjectStatistics', dependent: :delete
|
||||
|
||||
has_many :commit_statuses, dependent: :destroy, foreign_key: :gl_project_id
|
||||
has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline', foreign_key: :gl_project_id
|
||||
|
@ -220,6 +222,7 @@ class Project < ActiveRecord::Base
|
|||
scope :with_push, -> { joins(:events).where('events.action = ?', Event::PUSHED) }
|
||||
|
||||
scope :with_project_feature, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id') }
|
||||
scope :with_statistics, -> { includes(:statistics) }
|
||||
|
||||
# "enabled" here means "not disabled". It includes private features!
|
||||
scope :with_feature_enabled, ->(feature) {
|
||||
|
@ -332,8 +335,10 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def sort(method)
|
||||
if method == 'repository_size_desc'
|
||||
reorder(repository_size: :desc, id: :desc)
|
||||
if method == 'storage_size_desc'
|
||||
# storage_size is a joined column so we need to
|
||||
# pass a string to avoid AR adding the table name
|
||||
reorder('project_statistics.storage_size DESC, projects.id DESC')
|
||||
else
|
||||
order_by(method)
|
||||
end
|
||||
|
@ -921,7 +926,7 @@ class Project < ActiveRecord::Base
|
|||
Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present"
|
||||
|
||||
# we currently doesn't support renaming repository if it contains tags in container registry
|
||||
raise Exception.new('Project cannot be renamed, because tags are present in its container registry')
|
||||
raise StandardError.new('Project cannot be renamed, because tags are present in its container registry')
|
||||
end
|
||||
|
||||
if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace)
|
||||
|
@ -948,7 +953,7 @@ class Project < ActiveRecord::Base
|
|||
|
||||
# if we cannot move namespace directory we should rollback
|
||||
# db changes in order to prevent out of sync between db and fs
|
||||
raise Exception.new('repository cannot be renamed')
|
||||
raise StandardError.new('repository cannot be renamed')
|
||||
end
|
||||
|
||||
Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
|
||||
|
@ -1036,14 +1041,6 @@ class Project < ActiveRecord::Base
|
|||
forked? && project == forked_from_project
|
||||
end
|
||||
|
||||
def update_repository_size
|
||||
update_attribute(:repository_size, repository.size)
|
||||
end
|
||||
|
||||
def update_commit_count
|
||||
update_attribute(:commit_count, repository.commit_count)
|
||||
end
|
||||
|
||||
def forks_count
|
||||
forks.count
|
||||
end
|
||||
|
@ -1322,4 +1319,9 @@ class Project < ActiveRecord::Base
|
|||
def full_path_changed?
|
||||
path_changed? || namespace_id_changed?
|
||||
end
|
||||
|
||||
def update_project_statistics
|
||||
stats = statistics || build_statistics
|
||||
stats.update(namespace_id: namespace_id)
|
||||
end
|
||||
end
|
||||
|
|
43
app/models/project_statistics.rb
Normal file
43
app/models/project_statistics.rb
Normal file
|
@ -0,0 +1,43 @@
|
|||
class ProjectStatistics < ActiveRecord::Base
|
||||
belongs_to :project
|
||||
belongs_to :namespace
|
||||
|
||||
before_save :update_storage_size
|
||||
|
||||
STORAGE_COLUMNS = [:repository_size, :lfs_objects_size, :build_artifacts_size]
|
||||
STATISTICS_COLUMNS = [:commit_count] + STORAGE_COLUMNS
|
||||
|
||||
def total_repository_size
|
||||
repository_size + lfs_objects_size
|
||||
end
|
||||
|
||||
def refresh!(only: nil)
|
||||
STATISTICS_COLUMNS.each do |column, generator|
|
||||
if only.blank? || only.include?(column)
|
||||
public_send("update_#{column}")
|
||||
end
|
||||
end
|
||||
|
||||
save!
|
||||
end
|
||||
|
||||
def update_commit_count
|
||||
self.commit_count = project.repository.commit_count
|
||||
end
|
||||
|
||||
def update_repository_size
|
||||
self.repository_size = project.repository.size
|
||||
end
|
||||
|
||||
def update_lfs_objects_size
|
||||
self.lfs_objects_size = project.lfs_objects.sum(:size)
|
||||
end
|
||||
|
||||
def update_build_artifacts_size
|
||||
self.build_artifacts_size = project.builds.sum(:artifacts_size)
|
||||
end
|
||||
|
||||
def update_storage_size
|
||||
self.storage_size = STORAGE_COLUMNS.sum(&method(:read_attribute))
|
||||
end
|
||||
end
|
|
@ -4,7 +4,7 @@ class GroupPolicy < BasePolicy
|
|||
return unless @user
|
||||
|
||||
globally_viewable = @subject.public? || (@subject.internal? && !@user.external?)
|
||||
member = @subject.users.include?(@user)
|
||||
member = @subject.users_with_parents.include?(@user)
|
||||
owner = @user.admin? || @subject.has_owner?(@user)
|
||||
master = owner || @subject.has_master?(@user)
|
||||
|
||||
|
|
|
@ -171,9 +171,7 @@ class ProjectPolicy < BasePolicy
|
|||
def disabled_features!
|
||||
repository_enabled = project.feature_available?(:repository, user)
|
||||
|
||||
unless project.feature_available?(:issues, user)
|
||||
cannot!(*named_abilities(:issue))
|
||||
end
|
||||
block_issues_abilities
|
||||
|
||||
unless project.feature_available?(:merge_requests, user) && repository_enabled
|
||||
cannot!(*named_abilities(:merge_request))
|
||||
|
@ -245,11 +243,20 @@ class ProjectPolicy < BasePolicy
|
|||
def project_group_member?(user)
|
||||
project.group &&
|
||||
(
|
||||
project.group.members.exists?(user_id: user.id) ||
|
||||
project.group.members_with_parents.exists?(user_id: user.id) ||
|
||||
project.group.requesters.exists?(user_id: user.id)
|
||||
)
|
||||
end
|
||||
|
||||
def block_issues_abilities
|
||||
unless project.feature_available?(:issues, user)
|
||||
cannot! :read_issue if project.default_issues_tracker?
|
||||
cannot! :create_issue
|
||||
cannot! :update_issue
|
||||
cannot! :admin_issue
|
||||
end
|
||||
end
|
||||
|
||||
def named_abilities(name)
|
||||
[
|
||||
:"read_#{name}",
|
||||
|
|
|
@ -77,7 +77,7 @@ class GitPushService < BaseService
|
|||
types = []
|
||||
end
|
||||
|
||||
ProjectCacheWorker.perform_async(@project.id, types)
|
||||
ProjectCacheWorker.perform_async(@project.id, types, [:commit_count, :repository_size])
|
||||
end
|
||||
|
||||
# Schedules processing of commit messages.
|
||||
|
|
|
@ -12,7 +12,7 @@ class GitTagPushService < BaseService
|
|||
project.execute_hooks(@push_data.dup, :tag_push_hooks)
|
||||
project.execute_services(@push_data.dup, :tag_push_hooks)
|
||||
Ci::CreatePipelineService.new(project, current_user, @push_data).execute
|
||||
ProjectCacheWorker.perform_async(project.id)
|
||||
ProjectCacheWorker.perform_async(project.id, [], [:commit_count, :repository_size])
|
||||
|
||||
true
|
||||
end
|
||||
|
|
|
@ -12,6 +12,13 @@ module Groups
|
|||
return @group
|
||||
end
|
||||
|
||||
if @group.parent && !can?(current_user, :admin_group, @group.parent)
|
||||
@group.parent = nil
|
||||
@group.errors.add(:parent_id, 'manage access required to create subgroup')
|
||||
|
||||
return @group
|
||||
end
|
||||
|
||||
@group.name ||= @group.path.dup
|
||||
@group.save
|
||||
@group.add_owner(current_user)
|
||||
|
|
|
@ -36,14 +36,10 @@ class IssuableBaseService < BaseService
|
|||
end
|
||||
end
|
||||
|
||||
def filter_params(issuable_ability_name = :issue)
|
||||
filter_assignee
|
||||
filter_milestone
|
||||
filter_labels
|
||||
def filter_params(issuable)
|
||||
ability_name = :"admin_#{issuable.to_ability_name}"
|
||||
|
||||
ability = :"admin_#{issuable_ability_name}"
|
||||
|
||||
unless can?(current_user, ability, project)
|
||||
unless can?(current_user, ability_name, project)
|
||||
params.delete(:milestone_id)
|
||||
params.delete(:labels)
|
||||
params.delete(:add_label_ids)
|
||||
|
@ -52,14 +48,35 @@ class IssuableBaseService < BaseService
|
|||
params.delete(:assignee_id)
|
||||
params.delete(:due_date)
|
||||
end
|
||||
|
||||
filter_assignee(issuable)
|
||||
filter_milestone
|
||||
filter_labels
|
||||
end
|
||||
|
||||
def filter_assignee
|
||||
if params[:assignee_id] == IssuableFinder::NONE
|
||||
params[:assignee_id] = ''
|
||||
def filter_assignee(issuable)
|
||||
return unless params[:assignee_id].present?
|
||||
|
||||
assignee_id = params[:assignee_id]
|
||||
|
||||
if assignee_id.to_s == IssuableFinder::NONE
|
||||
params[:assignee_id] = ""
|
||||
else
|
||||
params.delete(:assignee_id) unless assignee_can_read?(issuable, assignee_id)
|
||||
end
|
||||
end
|
||||
|
||||
def assignee_can_read?(issuable, assignee_id)
|
||||
new_assignee = User.find_by_id(assignee_id)
|
||||
|
||||
return false unless new_assignee.present?
|
||||
|
||||
ability_name = :"read_#{issuable.to_ability_name}"
|
||||
resource = issuable.persisted? ? issuable : project
|
||||
|
||||
can?(new_assignee, ability_name, resource)
|
||||
end
|
||||
|
||||
def filter_milestone
|
||||
milestone_id = params[:milestone_id]
|
||||
return unless milestone_id
|
||||
|
@ -138,7 +155,7 @@ class IssuableBaseService < BaseService
|
|||
|
||||
def create(issuable)
|
||||
merge_slash_commands_into_params!(issuable)
|
||||
filter_params
|
||||
filter_params(issuable)
|
||||
|
||||
params.delete(:state_event)
|
||||
params[:author] ||= current_user
|
||||
|
@ -180,7 +197,7 @@ class IssuableBaseService < BaseService
|
|||
change_state(issuable)
|
||||
change_subscription(issuable)
|
||||
change_todo(issuable)
|
||||
filter_params
|
||||
filter_params(issuable)
|
||||
old_labels = issuable.labels.to_a
|
||||
old_mentioned_users = issuable.mentioned_users.to_a
|
||||
|
||||
|
|
|
@ -17,10 +17,6 @@ module Issues
|
|||
|
||||
private
|
||||
|
||||
def filter_params
|
||||
super(:issue)
|
||||
end
|
||||
|
||||
def execute_hooks(issue, action = 'open')
|
||||
issue_data = hook_data(issue, action)
|
||||
hooks_scope = issue.confidential? ? :confidential_issue_hooks : :issue_hooks
|
||||
|
|
|
@ -38,10 +38,6 @@ module MergeRequests
|
|||
|
||||
private
|
||||
|
||||
def filter_params
|
||||
super(:merge_request)
|
||||
end
|
||||
|
||||
def merge_requests_for(branch)
|
||||
origin_merge_requests = @project.origin_merge_requests
|
||||
.opened.where(source_branch: branch).to_a
|
||||
|
|
|
@ -41,7 +41,7 @@ module Notes
|
|||
# We must add the error after we call #save because errors are reset
|
||||
# when #save is called
|
||||
if only_commands
|
||||
note.errors.add(:commands_only, 'Your commands have been executed!')
|
||||
note.errors.add(:commands_only, 'Commands applied')
|
||||
end
|
||||
|
||||
note.commands_changes = command_params.keys
|
||||
|
|
|
@ -74,7 +74,7 @@ module Users
|
|||
# remove - The IDs of the authorization rows to remove.
|
||||
# add - Rows to insert in the form `[user id, project id, access level]`
|
||||
def update_authorizations(remove = [], add = [])
|
||||
return if remove.empty? && add.empty?
|
||||
return if remove.empty? && add.empty? && user.authorized_projects_populated
|
||||
|
||||
User.transaction do
|
||||
user.remove_project_authorizations(remove) unless remove.empty?
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
%p
|
||||
#{link_to @abuse_report.user.name, user_url(@abuse_report.user)}
|
||||
(@#{@abuse_report.user.username}) was reported for abuse by
|
||||
#{link_to @abuse_report.reporter.name, user_url(@abuse_report.reporter)}
|
||||
#{link_to @abuse_report.user.name, user_url(@abuse_report.user)}
|
||||
(@#{@abuse_report.user.username}) was reported for abuse by
|
||||
#{link_to @abuse_report.reporter.name, user_url(@abuse_report.reporter)}
|
||||
(@#{@abuse_report.reporter.username}).
|
||||
|
||||
%blockquote
|
||||
|
|
|
@ -321,7 +321,7 @@
|
|||
= f.text_field :recaptcha_site_key, class: 'form-control'
|
||||
.help-block
|
||||
Generate site and private keys at
|
||||
%a{ href: 'http://www.google.com/recaptcha', target: 'blank'} http://www.google.com/recaptcha
|
||||
%a{ href: 'http://www.google.com/recaptcha', target: 'blank' } http://www.google.com/recaptcha
|
||||
|
||||
.form-group
|
||||
= f.label :recaptcha_private_key, 'reCAPTCHA Private Key', class: 'control-label col-sm-2'
|
||||
|
@ -342,7 +342,7 @@
|
|||
= f.text_field :akismet_api_key, class: 'form-control'
|
||||
.help-block
|
||||
Generate API key at
|
||||
%a{ href: 'http://www.akismet.com', target: 'blank'} http://www.akismet.com
|
||||
%a{ href: 'http://www.akismet.com', target: 'blank' } http://www.akismet.com
|
||||
|
||||
%fieldset
|
||||
%legend Abuse reports
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
- submit_btn_css ||= 'btn btn-link btn-remove btn-sm'
|
||||
= form_tag admin_application_path(application) do
|
||||
%input{:name => "_method", :type => "hidden", :value => "delete"}/
|
||||
%input{ :name => "_method", :type => "hidden", :value => "delete" }/
|
||||
= submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
%th
|
||||
%tbody.oauth-applications
|
||||
- @applications.each do |application|
|
||||
%tr{:id => "application_#{application.id}"}
|
||||
%tr{ :id => "application_#{application.id}" }
|
||||
%td= link_to application.name, admin_application_path(application)
|
||||
%td= application.redirect_uri
|
||||
%td= application.access_tokens.map(&:resource_owner_id).uniq.count
|
||||
|
|
|
@ -43,4 +43,4 @@
|
|||
|
||||
|
||||
.panel.panel-default
|
||||
%iframe{src: sidekiq_path, width: '100%', height: 970, style: "border: none"}
|
||||
%iframe{ src: sidekiq_path, width: '100%', height: 970, style: "border: none" }
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
%br.clearfix
|
||||
|
||||
-if @broadcast_messages.any?
|
||||
- if @broadcast_messages.any?
|
||||
%table.table
|
||||
%thead
|
||||
%tr
|
||||
|
|
|
@ -1,27 +1,34 @@
|
|||
- page_title "Deploy Keys"
|
||||
.panel.panel-default.prepend-top-default
|
||||
.panel-heading
|
||||
Public deploy keys (#{@deploy_keys.count})
|
||||
.controls
|
||||
= link_to 'New Deploy Key', new_admin_deploy_key_path, class: "btn btn-new btn-sm"
|
||||
- if @deploy_keys.any?
|
||||
.table-holder
|
||||
%table.table
|
||||
%thead.panel-heading
|
||||
|
||||
%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'
|
||||
|
||||
- if @deploy_keys.any?
|
||||
.table-holder.deploy-keys-list
|
||||
%table.table
|
||||
%thead
|
||||
%tr
|
||||
%th.col-sm-2 Title
|
||||
%th.col-sm-4 Fingerprint
|
||||
%th.col-sm-2 Write access allowed
|
||||
%th.col-sm-2 Added at
|
||||
%th.col-sm-2
|
||||
%tbody
|
||||
- @deploy_keys.each do |deploy_key|
|
||||
%tr
|
||||
%th Title
|
||||
%th Fingerprint
|
||||
%th Added at
|
||||
%th
|
||||
%tbody
|
||||
- @deploy_keys.each do |deploy_key|
|
||||
%tr
|
||||
%td
|
||||
%strong= deploy_key.title
|
||||
%td
|
||||
%code.key-fingerprint= deploy_key.fingerprint
|
||||
%td
|
||||
%span.cgray
|
||||
added #{time_ago_with_tooltip(deploy_key.created_at)}
|
||||
%td
|
||||
= link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-sm btn-remove delete-key pull-right"
|
||||
%td
|
||||
%strong= deploy_key.title
|
||||
%td
|
||||
%code.key-fingerprint= deploy_key.fingerprint
|
||||
%td
|
||||
- if deploy_key.can_push?
|
||||
Yes
|
||||
- else
|
||||
No
|
||||
%td
|
||||
%span.cgray
|
||||
added #{time_ago_with_tooltip(deploy_key.created_at)}
|
||||
%td
|
||||
= link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-remove delete-key pull-right'
|
||||
|
|
|
@ -16,6 +16,14 @@
|
|||
Paste a machine public key here. Read more about how to generate it
|
||||
= link_to "here", help_page_path("ssh/README")
|
||||
= f.text_area :key, class: "form-control thin_area", rows: 5
|
||||
.form-group
|
||||
.control-label
|
||||
.col-sm-10
|
||||
= f.label :can_push do
|
||||
= f.check_box :can_push
|
||||
%strong Write access allowed
|
||||
%p.light.append-bottom-0
|
||||
Allow this key to push to repository as well? (Default only allows pull access.)
|
||||
|
||||
.form-actions
|
||||
= f.submit 'Create', class: "btn-create btn"
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
= link_to 'Edit', admin_group_edit_path(group), id: "edit_#{dom_id(group)}", class: 'btn'
|
||||
= link_to 'Delete', [:admin, group], data: { confirm: "Are you sure you want to remove #{group.name}?" }, method: :delete, class: 'btn btn-remove'
|
||||
.stats
|
||||
%span.badge
|
||||
= storage_counter(group.storage_size)
|
||||
|
||||
%span
|
||||
= icon('bookmark')
|
||||
= number_with_delimiter(group.projects.count)
|
||||
|
@ -13,7 +16,7 @@
|
|||
= icon('users')
|
||||
= number_with_delimiter(group.users.count)
|
||||
|
||||
%span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)}
|
||||
%span.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group) }
|
||||
= visibility_level_icon(group.visibility_level, fw: false)
|
||||
|
||||
.avatar-container.s40
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
= sort_title_recently_updated
|
||||
= link_to admin_groups_path(sort: sort_value_oldest_updated, name: project_name) do
|
||||
= sort_title_oldest_updated
|
||||
= 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
|
||||
%ul.content-list
|
||||
|
|
|
@ -38,6 +38,18 @@
|
|||
%strong
|
||||
= @group.created_at.to_s(:medium)
|
||||
|
||||
%li
|
||||
%span.light Storage:
|
||||
%strong= storage_counter(@group.storage_size)
|
||||
(
|
||||
= storage_counter(@group.repository_size)
|
||||
repositories,
|
||||
= storage_counter(@group.build_artifacts_size)
|
||||
build artifacts,
|
||||
= storage_counter(@group.lfs_objects_size)
|
||||
LFS
|
||||
)
|
||||
|
||||
%li
|
||||
%span.light Group Git LFS status:
|
||||
%strong
|
||||
|
@ -55,8 +67,8 @@
|
|||
%li
|
||||
%strong
|
||||
= link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project]
|
||||
%span.label.label-gray
|
||||
= repository_size(project)
|
||||
%span.badge
|
||||
= storage_counter(project.statistics.storage_size)
|
||||
%span.pull-right.light
|
||||
%span.monospace= project.path_with_namespace + ".git"
|
||||
.panel-footer
|
||||
|
@ -73,8 +85,8 @@
|
|||
%li
|
||||
%strong
|
||||
= link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project]
|
||||
%span.label.label-gray
|
||||
= repository_size(project)
|
||||
%span.badge
|
||||
= storage_counter(project.statistics.storage_size)
|
||||
%span.pull-right.light
|
||||
%span.monospace= project.path_with_namespace + ".git"
|
||||
|
||||
|
@ -91,7 +103,7 @@
|
|||
= form_tag admin_group_members_update_path(@group), id: "new_project_member", class: "bulk_import", method: :put do
|
||||
%div
|
||||
= users_select_tag(:user_ids, multiple: true, email_user: true, scope: :all)
|
||||
%div.prepend-top-10
|
||||
.prepend-top-10
|
||||
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
|
||||
%hr
|
||||
= button_tag 'Add users to group', class: "btn btn-create"
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
System hook will be triggered on set of events like creating project
|
||||
or adding ssh key. But you can also enable extra triggers like Push events.
|
||||
|
||||
%div.prepend-top-default
|
||||
.prepend-top-default
|
||||
= f.check_box :push_events, class: 'pull-left'
|
||||
.prepend-left-20
|
||||
= f.label :push_events, class: 'list-label' do
|
||||
|
@ -54,7 +54,7 @@
|
|||
= f.submit "Add System Hook", class: "btn btn-create"
|
||||
%hr
|
||||
|
||||
-if @hooks.any?
|
||||
- if @hooks.any?
|
||||
.panel.panel-default
|
||||
.panel-heading
|
||||
System hooks (#{@hooks.count})
|
||||
|
@ -70,4 +70,3 @@
|
|||
- if hook.send(trigger)
|
||||
%span.label.label-gray= trigger.titleize
|
||||
%span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
%li{id: dom_id(label)}
|
||||
%li{ id: dom_id(label) }
|
||||
.label-row
|
||||
= render_colored_label(label, tooltip: false)
|
||||
= markdown_field(label, :description)
|
||||
|
|
|
@ -69,8 +69,8 @@
|
|||
.controls
|
||||
- if project.archived
|
||||
%span.label.label-warning archived
|
||||
%span.label.label-gray
|
||||
= repository_size(project)
|
||||
%span.badge
|
||||
= storage_counter(project.statistics.storage_size)
|
||||
= link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn"
|
||||
= link_to 'Delete', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-remove"
|
||||
.title
|
||||
|
|
|
@ -65,9 +65,16 @@
|
|||
= @project.repository.path_to_repo
|
||||
|
||||
%li
|
||||
%span.light Size
|
||||
%strong
|
||||
= repository_size(@project)
|
||||
%span.light Storage:
|
||||
%strong= storage_counter(@project.statistics.storage_size)
|
||||
(
|
||||
= storage_counter(@project.statistics.repository_size)
|
||||
repository,
|
||||
= storage_counter(@project.statistics.build_artifacts_size)
|
||||
build artifacts,
|
||||
= storage_counter(@project.statistics.lfs_objects_size)
|
||||
LFS
|
||||
)
|
||||
|
||||
%li
|
||||
%span.light last commit:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
%tr{id: dom_id(runner)}
|
||||
%tr{ id: dom_id(runner) }
|
||||
%td
|
||||
- if runner.shared?
|
||||
%span.label.label-success shared
|
||||
|
|
|
@ -15,10 +15,8 @@
|
|||
%ul.nav-links
|
||||
= nav_link(path: 'users#show') do
|
||||
= link_to "Account", admin_user_path(@user)
|
||||
= nav_link(path: 'users#groups') do
|
||||
= link_to "Groups", groups_admin_user_path(@user)
|
||||
= nav_link(path: 'users#projects') do
|
||||
= link_to "Projects", projects_admin_user_path(@user)
|
||||
= link_to "Groups and projects", projects_admin_user_path(@user)
|
||||
= nav_link(path: 'users#keys') do
|
||||
= link_to "SSH keys", keys_admin_user_path(@user)
|
||||
= nav_link(controller: :identities) do
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn'
|
||||
- unless user == current_user
|
||||
.dropdown.inline
|
||||
%a.dropdown-new.btn.btn-default#project-settings-button{href: '#', data: { toggle: 'dropdown' } }
|
||||
%a.dropdown-new.btn.btn-default#project-settings-button{ href: '#', data: { toggle: 'dropdown' } }
|
||||
= icon('cog')
|
||||
= icon('caret-down')
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue