summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorFelipe Artur <fcardozo@gitlab.com>2017-09-15 20:14:53 +0000
committerFelipe Artur <fcardozo@gitlab.com>2017-09-15 20:14:53 +0000
commit25c959f94d3dda63120256735163e4a142b8711e (patch)
tree4c6e544a665dab5b59106ace9638dca16b71d806 /app
parentb20595b2bd0023ebbc2ec46017f5d1cb1533fb4f (diff)
parent3dd2248605ce2183f02ab268fc1f8b0b164f0bed (diff)
downloadgitlab-ce-25c959f94d3dda63120256735163e4a142b8711e.tar.gz
Merge branch 'master' into 'issue_37640'issue_37640
# Conflicts: # db/schema.rb
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/branches/branches_delete_modal.js8
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_user.js5
-rw-r--r--app/assets/javascripts/fly_out_nav.js2
-rw-r--r--app/assets/javascripts/labels_select.js7
-rw-r--r--app/assets/javascripts/lib/utils/animate.js49
-rw-r--r--app/assets/javascripts/main.js1
-rw-r--r--app/assets/javascripts/milestone_select.js3
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue61
-rw-r--r--app/assets/javascripts/monitoring/services/monitoring_service.js49
-rw-r--r--app/assets/javascripts/new_sidebar.js5
-rw-r--r--app/assets/stylesheets/framework.scss1
-rw-r--r--app/assets/stylesheets/framework/gitlab-theme.scss281
-rw-r--r--app/assets/stylesheets/framework/header.scss1
-rw-r--r--app/assets/stylesheets/framework/nav.scss2
-rw-r--r--app/assets/stylesheets/framework/variables.scss41
-rw-r--r--app/assets/stylesheets/new_nav.scss120
-rw-r--r--app/assets/stylesheets/new_sidebar.scss21
-rw-r--r--app/assets/stylesheets/pages/diff.scss10
-rw-r--r--app/assets/stylesheets/pages/issuable.scss6
-rw-r--r--app/assets/stylesheets/pages/login.scss2
-rw-r--r--app/assets/stylesheets/pages/profiles/preferences.scss64
-rw-r--r--app/assets/stylesheets/pages/projects.scss2
-rw-r--r--app/assets/stylesheets/pages/repo.scss14
-rw-r--r--app/controllers/admin/deploy_keys_controller.rb5
-rw-r--r--app/controllers/admin/users_controller.rb1
-rw-r--r--app/controllers/boards/issues_controller.rb13
-rw-r--r--app/controllers/ci/lints_controller.rb4
-rw-r--r--app/controllers/dashboard/projects_controller.rb2
-rw-r--r--app/controllers/profiles/gpg_keys_controller.rb4
-rw-r--r--app/controllers/profiles/keys_controller.rb4
-rw-r--r--app/controllers/profiles/preferences_controller.rb3
-rw-r--r--app/controllers/projects/compare_controller.rb2
-rw-r--r--app/controllers/projects/deploy_keys_controller.rb2
-rw-r--r--app/controllers/projects/pipelines_controller.rb8
-rw-r--r--app/helpers/auto_devops_helper.rb7
-rw-r--r--app/helpers/boards_helper.rb4
-rw-r--r--app/helpers/builds_helper.rb2
-rw-r--r--app/helpers/groups_helper.rb6
-rw-r--r--app/helpers/preferences_helper.rb4
-rw-r--r--app/helpers/projects_helper.rb2
-rw-r--r--app/helpers/tab_helper.rb4
-rw-r--r--app/models/application_setting.rb4
-rw-r--r--app/models/blob_viewer/gitlab_ci_yml.rb2
-rw-r--r--app/models/ci/build.rb4
-rw-r--r--app/models/ci/group_variable.rb2
-rw-r--r--app/models/ci/pipeline.rb10
-rw-r--r--app/models/ci/pipeline_schedule.rb2
-rw-r--r--app/models/ci/pipeline_schedule_variable.rb2
-rw-r--r--app/models/ci/pipeline_variable.rb2
-rw-r--r--app/models/ci/runner.rb2
-rw-r--r--app/models/ci/runner_project.rb2
-rw-r--r--app/models/ci/stage.rb2
-rw-r--r--app/models/ci/trigger.rb2
-rw-r--r--app/models/ci/trigger_request.rb2
-rw-r--r--app/models/ci/variable.rb2
-rw-r--r--app/models/deploy_key.rb6
-rw-r--r--app/models/environment.rb5
-rw-r--r--app/models/event.rb32
-rw-r--r--app/models/gpg_key.rb5
-rw-r--r--app/models/issue.rb3
-rw-r--r--app/models/key.rb5
-rw-r--r--app/models/label.rb7
-rw-r--r--app/models/namespace.rb9
-rw-r--r--app/models/personal_access_token.rb2
-rw-r--r--app/models/project.rb17
-rw-r--r--app/models/project_auto_devops.rb3
-rw-r--r--app/models/project_services/pipelines_email_service.rb2
-rw-r--r--app/models/repository.rb14
-rw-r--r--app/models/user.rb1
-rw-r--r--app/serializers/build_details_entity.rb4
-rw-r--r--app/services/ci/pipeline_trigger_service.rb2
-rw-r--r--app/services/deploy_keys/create_service.rb7
-rw-r--r--app/services/gpg_keys/create_service.rb9
-rw-r--r--app/services/keys/base_service.rb13
-rw-r--r--app/services/keys/create_service.rb9
-rw-r--r--app/views/admin/dashboard/index.html.haml5
-rw-r--r--app/views/devise/shared/_omniauth_box.html.haml8
-rw-r--r--app/views/layouts/application.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_admin.html.haml24
-rw-r--r--app/views/layouts/nav/sidebar/_group.html.haml10
-rw-r--r--app/views/layouts/nav/sidebar/_profile.html.haml24
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml22
-rw-r--r--app/views/profiles/preferences/show.html.haml20
-rw-r--r--app/views/profiles/preferences/update.js.erb4
-rw-r--r--app/views/projects/blob/viewers/_download.html.haml2
-rw-r--r--app/views/projects/branches/_branch.html.haml3
-rw-r--r--app/views/projects/branches/_delete_protected_modal.html.haml9
-rw-r--r--app/views/projects/buttons/_download.html.haml30
-rw-r--r--app/views/projects/commits/_commit.html.haml2
-rw-r--r--app/views/projects/compare/_form.html.haml20
-rw-r--r--app/views/projects/compare/index.html.haml18
-rw-r--r--app/views/projects/diffs/_stats.html.haml4
-rw-r--r--app/views/projects/diffs/viewers/_image.html.haml8
-rw-r--r--app/views/projects/runners/_form.html.haml4
-rw-r--r--app/views/shared/issuable/_filter.html.haml4
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml4
96 files changed, 833 insertions, 421 deletions
diff --git a/app/assets/javascripts/branches/branches_delete_modal.js b/app/assets/javascripts/branches/branches_delete_modal.js
index af8bcdc1794..cbc28374b80 100644
--- a/app/assets/javascripts/branches/branches_delete_modal.js
+++ b/app/assets/javascripts/branches/branches_delete_modal.js
@@ -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);
}
}
diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js
index 7246ccbb281..720fbc87ea0 100644
--- a/app/assets/javascripts/filtered_search/dropdown_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_user.js
@@ -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');
}
diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js
index 4b19f7b4188..ad8254167a2 100644
--- a/app/assets/javascripts/fly_out_nav.js
+++ b/app/assets/javascripts/fly_out_nav.js
@@ -148,7 +148,7 @@ export const documentMouseMove = (e) => {
export const subItemsMouseLeave = (relatedTarget) => {
clearTimeout(timeoutId);
- if (!relatedTarget.closest(`.${IS_OVER_CLASS}`)) {
+ if (relatedTarget && !relatedTarget.closest(`.${IS_OVER_CLASS}`)) {
hideMenu(currentOpenMenu);
}
};
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 7d7f91227f9..2538d9c2093 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -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({
diff --git a/app/assets/javascripts/lib/utils/animate.js b/app/assets/javascripts/lib/utils/animate.js
deleted file mode 100644
index d93c1d0da59..00000000000
--- a/app/assets/javascripts/lib/utils/animate.js
+++ /dev/null
@@ -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);
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 0bc31a56684..0f84470828a 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -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';
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index 04579058688..4675b1fcb8f 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -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 {
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index b596c4f383f..5d96b193fce 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -1,7 +1,6 @@
<script>
/* global Flash */
import _ from 'underscore';
- import statusCodes from '../../lib/utils/http_status';
import MonitoringService from '../services/monitoring_service';
import GraphGroup from './graph_group.vue';
import Graph from './graph.vue';
@@ -21,10 +20,9 @@
hasMetrics: gl.utils.convertPermissionToBoolean(metricsData.hasMetrics),
documentationPath: metricsData.documentationPath,
settingsPath: metricsData.settingsPath,
- endpoint: metricsData.additionalMetrics,
+ metricsEndpoint: metricsData.additionalMetrics,
deploymentEndpoint: metricsData.deploymentEndpoint,
showEmptyState: true,
- backOffRequestCounter: 0,
updateAspectRatio: false,
updatedAspectRatios: 0,
resizeThrottled: {},
@@ -39,50 +37,16 @@
methods: {
getGraphsData() {
- const maxNumberOfRequests = 3;
this.state = 'loading';
- gl.utils.backOff((next, stop) => {
- this.service.get().then((resp) => {
- if (resp.status === statusCodes.NO_CONTENT) {
- this.backOffRequestCounter = this.backOffRequestCounter += 1;
- if (this.backOffRequestCounter < maxNumberOfRequests) {
- next();
- } else {
- stop(new Error('Failed to connect to the prometheus server'));
- }
- } else {
- stop(resp);
- }
- }).catch(stop);
- })
- .then((resp) => {
- if (resp.status === statusCodes.NO_CONTENT) {
- this.state = 'unableToConnect';
- return false;
- }
- return resp.json();
- })
- .then((metricGroupsData) => {
- if (!metricGroupsData) return false;
- this.store.storeMetrics(metricGroupsData.data);
- return this.getDeploymentData();
- })
- .then((deploymentData) => {
- if (deploymentData !== false) {
- this.store.storeDeploymentData(deploymentData.deployments);
- this.showEmptyState = false;
- }
- return {};
- })
- .catch(() => {
- this.state = 'unableToConnect';
- });
- },
-
- getDeploymentData() {
- return this.service.getDeploymentData(this.deploymentEndpoint)
- .then(resp => resp.json())
- .catch(() => new Flash('Error getting deployment information.'));
+ Promise.all([
+ this.service.getGraphsData()
+ .then(data => this.store.storeMetrics(data)),
+ this.service.getDeploymentData()
+ .then(data => this.store.storeDeploymentData(data))
+ .catch(() => new Flash('Error getting deployment information.')),
+ ])
+ .then(() => { this.showEmptyState = false; })
+ .catch(() => { this.state = 'unableToConnect'; });
},
resize() {
@@ -99,7 +63,10 @@
},
created() {
- this.service = new MonitoringService(this.endpoint);
+ this.service = new MonitoringService({
+ metricsEndpoint: this.metricsEndpoint,
+ deploymentEndpoint: this.deploymentEndpoint,
+ });
eventHub.$on('toggleAspectRatio', this.toggleAspectRatio);
},
diff --git a/app/assets/javascripts/monitoring/services/monitoring_service.js b/app/assets/javascripts/monitoring/services/monitoring_service.js
index 1e9ae934853..4ed651d5740 100644
--- a/app/assets/javascripts/monitoring/services/monitoring_service.js
+++ b/app/assets/javascripts/monitoring/services/monitoring_service.js
@@ -1,19 +1,54 @@
import Vue from 'vue';
import VueResource from 'vue-resource';
+import statusCodes from '../../lib/utils/http_status';
Vue.use(VueResource);
+const MAX_REQUESTS = 3;
+
+function backOffRequest(makeRequestCallback) {
+ let requestCounter = 0;
+ return gl.utils.backOff((next, stop) => {
+ makeRequestCallback().then((resp) => {
+ if (resp.status === statusCodes.NO_CONTENT) {
+ requestCounter += 1;
+ if (requestCounter < MAX_REQUESTS) {
+ next();
+ } else {
+ stop(new Error('Failed to connect to the prometheus server'));
+ }
+ } else {
+ stop(resp);
+ }
+ }).catch(stop);
+ });
+}
+
export default class MonitoringService {
- constructor(endpoint) {
- this.graphs = Vue.resource(endpoint);
+ constructor({ metricsEndpoint, deploymentEndpoint }) {
+ this.metricsEndpoint = metricsEndpoint;
+ this.deploymentEndpoint = deploymentEndpoint;
}
- get() {
- return this.graphs.get();
+ getGraphsData() {
+ return backOffRequest(() => Vue.http.get(this.metricsEndpoint))
+ .then(resp => resp.json())
+ .then((response) => {
+ if (!response || !response.data) {
+ throw new Error('Unexpected metrics data response from prometheus endpoint');
+ }
+ return response.data;
+ });
}
- // eslint-disable-next-line class-methods-use-this
- getDeploymentData(endpoint) {
- return Vue.http.get(endpoint);
+ getDeploymentData() {
+ return backOffRequest(() => Vue.http.get(this.deploymentEndpoint))
+ .then(resp => resp.json())
+ .then((response) => {
+ if (!response || !response.deployments) {
+ throw new Error('Unexpected deployment data response from prometheus endpoint');
+ }
+ return response.deployments;
+ });
}
}
diff --git a/app/assets/javascripts/new_sidebar.js b/app/assets/javascripts/new_sidebar.js
index cea4f35096a..f2eb2338a1e 100644
--- a/app/assets/javascripts/new_sidebar.js
+++ b/app/assets/javascripts/new_sidebar.js
@@ -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() {
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index c0524bf6aa3..35e7a10379f 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -19,6 +19,7 @@
@import "framework/flash";
@import "framework/forms";
@import "framework/gfm";
+@import "framework/gitlab-theme";
@import "framework/header";
@import "framework/highlight";
@import "framework/issue_box";
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
new file mode 100644
index 00000000000..f844d6f1d5a
--- /dev/null
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
@@ -0,0 +1,281 @@
+/**
+ * Styles the GitLab application with a specific color theme
+ */
+
+@mixin gitlab-theme($color-100, $color-200, $color-500, $color-700, $color-800, $color-900, $color-alternate) {
+ // Header
+
+ header.navbar-gitlab-new {
+ background: linear-gradient(to right, $color-900, $color-800);
+
+ .navbar-collapse {
+ color: $color-200;
+ }
+
+ .container-fluid {
+ .navbar-toggle {
+ border-left: 1px solid lighten($color-700, 10%);
+ }
+ }
+
+ .navbar-sub-nav,
+ .navbar-nav {
+ > li {
+ > a:hover,
+ > a:focus {
+ background-color: rgba($color-200, .2);
+ }
+
+ &.active > a,
+ &.dropdown.open > a {
+ color: $color-900;
+ background-color: $color-alternate;
+
+ svg {
+ fill: currentColor;
+ }
+ }
+
+ &.line-separator {
+ border-left: 1px solid rgba($color-200, .2);
+ }
+ }
+ }
+
+ .navbar-sub-nav {
+ color: $color-200;
+ }
+
+ .nav {
+ > li {
+ color: $color-200;
+
+ > a {
+ svg {
+ fill: $color-200;
+ }
+
+ &.header-user-dropdown-toggle {
+ .header-user-avatar {
+ border-color: $color-200;
+ }
+ }
+
+ &:hover,
+ &:focus {
+ @media (min-width: $screen-sm-min) {
+ background-color: rgba($color-200, .2);
+ }
+
+ svg {
+ fill: currentColor;
+ }
+ }
+ }
+
+ &.active > a,
+ &.dropdown.open > a {
+ color: $color-900;
+ background-color: $color-alternate;
+
+ &:hover {
+ svg {
+ fill: $color-900;
+ }
+ }
+ }
+
+ .impersonated-user,
+ .impersonated-user:hover {
+ svg {
+ fill: $color-900;
+ }
+ }
+ }
+ }
+ }
+
+ .title {
+ > a {
+ &:hover,
+ &:focus {
+ background-color: rgba($color-200, .2);
+ }
+ }
+ }
+
+ .search {
+ form {
+ background-color: rgba($color-200, .2);
+
+ &:hover {
+ background-color: rgba($color-200, .3);
+ }
+ }
+
+ .location-badge {
+ color: $color-100;
+ background-color: rgba($color-200, .1);
+ border-right: 1px solid $color-800;
+ }
+
+ .search-input::placeholder {
+ color: rgba($color-200, .8);
+ }
+
+ .search-input-wrap {
+ .search-icon,
+ .clear-icon {
+ color: rgba($color-200, .8);
+ }
+ }
+
+ &.search-active {
+ form {
+ background-color: $white-light;
+ }
+
+ .location-badge {
+ color: $gl-text-color;
+ }
+
+ .search-input-wrap {
+ .search-icon {
+ color: rgba($color-200, .8);
+ }
+ }
+ }
+ }
+
+ .btn-sign-in {
+ background-color: $color-100;
+ color: $color-900;
+ }
+
+
+ // Sidebar
+ .nav-sidebar li.active {
+ box-shadow: inset 4px 0 0 $color-700;
+
+ > a {
+ color: $color-800;
+ }
+
+ svg {
+ 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;
+ }
+ }
+}
+
+
+body {
+ &.ui_indigo {
+ @include gitlab-theme($indigo-100, $indigo-200, $indigo-500, $indigo-700, $indigo-800, $indigo-900, $white-light);
+ }
+
+ &.ui_dark {
+ @include gitlab-theme($theme-gray-100, $theme-gray-200, $theme-gray-500, $theme-gray-700, $theme-gray-800, $theme-gray-900, $white-light);
+ }
+
+ &.ui_blue {
+ @include gitlab-theme($theme-blue-100, $theme-blue-200, $theme-blue-500, $theme-blue-700, $theme-blue-800, $theme-blue-900, $white-light);
+ }
+
+ &.ui_green {
+ @include gitlab-theme($theme-green-100, $theme-green-200, $theme-green-500, $theme-green-700, $theme-green-800, $theme-green-900, $white-light);
+ }
+
+ &.ui_light {
+ @include gitlab-theme($theme-gray-900, $theme-gray-700, $theme-gray-800, $theme-gray-700, $theme-gray-700, $theme-gray-100, $theme-gray-700);
+
+ header.navbar-gitlab-new {
+ background: $theme-gray-100;
+ box-shadow: 0 2px 0 0 $border-color;
+
+ .logo-text svg {
+ fill: $theme-gray-900;
+ }
+
+ .navbar-sub-nav,
+ .navbar-nav {
+ > li {
+ > a:hover,
+ > a:focus {
+ color: $theme-gray-900;
+ }
+
+ &.active > a {
+ color: $white-light;
+
+ &:hover {
+ color: $white-light;
+ }
+ }
+ }
+ }
+
+ .container-fluid {
+ .navbar-toggle,
+ .navbar-toggle:hover {
+ color: $theme-gray-700;
+ border-left: 1px solid $theme-gray-200;
+ }
+ }
+ }
+
+ .search {
+ form {
+ background-color: $white-light;
+ box-shadow: inset 0 0 0 1px $border-color;
+
+ &:hover {
+ background-color: $white-light;
+ box-shadow: inset 0 0 0 1px $blue-100;
+
+ .location-badge {
+ box-shadow: inset 0 0 0 1px $blue-100;
+ }
+ }
+ }
+
+ .search-input-wrap {
+ .search-icon {
+ color: $theme-gray-200;
+ }
+ }
+
+ .location-badge {
+ color: $theme-gray-700;
+ box-shadow: inset 0 0 0 1px $border-color;
+ background-color: $nav-badge-bg;
+ border-right: 0;
+ }
+ }
+
+ .nav-sidebar li.active {
+ > a {
+ color: $theme-gray-900;
+ }
+
+ svg {
+ fill: $theme-gray-900;
+ }
+ }
+
+ .sidebar-top-level-items > li.active .badge {
+ color: $theme-gray-900;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index b00a2d053e2..ab3c34df1fb 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -111,7 +111,6 @@ header {
svg {
height: 16px;
width: 23px;
- fill: currentColor;
}
}
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index 5ffa67a1220..2f7717760ec 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -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;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index e300b006026..a3da9fd44e8 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -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;
@@ -74,6 +75,8 @@ $red-700: #a62d19;
$red-800: #8b2615;
$red-900: #711e11;
+// GitLab themes
+
$indigo-50: #f7f7ff;
$indigo-100: #ebebfa;
$indigo-200: #d1d1f0;
@@ -86,6 +89,43 @@ $indigo-800: #393982;
$indigo-900: #292961;
$indigo-950: #1a1a40;
+$theme-gray-50: #fafafa;
+$theme-gray-100: #f2f2f2;
+$theme-gray-200: #dfdfdf;
+$theme-gray-300: #cccccc;
+$theme-gray-400: #bababa;
+$theme-gray-500: #a7a7a7;
+$theme-gray-600: #949494;
+$theme-gray-700: #707070;
+$theme-gray-800: #4f4f4f;
+$theme-gray-900: #2e2e2e;
+$theme-gray-950: #1f1f1f;
+
+$theme-blue-50: #f4f8fc;
+$theme-blue-100: #e6edf5;
+$theme-blue-200: #c8d7e6;
+$theme-blue-300: #97b3cf;
+$theme-blue-400: #648cb4;
+$theme-blue-500: #4a79a8;
+$theme-blue-600: #3e6fa0;
+$theme-blue-700: #305c88;
+$theme-blue-800: #25496e;
+$theme-blue-900: #1a3652;
+$theme-blue-950: #0f2235;
+
+$theme-green-50: #f2faf6;
+$theme-green-100: #e4f3ea;
+$theme-green-200: #c0dfcd;
+$theme-green-300: #8ac2a1;
+$theme-green-400: #52a274;
+$theme-green-500: #35935c;
+$theme-green-600: #288a50;
+$theme-green-700: #1c7441;
+$theme-green-800: #145d33;
+$theme-green-900: #0d4524;
+$theme-green-950: #072d16;
+
+
$black: #000;
$black-transparent: rgba(0, 0, 0, 0.3);
$almost-black: #242424;
@@ -95,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);
/*
diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss
index 2b6c0fc015c..58e205537ef 100644
--- a/app/assets/stylesheets/new_nav.scss
+++ b/app/assets/stylesheets/new_nav.scss
@@ -9,10 +9,20 @@
header.navbar-gitlab-new {
color: $white-light;
- background: linear-gradient(to right, $indigo-900, $indigo-800);
border-bottom: 0;
min-height: $new-navbar-height;
+ .logo-text {
+ line-height: initial;
+
+ svg {
+ width: 55px;
+ height: 14px;
+ margin: 0;
+ fill: $white-light;
+ }
+ }
+
.header-content {
display: -webkit-flex;
display: flex;
@@ -38,10 +48,10 @@ header.navbar-gitlab-new {
img {
height: 28px;
- margin-right: 10px;
+ margin-right: 8px;
}
- > a {
+ a {
display: -webkit-flex;
display: flex;
align-items: center;
@@ -54,22 +64,6 @@ header.navbar-gitlab-new {
margin-right: 8px;
}
}
-
- .logo-text {
- line-height: initial;
-
- svg {
- width: 55px;
- height: 14px;
- margin: 0;
- fill: $white-light;
- }
- }
-
- &:hover,
- &:focus {
- background-color: rgba($indigo-200, .2);
- }
}
}
@@ -106,7 +100,6 @@ header.navbar-gitlab-new {
.navbar-collapse {
padding-left: 0;
- color: $indigo-200;
box-shadow: 0;
@media (max-width: $screen-xs-max) {
@@ -132,7 +125,6 @@ header.navbar-gitlab-new {
font-size: 14px;
text-align: center;
color: currentColor;
- border-left: 1px solid lighten($indigo-700, 10%);
&:hover,
&:focus,
@@ -167,63 +159,49 @@ header.navbar-gitlab-new {
will-change: color;
margin: 4px 2px;
padding: 6px 8px;
- color: $indigo-200;
height: 32px;
@media (max-width: $screen-xs-max) {
padding: 0;
}
- svg {
- fill: $indigo-200;
- }
-
&.header-user-dropdown-toggle {
margin-left: 2px;
.header-user-avatar {
- border-color: $indigo-200;
margin-right: 0;
}
}
- }
-
- .header-new-dropdown-toggle {
- margin-right: 0;
- }
- > a:hover,
- > a:focus {
- text-decoration: none;
- outline: 0;
- opacity: 1;
- color: $white-light;
-
- @media (min-width: $screen-sm-min) {
- background-color: rgba($indigo-200, .2);
- }
+ &:hover,
+ &:focus {
+ text-decoration: none;
+ outline: 0;
+ opacity: 1;
+ color: $white-light;
- svg {
- fill: currentColor;
- }
+ svg {
+ fill: currentColor;
+ }
- &.header-user-dropdown-toggle {
- .header-user-avatar {
- border-color: $white-light;
+ &.header-user-dropdown-toggle {
+ .header-user-avatar {
+ border-color: $white-light;
+ }
}
}
}
+ .header-new-dropdown-toggle {
+ margin-right: 0;
+ }
+
.impersonated-user,
.impersonated-user:hover {
margin-right: 1px;
background-color: $white-light;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
-
- svg {
- fill: $indigo-900;
- }
}
.impersonation-btn,
@@ -241,8 +219,6 @@ header.navbar-gitlab-new {
&.active > a,
&.dropdown.open > a {
- color: $indigo-900;
- background-color: $white-light;
svg {
fill: currentColor;
@@ -256,7 +232,6 @@ header.navbar-gitlab-new {
display: -webkit-flex;
display: flex;
margin: 0 0 0 6px;
- color: $indigo-200;
.dropdown-chevron {
position: relative;
@@ -274,17 +249,6 @@ header.navbar-gitlab-new {
text-decoration: none;
outline: 0;
color: $white-light;
- background-color: rgba($indigo-200, .2);
-
- svg {
- fill: currentColor;
- }
- }
-
- &.active > a,
- &.dropdown.open > a {
- color: $indigo-900;
- background-color: $white-light;
svg {
fill: currentColor;
@@ -309,7 +273,6 @@ header.navbar-gitlab-new {
}
&.line-separator {
- border-left: 1px solid rgba($indigo-200, .2);
margin: 8px;
}
}
@@ -339,17 +302,14 @@ header.navbar-gitlab-new {
height: 32px;
border: 0;
border-radius: $border-radius-default;
- background-color: rgba($indigo-200, .2);
transition: border-color ease-in-out 0.15s, background-color ease-in-out 0.15s;
&:hover {
- background-color: rgba($indigo-200, .3);
box-shadow: none;
}
}
&.search-active form {
- background-color: $white-light;
box-shadow: none;
.search-input {
@@ -377,43 +337,26 @@ header.navbar-gitlab-new {
}
.search-input::placeholder {
- color: rgba($indigo-200, .8);
transition: color ease-in-out 0.15s;
}
.location-badge {
font-size: 12px;
- color: $indigo-100;
- background-color: rgba($indigo-200, .1);
- will-change: color;
margin: -4px 4px -4px -4px;
line-height: 25px;
padding: 4px 8px;
border-radius: 2px 0 0 2px;
- border-right: 1px solid $indigo-800;
height: 32px;
transition: border-color ease-in-out 0.15s;
}
- .search-input-wrap {
- .search-icon,
- .clear-icon {
- color: rgba($indigo-200, .8);
- }
- }
-
&.search-active {
.location-badge {
- color: $gl-text-color;
background-color: $nav-badge-bg;
border-color: $border-color;
}
.search-input-wrap {
- .search-icon {
- color: rgba($indigo-200, .8);
- }
-
.clear-icon {
color: $white-light;
}
@@ -488,6 +431,7 @@ header.navbar-gitlab-new {
.breadcrumb-item-text {
@include str-truncated(128px);
+ text-decoration: inherit;
}
.breadcrumbs-list-angle {
@@ -517,8 +461,6 @@ header.navbar-gitlab-new {
.btn-sign-in {
margin-top: 3px;
- background-color: $indigo-100;
- color: $indigo-900;
font-weight: $gl-font-weight-bold;
&:hover {
diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss
index 378ef8926d5..952002c83d1 100644
--- a/app/assets/stylesheets/new_sidebar.scss
+++ b/app/assets/stylesheets/new_sidebar.scss
@@ -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);
@@ -162,16 +160,9 @@ $new-sidebar-collapsed-width: 50px;
}
li.active {
- box-shadow: inset 4px 0 0 $active-border;
-
> a {
- color: $active-color;
font-weight: $gl-font-weight-bold;
}
-
- svg {
- fill: $active-color;
- }
}
@media (max-width: $screen-xs-max) {
@@ -224,7 +215,6 @@ $new-sidebar-collapsed-width: 50px;
&:hover,
&:focus {
background: $active-background;
- color: $active-color;
}
}
}
@@ -324,7 +314,6 @@ $new-sidebar-collapsed-width: 50px;
}
.badge {
- color: $active-color;
font-weight: $gl-font-weight-bold;
}
@@ -501,13 +490,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;
- }
-}
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 54c3c0173ae..951580ea1fe 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -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;
}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index d8a15faf7e9..d01ee4b033c 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -449,6 +449,12 @@
}
}
}
+
+ .milestone-title span {
+ @include str-truncated(100%);
+ display: block;
+ margin: 0 4px;
+ }
}
a {
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index d4dc43035eb..cf5f933a762 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -95,6 +95,8 @@
}
.omniauth-container {
+ font-size: 13px;
+
p {
margin: 0;
}
diff --git a/app/assets/stylesheets/pages/profiles/preferences.scss b/app/assets/stylesheets/pages/profiles/preferences.scss
index 305feaacaa1..c197494b152 100644
--- a/app/assets/stylesheets/pages/profiles/preferences.scss
+++ b/app/assets/stylesheets/pages/profiles/preferences.scss
@@ -1,3 +1,67 @@
+@mixin application-theme-preview($color-1, $color-2, $color-3, $color-4) {
+ .one {
+ background-color: $color-1;
+ border-top-left-radius: $border-radius-default;
+ }
+
+ .two {
+ background-color: $color-2;
+ border-top-right-radius: $border-radius-default;
+ }
+
+ .three {
+ background-color: $color-3;
+ border-bottom-left-radius: $border-radius-default;
+ }
+
+ .four {
+ background-color: $color-4;
+ border-bottom-right-radius: $border-radius-default;
+ }
+}
+
+.application-theme {
+ label {
+ margin-right: 20px;
+ text-align: center;
+ }
+
+ .preview {
+ font-size: 0;
+ margin-bottom: 10px;
+
+ &.indigo {
+ @include application-theme-preview($indigo-900, $indigo-700, $indigo-800, $indigo-500);
+ }
+
+ &.dark {
+ @include application-theme-preview($theme-gray-900, $theme-gray-700, $theme-gray-800, $theme-gray-600);
+ }
+
+ &.light {
+ @include application-theme-preview($theme-gray-600, $theme-gray-200, $theme-gray-400, $theme-gray-100);
+ }
+
+ &.blue {
+ @include application-theme-preview($theme-blue-900, $theme-blue-700, $theme-blue-800, $theme-blue-500);
+ }
+
+ &.green {
+ @include application-theme-preview($theme-green-900, $theme-green-700, $theme-green-800, $theme-green-500);
+ }
+ }
+
+ .preview-row {
+ display: block;
+ }
+
+ .quadrant {
+ display: inline-block;
+ height: 50px;
+ width: 80px;
+ }
+}
+
.syntax-theme {
label {
margin-right: 20px;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 94e4f4334d4..6400b72742c 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -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 {
diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss
index 69abb13add4..7dfcf7b7d9c 100644
--- a/app/assets/stylesheets/pages/repo.scss
+++ b/app/assets/stylesheets/pages/repo.scss
@@ -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;
}
diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb
index e5cba774dcb..a7ab481519d 100644
--- a/app/controllers/admin/deploy_keys_controller.rb
+++ b/app/controllers/admin/deploy_keys_controller.rb
@@ -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'
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 9ec7719fabb..cbcef70e957 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -211,6 +211,7 @@ class Admin::UsersController < Admin::ApplicationController
:provider,
:remember_me,
:skype,
+ :theme_id,
:twitter,
:username,
:website_url
diff --git a/app/controllers/boards/issues_controller.rb b/app/controllers/boards/issues_controller.rb
index 8d4ec2d6d9d..0d74078645a 100644
--- a/app/controllers/boards/issues_controller.rb
+++ b/app/controllers/boards/issues_controller.rb
@@ -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
diff --git a/app/controllers/ci/lints_controller.rb b/app/controllers/ci/lints_controller.rb
index 3eb485de9db..be667687c18 100644
--- a/app/controllers/ci/lints_controller.rb
+++ b/app/controllers/ci/lints_controller.rb
@@ -7,11 +7,11 @@ module Ci
def create
@content = params[:content]
- @error = Ci::GitlabCiYamlProcessor.validation_message(@content)
+ @error = Gitlab::Ci::YamlProcessor.validation_message(@content)
@status = @error.blank?
if @error.blank?
- @config_processor = Ci::GitlabCiYamlProcessor.new(@content)
+ @config_processor = Gitlab::Ci::YamlProcessor.new(@content)
@stages = @config_processor.stages
@builds = @config_processor.builds
@jobs = @config_processor.jobs
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index f71ab702e71..cd94a36a6e7 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -48,7 +48,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
ProjectsFinder
.new(params: finder_params, current_user: current_user)
.execute
- .includes(:route, :creator, namespace: :route)
+ .includes(:route, :creator, namespace: [:route, :owner])
end
def load_events
diff --git a/app/controllers/profiles/gpg_keys_controller.rb b/app/controllers/profiles/gpg_keys_controller.rb
index 6779cc6ddac..689c76059f6 100644
--- a/app/controllers/profiles/gpg_keys_controller.rb
+++ b/app/controllers/profiles/gpg_keys_controller.rb
@@ -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?)
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index f9f0e8eef83..89d6d7f1b52 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -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?)
diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb
index 1e557c47638..cce2a847b53 100644
--- a/app/controllers/profiles/preferences_controller.rb
+++ b/app/controllers/profiles/preferences_controller.rb
@@ -35,7 +35,8 @@ class Profiles::PreferencesController < Profiles::ApplicationController
:color_scheme_id,
:layout,
:dashboard,
- :project_view
+ :project_view,
+ :theme_id
)
end
end
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index 193549663ac..3c8eaa24080 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -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
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index c2e621fa190..cf8829ba95b 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -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
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index a3bfbf0694e..7ad7b3003af 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -132,10 +132,10 @@ class Projects::PipelinesController < Projects::ApplicationController
def charts
@charts = {}
- @charts[:week] = Ci::Charts::WeekChart.new(project)
- @charts[:month] = Ci::Charts::MonthChart.new(project)
- @charts[:year] = Ci::Charts::YearChart.new(project)
- @charts[:pipeline_times] = Ci::Charts::PipelineTime.new(project)
+ @charts[:week] = Gitlab::Ci::Charts::WeekChart.new(project)
+ @charts[:month] = Gitlab::Ci::Charts::MonthChart.new(project)
+ @charts[:year] = Gitlab::Ci::Charts::YearChart.new(project)
+ @charts[:pipeline_times] = Gitlab::Ci::Charts::PipelineTime.new(project)
@counts = {}
@counts[:total] = @project.pipelines.count(:all)
diff --git a/app/helpers/auto_devops_helper.rb b/app/helpers/auto_devops_helper.rb
index 4ff38f86b5f..c455d18cff8 100644
--- a/app/helpers/auto_devops_helper.rb
+++ b/app/helpers/auto_devops_helper.rb
@@ -1,7 +1,10 @@
module AutoDevopsHelper
def show_auto_devops_callout?(project)
- show_callout?('auto_devops_settings_dismissed') &&
+ Feature.get(:auto_devops_banner_disabled).off? &&
+ show_callout?('auto_devops_settings_dismissed') &&
can?(current_user, :admin_pipeline, project) &&
- project.has_auto_devops_implicitly_disabled?
+ project.has_auto_devops_implicitly_disabled? &&
+ !project.repository.gitlab_ci_yml &&
+ project.ci_services.active.none?
end
end
diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb
index 4bd61aa8f86..62ac208f16a 100644
--- a/app/helpers/boards_helper.rb
+++ b/app/helpers/boards_helper.rb
@@ -77,4 +77,8 @@ module BoardsHelper
'max-select': dropdown_options[:data][:'max-select']
}
end
+
+ def boards_link_text
+ _("Board")
+ end
end
diff --git a/app/helpers/builds_helper.rb b/app/helpers/builds_helper.rb
index 85bc784d53c..aa3a9a055a0 100644
--- a/app/helpers/builds_helper.rb
+++ b/app/helpers/builds_helper.rb
@@ -30,7 +30,7 @@ module BuildsHelper
def build_failed_issue_options
{
- title: "Build Failed ##{@build.id}",
+ title: "Job Failed ##{@build.id}",
description: project_job_url(@project, @build)
}
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 36b79da1bde..e8efe8fab27 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -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)
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index d36bb4ab074..0d7347ed30d 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -40,6 +40,10 @@ module PreferencesHelper
]
end
+ def user_application_theme
+ @user_application_theme ||= Gitlab::Themes.for_user(current_user).css_class
+ end
+
def user_color_scheme
Gitlab::ColorSchemes.for_user(current_user).css_class
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 0c8cb9ba235..ddeff490d3a 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -320,7 +320,7 @@ module ProjectsHelper
def git_user_name
if current_user
- current_user.name
+ current_user.name.gsub('"', '\"')
else
_("Your name")
end
diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb
index 3308ab0c259..ee701076a14 100644
--- a/app/helpers/tab_helper.rb
+++ b/app/helpers/tab_helper.rb
@@ -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
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index aede9b5f9da..c0cc60d5ebf 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -137,11 +137,11 @@ class ApplicationSetting < ActiveRecord::Base
validates :housekeeping_full_repack_period,
presence: true,
- numericality: { only_integer: true, greater_than: :housekeeping_incremental_repack_period }
+ numericality: { only_integer: true, greater_than_or_equal_to: :housekeeping_incremental_repack_period }
validates :housekeeping_gc_period,
presence: true,
- numericality: { only_integer: true, greater_than: :housekeeping_full_repack_period }
+ numericality: { only_integer: true, greater_than_or_equal_to: :housekeeping_full_repack_period }
validates :terminal_max_session_time,
presence: true,
diff --git a/app/models/blob_viewer/gitlab_ci_yml.rb b/app/models/blob_viewer/gitlab_ci_yml.rb
index 7267c3965d3..53bc247dec1 100644
--- a/app/models/blob_viewer/gitlab_ci_yml.rb
+++ b/app/models/blob_viewer/gitlab_ci_yml.rb
@@ -13,7 +13,7 @@ module BlobViewer
prepare!
- @validation_message = Ci::GitlabCiYamlProcessor.validation_message(blob.data)
+ @validation_message = Gitlab::Ci::YamlProcessor.validation_message(blob.data)
end
def valid?
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 5ebe6f180e6..ee544d8ac56 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -446,8 +446,8 @@ module Ci
return unless trace
trace = trace.dup
- Ci::MaskSecret.mask!(trace, project.runners_token) if project
- Ci::MaskSecret.mask!(trace, token)
+ Gitlab::Ci::MaskSecret.mask!(trace, project.runners_token) if project
+ Gitlab::Ci::MaskSecret.mask!(trace, token)
trace
end
diff --git a/app/models/ci/group_variable.rb b/app/models/ci/group_variable.rb
index f64bc245a67..afeae69ba39 100644
--- a/app/models/ci/group_variable.rb
+++ b/app/models/ci/group_variable.rb
@@ -1,6 +1,6 @@
module Ci
class GroupVariable < ActiveRecord::Base
- extend Ci::Model
+ extend Gitlab::Ci::Model
include HasVariable
include Presentable
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 871c76fbad3..8d017b9b3b1 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -1,6 +1,6 @@
module Ci
class Pipeline < ActiveRecord::Base
- extend Ci::Model
+ extend Gitlab::Ci::Model
include HasStatus
include Importable
include AfterCommitQueue
@@ -336,8 +336,8 @@ module Ci
return @config_processor if defined?(@config_processor)
@config_processor ||= begin
- Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.full_path)
- rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e
+ Gitlab::Ci::YamlProcessor.new(ci_yaml_file, project.full_path)
+ rescue Gitlab::Ci::YamlProcessor::ValidationError, Psych::SyntaxError => e
self.yaml_errors = e.message
nil
rescue
@@ -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
diff --git a/app/models/ci/pipeline_schedule.rb b/app/models/ci/pipeline_schedule.rb
index e7e02587759..10ead6b6d3b 100644
--- a/app/models/ci/pipeline_schedule.rb
+++ b/app/models/ci/pipeline_schedule.rb
@@ -1,6 +1,6 @@
module Ci
class PipelineSchedule < ActiveRecord::Base
- extend Ci::Model
+ extend Gitlab::Ci::Model
include Importable
acts_as_paranoid
diff --git a/app/models/ci/pipeline_schedule_variable.rb b/app/models/ci/pipeline_schedule_variable.rb
index ee5b8733fac..af989fb14b4 100644
--- a/app/models/ci/pipeline_schedule_variable.rb
+++ b/app/models/ci/pipeline_schedule_variable.rb
@@ -1,6 +1,6 @@
module Ci
class PipelineScheduleVariable < ActiveRecord::Base
- extend Ci::Model
+ extend Gitlab::Ci::Model
include HasVariable
belongs_to :pipeline_schedule
diff --git a/app/models/ci/pipeline_variable.rb b/app/models/ci/pipeline_variable.rb
index 00b419c3efa..de5aae17a15 100644
--- a/app/models/ci/pipeline_variable.rb
+++ b/app/models/ci/pipeline_variable.rb
@@ -1,6 +1,6 @@
module Ci
class PipelineVariable < ActiveRecord::Base
- extend Ci::Model
+ extend Gitlab::Ci::Model
include HasVariable
belongs_to :pipeline
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index b1798084787..a0d07902ba2 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -1,6 +1,6 @@
module Ci
class Runner < ActiveRecord::Base
- extend Ci::Model
+ extend Gitlab::Ci::Model
RUNNER_QUEUE_EXPIRY_TIME = 60.minutes
ONLINE_CONTACT_TIMEOUT = 1.hour
diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb
index 5f01a0daae9..505d178ba8e 100644
--- a/app/models/ci/runner_project.rb
+++ b/app/models/ci/runner_project.rb
@@ -1,6 +1,6 @@
module Ci
class RunnerProject < ActiveRecord::Base
- extend Ci::Model
+ extend Gitlab::Ci::Model
belongs_to :runner
belongs_to :project
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index 754c37518b3..75b8ea2a371 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -1,6 +1,6 @@
module Ci
class Stage < ActiveRecord::Base
- extend Ci::Model
+ extend Gitlab::Ci::Model
include Importable
include HasStatus
include Gitlab::OptimisticLocking
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index 6df41a3f301..b5290bcaf53 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -1,6 +1,6 @@
module Ci
class Trigger < ActiveRecord::Base
- extend Ci::Model
+ extend Gitlab::Ci::Model
acts_as_paranoid
diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb
index 2c860598281..215b1cf6753 100644
--- a/app/models/ci/trigger_request.rb
+++ b/app/models/ci/trigger_request.rb
@@ -1,6 +1,6 @@
module Ci
class TriggerRequest < ActiveRecord::Base
- extend Ci::Model
+ extend Gitlab::Ci::Model
belongs_to :trigger
belongs_to :pipeline, foreign_key: :commit_id
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index cf0fe04ddaf..67d3ec81b6f 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -1,6 +1,6 @@
module Ci
class Variable < ActiveRecord::Base
- extend Ci::Model
+ extend Gitlab::Ci::Model
include HasVariable
include Presentable
diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb
index 51768dd96bc..eae5eee4fee 100644
--- a/app/models/deploy_key.rb
+++ b/app/models/deploy_key.rb
@@ -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
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 9b05f8b1cd5..44e39e21442 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -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
diff --git a/app/models/event.rb b/app/models/event.rb
index 8e9490b66f4..0b1f053a7e6 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -241,13 +241,7 @@ class Event < ActiveRecord::Base
def action_name
if push?
- if new_ref?
- "pushed new"
- elsif rm_ref?
- "deleted"
- else
- "pushed to"
- end
+ push_action_name
elsif closed?
"closed"
elsif merged?
@@ -263,11 +257,7 @@ class Event < ActiveRecord::Base
elsif commented?
"commented on"
elsif created_project?
- if project.external_import?
- "imported"
- else
- "created"
- end
+ created_project_action_name
else
"opened"
end
@@ -360,6 +350,24 @@ class Event < ActiveRecord::Base
private
+ def push_action_name
+ if new_ref?
+ "pushed new"
+ elsif rm_ref?
+ "deleted"
+ else
+ "pushed to"
+ end
+ end
+
+ def created_project_action_name
+ if project.external_import?
+ "imported"
+ else
+ "created"
+ end
+ end
+
def recent_update?
project.last_activity_at > RESET_PROJECT_ACTIVITY_INTERVAL.ago
end
diff --git a/app/models/gpg_key.rb b/app/models/gpg_key.rb
index 1633acd4fa9..44deae4234b 100644
--- a/app/models/gpg_key.rb
+++ b/app/models/gpg_key.rb
@@ -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
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 8c7d492e605..cd5056aae5e 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -30,9 +30,6 @@ class Issue < ActiveRecord::Base
has_many :issue_assignees
has_many :assignees, class_name: "User", through: :issue_assignees
- has_many :issue_assignees
- has_many :assignees, class_name: "User", through: :issue_assignees
-
validates :project, presence: true
scope :in_projects, ->(project_ids) { where(project_id: project_ids) }
diff --git a/app/models/key.rb b/app/models/key.rb
index a6b4dcfec0d..4fa6cac2fd0 100644
--- a/app/models/key.rb
+++ b/app/models/key.rb
@@ -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
diff --git a/app/models/label.rb b/app/models/label.rb
index 958141a7358..899028a01a0 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -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?
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 4a9a23fea1f..e279d8dd8c5 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -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
diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb
index 654be927ed8..ec0ebe4d353 100644
--- a/app/models/personal_access_token.rb
+++ b/app/models/personal_access_token.rb
@@ -28,7 +28,7 @@ class PersonalAccessToken < ActiveRecord::Base
protected
def validate_scopes
- unless 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
diff --git a/app/models/project.rb b/app/models/project.rb
index c422819a082..94ae0acbe1a 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -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
diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb
index 53731579e87..7af3b6870e2 100644
--- a/app/models/project_auto_devops.rb
+++ b/app/models/project_auto_devops.rb
@@ -1,6 +1,9 @@
class ProjectAutoDevops < ActiveRecord::Base
belongs_to :project
+ scope :enabled, -> { where(enabled: true) }
+ scope :disabled, -> { where(enabled: false) }
+
validates :domain, allow_blank: true, hostname: { allow_numeric_hostname: true }
def variables
diff --git a/app/models/project_services/pipelines_email_service.rb b/app/models/project_services/pipelines_email_service.rb
index 9d37184be2c..6a3118a11b8 100644
--- a/app/models/project_services/pipelines_email_service.rb
+++ b/app/models/project_services/pipelines_email_service.rb
@@ -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
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 035f85a0b46..f2b54705e7b 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -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
diff --git a/app/models/user.rb b/app/models/user.rb
index 358b04ac71f..09c9b3250eb 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -35,6 +35,7 @@ class User < ActiveRecord::Base
default_value_for :project_view, :files
default_value_for :notified_of_own_activity, false
default_value_for :preferred_language, I18n.default_locale
+ default_value_for :theme_id, gitlab_config.default_theme
attr_encrypted :otp_secret,
key: Gitlab::Application.secrets.otp_key_base,
diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb
index 743a08acefe..8c89eea607f 100644
--- a/app/serializers/build_details_entity.rb
+++ b/app/serializers/build_details_entity.rb
@@ -32,8 +32,8 @@ class BuildDetailsEntity < JobEntity
private
def build_failed_issue_options
- { title: "Build Failed ##{build.id}",
- description: project_job_path(project, build) }
+ { title: "Job Failed ##{build.id}",
+ description: "Job [##{build.id}](#{project_job_path(project, build)}) failed for #{build.sha}:\n" }
end
def current_user
diff --git a/app/services/ci/pipeline_trigger_service.rb b/app/services/ci/pipeline_trigger_service.rb
index 1e5ad28ba57..120af8c1e61 100644
--- a/app/services/ci/pipeline_trigger_service.rb
+++ b/app/services/ci/pipeline_trigger_service.rb
@@ -14,7 +14,7 @@ module Ci
pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: params[:ref])
.execute(:trigger, ignore_skip_ci: true) do |pipeline|
- trigger.trigger_requests.create!(pipeline: pipeline)
+ pipeline.trigger_requests.create!(trigger: trigger)
create_pipeline_variables!(pipeline)
end
diff --git a/app/services/deploy_keys/create_service.rb b/app/services/deploy_keys/create_service.rb
new file mode 100644
index 00000000000..16de3d08df2
--- /dev/null
+++ b/app/services/deploy_keys/create_service.rb
@@ -0,0 +1,7 @@
+module DeployKeys
+ class CreateService < Keys::BaseService
+ def execute
+ DeployKey.create(params.merge(user: user))
+ end
+ end
+end
diff --git a/app/services/gpg_keys/create_service.rb b/app/services/gpg_keys/create_service.rb
new file mode 100644
index 00000000000..e822a89c4d3
--- /dev/null
+++ b/app/services/gpg_keys/create_service.rb
@@ -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
diff --git a/app/services/keys/base_service.rb b/app/services/keys/base_service.rb
new file mode 100644
index 00000000000..545832d0bd4
--- /dev/null
+++ b/app/services/keys/base_service.rb
@@ -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
diff --git a/app/services/keys/create_service.rb b/app/services/keys/create_service.rb
new file mode 100644
index 00000000000..e2e5a6c46c5
--- /dev/null
+++ b/app/services/keys/create_service.rb
@@ -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
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 069f8f89e0b..703f4165128 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -111,6 +111,11 @@
GitLab API
%span.pull-right
= API::API::version
+ - if Gitlab.config.pages.enabled
+ %p
+ GitLab Pages
+ %span.pull-right
+ = Gitlab::Pages::VERSION
%p
Git
%span.pull-right
diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml
index bfd7dd25a7d..546cec4d565 100644
--- a/app/views/devise/shared/_omniauth_box.html.haml
+++ b/app/views/devise/shared/_omniauth_box.html.haml
@@ -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
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 65ac8aaa59b..0ca34b276a7 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -1,7 +1,7 @@
!!! 5
%html{ lang: I18n.locale, class: page_class }
= render "layouts/head"
- %body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}", find_file: find_file_path } }
+ %body{ class: "#{user_application_theme} #{@body_class}", data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}", find_file: find_file_path } }
= render "layouts/init_auto_complete" if @gfm_form
= render 'peek/bar'
= render "layouts/header/default"
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index fcebb385a65..615238b94ad 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -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
diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml
index e01dfa7c854..910e0d8b5ec 100644
--- a/app/views/layouts/nav/sidebar/_group.html.haml
+++ b/app/views/layouts/nav/sidebar/_group.html.haml
@@ -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,7 +77,7 @@
#{ _('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
@@ -89,7 +89,7 @@
#{ _('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
diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml
index 4c26d107ea7..2c402591f62 100644
--- a/app/views/layouts/nav/sidebar/_profile.html.haml
+++ b/app/views/layouts/nav/sidebar/_profile.html.haml
@@ -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
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index 9589e81c750..29f1fc6b354 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -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
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 352c2d66bab..66d1d1e8d44 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -3,6 +3,26 @@
= render 'profiles/head'
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f|
+ .col-lg-4.application-theme
+ %h4.prepend-top-0
+ GitLab navigation theme
+ %p Customize the appearance of the application header and navigation sidebar.
+ .col-lg-8.application-theme
+ - Gitlab::Themes.each do |theme|
+ = label_tag do
+ .preview{ class: theme.name.downcase }
+ .preview-row
+ .quadrant.one
+ .quadrant.two
+ .preview-row
+ .quadrant.three
+ .quadrant.four
+ = f.radio_button :theme_id, theme.id, checked: Gitlab::Themes.for_user(@user).id == theme.id
+ = theme.name
+
+ .col-sm-12
+ %hr
+
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
Syntax highlighting theme
diff --git a/app/views/profiles/preferences/update.js.erb b/app/views/profiles/preferences/update.js.erb
index 431ab9d052b..8966dd3fd86 100644
--- a/app/views/profiles/preferences/update.js.erb
+++ b/app/views/profiles/preferences/update.js.erb
@@ -1,3 +1,7 @@
+// Remove body class for any previous theme, re-add current one
+$('body').removeClass('<%= Gitlab::Themes.body_classes %>')
+$('body').addClass('<%= user_application_theme %>')
+
// Toggle container-fluid class
if ('<%= current_user.layout %>' === 'fluid') {
$('.content-wrapper .container-fluid').removeClass('container-limited')
diff --git a/app/views/projects/blob/viewers/_download.html.haml b/app/views/projects/blob/viewers/_download.html.haml
index 6d1138f7959..253566c43be 100644
--- a/app/views/projects/blob/viewers/_download.html.haml
+++ b/app/views/projects/blob/viewers/_download.html.haml
@@ -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')
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index 19712a8f1be..05c1d2b383c 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -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",
diff --git a/app/views/projects/branches/_delete_protected_modal.html.haml b/app/views/projects/branches/_delete_protected_modal.html.haml
index c5888afa54d..f00a0ee6925 100644
--- a/app/views/projects/branches/_delete_protected_modal.html.haml
+++ b/app/views/projects/branches/_delete_protected_modal.html.haml
@@ -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
diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml
index 883922dbf04..9d85e027ac9 100644
--- a/app/views/projects/buttons/_download.html.haml
+++ b/app/views/projects/buttons/_download.html.haml
@@ -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}'
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index b8655808d89..a16ffb433a5 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -32,7 +32,7 @@
.commiter
- commit_author_link = commit_author_link(commit, avatar: false, size: 24)
- - commit_timeago = time_ago_with_tooltip(commit.committed_date)
+ - commit_timeago = time_ago_with_tooltip(commit.committed_date, placement: 'bottom')
- commit_text = _('%{commit_author_link} committed %{commit_timeago}') % { commit_author_link: commit_author_link, commit_timeago: commit_timeago }
#{ commit_text.html_safe }
diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml
index 94b7db5eb25..a518fced2b4 100644
--- a/app/views/projects/compare/_form.html.haml
+++ b/app/views/projects/compare/_form.html.haml
@@ -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'
&nbsp;
= button_tag "Compare", class: "btn btn-create commits-compare-btn"
- if @merge_request.present?
diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml
index 2632fea6eba..1ce3ad0c0fd 100644
--- a/app/views/projects/compare/index.html.haml
+++ b/app/views/projects/compare/index.html.haml
@@ -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"
diff --git a/app/views/projects/diffs/_stats.html.haml b/app/views/projects/diffs/_stats.html.haml
index ad2d355ab4a..2de2cf9e38c 100644
--- a/app/views/projects/diffs/_stats.html.haml
+++ b/app/views/projects/diffs/_stats.html.haml
@@ -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}
diff --git a/app/views/projects/diffs/viewers/_image.html.haml b/app/views/projects/diffs/viewers/_image.html.haml
index aa004a739d7..01879556894 100644
--- a/app/views/projects/diffs/viewers/_image.html.haml
+++ b/app/views/projects/diffs/viewers/_image.html.haml
@@ -41,10 +41,10 @@
.swipe.view.hide
.swipe-frame
.frame.deleted
- = image_tag(old_blob_raw_path, alt: diff_file.old_path)
+ = image_tag(old_blob_raw_path, alt: diff_file.old_path, lazy: false)
.swipe-wrap
.frame.added
- = image_tag(blob_raw_path, alt: diff_file.new_path)
+ = image_tag(blob_raw_path, alt: diff_file.new_path, lazy: false)
%span.swipe-bar
%span.top-handle
%span.bottom-handle
@@ -52,9 +52,9 @@
.onion-skin.view.hide
.onion-skin-frame
.frame.deleted
- = image_tag(old_blob_raw_path, alt: diff_file.old_path)
+ = image_tag(old_blob_raw_path, alt: diff_file.old_path, lazy: false)
.frame.added
- = image_tag(blob_raw_path, alt: diff_file.new_path)
+ = image_tag(blob_raw_path, alt: diff_file.new_path, lazy: false)
.controls
.transparent
.drag-track
diff --git a/app/views/projects/runners/_form.html.haml b/app/views/projects/runners/_form.html.haml
index ac8e15a48b2..e660fce652f 100644
--- a/app/views/projects/runners/_form.html.haml
+++ b/app/views/projects/runners/_form.html.haml
@@ -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
@@ -39,6 +39,6 @@
Tags
.col-sm-10
= f.text_field :tag_list, value: runner.tag_list.sort.join(', '), class: 'form-control'
- .help-block You can setup jobs to only use Runners with specific tags
+ .help-block You can setup jobs to only use Runners with specific tags. Separate tags with commas.
.form-actions
= f.submit 'Save changes', class: 'btn btn-save'
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index c4ed7f6e750..d3f0aa2d339 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -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
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 0afa48b392c..9cae3f51825 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -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