Merge branch 'master' into sh-headless-chrome-support
* master: (97 commits) Eliminate N+1 queries in loading discussions.json endpoint Clean up read_registry scope changes Add missing import statements Improve “New project“ page description Fix notification message when admin label was modified Remove gaps under nav on build page Replace the 'project/snippets.feature' spinach test with an rspec analog Use correct group members path for members flyout link Fix docs for lightweight tag creation via API Replace the 'project/commits/revert.feature' spinach test with an rspec analog Merge branch 'rs-incoming-email-domain-docs' into 'security-10-0' Replace the 'project/archived.feature' spinach test with an rspec analog Fix broken link in docs/api/wiki.md Fixed the new sidebars width when browser has scrollbars Improve 'spec/features/profiles/*' specs Replace the 'search.feature' spinach test with an rspec analog dedupe yarn packages add dependency approvals (all MIT license) update build image to latest with node 8.x, yarn 1.0.2, and chrome 61 Ensure we use `Entities::User` for non-admin `users/:id` API requests ...
This commit is contained in:
commit
27a28d9970
|
@ -1,4 +1,4 @@
|
|||
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.13-chrome-60.0-node-7.1-postgresql-9.6"
|
||||
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.13-chrome-61.0-node-8.x-yarn-1.0-postgresql-9.6"
|
||||
|
||||
.default-cache: &default-cache
|
||||
key: "ruby-233-with-yarn"
|
||||
|
@ -191,6 +191,9 @@ review-docs-deploy:
|
|||
stage: build
|
||||
environment:
|
||||
name: review-docs/$CI_COMMIT_REF_NAME
|
||||
# DOCS_REVIEW_APPS_DOMAIN and DOCS_GITLAB_REPO_SUFFIX are secret variables
|
||||
# Discussion: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14236/diffs#note_40140693
|
||||
url: http://$CI_COMMIT_REF_SLUG-built-from-ce-ee.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX
|
||||
on_stop: review-docs-cleanup
|
||||
script:
|
||||
- gem install gitlab --no-doc
|
||||
|
|
|
@ -286,7 +286,10 @@ might be edited to make them small and simple.
|
|||
|
||||
Please submit Feature Proposals using the ['Feature Proposal' issue template](.gitlab/issue_templates/Feature Proposal.md) provided on the issue tracker.
|
||||
|
||||
For changes in the interface, it can be helpful to create a mockup first.
|
||||
For changes in the interface, it is helpful to include a mockup. Issues that add to, or change, the interface should
|
||||
be given the ~"UX" label. This will allow the UX team to provide input and guidance. You may
|
||||
need to ask one of the [core team] members to add the label, if you do not have permissions to do it by yourself.
|
||||
|
||||
If you want to create something yourself, consider opening an issue first to
|
||||
discuss whether it is interesting to include this in GitLab.
|
||||
|
||||
|
|
|
@ -358,7 +358,7 @@ GEM
|
|||
rake
|
||||
grape_logging (1.7.0)
|
||||
grape
|
||||
grpc (1.4.5)
|
||||
grpc (1.6.0)
|
||||
google-protobuf (~> 3.1)
|
||||
googleauth (~> 0.5.1)
|
||||
haml (4.0.7)
|
||||
|
|
|
@ -7,6 +7,7 @@ class DeleteModal {
|
|||
this.$branchName = $('.js-branch-name', this.$modal);
|
||||
this.$confirmInput = $('.js-delete-branch-input', this.$modal);
|
||||
this.$deleteBtn = $('.js-delete-branch', this.$modal);
|
||||
this.$notMerged = $('.js-not-merged', this.$modal);
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
|
@ -16,8 +17,10 @@ class DeleteModal {
|
|||
}
|
||||
|
||||
setModalData(e) {
|
||||
this.branchName = e.currentTarget.dataset.branchName || '';
|
||||
this.deletePath = e.currentTarget.dataset.deletePath || '';
|
||||
const branchData = e.currentTarget.dataset;
|
||||
this.branchName = branchData.branchName || '';
|
||||
this.deletePath = branchData.deletePath || '';
|
||||
this.isMerged = !!branchData.isMerged;
|
||||
this.updateModal();
|
||||
}
|
||||
|
||||
|
@ -30,6 +33,7 @@ class DeleteModal {
|
|||
this.$confirmInput.val('');
|
||||
this.$deleteBtn.attr('href', this.deletePath);
|
||||
this.$deleteBtn.attr('disabled', true);
|
||||
this.$notMerged.toggleClass('hidden', this.isMerged);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
import Vue from 'vue';
|
||||
|
||||
import '../mixins/discussion';
|
||||
|
||||
const JumpToDiscussion = Vue.extend({
|
||||
mixins: [DiscussionMixins],
|
||||
props: {
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
import Vue from 'vue';
|
||||
|
||||
import '../mixins/discussion';
|
||||
|
||||
window.ResolveCount = Vue.extend({
|
||||
mixins: [DiscussionMixins],
|
||||
props: {
|
||||
|
|
|
@ -15,6 +15,7 @@ class DropdownUser extends gl.FilteredSearchDropdown {
|
|||
params: {
|
||||
per_page: 20,
|
||||
active: true,
|
||||
group_id: this.getGroupId(),
|
||||
project_id: this.getProjectId(),
|
||||
current_user: true,
|
||||
},
|
||||
|
@ -47,6 +48,10 @@ class DropdownUser extends gl.FilteredSearchDropdown {
|
|||
super.renderContent(forceShowList);
|
||||
}
|
||||
|
||||
getGroupId() {
|
||||
return this.input.getAttribute('data-group-id');
|
||||
}
|
||||
|
||||
getProjectId() {
|
||||
return this.input.getAttribute('data-project-id');
|
||||
}
|
||||
|
|
|
@ -77,10 +77,11 @@ export const hideMenu = (el) => {
|
|||
export const moveSubItemsToPosition = (el, subItems) => {
|
||||
const boundingRect = el.getBoundingClientRect();
|
||||
const top = calculateTop(boundingRect, subItems.offsetHeight);
|
||||
const left = sidebar ? sidebar.offsetWidth : 50;
|
||||
const isAbove = top < boundingRect.top;
|
||||
|
||||
subItems.classList.add('fly-out-list');
|
||||
subItems.style.transform = `translate3d(0, ${Math.floor(top) - headerHeight}px, 0)`; // eslint-disable-line no-param-reassign
|
||||
subItems.style.transform = `translate3d(${left}px, ${Math.floor(top) - headerHeight}px, 0)`; // eslint-disable-line no-param-reassign
|
||||
|
||||
const subItemsRect = subItems.getBoundingClientRect();
|
||||
|
||||
|
|
|
@ -127,13 +127,6 @@ import DropdownUtils from './filtered_search/dropdown_utils';
|
|||
$('.has-tooltip', $value).tooltip({
|
||||
container: 'body'
|
||||
});
|
||||
return $value.find('a').each(function(i) {
|
||||
return setTimeout((function(_this) {
|
||||
return function() {
|
||||
return gl.animate.animate($(_this), 'pulse');
|
||||
};
|
||||
})(this), 200 * i);
|
||||
});
|
||||
});
|
||||
};
|
||||
$dropdown.glDropdown({
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-param-reassign, no-void, prefer-template, no-var, new-cap, prefer-arrow-callback, consistent-return, max-len */
|
||||
(function() {
|
||||
(function(w) {
|
||||
if (w.gl == null) {
|
||||
w.gl = {};
|
||||
}
|
||||
if (gl.animate == null) {
|
||||
gl.animate = {};
|
||||
}
|
||||
gl.animate.animate = function($el, animation, options, done) {
|
||||
if ((options != null ? options.cssStart : void 0) != null) {
|
||||
$el.css(options.cssStart);
|
||||
}
|
||||
$el.removeClass(animation + ' animated').addClass(animation + ' animated').one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() {
|
||||
$(this).removeClass(animation + ' animated');
|
||||
if (done != null) {
|
||||
done();
|
||||
}
|
||||
if ((options != null ? options.cssEnd : void 0) != null) {
|
||||
$el.css(options.cssEnd);
|
||||
}
|
||||
});
|
||||
};
|
||||
gl.animate.animateEach = function($els, animation, time, options, done) {
|
||||
var dfd;
|
||||
dfd = $.Deferred();
|
||||
if (!$els.length) {
|
||||
dfd.resolve();
|
||||
}
|
||||
$els.each(function(i) {
|
||||
setTimeout((function(_this) {
|
||||
return function() {
|
||||
var $this;
|
||||
$this = $(_this);
|
||||
return gl.animate.animate($this, animation, options, function() {
|
||||
if (i === $els.length - 1) {
|
||||
dfd.resolve();
|
||||
if (done != null) {
|
||||
return done();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
})(this), time * i);
|
||||
});
|
||||
return dfd.promise();
|
||||
};
|
||||
})(window);
|
||||
}).call(window);
|
|
@ -39,7 +39,6 @@ import './commit/file';
|
|||
import './commit/image_file';
|
||||
|
||||
// lib/utils
|
||||
import './lib/utils/animate';
|
||||
import './lib/utils/bootstrap_linked_tabs';
|
||||
import './lib/utils/common_utils';
|
||||
import './lib/utils/datetime_utility';
|
||||
|
|
|
@ -45,7 +45,7 @@ import _ from 'underscore';
|
|||
if (issueUpdateURL) {
|
||||
milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>');
|
||||
milestoneLinkNoneTemplate = '<span class="no-value">None</span>';
|
||||
collapsedSidebarLabelTemplate = _.template('<span class="has-tooltip" data-container="body" title="<%- remaining %>" data-placement="left"> <%- title %> </span>');
|
||||
collapsedSidebarLabelTemplate = _.template('<span class="has-tooltip" data-container="body" title="<%- name %><br /><%- remaining %>" data-placement="left" data-html="true"> <%- title %> </span>');
|
||||
}
|
||||
return $dropdown.glDropdown({
|
||||
showMenuAbove: showMenuAbove,
|
||||
|
@ -208,6 +208,7 @@ import _ from 'underscore';
|
|||
if (data.milestone != null) {
|
||||
data.milestone.full_path = _this.currentProject.full_path;
|
||||
data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date);
|
||||
data.milestone.name = data.milestone.title;
|
||||
$value.html(milestoneLinkTemplate(data.milestone));
|
||||
return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone));
|
||||
} else {
|
||||
|
|
|
@ -15,7 +15,6 @@ export default class NewNavSidebar {
|
|||
this.$openSidebar = $('.toggle-mobile-nav');
|
||||
this.$closeSidebar = $('.close-nav-button');
|
||||
this.$sidebarToggle = $('.js-toggle-sidebar');
|
||||
this.$topLevelLinks = $('.sidebar-top-level-items > li > a');
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
|
@ -56,10 +55,6 @@ export default class NewNavSidebar {
|
|||
this.$page.toggleClass('page-with-icon-sidebar', breakpoint === 'sm' ? true : collapsed);
|
||||
}
|
||||
NewNavSidebar.setCollapsedCookie(collapsed);
|
||||
|
||||
this.$topLevelLinks.attr('title', function updateTopLevelTitle() {
|
||||
return collapsed ? this.getAttribute('aria-label') : '';
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
<div class="note-actions">
|
||||
<span
|
||||
v-if="accessLevel"
|
||||
class="note-role">{{accessLevel}}</span>
|
||||
class="note-role note-role-access">{{accessLevel}}</span>
|
||||
<div
|
||||
v-if="canAddAwardEmoji"
|
||||
class="note-actions-item">
|
||||
|
|
|
@ -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 */
|
||||
import Cookies from 'js-cookie';
|
||||
import Mousetrap from 'mousetrap';
|
||||
|
||||
import findAndFollowLink from './shortcuts_dashboard_navigation';
|
||||
|
||||
|
|
|
@ -158,11 +158,23 @@
|
|||
box-shadow: inset 4px 0 0 $color-700;
|
||||
|
||||
> a {
|
||||
color: $color-900;
|
||||
color: $color-800;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: $color-900;
|
||||
fill: $color-800;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-top-level-items > li.active .badge {
|
||||
color: $color-800;
|
||||
}
|
||||
|
||||
.nav-links li.active a {
|
||||
border-bottom-color: $color-500;
|
||||
|
||||
.badge {
|
||||
font-weight: $gl-font-weight-bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -261,5 +273,9 @@ body {
|
|||
fill: $theme-gray-900;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-top-level-items > li.active .badge {
|
||||
color: $theme-gray-900;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -328,7 +328,7 @@
|
|||
border-bottom: 1px solid $border-color;
|
||||
transition: padding $sidebar-transition-duration;
|
||||
text-align: center;
|
||||
margin-top: $header-height;
|
||||
margin-top: $new-navbar-height;
|
||||
|
||||
.container-fluid {
|
||||
position: relative;
|
||||
|
|
|
@ -78,16 +78,16 @@
|
|||
|
||||
.right-sidebar {
|
||||
border-left: 1px solid $border-color;
|
||||
height: calc(100% - #{$header-height});
|
||||
height: calc(100% - #{$new-navbar-height});
|
||||
|
||||
&.affix {
|
||||
position: fixed;
|
||||
top: $header-height;
|
||||
top: $new-navbar-height;
|
||||
}
|
||||
}
|
||||
|
||||
.with-performance-bar .right-sidebar.affix {
|
||||
top: $header-height + $performance-bar-height;
|
||||
top: $new-navbar-height + $performance-bar-height;
|
||||
}
|
||||
|
||||
@mixin maintain-sidebar-dimensions {
|
||||
|
|
|
@ -13,6 +13,7 @@ $sidebar-breakpoint: 1024px;
|
|||
$darken-normal-factor: 7%;
|
||||
$darken-dark-factor: 10%;
|
||||
$darken-border-factor: 5%;
|
||||
$darken-border-dashed-factor: 25%;
|
||||
|
||||
$white-light: #fff;
|
||||
$white-normal: #f0f0f0;
|
||||
|
@ -134,6 +135,7 @@ $border-white-normal: darken($white-normal, $darken-border-factor);
|
|||
|
||||
$border-gray-light: darken($gray-light, $darken-border-factor);
|
||||
$border-gray-normal: darken($gray-normal, $darken-border-factor);
|
||||
$border-gray-normal-dashed: darken($gray-normal, $darken-border-dashed-factor);
|
||||
$border-gray-dark: darken($white-normal, $darken-border-factor);
|
||||
|
||||
/*
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
@import "bootstrap/variables";
|
||||
|
||||
$active-background: rgba(0, 0, 0, .04);
|
||||
$active-border: $indigo-500;
|
||||
$active-color: $indigo-700;
|
||||
$active-hover-background: $active-background;
|
||||
$active-hover-color: $gl-text-color;
|
||||
$inactive-badge-background: rgba(0, 0, 0, .08);
|
||||
|
@ -107,7 +105,8 @@ $new-sidebar-collapsed-width: 50px;
|
|||
}
|
||||
|
||||
&.sidebar-icons-only {
|
||||
width: $new-sidebar-collapsed-width;
|
||||
width: auto;
|
||||
min-width: $new-sidebar-collapsed-width;
|
||||
|
||||
.nav-sidebar-inner-scroll {
|
||||
overflow-x: hidden;
|
||||
|
@ -126,6 +125,10 @@ $new-sidebar-collapsed-width: 50px;
|
|||
.fly-out-top-item {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.nav-sidebar-expanded {
|
||||
|
@ -189,7 +192,7 @@ $new-sidebar-collapsed-width: 50px;
|
|||
.nav-sidebar-inner-scroll {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.with-performance-bar .nav-sidebar {
|
||||
|
@ -217,7 +220,6 @@ $new-sidebar-collapsed-width: 50px;
|
|||
&:hover,
|
||||
&:focus {
|
||||
background: $active-background;
|
||||
color: $active-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -251,7 +253,7 @@ $new-sidebar-collapsed-width: 50px;
|
|||
@media (min-width: $screen-sm-min) {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: $new-sidebar-width;
|
||||
left: 0;
|
||||
min-width: 150px;
|
||||
margin-top: -1px;
|
||||
padding: 4px 1px;
|
||||
|
@ -317,7 +319,6 @@ $new-sidebar-collapsed-width: 50px;
|
|||
}
|
||||
|
||||
.badge {
|
||||
color: $active-color;
|
||||
font-weight: $gl-font-weight-bold;
|
||||
}
|
||||
|
||||
|
@ -390,10 +391,6 @@ $new-sidebar-collapsed-width: 50px;
|
|||
}
|
||||
|
||||
.sidebar-sub-level-items {
|
||||
@media (min-width: $screen-sm-min) {
|
||||
left: $new-sidebar-collapsed-width;
|
||||
}
|
||||
|
||||
&:not(.flyout-list) {
|
||||
display: none;
|
||||
}
|
||||
|
@ -494,13 +491,3 @@ $new-sidebar-collapsed-width: 50px;
|
|||
.with-performance-bar .boards-list {
|
||||
height: calc(100vh - #{$new-navbar-height} - #{$performance-bar-height});
|
||||
}
|
||||
|
||||
|
||||
// Change color of all horizontal tabs to match the new indigo color
|
||||
.nav-links li.active a {
|
||||
border-bottom-color: $active-border;
|
||||
|
||||
.badge {
|
||||
font-weight: $gl-font-weight-bold;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,10 +64,10 @@
|
|||
color: $gl-text-color;
|
||||
position: sticky;
|
||||
position: -webkit-sticky;
|
||||
top: $header-height;
|
||||
top: $new-navbar-height;
|
||||
|
||||
&.affix {
|
||||
top: $header-height;
|
||||
top: $new-navbar-height;
|
||||
}
|
||||
|
||||
// with sidebar
|
||||
|
@ -174,10 +174,10 @@
|
|||
|
||||
.with-performance-bar .build-page {
|
||||
.top-bar {
|
||||
top: $header-height + $performance-bar-height;
|
||||
top: $new-navbar-height + $performance-bar-height;
|
||||
|
||||
&.affix {
|
||||
top: $header-height + $performance-bar-height;
|
||||
top: $new-navbar-height + $performance-bar-height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -634,8 +634,16 @@
|
|||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.diff-changed-file {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.diff-file-changes-path {
|
||||
@include str-truncated(78%);
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
|
@ -449,6 +449,12 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.milestone-title span {
|
||||
@include str-truncated(100%);
|
||||
display: block;
|
||||
margin: 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
|
|
|
@ -95,6 +95,8 @@
|
|||
}
|
||||
|
||||
.omniauth-container {
|
||||
font-size: 13px;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
@ -209,6 +209,11 @@
|
|||
}
|
||||
|
||||
.stage-cell {
|
||||
@media (min-width: $screen-md-min) {
|
||||
min-width: 148px;
|
||||
margin-right: -4px;
|
||||
}
|
||||
|
||||
.mini-pipeline-graph-dropdown-toggle svg {
|
||||
height: $ci-action-icon-size;
|
||||
width: $ci-action-icon-size;
|
||||
|
|
|
@ -752,7 +752,7 @@ a.deploy-project-label {
|
|||
}
|
||||
|
||||
li.missing {
|
||||
border: 1px dashed $border-gray-normal;
|
||||
border: 1px dashed $border-gray-normal-dashed;
|
||||
border-radius: $border-radius-default;
|
||||
|
||||
a {
|
||||
|
|
|
@ -71,6 +71,11 @@
|
|||
height: 100%;
|
||||
|
||||
.monaco-editor.vs {
|
||||
.current-line {
|
||||
border: none;
|
||||
background: $well-light-border;
|
||||
}
|
||||
|
||||
.line-numbers {
|
||||
cursor: pointer;
|
||||
|
||||
|
@ -84,6 +89,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
.blob-no-preview {
|
||||
.vertical-center {
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&.edit-mode {
|
||||
.blob-viewer-container {
|
||||
overflow: hidden;
|
||||
|
@ -103,7 +115,7 @@
|
|||
overflow: auto;
|
||||
|
||||
> div,
|
||||
.file-content {
|
||||
.file-content:not(.wiki) {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,8 @@ class Admin::DeployKeysController < Admin::ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
@deploy_key = deploy_keys.new(create_params.merge(user: current_user))
|
||||
|
||||
if @deploy_key.save
|
||||
@deploy_key = DeployKeys::CreateService.new(current_user, create_params.merge(public: true)).execute
|
||||
if @deploy_key.persisted?
|
||||
redirect_to admin_deploy_keys_path
|
||||
else
|
||||
render 'new'
|
||||
|
|
|
@ -29,7 +29,7 @@ class Admin::LabelsController < Admin::ApplicationController
|
|||
@label = Labels::UpdateService.new(label_params).execute(@label)
|
||||
|
||||
if @label.valid?
|
||||
redirect_to admin_labels_path, notice: 'label was successfully updated.'
|
||||
redirect_to admin_labels_path, notice: 'Label was successfully updated.'
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
|
|
|
@ -3,31 +3,10 @@ class AutocompleteController < ApplicationController
|
|||
|
||||
skip_before_action :authenticate_user!, only: [:users, :award_emojis]
|
||||
before_action :load_project, only: [:users]
|
||||
before_action :find_users, only: [:users]
|
||||
before_action :load_group, only: [:users]
|
||||
|
||||
def users
|
||||
@users ||= User.none
|
||||
@users = @users.active
|
||||
@users = @users.reorder(:name)
|
||||
@users = @users.search(params[:search]) if params[:search].present?
|
||||
@users = @users.where.not(id: params[:skip_users]) if params[:skip_users].present?
|
||||
@users = @users.page(params[:page]).per(params[:per_page])
|
||||
|
||||
if params[:todo_filter].present? && current_user
|
||||
@users = @users.todo_authors(current_user.id, params[:todo_state_filter])
|
||||
end
|
||||
|
||||
if params[:search].blank?
|
||||
# Include current user if available to filter by "Me"
|
||||
if params[:current_user].present? && current_user
|
||||
@users = [current_user, *@users].uniq
|
||||
end
|
||||
|
||||
if params[:author_id].present? && current_user
|
||||
author = User.find_by_id(params[:author_id])
|
||||
@users = [author, *@users].uniq if author
|
||||
end
|
||||
end
|
||||
@users = AutocompleteUsersFinder.new(params: params, current_user: current_user, project: @project, group: @group).execute
|
||||
|
||||
render json: @users, only: [:name, :username, :id], methods: [:avatar_url]
|
||||
end
|
||||
|
@ -60,26 +39,14 @@ class AutocompleteController < ApplicationController
|
|||
|
||||
private
|
||||
|
||||
def find_users
|
||||
@users =
|
||||
if @project
|
||||
user_ids = @project.team.users.pluck(:id)
|
||||
|
||||
if params[:author_id].present?
|
||||
user_ids << params[:author_id]
|
||||
end
|
||||
|
||||
User.where(id: user_ids)
|
||||
elsif params[:group_id].present?
|
||||
def load_group
|
||||
@group ||= begin
|
||||
if @project.blank? && params[:group_id].present?
|
||||
group = Group.find(params[:group_id])
|
||||
return render_404 unless can?(current_user, :read_group, group)
|
||||
|
||||
group.users
|
||||
elsif current_user
|
||||
User.all
|
||||
else
|
||||
User.none
|
||||
group
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def load_project
|
||||
|
|
|
@ -11,9 +11,15 @@ module Boards
|
|||
issues = Boards::Issues::ListService.new(board_parent, current_user, filter_params).execute
|
||||
issues = issues.page(params[:page]).per(params[:per] || 20)
|
||||
make_sure_position_is_set(issues)
|
||||
issues = issues.preload(:project,
|
||||
:milestone,
|
||||
:assignees,
|
||||
labels: [:priorities],
|
||||
notes: [:award_emoji, :author]
|
||||
)
|
||||
|
||||
render json: {
|
||||
issues: serialize_as_json(issues.preload(:project)),
|
||||
issues: serialize_as_json(issues),
|
||||
size: issues.total_count
|
||||
}
|
||||
end
|
||||
|
@ -76,14 +82,13 @@ module Boards
|
|||
|
||||
def serialize_as_json(resource)
|
||||
resource.as_json(
|
||||
labels: true,
|
||||
only: [:id, :iid, :project_id, :title, :confidential, :due_date, :relative_position],
|
||||
labels: true,
|
||||
include: {
|
||||
project: { only: [:id, :path] },
|
||||
assignees: { only: [:id, :name, :username], methods: [:avatar_url] },
|
||||
milestone: { only: [:id, :title] }
|
||||
},
|
||||
user: current_user
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,9 +7,9 @@ class Profiles::GpgKeysController < Profiles::ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
@gpg_key = current_user.gpg_keys.new(gpg_key_params)
|
||||
@gpg_key = GpgKeys::CreateService.new(current_user, gpg_key_params).execute
|
||||
|
||||
if @gpg_key.save
|
||||
if @gpg_key.persisted?
|
||||
redirect_to profile_gpg_keys_path
|
||||
else
|
||||
@gpg_keys = current_user.gpg_keys.select(&:persisted?)
|
||||
|
|
|
@ -11,9 +11,9 @@ class Profiles::KeysController < Profiles::ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
@key = current_user.keys.new(key_params)
|
||||
@key = Keys::CreateService.new(current_user, key_params).execute
|
||||
|
||||
if @key.save
|
||||
if @key.persisted?
|
||||
redirect_to profile_key_path(@key)
|
||||
else
|
||||
@keys = current_user.keys.select(&:persisted?)
|
||||
|
|
|
@ -38,7 +38,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
|
|||
end
|
||||
|
||||
def set_index_vars
|
||||
@scopes = Gitlab::Auth::AVAILABLE_SCOPES
|
||||
@scopes = Gitlab::Auth.available_scopes
|
||||
|
||||
@personal_access_token = finder.build
|
||||
@inactive_personal_access_tokens = finder(state: 'inactive').execute
|
||||
|
|
|
@ -27,7 +27,7 @@ class Projects::CompareController < Projects::ApplicationController
|
|||
|
||||
def create
|
||||
if params[:from].blank? || params[:to].blank?
|
||||
flash[:alert] = "You must select from and to branches"
|
||||
flash[:alert] = "You must select a Source and a Target revision"
|
||||
from_to_vars = {
|
||||
from: params[:from].presence,
|
||||
to: params[:to].presence
|
||||
|
|
|
@ -22,7 +22,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
@key = DeployKey.new(create_params.merge(user: current_user))
|
||||
@key = DeployKeys::CreateService.new(current_user, create_params).execute
|
||||
|
||||
unless @key.valid? && @project.deploy_keys << @key
|
||||
flash[:alert] = @key.errors.full_messages.join(', ').html_safe
|
||||
|
|
|
@ -87,9 +87,9 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
.inc_relations_for_view
|
||||
.includes(:noteable)
|
||||
.fresh
|
||||
.reject { |n| n.cross_reference_not_visible_for?(current_user) }
|
||||
|
||||
prepare_notes_for_rendering(notes)
|
||||
notes = prepare_notes_for_rendering(notes)
|
||||
notes = notes.reject { |n| n.cross_reference_not_visible_for?(current_user) }
|
||||
|
||||
discussions = Discussion.build_collection(notes, @issue)
|
||||
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
class AutocompleteUsersFinder
|
||||
attr_reader :current_user, :project, :group, :search, :skip_users,
|
||||
:page, :per_page, :author_id, :params
|
||||
|
||||
def initialize(params:, current_user:, project:, group:)
|
||||
@current_user = current_user
|
||||
@project = project
|
||||
@group = group
|
||||
@search = params[:search]
|
||||
@skip_users = params[:skip_users]
|
||||
@page = params[:page]
|
||||
@per_page = params[:per_page]
|
||||
@author_id = params[:author_id]
|
||||
@params = params
|
||||
end
|
||||
|
||||
def execute
|
||||
items = find_users
|
||||
items = items.active
|
||||
items = items.reorder(:name)
|
||||
items = items.search(search) if search.present?
|
||||
items = items.where.not(id: skip_users) if skip_users.present?
|
||||
items = items.page(page).per(per_page)
|
||||
|
||||
if params[:todo_filter].present? && current_user
|
||||
items = items.todo_authors(current_user.id, params[:todo_state_filter])
|
||||
end
|
||||
|
||||
if search.blank?
|
||||
# Include current user if available to filter by "Me"
|
||||
if params[:current_user].present? && current_user
|
||||
items = [current_user, *items].uniq
|
||||
end
|
||||
|
||||
if author_id.present? && current_user
|
||||
author = User.find_by_id(author_id)
|
||||
items = [author, *items].uniq if author
|
||||
end
|
||||
end
|
||||
|
||||
items
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_users
|
||||
return users_from_project if project
|
||||
return group.users if group
|
||||
return User.all if current_user
|
||||
|
||||
User.none
|
||||
end
|
||||
|
||||
def users_from_project
|
||||
user_ids = project.team.users.pluck(:id)
|
||||
user_ids << author_id if author_id.present?
|
||||
|
||||
User.where(id: user_ids)
|
||||
end
|
||||
end
|
|
@ -77,4 +77,8 @@ module BoardsHelper
|
|||
'max-select': dropdown_options[:data][:'max-select']
|
||||
}
|
||||
end
|
||||
|
||||
def boards_link_text
|
||||
_("Board")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,7 +21,7 @@ module GroupsHelper
|
|||
|
||||
group.ancestors.reverse.each_with_index do |parent, index|
|
||||
if index > 0
|
||||
add_to_breadcrumb_dropdown(group_title_link(parent, hidable: false, show_avatar: true), location: :before)
|
||||
add_to_breadcrumb_dropdown(group_title_link(parent, hidable: false, show_avatar: true, for_dropdown: true), location: :before)
|
||||
else
|
||||
full_title += breadcrumb_list_item group_title_link(parent, hidable: false)
|
||||
end
|
||||
|
@ -85,8 +85,8 @@ module GroupsHelper
|
|||
|
||||
private
|
||||
|
||||
def group_title_link(group, hidable: false, show_avatar: false)
|
||||
link_to(group_path(group), class: "group-path breadcrumb-item-text js-breadcrumb-item-text #{'hidable' if hidable}") do
|
||||
def group_title_link(group, hidable: false, show_avatar: false, for_dropdown: false)
|
||||
link_to(group_path(group), class: "group-path #{'breadcrumb-item-text' unless for_dropdown} js-breadcrumb-item-text #{'hidable' if hidable}") do
|
||||
output =
|
||||
if (group.try(:avatar_url) || show_avatar) && !Rails.env.test?
|
||||
image_tag(group_icon(group), class: "avatar-tile", width: 15, height: 15)
|
||||
|
|
|
@ -119,8 +119,4 @@ module TabHelper
|
|||
|
||||
'active' if current_controller?('oauth/applications')
|
||||
end
|
||||
|
||||
def sidebar_link(href, title: nil, css: nil, &block)
|
||||
link_to capture(&block), href, title: (title if collapsed_sidebar?), class: css, aria: { label: title }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -453,6 +453,10 @@ module Ci
|
|||
.fabricate!
|
||||
end
|
||||
|
||||
def latest_builds_with_artifacts
|
||||
@latest_builds_with_artifacts ||= builds.latest.with_artifacts
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ci_yaml_from_repo
|
||||
|
|
|
@ -28,10 +28,4 @@ class DeployKey < Key
|
|||
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
|
||||
|
|
|
@ -6,7 +6,10 @@ class Environment < ActiveRecord::Base
|
|||
|
||||
belongs_to :project, required: true, validate: true
|
||||
|
||||
has_many :deployments, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :deployments,
|
||||
-> (env) { where(project_id: env.project_id) },
|
||||
dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
|
||||
|
||||
has_one :last_deployment, -> { order('deployments.id DESC') }, class_name: 'Deployment'
|
||||
|
||||
before_validation :nullify_external_url
|
||||
|
|
|
@ -36,7 +36,6 @@ class GpgKey < ActiveRecord::Base
|
|||
|
||||
before_validation :extract_fingerprint, :extract_primary_keyid
|
||||
after_commit :update_invalid_gpg_signatures, on: :create
|
||||
after_commit :notify_user, on: :create
|
||||
|
||||
def primary_keyid
|
||||
super&.upcase
|
||||
|
@ -107,8 +106,4 @@ class GpgKey < ActiveRecord::Base
|
|||
# only allows one key
|
||||
self.primary_keyid = Gitlab::Gpg.primary_keyids_from_key(key).first
|
||||
end
|
||||
|
||||
def notify_user
|
||||
NotificationService.new.new_gpg_key(self)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,7 +28,6 @@ class Key < ActiveRecord::Base
|
|||
delegate :name, :email, to: :user, prefix: true
|
||||
|
||||
after_commit :add_to_shell, on: :create
|
||||
after_commit :notify_user, on: :create
|
||||
after_create :post_create_hook
|
||||
after_commit :remove_from_shell, on: :destroy
|
||||
after_destroy :post_destroy_hook
|
||||
|
@ -118,8 +117,4 @@ class Key < ActiveRecord::Base
|
|||
|
||||
"type is forbidden. Must be #{allowed_types}"
|
||||
end
|
||||
|
||||
def notify_user
|
||||
NotificationService.new.new_key(self)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -127,7 +127,12 @@ class Label < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def priority(project)
|
||||
priorities.find_by(project: project).try(:priority)
|
||||
priority = if priorities.loaded?
|
||||
priorities.first { |p| p.project == project }
|
||||
else
|
||||
priorities.find_by(project: project)
|
||||
end
|
||||
priority.try(:priority)
|
||||
end
|
||||
|
||||
def template?
|
||||
|
|
|
@ -231,6 +231,13 @@ class Namespace < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def force_share_with_group_lock_on_descendants
|
||||
descendants.update_all(share_with_group_lock: true)
|
||||
return unless Group.supports_nested_groups?
|
||||
|
||||
# We can't use `descendants.update_all` since Rails will throw away the WITH
|
||||
# RECURSIVE statement. We also can't use WHERE EXISTS since we can't use
|
||||
# different table aliases, hence we're just using WHERE IN. Since we have a
|
||||
# maximum of 20 nested groups this should be fine.
|
||||
Namespace.where(id: descendants.select(:id))
|
||||
.update_all(share_with_group_lock: true)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,7 +28,7 @@ class PersonalAccessToken < ActiveRecord::Base
|
|||
protected
|
||||
|
||||
def validate_scopes
|
||||
unless revoked || scopes.all? { |scope| Gitlab::Auth::AVAILABLE_SCOPES.include?(scope.to_sym) }
|
||||
unless revoked || scopes.all? { |scope| Gitlab::Auth.available_scopes.include?(scope.to_sym) }
|
||||
errors.add :scopes, "can only contain available scopes"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -161,7 +161,7 @@ class Project < ActiveRecord::Base
|
|||
has_many :notification_settings, as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
|
||||
|
||||
has_one :import_data, class_name: 'ProjectImportData', inverse_of: :project, autosave: true
|
||||
has_one :project_feature
|
||||
has_one :project_feature, inverse_of: :project
|
||||
has_one :statistics, class_name: 'ProjectStatistics'
|
||||
|
||||
# Container repositories need to remove data from the container registry,
|
||||
|
@ -190,7 +190,7 @@ class Project < ActiveRecord::Base
|
|||
has_one :auto_devops, class_name: 'ProjectAutoDevops'
|
||||
|
||||
accepts_nested_attributes_for :variables, allow_destroy: true
|
||||
accepts_nested_attributes_for :project_feature
|
||||
accepts_nested_attributes_for :project_feature, update_only: true
|
||||
accepts_nested_attributes_for :import_data
|
||||
accepts_nested_attributes_for :auto_devops
|
||||
|
||||
|
@ -1163,6 +1163,23 @@ class Project < ActiveRecord::Base
|
|||
pipelines.order(id: :desc).find_by(sha: sha, ref: ref)
|
||||
end
|
||||
|
||||
def latest_successful_pipeline_for_default_branch
|
||||
if defined?(@latest_successful_pipeline_for_default_branch)
|
||||
return @latest_successful_pipeline_for_default_branch
|
||||
end
|
||||
|
||||
@latest_successful_pipeline_for_default_branch =
|
||||
pipelines.latest_successful_for(default_branch)
|
||||
end
|
||||
|
||||
def latest_successful_pipeline_for(ref = nil)
|
||||
if ref && ref != default_branch
|
||||
pipelines.latest_successful_for(ref)
|
||||
else
|
||||
latest_successful_pipeline_for_default_branch
|
||||
end
|
||||
end
|
||||
|
||||
def enable_ci
|
||||
project_feature.update_attribute(:builds_access_level, ProjectFeature::ENABLED)
|
||||
end
|
||||
|
|
|
@ -41,6 +41,8 @@ class ProjectFeature < ActiveRecord::Base
|
|||
# http://stackoverflow.com/questions/1540645/how-to-disable-default-scope-for-a-belongs-to
|
||||
belongs_to :project, -> { unscope(where: :pending_delete) }
|
||||
|
||||
validates :project, presence: true
|
||||
|
||||
validate :repository_children_level
|
||||
|
||||
default_value_for :builds_access_level, value: ENABLED, allows_nil: false
|
||||
|
|
|
@ -80,6 +80,6 @@ class PipelinesEmailService < Service
|
|||
end
|
||||
|
||||
def retrieve_recipients(data)
|
||||
recipients.to_s.split(',').reject(&:blank?)
|
||||
recipients.to_s.split(/[,(?:\r?\n) ]+/).reject(&:empty?)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -146,7 +146,7 @@ class ProjectTeam
|
|||
def member?(user, min_access_level = Gitlab::Access::GUEST)
|
||||
return false unless user
|
||||
|
||||
user.authorized_project?(project, min_access_level)
|
||||
max_member_access(user.id) >= min_access_level
|
||||
end
|
||||
|
||||
def human_max_access(user_id)
|
||||
|
|
|
@ -90,6 +90,12 @@ class Repository
|
|||
)
|
||||
end
|
||||
|
||||
# we need to have this method here because it is not cached in ::Git and
|
||||
# the method is called multiple times for every request
|
||||
def has_visible_content?
|
||||
branch_count > 0
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class.name}:#{@disk_path}>"
|
||||
end
|
||||
|
@ -166,7 +172,7 @@ class Repository
|
|||
end
|
||||
|
||||
def add_branch(user, branch_name, ref)
|
||||
branch = raw_repository.add_branch(branch_name, committer: user, target: ref)
|
||||
branch = raw_repository.add_branch(branch_name, user: user, target: ref)
|
||||
|
||||
after_create_branch
|
||||
|
||||
|
@ -176,7 +182,7 @@ class Repository
|
|||
end
|
||||
|
||||
def add_tag(user, tag_name, target, message = nil)
|
||||
raw_repository.add_tag(tag_name, committer: user, target: target, message: message)
|
||||
raw_repository.add_tag(tag_name, user: user, target: target, message: message)
|
||||
rescue Gitlab::Git::Repository::InvalidRef
|
||||
false
|
||||
end
|
||||
|
@ -184,7 +190,7 @@ class Repository
|
|||
def rm_branch(user, branch_name)
|
||||
before_remove_branch
|
||||
|
||||
raw_repository.rm_branch(branch_name, committer: user)
|
||||
raw_repository.rm_branch(branch_name, user: user)
|
||||
|
||||
after_remove_branch
|
||||
true
|
||||
|
@ -193,7 +199,7 @@ class Repository
|
|||
def rm_tag(user, tag_name)
|
||||
before_remove_tag
|
||||
|
||||
raw_repository.rm_tag(tag_name, committer: user)
|
||||
raw_repository.rm_tag(tag_name, user: user)
|
||||
|
||||
after_remove_tag
|
||||
true
|
||||
|
@ -762,17 +768,23 @@ class Repository
|
|||
multi_action(**options)
|
||||
end
|
||||
|
||||
def with_cache_hooks
|
||||
result = yield
|
||||
|
||||
return unless result
|
||||
|
||||
after_create if result.repo_created?
|
||||
after_create_branch if result.branch_created?
|
||||
|
||||
result.newrev
|
||||
end
|
||||
|
||||
def with_branch(user, *args)
|
||||
result = Gitlab::Git::OperationService.new(user, raw_repository).with_branch(*args) do |start_commit|
|
||||
yield start_commit
|
||||
with_cache_hooks do
|
||||
Gitlab::Git::OperationService.new(user, raw_repository).with_branch(*args) do |start_commit|
|
||||
yield start_commit
|
||||
end
|
||||
end
|
||||
|
||||
newrev, should_run_after_create, should_run_after_create_branch = result
|
||||
|
||||
after_create if should_run_after_create
|
||||
after_create_branch if should_run_after_create_branch
|
||||
|
||||
newrev
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/ParameterLists
|
||||
|
@ -837,30 +849,13 @@ class Repository
|
|||
end
|
||||
end
|
||||
|
||||
def merge(user, source, merge_request, options = {})
|
||||
with_branch(
|
||||
user,
|
||||
merge_request.target_branch) do |start_commit|
|
||||
our_commit = start_commit.sha
|
||||
their_commit = source
|
||||
|
||||
raise 'Invalid merge target' unless our_commit
|
||||
raise 'Invalid merge source' unless their_commit
|
||||
|
||||
merge_index = rugged.merge_commits(our_commit, their_commit)
|
||||
break if merge_index.conflicts?
|
||||
|
||||
actual_options = options.merge(
|
||||
parents: [our_commit, their_commit],
|
||||
tree: merge_index.write_tree(rugged)
|
||||
)
|
||||
|
||||
commit_id = create_commit(actual_options)
|
||||
merge_request.update(in_progress_merge_commit_sha: commit_id)
|
||||
commit_id
|
||||
def merge(user, source_sha, merge_request, message)
|
||||
with_cache_hooks do
|
||||
raw_repository.merge(user, source_sha, merge_request.target_branch, message) do |commit_id|
|
||||
merge_request.update(in_progress_merge_commit_sha: commit_id)
|
||||
nil # Return value does not matter.
|
||||
end
|
||||
end
|
||||
rescue Gitlab::Git::CommitError # when merge_index.conflicts?
|
||||
false
|
||||
end
|
||||
|
||||
def revert(
|
||||
|
@ -1151,12 +1146,6 @@ class Repository
|
|||
Gitlab::Metrics.add_event(event, { path: full_path }.merge(tags))
|
||||
end
|
||||
|
||||
def create_commit(params = {})
|
||||
params[:message].delete!("\r")
|
||||
|
||||
Rugged::Commit.create(rugged, params)
|
||||
end
|
||||
|
||||
def last_commit_for_path_by_gitaly(sha, path)
|
||||
c = raw_repository.gitaly_commit_client.last_commit_for_path(sha, path)
|
||||
commit(c)
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
module DeployKeys
|
||||
class CreateService < Keys::BaseService
|
||||
def execute
|
||||
DeployKey.create(params.merge(user: user))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
module GpgKeys
|
||||
class CreateService < Keys::BaseService
|
||||
def execute
|
||||
key = user.gpg_keys.create(params)
|
||||
notification_service.new_gpg_key(key) if key.persisted?
|
||||
key
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
module Keys
|
||||
class BaseService
|
||||
attr_accessor :user, :params
|
||||
|
||||
def initialize(user, params)
|
||||
@user, @params = user, params
|
||||
end
|
||||
|
||||
def notification_service
|
||||
NotificationService.new
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
module Keys
|
||||
class CreateService < ::Keys::BaseService
|
||||
def execute
|
||||
key = user.keys.create(params)
|
||||
notification_service.new_key(key) if key.persisted?
|
||||
key
|
||||
end
|
||||
end
|
||||
end
|
|
@ -38,15 +38,9 @@ module MergeRequests
|
|||
private
|
||||
|
||||
def commit
|
||||
committer = repository.user_to_committer(current_user)
|
||||
message = params[:commit_message] || merge_request.merge_commit_message
|
||||
|
||||
options = {
|
||||
message: params[:commit_message] || merge_request.merge_commit_message,
|
||||
author: committer,
|
||||
committer: committer
|
||||
}
|
||||
|
||||
commit_id = repository.merge(current_user, source, merge_request, options)
|
||||
commit_id = repository.merge(current_user, source, merge_request, message)
|
||||
|
||||
raise MergeError, 'Conflicts detected during merge' unless commit_id
|
||||
|
||||
|
|
|
@ -24,7 +24,10 @@ module Projects
|
|||
|
||||
success
|
||||
else
|
||||
error('Project could not be updated!')
|
||||
model_errors = project.errors.full_messages.to_sentence
|
||||
error_message = model_errors.presence || 'Project could not be updated!'
|
||||
|
||||
error(error_message)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
%span.light
|
||||
- has_icon = provider_has_icon?(provider)
|
||||
= link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: 'oauth-login' + (has_icon ? ' oauth-image-link' : ' btn'), id: "oauth-login-#{provider}"
|
||||
%fieldset.prepend-top-10
|
||||
= check_box_tag :remember_me
|
||||
= label_tag :remember_me, 'Remember me'
|
||||
%fieldset.prepend-top-10.checkbox.remember-me
|
||||
%label
|
||||
= check_box_tag :remember_me, nil, false, class: 'remember-me-checkbox'
|
||||
%span
|
||||
Remember me
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
.sidebar-context-title Admin Area
|
||||
%ul.sidebar-top-level-items
|
||||
= nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts conversational_development_index), html_options: {class: 'home'}) do
|
||||
= sidebar_link admin_root_path, title: _('Overview'), css: 'shortcuts-tree' do
|
||||
= link_to admin_root_path, class: 'shortcuts-tree' do
|
||||
.nav-icon-container
|
||||
= custom_icon('overview')
|
||||
%span.nav-item-name
|
||||
|
@ -53,7 +53,7 @@
|
|||
ConvDev Index
|
||||
|
||||
= nav_link(controller: %w(system_info background_jobs logs health_check requests_profiles)) do
|
||||
= sidebar_link admin_system_info_path, title: _('Monitoring') do
|
||||
= link_to admin_system_info_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('monitoring')
|
||||
%span.nav-item-name
|
||||
|
@ -87,7 +87,7 @@
|
|||
Requests Profiles
|
||||
|
||||
= nav_link(controller: :broadcast_messages) do
|
||||
= sidebar_link admin_broadcast_messages_path, title: _('Messages') do
|
||||
= link_to admin_broadcast_messages_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('messages')
|
||||
%span.nav-item-name
|
||||
|
@ -99,7 +99,7 @@
|
|||
#{ _('Messages') }
|
||||
|
||||
= nav_link(controller: [:hooks, :hook_logs]) do
|
||||
= sidebar_link admin_hooks_path, title: _('Hooks') do
|
||||
= link_to admin_hooks_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('system_hooks')
|
||||
%span.nav-item-name
|
||||
|
@ -111,7 +111,7 @@
|
|||
#{ _('System Hooks') }
|
||||
|
||||
= nav_link(controller: :applications) do
|
||||
= sidebar_link admin_applications_path, title: _('Applications') do
|
||||
= link_to admin_applications_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('applications')
|
||||
%span.nav-item-name
|
||||
|
@ -123,7 +123,7 @@
|
|||
#{ _('Applications') }
|
||||
|
||||
= nav_link(controller: :abuse_reports) do
|
||||
= sidebar_link admin_abuse_reports_path, title: _("Abuse Reports") do
|
||||
= link_to admin_abuse_reports_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('abuse_reports')
|
||||
%span.nav-item-name
|
||||
|
@ -138,7 +138,7 @@
|
|||
|
||||
- if akismet_enabled?
|
||||
= nav_link(controller: :spam_logs) do
|
||||
= sidebar_link admin_spam_logs_path, title: _("Spam Logs") do
|
||||
= link_to admin_spam_logs_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('spam_logs')
|
||||
%span.nav-item-name
|
||||
|
@ -150,7 +150,7 @@
|
|||
#{ _('Spam Logs') }
|
||||
|
||||
= nav_link(controller: :deploy_keys) do
|
||||
= sidebar_link admin_deploy_keys_path, title: _('Deploy Keys') do
|
||||
= link_to admin_deploy_keys_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('key')
|
||||
%span.nav-item-name
|
||||
|
@ -162,7 +162,7 @@
|
|||
#{ _('Deploy Keys') }
|
||||
|
||||
= nav_link(controller: :services) do
|
||||
= sidebar_link admin_application_settings_services_path, title: _('Service Templates') do
|
||||
= link_to admin_application_settings_services_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('service_templates')
|
||||
%span.nav-item-name
|
||||
|
@ -174,7 +174,7 @@
|
|||
#{ _('Service Templates') }
|
||||
|
||||
= nav_link(controller: :labels) do
|
||||
= sidebar_link admin_labels_path, title: _('Labels') do
|
||||
= link_to admin_labels_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('labels')
|
||||
%span.nav-item-name
|
||||
|
@ -186,7 +186,7 @@
|
|||
#{ _('Labels') }
|
||||
|
||||
= nav_link(controller: :appearances) do
|
||||
= sidebar_link admin_appearances_path, title: _('Appearances') do
|
||||
= link_to admin_appearances_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('appearance')
|
||||
%span.nav-item-name
|
||||
|
@ -198,7 +198,7 @@
|
|||
#{ _('Appearance') }
|
||||
|
||||
= nav_link(controller: :application_settings) do
|
||||
= sidebar_link admin_application_settings_path, title: _('Settings') do
|
||||
= link_to admin_application_settings_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('settings')
|
||||
%span.nav-item-name
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
= @group.name
|
||||
%ul.sidebar-top-level-items
|
||||
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: 'home' }) do
|
||||
= sidebar_link group_path(@group), title: _('Group overview') do
|
||||
= link_to group_path(@group) do
|
||||
.nav-icon-container
|
||||
= custom_icon('project')
|
||||
%span.nav-item-name
|
||||
|
@ -34,7 +34,7 @@
|
|||
Activity
|
||||
|
||||
= nav_link(path: ['groups#issues', 'labels#index', 'milestones#index']) do
|
||||
= sidebar_link issues_group_path(@group), title: _('Issues') do
|
||||
= link_to issues_group_path(@group) do
|
||||
.nav-icon-container
|
||||
= custom_icon('issues')
|
||||
%span.nav-item-name
|
||||
|
@ -64,7 +64,7 @@
|
|||
Milestones
|
||||
|
||||
= nav_link(path: 'groups#merge_requests') do
|
||||
= sidebar_link merge_requests_group_path(@group), title: _('Merge Requests') do
|
||||
= link_to merge_requests_group_path(@group) do
|
||||
.nav-icon-container
|
||||
= custom_icon('mr_bold')
|
||||
%span.nav-item-name
|
||||
|
@ -77,19 +77,19 @@
|
|||
#{ _('Merge Requests') }
|
||||
%span.badge.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(merge_requests.count)
|
||||
= nav_link(path: 'group_members#index') do
|
||||
= sidebar_link group_group_members_path(@group), title: _('Members') do
|
||||
= link_to group_group_members_path(@group) do
|
||||
.nav-icon-container
|
||||
= custom_icon('members')
|
||||
%span.nav-item-name
|
||||
Members
|
||||
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||
= nav_link(path: 'group_members#index', html_options: { class: "fly-out-top-item" } ) do
|
||||
= link_to merge_requests_group_path(@group) do
|
||||
= link_to group_group_members_path(@group) do
|
||||
%strong.fly-out-top-item-name
|
||||
#{ _('Members') }
|
||||
- if current_user && can?(current_user, :admin_group, @group)
|
||||
= nav_link(path: %w[groups#projects groups#edit ci_cd#show]) do
|
||||
= sidebar_link edit_group_path(@group), title: _('Settings') do
|
||||
= link_to edit_group_path(@group) do
|
||||
.nav-icon-container
|
||||
= custom_icon('settings')
|
||||
%span.nav-item-name
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
.sidebar-context-title User Settings
|
||||
%ul.sidebar-top-level-items
|
||||
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
|
||||
= sidebar_link profile_path, title: _('Profile Settings') do
|
||||
= link_to profile_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('profile')
|
||||
%span.nav-item-name
|
||||
|
@ -18,7 +18,7 @@
|
|||
%strong.fly-out-top-item-name
|
||||
#{ _('Profile') }
|
||||
= nav_link(controller: [:accounts, :two_factor_auths]) do
|
||||
= sidebar_link profile_account_path, title: _('Account') do
|
||||
= link_to profile_account_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('account')
|
||||
%span.nav-item-name
|
||||
|
@ -30,7 +30,7 @@
|
|||
#{ _('Account') }
|
||||
- if current_application_settings.user_oauth_applications?
|
||||
= nav_link(controller: 'oauth/applications') do
|
||||
= sidebar_link applications_profile_path, title: _('Applications') do
|
||||
= link_to applications_profile_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('applications')
|
||||
%span.nav-item-name
|
||||
|
@ -41,7 +41,7 @@
|
|||
%strong.fly-out-top-item-name
|
||||
#{ _('Applications') }
|
||||
= nav_link(controller: :chat_names) do
|
||||
= sidebar_link profile_chat_names_path, title: _('Chat') do
|
||||
= link_to profile_chat_names_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('chat')
|
||||
%span.nav-item-name
|
||||
|
@ -52,7 +52,7 @@
|
|||
%strong.fly-out-top-item-name
|
||||
#{ _('Chat') }
|
||||
= nav_link(controller: :personal_access_tokens) do
|
||||
= sidebar_link profile_personal_access_tokens_path, title: _('Access Tokens') do
|
||||
= link_to profile_personal_access_tokens_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('access_tokens')
|
||||
%span.nav-item-name
|
||||
|
@ -63,7 +63,7 @@
|
|||
%strong.fly-out-top-item-name
|
||||
#{ _('Access Tokens') }
|
||||
= nav_link(controller: :emails) do
|
||||
= sidebar_link profile_emails_path, title: _('Emails') do
|
||||
= link_to profile_emails_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('emails')
|
||||
%span.nav-item-name
|
||||
|
@ -75,7 +75,7 @@
|
|||
#{ _('Emails') }
|
||||
- unless current_user.ldap_user?
|
||||
= nav_link(controller: :passwords) do
|
||||
= sidebar_link edit_profile_password_path, title: _('Password') do
|
||||
= link_to edit_profile_password_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('lock')
|
||||
%span.nav-item-name
|
||||
|
@ -86,7 +86,7 @@
|
|||
%strong.fly-out-top-item-name
|
||||
#{ _('Password') }
|
||||
= nav_link(controller: :notifications) do
|
||||
= sidebar_link profile_notifications_path, title: _('Notifications') do
|
||||
= link_to profile_notifications_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('notifications')
|
||||
%span.nav-item-name
|
||||
|
@ -97,7 +97,7 @@
|
|||
%strong.fly-out-top-item-name
|
||||
#{ _('Notifications') }
|
||||
= nav_link(controller: :keys) do
|
||||
= sidebar_link profile_keys_path, title: _('SSH Keys') do
|
||||
= link_to profile_keys_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('key')
|
||||
%span.nav-item-name
|
||||
|
@ -108,7 +108,7 @@
|
|||
%strong.fly-out-top-item-name
|
||||
#{ _('SSH Keys') }
|
||||
= nav_link(controller: :gpg_keys) do
|
||||
= sidebar_link profile_gpg_keys_path, title: _('GPG Keys') do
|
||||
= link_to profile_gpg_keys_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('key_2')
|
||||
%span.nav-item-name
|
||||
|
@ -119,7 +119,7 @@
|
|||
%strong.fly-out-top-item-name
|
||||
#{ _('GPG Keys') }
|
||||
= nav_link(controller: :preferences) do
|
||||
= sidebar_link profile_preferences_path, title: _('Preferences') do
|
||||
= link_to profile_preferences_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('preferences')
|
||||
%span.nav-item-name
|
||||
|
@ -130,7 +130,7 @@
|
|||
%strong.fly-out-top-item-name
|
||||
#{ _('Preferences') }
|
||||
= nav_link(path: 'profiles#audit_log') do
|
||||
= sidebar_link audit_log_profile_path, title: _('Authentication log') do
|
||||
= link_to audit_log_profile_path do
|
||||
.nav-icon-container
|
||||
= custom_icon('authentication_log')
|
||||
%span.nav-item-name
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
= @project.name
|
||||
%ul.sidebar-top-level-items
|
||||
= nav_link(path: ['projects#show', 'projects#activity', 'cycle_analytics#show'], html_options: { class: 'home' }) do
|
||||
= sidebar_link project_path(@project), title: _('Project overview'), css: 'shortcuts-project' do
|
||||
= link_to project_path(@project), class: 'shortcuts-project' do
|
||||
.nav-icon-container
|
||||
= custom_icon('project')
|
||||
%span.nav-item-name
|
||||
|
@ -36,7 +36,7 @@
|
|||
|
||||
- if project_nav_tab? :files
|
||||
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare projects/repositories tags branches releases graphs network)) do
|
||||
= sidebar_link project_tree_path(@project), title: _('Repository'), css: 'shortcuts-tree' do
|
||||
= link_to project_tree_path(@project), class: 'shortcuts-tree' do
|
||||
.nav-icon-container
|
||||
= custom_icon('doc_text')
|
||||
%span.nav-item-name
|
||||
|
@ -82,7 +82,7 @@
|
|||
|
||||
- if project_nav_tab? :container_registry
|
||||
= nav_link(controller: %w[projects/registry/repositories]) do
|
||||
= sidebar_link project_container_registry_index_path(@project), title: _('Container Registry'), css: 'shortcuts-container-registry' do
|
||||
= link_to project_container_registry_index_path(@project), class: 'shortcuts-container-registry' do
|
||||
.nav-icon-container
|
||||
= custom_icon('container_registry')
|
||||
%span.nav-item-name
|
||||
|
@ -90,7 +90,7 @@
|
|||
|
||||
- if project_nav_tab? :issues
|
||||
= nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do
|
||||
= sidebar_link project_issues_path(@project), title: _('Issues'), css: 'shortcuts-issues' do
|
||||
= link_to project_issues_path(@project), class: 'shortcuts-issues' do
|
||||
.nav-icon-container
|
||||
= custom_icon('issues')
|
||||
%span.nav-item-name
|
||||
|
@ -114,9 +114,9 @@
|
|||
List
|
||||
|
||||
= nav_link(controller: :boards) do
|
||||
= link_to project_boards_path(@project), title: 'Board' do
|
||||
= link_to project_boards_path(@project), title: boards_link_text do
|
||||
%span
|
||||
Board
|
||||
= boards_link_text
|
||||
.feature-highlight.js-feature-highlight{ disabled: true, data: { trigger: 'manual', container: 'body', toggle: 'popover', placement: 'right', highlight: 'issue-boards' } }
|
||||
.feature-highlight-popover-content
|
||||
= render 'feature_highlight/issue_boards.svg'
|
||||
|
@ -144,7 +144,7 @@
|
|||
|
||||
- if project_nav_tab? :merge_requests
|
||||
= nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do
|
||||
= sidebar_link project_merge_requests_path(@project), title: _('Merge Requests'), css: 'shortcuts-merge_requests' do
|
||||
= link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests' do
|
||||
.nav-icon-container
|
||||
= custom_icon('mr_bold')
|
||||
%span.nav-item-name
|
||||
|
@ -161,7 +161,7 @@
|
|||
|
||||
- if project_nav_tab? :pipelines
|
||||
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do
|
||||
= sidebar_link project_pipelines_path(@project), title: _('CI / CD'), css: 'shortcuts-pipelines' do
|
||||
= link_to project_pipelines_path(@project), class: 'shortcuts-pipelines' do
|
||||
.nav-icon-container
|
||||
= custom_icon('pipeline')
|
||||
%span.nav-item-name
|
||||
|
@ -205,7 +205,7 @@
|
|||
|
||||
- if project_nav_tab? :wiki
|
||||
= nav_link(controller: :wikis) do
|
||||
= sidebar_link get_project_wiki_path(@project), title: _('Wiki'), css: 'shortcuts-wiki' do
|
||||
= link_to get_project_wiki_path(@project), class: 'shortcuts-wiki' do
|
||||
.nav-icon-container
|
||||
= custom_icon('wiki')
|
||||
%span.nav-item-name
|
||||
|
@ -218,7 +218,7 @@
|
|||
|
||||
- if project_nav_tab? :snippets
|
||||
= nav_link(controller: :snippets) do
|
||||
= sidebar_link project_snippets_path(@project), title: _('Snippets'), css: 'shortcuts-snippets' do
|
||||
= link_to project_snippets_path(@project), class: 'shortcuts-snippets' do
|
||||
.nav-icon-container
|
||||
= custom_icon('snippets')
|
||||
%span.nav-item-name
|
||||
|
@ -231,7 +231,7 @@
|
|||
|
||||
- if project_nav_tab? :settings
|
||||
= nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show]) do
|
||||
= sidebar_link edit_project_path(@project), title: _('Settings'), css: 'shortcuts-tree' do
|
||||
= link_to edit_project_path(@project), class: 'shortcuts-tree' do
|
||||
.nav-icon-container
|
||||
= custom_icon('settings')
|
||||
%span.nav-item-name
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
.preview-row
|
||||
.quadrant.three
|
||||
.quadrant.four
|
||||
= f.radio_button :theme_id, theme.id
|
||||
= f.radio_button :theme_id, theme.id, checked: Gitlab::Themes.for_user(@user).id == theme.id
|
||||
= theme.name
|
||||
|
||||
.col-sm-12
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.file-content.blob_file.blob-no-preview
|
||||
.center
|
||||
.center.render-error.vertical-center
|
||||
= link_to blob_raw_path do
|
||||
%h1.light
|
||||
= icon('download')
|
||||
|
|
|
@ -43,7 +43,8 @@
|
|||
data: { toggle: "modal",
|
||||
target: "#modal-delete-branch",
|
||||
delete_path: project_branch_path(@project, branch.name),
|
||||
branch_name: branch.name } }
|
||||
branch_name: branch.name,
|
||||
is_merged: ("true" if @repository.merged_to_root_ref?(branch.name)) } }
|
||||
= icon("trash-o")
|
||||
- else
|
||||
%button{ class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip disabled",
|
||||
|
|
|
@ -6,13 +6,18 @@
|
|||
%h3.page-title
|
||||
Delete protected branch
|
||||
= surround "'", "'?" do
|
||||
%span.js-branch-name>[branch name]
|
||||
%span.js-branch-name.ref-name>[branch name]
|
||||
|
||||
.modal-body
|
||||
%p
|
||||
You’re about to permanently delete the protected branch
|
||||
= succeed '.' do
|
||||
%strong.js-branch-name [branch name]
|
||||
%strong.js-branch-name.ref-name [branch name]
|
||||
%p.js-not-merged
|
||||
- default_branch = capture do
|
||||
%span.ref-name= @repository.root_ref
|
||||
= s_("Branches|This branch hasn’t been merged into %{default_branch}.").html_safe % { default_branch: default_branch }
|
||||
= s_("Branches|To avoid data loss, consider merging this branch before deleting it.")
|
||||
%p
|
||||
Once you confirm and press
|
||||
= succeed ',' do
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
- pipeline = local_assigns.fetch(:pipeline) { project.pipelines.latest_successful_for(ref) }
|
||||
- pipeline = local_assigns.fetch(:pipeline) { project.latest_successful_pipeline_for(ref) }
|
||||
|
||||
- if !project.empty_repo? && can?(current_user, :download_code, project)
|
||||
.project-action-button.dropdown.inline>
|
||||
|
@ -26,18 +26,16 @@
|
|||
%i.fa.fa-download
|
||||
%span= _('Download tar')
|
||||
|
||||
- if pipeline
|
||||
- artifacts = pipeline.builds.latest.with_artifacts
|
||||
- if artifacts.any?
|
||||
%li.dropdown-header Artifacts
|
||||
- unless pipeline.latest?
|
||||
- latest_pipeline = project.pipeline_for(ref)
|
||||
%li
|
||||
.unclickable= ci_status_for_statuseable(latest_pipeline)
|
||||
%li.dropdown-header Previous Artifacts
|
||||
- artifacts.each do |job|
|
||||
%li
|
||||
= link_to latest_succeeded_project_artifacts_path(project, "#{ref}/download", job: job.name), rel: 'nofollow', download: '' do
|
||||
%i.fa.fa-download
|
||||
%span
|
||||
#{ s_('DownloadArtifacts|Download') } '#{job.name}'
|
||||
- if pipeline && pipeline.latest_builds_with_artifacts.any?
|
||||
%li.dropdown-header Artifacts
|
||||
- unless pipeline.latest?
|
||||
- latest_pipeline = project.pipeline_for(ref)
|
||||
%li
|
||||
.unclickable= ci_status_for_statuseable(latest_pipeline)
|
||||
%li.dropdown-header Previous Artifacts
|
||||
- pipeline.latest_builds_with_artifacts.each do |job|
|
||||
%li
|
||||
= link_to latest_succeeded_project_artifacts_path(project, "#{ref}/download", job: job.name), rel: 'nofollow', download: '' do
|
||||
%i.fa.fa-download
|
||||
%span
|
||||
#{s_('DownloadArtifacts|Download')} '#{job.name}'
|
||||
|
|
|
@ -2,22 +2,22 @@
|
|||
.clearfix
|
||||
- if params[:to] && params[:from]
|
||||
.compare-switch-container
|
||||
= link_to icon('exchange'), {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has-tooltip btn btn-white', title: 'Switch base of comparison'}
|
||||
.form-group.dropdown.compare-form-group.from.js-compare-from-dropdown
|
||||
.input-group.inline-input-group
|
||||
%span.input-group-addon from
|
||||
= hidden_field_tag :from, params[:from]
|
||||
= button_tag type: 'button', title: params[:from], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip git-revision-dropdown-toggle", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do
|
||||
.dropdown-toggle-text.str-truncated= params[:from] || 'Select branch/tag'
|
||||
= render 'shared/ref_dropdown'
|
||||
.compare-ellipsis.inline ...
|
||||
= link_to icon('exchange'), { from: params[:to], to: params[:from] }, class: 'commits-compare-switch has-tooltip btn btn-white', title: 'Swap revisions'
|
||||
.form-group.dropdown.compare-form-group.to.js-compare-to-dropdown
|
||||
.input-group.inline-input-group
|
||||
%span.input-group-addon to
|
||||
%span.input-group-addon Source
|
||||
= hidden_field_tag :to, params[:to]
|
||||
= button_tag type: 'button', title: params[:to], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip git-revision-dropdown-toggle", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-to-dropdown", selected: params[:to], field_name: :to } do
|
||||
.dropdown-toggle-text.str-truncated= params[:to] || 'Select branch/tag'
|
||||
= render 'shared/ref_dropdown'
|
||||
.compare-ellipsis.inline ...
|
||||
.form-group.dropdown.compare-form-group.from.js-compare-from-dropdown
|
||||
.input-group.inline-input-group
|
||||
%span.input-group-addon Target
|
||||
= hidden_field_tag :from, params[:from]
|
||||
= button_tag type: 'button', title: params[:from], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip git-revision-dropdown-toggle", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do
|
||||
.dropdown-toggle-text.str-truncated= params[:from] || 'Select branch/tag'
|
||||
= render 'shared/ref_dropdown'
|
||||
|
||||
= button_tag "Compare", class: "btn btn-create commits-compare-btn"
|
||||
- if @merge_request.present?
|
||||
|
|
|
@ -7,13 +7,19 @@
|
|||
.sub-header-block
|
||||
Compare Git revisions.
|
||||
%br
|
||||
Fill input field with commit SHA like
|
||||
%code.ref-name 4eedf23
|
||||
or branch/tag name like
|
||||
%code.ref-name master
|
||||
and press compare button for the commits list and a code diff.
|
||||
Choose a branch/tag (e.g.
|
||||
= succeed ')' do
|
||||
%code.ref-name master
|
||||
or enter a commit SHA (e.g.
|
||||
= succeed ')' do
|
||||
%code.ref-name 4eedf23
|
||||
to see what's changed or to create a merge request.
|
||||
%br
|
||||
Changes are shown <b>from</b> the version in the first field <b>to</b> the version in the second field.
|
||||
Changes are shown as if the
|
||||
%b source
|
||||
revision was being merged into the
|
||||
%b target
|
||||
revision.
|
||||
|
||||
.prepend-top-20
|
||||
= render "form"
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
%ul
|
||||
- diff_files.each do |diff_file|
|
||||
%li
|
||||
%a{ href: "##{hexdigest(diff_file.file_path)}", title: diff_file.new_path }
|
||||
%a.diff-changed-file{ href: "##{hexdigest(diff_file.file_path)}", title: diff_file.new_path }
|
||||
= icon("#{diff_file_changed_icon(diff_file)} fw", class: "#{diff_file_changed_icon_color(diff_file)} append-right-5")
|
||||
%span.diff-file-changes-path= diff_file.new_path
|
||||
%span.diff-file-changes-path.append-right-5= diff_file.new_path
|
||||
.pull-right
|
||||
%span.cgreen<
|
||||
+#{diff_file.added_lines}
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
New project
|
||||
- if import_sources_enabled?
|
||||
%p
|
||||
Create or Import your project from popular Git services
|
||||
A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), #{link_to 'among other things', help_page_path("user/project/index.md", anchor: "projects-features"), target: '_blank'}.
|
||||
%p
|
||||
All features are enabled when you create a project, but you can disable the ones you don’t need in the project settings.
|
||||
.col-lg-9.js-toggle-container
|
||||
= form_for @project, html: { class: 'new_project' } do |f|
|
||||
.create-project-options
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
.col-sm-10
|
||||
.checkbox
|
||||
= f.check_box :access_level, {}, 'ref_protected', 'not_protected'
|
||||
%span.light This runner will only run on pipelines trigged on protected branches
|
||||
%span.light This runner will only run on pipelines triggered on protected branches
|
||||
.form-group
|
||||
= label :run_untagged, 'Run untagged jobs', class: 'control-label'
|
||||
.col-sm-10
|
||||
|
|
|
@ -11,13 +11,13 @@
|
|||
- if params[:author_id].present?
|
||||
= hidden_field_tag(:author_id, params[:author_id])
|
||||
= dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit",
|
||||
placeholder: "Search authors", data: { any_user: "Any Author", first_user: current_user.try(:username), current_user: true, project_id: @project.try(:id), selected: params[:author_id], field_name: "author_id", default_label: "Author" } })
|
||||
placeholder: "Search authors", data: { any_user: "Any Author", first_user: current_user&.username, current_user: true, project_id: @project&.id, group_id: @group&.id, selected: params[:author_id], field_name: "author_id", default_label: "Author" } })
|
||||
|
||||
.filter-item.inline
|
||||
- if params[:assignee_id].present?
|
||||
= hidden_field_tag(:assignee_id, params[:assignee_id])
|
||||
= dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
|
||||
placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: current_user.try(:username), null_user: true, current_user: true, project_id: @project.try(:id), group_id: @group&.id, selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } })
|
||||
placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: current_user&.username, null_user: true, current_user: true, project_id: @project&.id, group_id: @group&.id, selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } })
|
||||
|
||||
.filter-item.inline.milestone-filter
|
||||
= render "shared/issuable/milestone_dropdown", selected: finder.milestones.try(:first), name: :milestone_title, show_any: true, show_upcoming: true, show_started: true
|
||||
|
|
|
@ -24,9 +24,9 @@
|
|||
.block.milestone
|
||||
.sidebar-collapsed-icon
|
||||
= icon('clock-o', 'aria-hidden': 'true')
|
||||
%span
|
||||
%span.milestone-title
|
||||
- if issuable.milestone
|
||||
%span.has-tooltip{ title: milestone_remaining_days(issuable.milestone), data: { container: 'body', html: 1, placement: 'left' } }
|
||||
%span.has-tooltip{ title: "#{issuable.milestone.title}<br>#{milestone_remaining_days(issuable.milestone)}", data: { container: 'body', html: 1, placement: 'left' } }
|
||||
= issuable.milestone.title
|
||||
- else
|
||||
None
|
||||
|
|
|
@ -26,6 +26,6 @@
|
|||
|
||||
%span.assignee-icon
|
||||
- assignees.each do |assignee|
|
||||
= link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: assignee.id, state: 'all' }),
|
||||
= link_to polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, assignee_id: assignee.id, state: 'all' }),
|
||||
class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do
|
||||
- image_tag(avatar_icon(assignee, 16), class: "avatar s16", alt: '')
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Return only group's members in user dropdowns on issuables list pages
|
||||
merge_request: 14249
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Make the labels in the Compare form less confusing
|
||||
merge_request: 14225
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Extract AutocompleteController#users into finder
|
||||
merge_request: 13778
|
||||
author: Maxim Rydkin, Mayra Cabrera
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Optimize the boards' issues fetching.
|
||||
merge_request: 14198
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: creation of keys moved to services
|
||||
merge_request: 13331
|
||||
author: haseebeqx
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Truncate milestone title if sidebar is collapsed
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix the diff file header from being html escaped for renamed files.
|
||||
merge_request: 14121
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix mini graph pipeline breakin in merge request view
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Allow using newlines in pipeline email service recipients
|
||||
merge_request: 14250
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: changed dashed border button color to be darker
|
||||
merge_request: !14041
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Constrain environment deployments to project IDs
|
||||
merge_request:
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: "Disallow NULL values for environments.project_id"
|
||||
merge_request:
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix docs for lightweight tag creation via API
|
||||
merge_request:
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fixed the sidebar scrollbar overlapping links
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Fix project feature being deleted when updating project with invalid visibility
|
||||
level
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: "Memoize the latest builds of a pipeline on a project's homepage"
|
||||
merge_request:
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fixed milestone issuable assignee link URL
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Memoize pipelines for project download buttons
|
||||
merge_request:
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove animate.js and label animation.
|
||||
merge_request:
|
||||
author:
|
||||
type: removed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replace the 'project/archived.feature' spinach test with an rspec analog
|
||||
merge_request: 14322
|
||||
author: Vitaliy @blackst0ne Klachkov
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replace the 'project/commits/revert.feature' spinach test with an rspec analog
|
||||
merge_request: 14325
|
||||
author: Vitaliy @blackst0ne Klachkov
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replace the 'project/snippets.feature' spinach test with an rspec analog
|
||||
merge_request: 14326
|
||||
author: Vitaliy @blackst0ne Klachkov
|
||||
type: other
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue